Geolocation control (#4327)
* added static map field * leaflet draw plugin added to map control * Editable layer working no icons * Save and load data in form field * images and icons working * Locate plugin added, and loads on geolocation * organized map control code * loads layer as per form * new form clears editableLayers * update: leaflet 1.2.0 and leaflet-draw 0.4.2 * changed to ERPNext colors * Multiple map fields and fixes * fixes codacy suggestions * loaded geojson data editable * Map layers editable * add only one layer on draw:create * code organized with helper functions * Replaced Leaflet Draw with Leaflet Editable * read values from database * layers saved in db * Using Patched Leaflet Draw (mobile-friendly) * Working Map field with multiple forms open * Leaflet Draw css image path fix * Leaflet Draw Fixes Leaflet draw css image paths Fixes Circle marker and Circle * locate to geolocation or set featurecollection as center * [fix] leaflet fitBounds padding 50,50 * [Fix] Leaflet (auto) locate * Map field basic test * added refresh button on map * Mute map geojson in print format * renamed Map field to Geolocation * Suggested changes and fixes * eslint disable * csslint allow important
|
|
@ -119,6 +119,7 @@
|
||||||
"getCookies": true,
|
"getCookies": true,
|
||||||
"get_url_arg": true,
|
"get_url_arg": true,
|
||||||
"QUnit": true,
|
"QUnit": true,
|
||||||
"JsBarcode": true
|
"JsBarcode": true,
|
||||||
|
"L": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "fieldtype",
|
"oldfieldname": "fieldtype",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nHeading\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nText\nText Editor\nTime\nSignature",
|
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nText\nText Editor\nTime\nSignature",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
|
|
@ -1364,7 +1364,7 @@
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2017-10-07 19:20:15.888708",
|
"modified": "2017-10-24 11:39:56.795852",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Core",
|
"module": "Core",
|
||||||
"name": "DocField",
|
"name": "DocField",
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,7 @@
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "fieldtype",
|
"oldfieldname": "fieldtype",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "Attach\nAttach Image\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nText\nText Editor\nTime\nSignature",
|
"options": "Attach\nAttach Image\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nGeolocation\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nText\nText Editor\nTime\nSignature",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
|
|
@ -1161,7 +1161,7 @@
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2017-07-06 17:23:43.835189",
|
"modified": "2017-10-24 11:40:37.986457",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Custom",
|
"module": "Custom",
|
||||||
"name": "Custom Field",
|
"name": "Custom Field",
|
||||||
|
|
|
||||||
23
frappe/custom/doctype/custom_field/test_custom_field.js
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
// rename this file from _test_[name] to test_[name] to activate
|
||||||
|
// and remove above this line
|
||||||
|
|
||||||
|
QUnit.test("test: Custom Field", function (assert) {
|
||||||
|
let done = assert.async();
|
||||||
|
|
||||||
|
// number of asserts
|
||||||
|
assert.expect(1);
|
||||||
|
|
||||||
|
frappe.run_serially([
|
||||||
|
// insert a new Custom Field
|
||||||
|
() => frappe.tests.make('Custom Field', [
|
||||||
|
// values to be set
|
||||||
|
{key: 'value'}
|
||||||
|
]),
|
||||||
|
() => {
|
||||||
|
assert.equal(cur_frm.doc.key, 'value');
|
||||||
|
},
|
||||||
|
() => done()
|
||||||
|
]);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -66,7 +66,7 @@ docfield_properties = {
|
||||||
|
|
||||||
allowed_fieldtype_change = (('Currency', 'Float', 'Percent'), ('Small Text', 'Data'),
|
allowed_fieldtype_change = (('Currency', 'Float', 'Percent'), ('Small Text', 'Data'),
|
||||||
('Text', 'Data'), ('Text', 'Text Editor', 'Code', 'Signature'), ('Data', 'Select'),
|
('Text', 'Data'), ('Text', 'Text Editor', 'Code', 'Signature'), ('Data', 'Select'),
|
||||||
('Text', 'Small Text'), ('Text', 'Data', 'Barcode'))
|
('Text', 'Small Text'), ('Text', 'Data', 'Barcode'), ('Code', 'Geolocation'))
|
||||||
|
|
||||||
allowed_fieldtype_for_options_change = ('Read Only', 'HTML', 'Select',)
|
allowed_fieldtype_for_options_change = ('Read Only', 'HTML', 'Select',)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "fieldtype",
|
"oldfieldname": "fieldtype",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nHeading\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSignature\nSmall Text\nTable\nText\nText Editor\nTime",
|
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSignature\nSmall Text\nTable\nText\nText Editor\nTime",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
|
|
@ -1202,7 +1202,7 @@
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2017-10-11 06:45:20.172291",
|
"modified": "2017-10-24 11:41:31.075929",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Custom",
|
"module": "Custom",
|
||||||
"name": "Customize Form Field",
|
"name": "Customize Form Field",
|
||||||
|
|
|
||||||
|
|
@ -26,25 +26,26 @@ type_map = {
|
||||||
,'Float': ('decimal', '18,6')
|
,'Float': ('decimal', '18,6')
|
||||||
,'Percent': ('decimal', '18,6')
|
,'Percent': ('decimal', '18,6')
|
||||||
,'Check': ('int', '1')
|
,'Check': ('int', '1')
|
||||||
,'Small Text': ('text', '')
|
,'Small Text': ('text', '')
|
||||||
,'Long Text': ('longtext', '')
|
,'Long Text': ('longtext', '')
|
||||||
,'Code': ('longtext', '')
|
,'Code': ('longtext', '')
|
||||||
,'Text Editor': ('longtext', '')
|
,'Text Editor': ('longtext', '')
|
||||||
,'Date': ('date', '')
|
,'Date': ('date', '')
|
||||||
,'Datetime': ('datetime', '6')
|
,'Datetime': ('datetime', '6')
|
||||||
,'Time': ('time', '6')
|
,'Time': ('time', '6')
|
||||||
,'Text': ('text', '')
|
,'Text': ('text', '')
|
||||||
,'Data': ('varchar', varchar_len)
|
,'Data': ('varchar', varchar_len)
|
||||||
,'Link': ('varchar', varchar_len)
|
,'Link': ('varchar', varchar_len)
|
||||||
,'Dynamic Link':('varchar', varchar_len)
|
,'Dynamic Link': ('varchar', varchar_len)
|
||||||
,'Password': ('varchar', varchar_len)
|
,'Password': ('varchar', varchar_len)
|
||||||
,'Select': ('varchar', varchar_len)
|
,'Select': ('varchar', varchar_len)
|
||||||
,'Read Only': ('varchar', varchar_len)
|
,'Read Only': ('varchar', varchar_len)
|
||||||
,'Attach': ('text', '')
|
,'Attach': ('text', '')
|
||||||
,'Attach Image':('text', '')
|
,'Attach Image': ('text', '')
|
||||||
,'Signature': ('longtext', '')
|
,'Signature': ('longtext', '')
|
||||||
,'Color': ('varchar', varchar_len)
|
,'Color': ('varchar', varchar_len)
|
||||||
,'Barcode': ('longtext', '')
|
,'Barcode': ('longtext', '')
|
||||||
|
,'Geolocation': ('longtext', '')
|
||||||
}
|
}
|
||||||
|
|
||||||
default_columns = ['name', 'creation', 'modified', 'modified_by', 'owner',
|
default_columns = ['name', 'creation', 'modified', 'modified_by', 'owner',
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,8 @@
|
||||||
"public/js/frappe/form/controls/html.js",
|
"public/js/frappe/form/controls/html.js",
|
||||||
"public/js/frappe/form/controls/heading.js",
|
"public/js/frappe/form/controls/heading.js",
|
||||||
"public/js/frappe/form/controls/autocomplete.js",
|
"public/js/frappe/form/controls/autocomplete.js",
|
||||||
"public/js/frappe/form/controls/barcode.js"
|
"public/js/frappe/form/controls/barcode.js",
|
||||||
|
"public/js/frappe/form/controls/geolocation.js"
|
||||||
],
|
],
|
||||||
"js/dialog.min.js": [
|
"js/dialog.min.js": [
|
||||||
"public/js/frappe/dom.js",
|
"public/js/frappe/dom.js",
|
||||||
|
|
@ -94,12 +95,17 @@
|
||||||
"public/js/frappe/form/controls/read_only.js",
|
"public/js/frappe/form/controls/read_only.js",
|
||||||
"public/js/frappe/form/controls/button.js",
|
"public/js/frappe/form/controls/button.js",
|
||||||
"public/js/frappe/form/controls/html.js",
|
"public/js/frappe/form/controls/html.js",
|
||||||
"public/js/frappe/form/controls/heading.js"
|
"public/js/frappe/form/controls/heading.js",
|
||||||
|
"public/js/frappe/form/controls/geolocation.js"
|
||||||
],
|
],
|
||||||
"css/desk.min.css": [
|
"css/desk.min.css": [
|
||||||
"public/js/lib/datepicker/datepicker.min.css",
|
"public/js/lib/datepicker/datepicker.min.css",
|
||||||
"public/js/lib/awesomplete/awesomplete.css",
|
"public/js/lib/awesomplete/awesomplete.css",
|
||||||
"public/js/lib/summernote/summernote.css",
|
"public/js/lib/summernote/summernote.css",
|
||||||
|
"public/js/lib/leaflet/leaflet.css",
|
||||||
|
"public/js/lib/leaflet/leaflet.draw.css",
|
||||||
|
"public/js/lib/leaflet/L.Control.Locate.css",
|
||||||
|
"public/js/lib/leaflet/easy-button.css",
|
||||||
"public/css/bootstrap.css",
|
"public/css/bootstrap.css",
|
||||||
"public/css/font-awesome.css",
|
"public/css/font-awesome.css",
|
||||||
"public/css/octicons/octicons.css",
|
"public/css/octicons/octicons.css",
|
||||||
|
|
@ -137,7 +143,11 @@
|
||||||
"public/js/lib/datepicker/datepicker.min.js",
|
"public/js/lib/datepicker/datepicker.min.js",
|
||||||
"public/js/lib/datepicker/locale-all.js",
|
"public/js/lib/datepicker/locale-all.js",
|
||||||
"public/js/lib/jquery.jrumble.min.js",
|
"public/js/lib/jquery.jrumble.min.js",
|
||||||
"public/js/lib/webcam.min.js"
|
"public/js/lib/webcam.min.js",
|
||||||
|
"public/js/lib/leaflet/leaflet.js",
|
||||||
|
"public/js/lib/leaflet/leaflet.draw.js",
|
||||||
|
"public/js/lib/leaflet/L.Control.Locate.js",
|
||||||
|
"public/js/lib/leaflet/easy-button.js"
|
||||||
],
|
],
|
||||||
"js/desk.min.js": [
|
"js/desk.min.js": [
|
||||||
"public/js/frappe/class.js",
|
"public/js/frappe/class.js",
|
||||||
|
|
|
||||||
BIN
frappe/public/images/leaflet/layers-2x.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
frappe/public/images/leaflet/layers.png
Normal file
|
After Width: | Height: | Size: 696 B |
BIN
frappe/public/images/leaflet/leafletmarker-icon.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
frappe/public/images/leaflet/leafletmarker-shadow.png
Normal file
|
After Width: | Height: | Size: 618 B |
BIN
frappe/public/images/leaflet/lego.png
Normal file
|
After Width: | Height: | Size: 221 KiB |
BIN
frappe/public/images/leaflet/marker-icon-2x.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
frappe/public/images/leaflet/marker-icon.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
frappe/public/images/leaflet/marker-shadow.png
Normal file
|
After Width: | Height: | Size: 618 B |
BIN
frappe/public/images/leaflet/spritesheet-2x.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
frappe/public/images/leaflet/spritesheet.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
156
frappe/public/images/leaflet/spritesheet.svg
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
viewBox="0 0 600 60"
|
||||||
|
height="60"
|
||||||
|
width="600"
|
||||||
|
id="svg4225"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.91 r13725"
|
||||||
|
sodipodi:docname="spritesheet.svg"
|
||||||
|
inkscape:export-filename="/home/fpuga/development/upstream/icarto.Leaflet.draw/src/images/spritesheet-2x.png"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-ydpi="90">
|
||||||
|
<metadata
|
||||||
|
id="metadata4258">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs4256" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1056"
|
||||||
|
id="namedview4254"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.3101852"
|
||||||
|
inkscape:cx="237.56928"
|
||||||
|
inkscape:cy="7.2419621"
|
||||||
|
inkscape:window-x="1920"
|
||||||
|
inkscape:window-y="24"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg4225" />
|
||||||
|
<g
|
||||||
|
id="enabled"
|
||||||
|
style="fill:#464646;fill-opacity:1">
|
||||||
|
<g
|
||||||
|
id="polyline"
|
||||||
|
style="fill:#464646;fill-opacity:1">
|
||||||
|
<path
|
||||||
|
d="m 18,36 0,6 6,0 0,-6 -6,0 z m 4,4 -2,0 0,-2 2,0 0,2 z"
|
||||||
|
id="path4229"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#464646;fill-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="m 36,18 0,6 6,0 0,-6 -6,0 z m 4,4 -2,0 0,-2 2,0 0,2 z"
|
||||||
|
id="path4231"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#464646;fill-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="m 23.142,39.145 -2.285,-2.29 16,-15.998 2.285,2.285 z"
|
||||||
|
id="path4233"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#464646;fill-opacity:1" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
id="polygon"
|
||||||
|
d="M 100,24.565 97.904,39.395 83.07,42 76,28.773 86.463,18 Z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#464646;fill-opacity:1" />
|
||||||
|
<path
|
||||||
|
id="rectangle"
|
||||||
|
d="m 140,20 20,0 0,20 -20,0 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#464646;fill-opacity:1" />
|
||||||
|
<path
|
||||||
|
id="circle"
|
||||||
|
d="m 221,30 c 0,6.078 -4.926,11 -11,11 -6.074,0 -11,-4.922 -11,-11 0,-6.074 4.926,-11 11,-11 6.074,0 11,4.926 11,11 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#464646;fill-opacity:1" />
|
||||||
|
<path
|
||||||
|
id="marker"
|
||||||
|
d="m 270,19 c -4.971,0 -9,4.029 -9,9 0,4.971 5.001,12 9,14 4.001,-2 9,-9.029 9,-14 0,-4.971 -4.029,-9 -9,-9 z m 0,12.5 c -2.484,0 -4.5,-2.014 -4.5,-4.5 0,-2.484 2.016,-4.5 4.5,-4.5 2.485,0 4.5,2.016 4.5,4.5 0,2.486 -2.015,4.5 -4.5,4.5 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#464646;fill-opacity:1" />
|
||||||
|
<g
|
||||||
|
id="edit"
|
||||||
|
style="fill:#464646;fill-opacity:1">
|
||||||
|
<path
|
||||||
|
d="m 337,30.156 0,0.407 0,5.604 c 0,1.658 -1.344,3 -3,3 l -10,0 c -1.655,0 -3,-1.342 -3,-3 l 0,-10 c 0,-1.657 1.345,-3 3,-3 l 6.345,0 3.19,-3.17 -9.535,0 c -3.313,0 -6,2.687 -6,6 l 0,10 c 0,3.313 2.687,6 6,6 l 10,0 c 3.314,0 6,-2.687 6,-6 l 0,-8.809 -3,2.968"
|
||||||
|
id="path4240"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#464646;fill-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="m 338.72,24.637 -8.892,8.892 -2.828,0 0,-2.829 8.89,-8.89 z"
|
||||||
|
id="path4242"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#464646;fill-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="m 338.697,17.826 4,0 0,4 -4,0 z"
|
||||||
|
transform="matrix(-0.70698336,-0.70723018,0.70723018,-0.70698336,567.55917,274.78273)"
|
||||||
|
id="path4244"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#464646;fill-opacity:1" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="remove"
|
||||||
|
style="fill:#464646;fill-opacity:1">
|
||||||
|
<path
|
||||||
|
d="m 381,42 18,0 0,-18 -18,0 0,18 z m 14,-16 2,0 0,14 -2,0 0,-14 z m -4,0 2,0 0,14 -2,0 0,-14 z m -4,0 2,0 0,14 -2,0 0,-14 z m -4,0 2,0 0,14 -2,0 0,-14 z"
|
||||||
|
id="path4247"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#464646;fill-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="m 395,20 0,-4 -10,0 0,4 -6,0 0,2 22,0 0,-2 -6,0 z m -2,0 -6,0 0,-2 6,0 0,2 z"
|
||||||
|
id="path4249"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#464646;fill-opacity:1" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="disabled"
|
||||||
|
transform="translate(120,0)"
|
||||||
|
style="fill:#bbbbbb">
|
||||||
|
<use
|
||||||
|
xlink:href="#edit"
|
||||||
|
id="edit-disabled"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="100%"
|
||||||
|
height="100%" />
|
||||||
|
<use
|
||||||
|
xlink:href="#remove"
|
||||||
|
id="remove-disabled"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="100%"
|
||||||
|
height="100%" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#464646;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="circle-3"
|
||||||
|
d="m 581.65725,30 c 0,6.078 -4.926,11 -11,11 -6.074,0 -11,-4.922 -11,-11 0,-6.074 4.926,-11 11,-11 6.074,0 11,4.926 11,11 z"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 5.4 KiB |
|
|
@ -10,6 +10,11 @@ frappe.dom = {
|
||||||
by_id: function(id) {
|
by_id: function(id) {
|
||||||
return document.getElementById(id);
|
return document.getElementById(id);
|
||||||
},
|
},
|
||||||
|
get_unique_id: function() {
|
||||||
|
const id = 'unique-' + frappe.dom.id_count;
|
||||||
|
frappe.dom.id_count++;
|
||||||
|
return id;
|
||||||
|
},
|
||||||
set_unique_id: function(ele) {
|
set_unique_id: function(ele) {
|
||||||
var $ele = $(ele);
|
var $ele = $(ele);
|
||||||
if($ele.attr('id')) {
|
if($ele.attr('id')) {
|
||||||
|
|
|
||||||
183
frappe/public/js/frappe/form/controls/geolocation.js
Normal file
|
|
@ -0,0 +1,183 @@
|
||||||
|
frappe.ui.form.ControlGeolocation = frappe.ui.form.ControlData.extend({
|
||||||
|
make_wrapper() {
|
||||||
|
// Create the elements for map area
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
let $input_wrapper = this.$wrapper.find('.control-input-wrapper');
|
||||||
|
this.map_id = frappe.dom.get_unique_id();
|
||||||
|
this.map_area = $(
|
||||||
|
`<div class="map-wrapper border">
|
||||||
|
<div id="` + this.map_id + `" style="min-height: 400px; z-index: 1; max-width:100%"></div>
|
||||||
|
</div>`
|
||||||
|
);
|
||||||
|
this.map_area.prependTo($input_wrapper);
|
||||||
|
this.$wrapper.find('.control-input').addClass("hidden");
|
||||||
|
this.bind_leaflet_map();
|
||||||
|
this.bind_leaflet_draw_control();
|
||||||
|
this.bind_leaflet_locate_control();
|
||||||
|
this.bind_leaflet_refresh_button();
|
||||||
|
},
|
||||||
|
|
||||||
|
format_for_input(value) {
|
||||||
|
// render raw value from db into map
|
||||||
|
this.clear_editable_layers();
|
||||||
|
if(value) {
|
||||||
|
var data_layers = new L.FeatureGroup()
|
||||||
|
.addLayer(L.geoJson(JSON.parse(value),{
|
||||||
|
pointToLayer: function(geoJsonPoint, latlng) {
|
||||||
|
if (geoJsonPoint.properties.point_type == "circle"){
|
||||||
|
return L.circle(latlng, {radius: geoJsonPoint.properties.radius});
|
||||||
|
} else if (geoJsonPoint.properties.point_type == "circlemarker") {
|
||||||
|
return L.circleMarker(latlng, {radius: geoJsonPoint.properties.radius});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return L.marker(latlng);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
this.add_non_group_layers(data_layers, this.editableLayers);
|
||||||
|
try {
|
||||||
|
this.map.flyToBounds(this.editableLayers.getBounds(), {
|
||||||
|
padding: [50,50]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
// suppress error if layer has a point.
|
||||||
|
}
|
||||||
|
this.editableLayers.addTo(this.map);
|
||||||
|
} else if ((value===undefined) || (value == JSON.stringify(new L.FeatureGroup().toGeoJSON()))) {
|
||||||
|
this.locate_control.start();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
bind_leaflet_map() {
|
||||||
|
|
||||||
|
var circleToGeoJSON = L.Circle.prototype.toGeoJSON;
|
||||||
|
L.Circle.include({
|
||||||
|
toGeoJSON: function() {
|
||||||
|
var feature = circleToGeoJSON.call(this);
|
||||||
|
feature.properties = {
|
||||||
|
point_type: 'circle',
|
||||||
|
radius: this.getRadius()
|
||||||
|
};
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
L.CircleMarker.include({
|
||||||
|
toGeoJSON: function() {
|
||||||
|
var feature = circleToGeoJSON.call(this);
|
||||||
|
feature.properties = {
|
||||||
|
point_type: 'circlemarker',
|
||||||
|
radius: this.getRadius()
|
||||||
|
};
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
L.Icon.Default.imagePath = '/assets/frappe/images/leaflet/';
|
||||||
|
this.map = L.map(this.map_id).setView([19.0800, 72.8961], 13);
|
||||||
|
|
||||||
|
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
|
||||||
|
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
}).addTo(this.map);
|
||||||
|
},
|
||||||
|
|
||||||
|
bind_leaflet_locate_control() {
|
||||||
|
// To request location update and set location, sets current geolocation on load
|
||||||
|
this.locate_control = L.control.locate({position:'topright'});
|
||||||
|
this.locate_control.addTo(this.map);
|
||||||
|
},
|
||||||
|
|
||||||
|
bind_leaflet_draw_control() {
|
||||||
|
this.editableLayers = new L.FeatureGroup();
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
position: 'topleft',
|
||||||
|
draw: {
|
||||||
|
polyline: {
|
||||||
|
shapeOptions: {
|
||||||
|
color: frappe.ui.color.get('blue'),
|
||||||
|
weight: 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
polygon: {
|
||||||
|
allowIntersection: false, // Restricts shapes to simple polygons
|
||||||
|
drawError: {
|
||||||
|
color: frappe.ui.color.get('orange'), // Color the shape will turn when intersects
|
||||||
|
message: '<strong>Oh snap!<strong> you can\'t draw that!' // Message that will show when intersect
|
||||||
|
},
|
||||||
|
shapeOptions: {
|
||||||
|
color: frappe.ui.color.get('blue')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
circle: true,
|
||||||
|
rectangle: {
|
||||||
|
shapeOptions: {
|
||||||
|
clickable: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
featureGroup: this.editableLayers, //REQUIRED!!
|
||||||
|
remove: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// create control and add to map
|
||||||
|
var drawControl = new L.Control.Draw(options);
|
||||||
|
|
||||||
|
this.map.addControl(drawControl);
|
||||||
|
|
||||||
|
this.map.on('draw:created', (e) => {
|
||||||
|
var type = e.layerType,
|
||||||
|
layer = e.layer;
|
||||||
|
if (type === 'marker') {
|
||||||
|
layer.bindPopup('Marker');
|
||||||
|
}
|
||||||
|
this.editableLayers.addLayer(layer);
|
||||||
|
this.set_value(JSON.stringify(this.editableLayers.toGeoJSON()));
|
||||||
|
});
|
||||||
|
|
||||||
|
this.map.on('draw:deleted draw:edited', (e) => {
|
||||||
|
var layer = e.layer;
|
||||||
|
this.editableLayers.removeLayer(layer);
|
||||||
|
this.set_value(JSON.stringify(this.editableLayers.toGeoJSON()));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
bind_leaflet_refresh_button() {
|
||||||
|
L.easyButton({
|
||||||
|
id: 'refresh-map-'+this.df.fieldname,
|
||||||
|
position: 'topright',
|
||||||
|
type: 'replace',
|
||||||
|
leafletClasses: true,
|
||||||
|
states:[{
|
||||||
|
stateName: 'refresh-map',
|
||||||
|
onClick: function(button, map){
|
||||||
|
map._onResize();
|
||||||
|
},
|
||||||
|
title: 'Refresh map',
|
||||||
|
icon: 'fa fa-refresh'
|
||||||
|
}]
|
||||||
|
}).addTo(this.map);
|
||||||
|
},
|
||||||
|
|
||||||
|
add_non_group_layers(source_layer, target_group) {
|
||||||
|
// https://gis.stackexchange.com/a/203773
|
||||||
|
// Would benefit from https://github.com/Leaflet/Leaflet/issues/4461
|
||||||
|
if (source_layer instanceof L.LayerGroup) {
|
||||||
|
source_layer.eachLayer((layer)=>{
|
||||||
|
this.add_non_group_layers(layer, target_group);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
target_group.addLayer(source_layer);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
clear_editable_layers() {
|
||||||
|
this.editableLayers.eachLayer((l)=>{
|
||||||
|
this.editableLayers.removeLayer(l);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
12
frappe/public/js/lib/leaflet/L.Control.Locate.css
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
/* Compatible with Leaflet 0.7 */
|
||||||
|
.leaflet-control-locate a {
|
||||||
|
font-size: 1.4em;
|
||||||
|
color: #444;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.leaflet-control-locate.active a {
|
||||||
|
color: #2074B6;
|
||||||
|
}
|
||||||
|
.leaflet-control-locate.active.following a {
|
||||||
|
color: #FC8428;
|
||||||
|
}
|
||||||
591
frappe/public/js/lib/leaflet/L.Control.Locate.js
Normal file
|
|
@ -0,0 +1,591 @@
|
||||||
|
/*!
|
||||||
|
Copyright (c) 2016 Dominik Moritz
|
||||||
|
|
||||||
|
This file is part of the leaflet locate control. It is licensed under the MIT license.
|
||||||
|
You can find the project at: https://github.com/domoritz/leaflet-locatecontrol
|
||||||
|
*/
|
||||||
|
(function (factory, window) {
|
||||||
|
// see https://github.com/Leaflet/Leaflet/blob/master/PLUGIN-GUIDE.md#module-loaders
|
||||||
|
// for details on how to structure a leaflet plugin.
|
||||||
|
|
||||||
|
// define an AMD module that relies on 'leaflet'
|
||||||
|
if (typeof define === 'function' && define.amd) {
|
||||||
|
define(['leaflet'], factory);
|
||||||
|
|
||||||
|
// define a Common JS module that relies on 'leaflet'
|
||||||
|
} else if (typeof exports === 'object') {
|
||||||
|
if (typeof window !== 'undefined' && window.L) {
|
||||||
|
module.exports = factory(L);
|
||||||
|
} else {
|
||||||
|
module.exports = factory(require('leaflet'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// attach your plugin to the global 'L' variable
|
||||||
|
if (typeof window !== 'undefined' && window.L){
|
||||||
|
window.L.Control.Locate = factory(L);
|
||||||
|
}
|
||||||
|
} (function (L) {
|
||||||
|
var LDomUtilApplyClassesMethod = function(method, element, classNames) {
|
||||||
|
classNames = classNames.split(' ');
|
||||||
|
classNames.forEach(function(className) {
|
||||||
|
L.DomUtil[method].call(this, element, className);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var addClasses = function(el, names) { LDomUtilApplyClassesMethod('addClass', el, names); };
|
||||||
|
var removeClasses = function(el, names) { LDomUtilApplyClassesMethod('removeClass', el, names); };
|
||||||
|
|
||||||
|
var LocateControl = L.Control.extend({
|
||||||
|
options: {
|
||||||
|
/** Position of the control */
|
||||||
|
position: 'topleft',
|
||||||
|
/** The layer that the user's location should be drawn on. By default creates a new layer. */
|
||||||
|
layer: undefined,
|
||||||
|
/**
|
||||||
|
* Automatically sets the map view (zoom and pan) to the user's location as it updates.
|
||||||
|
* While the map is following the user's location, the control is in the `following` state,
|
||||||
|
* which changes the style of the control and the circle marker.
|
||||||
|
*
|
||||||
|
* Possible values:
|
||||||
|
* - false: never updates the map view when location changes.
|
||||||
|
* - 'once': set the view when the location is first determined
|
||||||
|
* - 'always': always updates the map view when location changes.
|
||||||
|
* The map view follows the users location.
|
||||||
|
* - 'untilPan': (default) like 'always', except stops updating the
|
||||||
|
* view if the user has manually panned the map.
|
||||||
|
* The map view follows the users location until she pans.
|
||||||
|
*/
|
||||||
|
setView: 'untilPan',
|
||||||
|
/** Keep the current map zoom level when setting the view and only pan. */
|
||||||
|
keepCurrentZoomLevel: false,
|
||||||
|
/** Smooth pan and zoom to the location of the marker. Only works in Leaflet 1.0+. */
|
||||||
|
flyTo: false,
|
||||||
|
/**
|
||||||
|
* The user location can be inside and outside the current view when the user clicks on the
|
||||||
|
* control that is already active. Both cases can be configures separately.
|
||||||
|
* Possible values are:
|
||||||
|
* - 'setView': zoom and pan to the current location
|
||||||
|
* - 'stop': stop locating and remove the location marker
|
||||||
|
*/
|
||||||
|
clickBehavior: {
|
||||||
|
/** What should happen if the user clicks on the control while the location is within the current view. */
|
||||||
|
inView: 'stop',
|
||||||
|
/** What should happen if the user clicks on the control while the location is outside the current view. */
|
||||||
|
outOfView: 'setView',
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* If set, save the map bounds just before centering to the user's
|
||||||
|
* location. When control is disabled, set the view back to the
|
||||||
|
* bounds that were saved.
|
||||||
|
*/
|
||||||
|
returnToPrevBounds: false,
|
||||||
|
/**
|
||||||
|
* Keep a cache of the location after the user deactivates the control. If set to false, the user has to wait
|
||||||
|
* until the locate API returns a new location before they see where they are again.
|
||||||
|
*/
|
||||||
|
cacheLocation: true,
|
||||||
|
/** If set, a circle that shows the location accuracy is drawn. */
|
||||||
|
drawCircle: true,
|
||||||
|
/** If set, the marker at the users' location is drawn. */
|
||||||
|
drawMarker: true,
|
||||||
|
/** The class to be used to create the marker. For example L.CircleMarker or L.Marker */
|
||||||
|
markerClass: L.CircleMarker,
|
||||||
|
/** Accuracy circle style properties. */
|
||||||
|
circleStyle: {
|
||||||
|
color: '#136AEC',
|
||||||
|
fillColor: '#136AEC',
|
||||||
|
fillOpacity: 0.15,
|
||||||
|
weight: 2,
|
||||||
|
opacity: 0.5
|
||||||
|
},
|
||||||
|
/** Inner marker style properties. Only works if your marker class supports `setStyle`. */
|
||||||
|
markerStyle: {
|
||||||
|
color: '#136AEC',
|
||||||
|
fillColor: '#2A93EE',
|
||||||
|
fillOpacity: 0.7,
|
||||||
|
weight: 2,
|
||||||
|
opacity: 0.9,
|
||||||
|
radius: 5
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Changes to accuracy circle and inner marker while following.
|
||||||
|
* It is only necessary to provide the properties that should change.
|
||||||
|
*/
|
||||||
|
followCircleStyle: {},
|
||||||
|
followMarkerStyle: {
|
||||||
|
// color: '#FFA500',
|
||||||
|
// fillColor: '#FFB000'
|
||||||
|
},
|
||||||
|
/** The CSS class for the icon. For example fa-location-arrow or fa-map-marker */
|
||||||
|
icon: 'fa fa-map-marker',
|
||||||
|
iconLoading: 'fa fa-spinner fa-spin',
|
||||||
|
/** The element to be created for icons. For example span or i */
|
||||||
|
iconElementTag: 'span',
|
||||||
|
/** Padding around the accuracy circle. */
|
||||||
|
circlePadding: [0, 0],
|
||||||
|
/** Use metric units. */
|
||||||
|
metric: true,
|
||||||
|
/**
|
||||||
|
* This callback can be used in case you would like to override button creation behavior.
|
||||||
|
* This is useful for DOM manipulation frameworks such as angular etc.
|
||||||
|
* This function should return an object with HtmlElement for the button (link property) and the icon (icon property).
|
||||||
|
*/
|
||||||
|
createButtonCallback: function (container, options) {
|
||||||
|
var link = L.DomUtil.create('a', 'leaflet-bar-part leaflet-bar-part-single', container);
|
||||||
|
link.title = options.strings.title;
|
||||||
|
var icon = L.DomUtil.create(options.iconElementTag, options.icon, link);
|
||||||
|
return { link: link, icon: icon };
|
||||||
|
},
|
||||||
|
/** This event is called in case of any location error that is not a time out error. */
|
||||||
|
onLocationError: function(err, control) {
|
||||||
|
alert(err.message);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* This even is called when the user's location is outside the bounds set on the map.
|
||||||
|
* The event is called repeatedly when the location changes.
|
||||||
|
*/
|
||||||
|
onLocationOutsideMapBounds: function(control) {
|
||||||
|
control.stop();
|
||||||
|
alert(control.options.strings.outsideMapBoundsMsg);
|
||||||
|
},
|
||||||
|
/** Display a pop-up when the user click on the inner marker. */
|
||||||
|
showPopup: true,
|
||||||
|
strings: {
|
||||||
|
title: "Show me where I am",
|
||||||
|
metersUnit: "meters",
|
||||||
|
feetUnit: "feet",
|
||||||
|
popup: "You are within {distance} {unit} from this point",
|
||||||
|
outsideMapBoundsMsg: "You seem located outside the boundaries of the map"
|
||||||
|
},
|
||||||
|
/** The default options passed to leaflets locate method. */
|
||||||
|
locateOptions: {
|
||||||
|
maxZoom: Infinity,
|
||||||
|
watch: true, // if you overwrite this, visualization cannot be updated
|
||||||
|
setView: false // have to set this to false because we have to
|
||||||
|
// do setView manually
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize: function (options) {
|
||||||
|
// set default options if nothing is set (merge one step deep)
|
||||||
|
for (var i in options) {
|
||||||
|
if (typeof this.options[i] === 'object') {
|
||||||
|
L.extend(this.options[i], options[i]);
|
||||||
|
} else {
|
||||||
|
this.options[i] = options[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// extend the follow marker style and circle from the normal style
|
||||||
|
this.options.followMarkerStyle = L.extend({}, this.options.markerStyle, this.options.followMarkerStyle);
|
||||||
|
this.options.followCircleStyle = L.extend({}, this.options.circleStyle, this.options.followCircleStyle);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add control to map. Returns the container for the control.
|
||||||
|
*/
|
||||||
|
onAdd: function (map) {
|
||||||
|
var container = L.DomUtil.create('div',
|
||||||
|
'leaflet-control-locate leaflet-bar leaflet-control');
|
||||||
|
|
||||||
|
this._layer = this.options.layer || new L.LayerGroup();
|
||||||
|
this._layer.addTo(map);
|
||||||
|
this._event = undefined;
|
||||||
|
this._prevBounds = null;
|
||||||
|
|
||||||
|
var linkAndIcon = this.options.createButtonCallback(container, this.options);
|
||||||
|
this._link = linkAndIcon.link;
|
||||||
|
this._icon = linkAndIcon.icon;
|
||||||
|
|
||||||
|
L.DomEvent
|
||||||
|
.on(this._link, 'click', L.DomEvent.stopPropagation)
|
||||||
|
.on(this._link, 'click', L.DomEvent.preventDefault)
|
||||||
|
.on(this._link, 'click', this._onClick, this)
|
||||||
|
.on(this._link, 'dblclick', L.DomEvent.stopPropagation);
|
||||||
|
|
||||||
|
this._resetVariables();
|
||||||
|
|
||||||
|
this._map.on('unload', this._unload, this);
|
||||||
|
|
||||||
|
return container;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when the user clicks on the control.
|
||||||
|
*/
|
||||||
|
_onClick: function() {
|
||||||
|
this._justClicked = true;
|
||||||
|
this._userPanned = false;
|
||||||
|
|
||||||
|
if (this._active && !this._event) {
|
||||||
|
// click while requesting
|
||||||
|
this.stop();
|
||||||
|
} else if (this._active && this._event !== undefined) {
|
||||||
|
var behavior = this._map.getBounds().contains(this._event.latlng) ?
|
||||||
|
this.options.clickBehavior.inView : this.options.clickBehavior.outOfView;
|
||||||
|
switch (behavior) {
|
||||||
|
case 'setView':
|
||||||
|
this.setView();
|
||||||
|
break;
|
||||||
|
case 'stop':
|
||||||
|
this.stop();
|
||||||
|
if (this.options.returnToPrevBounds) {
|
||||||
|
var f = this.options.flyTo ? this._map.flyToBounds : this._map.fitBounds;
|
||||||
|
f.bind(this._map)(this._prevBounds);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.options.returnToPrevBounds) {
|
||||||
|
this._prevBounds = this._map.getBounds();
|
||||||
|
}
|
||||||
|
this.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._updateContainerStyle();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the plugin:
|
||||||
|
* - activates the engine
|
||||||
|
* - draws the marker (if coordinates available)
|
||||||
|
*/
|
||||||
|
start: function() {
|
||||||
|
this._activate();
|
||||||
|
|
||||||
|
if (this._event) {
|
||||||
|
this._drawMarker(this._map);
|
||||||
|
|
||||||
|
// if we already have a location but the user clicked on the control
|
||||||
|
if (this.options.setView) {
|
||||||
|
this.setView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._updateContainerStyle();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the plugin:
|
||||||
|
* - deactivates the engine
|
||||||
|
* - reinitializes the button
|
||||||
|
* - removes the marker
|
||||||
|
*/
|
||||||
|
stop: function() {
|
||||||
|
this._deactivate();
|
||||||
|
|
||||||
|
this._cleanClasses();
|
||||||
|
this._resetVariables();
|
||||||
|
|
||||||
|
this._removeMarker();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method launches the location engine.
|
||||||
|
* It is called before the marker is updated,
|
||||||
|
* event if it does not mean that the event will be ready.
|
||||||
|
*
|
||||||
|
* Override it if you want to add more functionalities.
|
||||||
|
* It should set the this._active to true and do nothing if
|
||||||
|
* this._active is true.
|
||||||
|
*/
|
||||||
|
_activate: function() {
|
||||||
|
if (!this._active) {
|
||||||
|
this._map.locate(this.options.locateOptions);
|
||||||
|
this._active = true;
|
||||||
|
|
||||||
|
// bind event listeners
|
||||||
|
this._map.on('locationfound', this._onLocationFound, this);
|
||||||
|
this._map.on('locationerror', this._onLocationError, this);
|
||||||
|
this._map.on('dragstart', this._onDrag, this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called to stop the location engine.
|
||||||
|
*
|
||||||
|
* Override it to shutdown any functionalities you added on start.
|
||||||
|
*/
|
||||||
|
_deactivate: function() {
|
||||||
|
this._map.stopLocate();
|
||||||
|
this._active = false;
|
||||||
|
|
||||||
|
if (!this.options.cacheLocation) {
|
||||||
|
this._event = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unbind event listeners
|
||||||
|
this._map.off('locationfound', this._onLocationFound, this);
|
||||||
|
this._map.off('locationerror', this._onLocationError, this);
|
||||||
|
this._map.off('dragstart', this._onDrag, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zoom (unless we should keep the zoom level) and an to the current view.
|
||||||
|
*/
|
||||||
|
setView: function() {
|
||||||
|
this._drawMarker();
|
||||||
|
if (this._isOutsideMapBounds()) {
|
||||||
|
this._event = undefined; // clear the current location so we can get back into the bounds
|
||||||
|
this.options.onLocationOutsideMapBounds(this);
|
||||||
|
} else {
|
||||||
|
if (this.options.keepCurrentZoomLevel) {
|
||||||
|
var f = this.options.flyTo ? this._map.flyTo : this._map.panTo;
|
||||||
|
f.bind(this._map)([this._event.latitude, this._event.longitude]);
|
||||||
|
} else {
|
||||||
|
var f = this.options.flyTo ? this._map.flyToBounds : this._map.fitBounds;
|
||||||
|
f.bind(this._map)(this._event.bounds, {
|
||||||
|
padding: this.options.circlePadding,
|
||||||
|
maxZoom: this.options.locateOptions.maxZoom
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw the marker and accuracy circle on the map.
|
||||||
|
*
|
||||||
|
* Uses the event retrieved from onLocationFound from the map.
|
||||||
|
*/
|
||||||
|
_drawMarker: function() {
|
||||||
|
if (this._event.accuracy === undefined) {
|
||||||
|
this._event.accuracy = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var radius = this._event.accuracy;
|
||||||
|
var latlng = this._event.latlng;
|
||||||
|
|
||||||
|
// circle with the radius of the location's accuracy
|
||||||
|
if (this.options.drawCircle) {
|
||||||
|
var style = this._isFollowing() ? this.options.followCircleStyle : this.options.circleStyle;
|
||||||
|
|
||||||
|
if (!this._circle) {
|
||||||
|
this._circle = L.circle(latlng, radius, style).addTo(this._layer);
|
||||||
|
} else {
|
||||||
|
this._circle.setLatLng(latlng).setRadius(radius).setStyle(style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var distance, unit;
|
||||||
|
if (this.options.metric) {
|
||||||
|
distance = radius.toFixed(0);
|
||||||
|
unit = this.options.strings.metersUnit;
|
||||||
|
} else {
|
||||||
|
distance = (radius * 3.2808399).toFixed(0);
|
||||||
|
unit = this.options.strings.feetUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// small inner marker
|
||||||
|
if (this.options.drawMarker) {
|
||||||
|
var mStyle = this._isFollowing() ? this.options.followMarkerStyle : this.options.markerStyle;
|
||||||
|
if (!this._marker) {
|
||||||
|
this._marker = new this.options.markerClass(latlng, mStyle).addTo(this._layer);
|
||||||
|
} else {
|
||||||
|
this._marker.setLatLng(latlng);
|
||||||
|
// If the markerClass can be updated with setStyle, update it.
|
||||||
|
if (this._marker.setStyle) {
|
||||||
|
this._marker.setStyle(mStyle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var t = this.options.strings.popup;
|
||||||
|
if (this.options.showPopup && t && this._marker) {
|
||||||
|
this._marker
|
||||||
|
.bindPopup(L.Util.template(t, {distance: distance, unit: unit}))
|
||||||
|
._popup.setLatLng(latlng);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the marker from map.
|
||||||
|
*/
|
||||||
|
_removeMarker: function() {
|
||||||
|
this._layer.clearLayers();
|
||||||
|
this._marker = undefined;
|
||||||
|
this._circle = undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unload the plugin and all event listeners.
|
||||||
|
* Kind of the opposite of onAdd.
|
||||||
|
*/
|
||||||
|
_unload: function() {
|
||||||
|
this.stop();
|
||||||
|
this._map.off('unload', this._unload, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls deactivate and dispatches an error.
|
||||||
|
*/
|
||||||
|
_onLocationError: function(err) {
|
||||||
|
// ignore time out error if the location is watched
|
||||||
|
if (err.code == 3 && this.options.locateOptions.watch) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.stop();
|
||||||
|
this.options.onLocationError(err, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the received event and updates the marker.
|
||||||
|
*/
|
||||||
|
_onLocationFound: function(e) {
|
||||||
|
// no need to do anything if the location has not changed
|
||||||
|
if (this._event &&
|
||||||
|
(this._event.latlng.lat === e.latlng.lat &&
|
||||||
|
this._event.latlng.lng === e.latlng.lng &&
|
||||||
|
this._event.accuracy === e.accuracy)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._active) {
|
||||||
|
// we may have a stray event
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._event = e;
|
||||||
|
|
||||||
|
this._drawMarker();
|
||||||
|
this._updateContainerStyle();
|
||||||
|
|
||||||
|
switch (this.options.setView) {
|
||||||
|
case 'once':
|
||||||
|
if (this._justClicked) {
|
||||||
|
this.setView();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'untilPan':
|
||||||
|
if (!this._userPanned) {
|
||||||
|
this.setView();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'always':
|
||||||
|
this.setView();
|
||||||
|
break;
|
||||||
|
case false:
|
||||||
|
// don't set the view
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._justClicked = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the user drags. Need a separate even so we can bind and unbind even listeners.
|
||||||
|
*/
|
||||||
|
_onDrag: function() {
|
||||||
|
// only react to drags once we have a location
|
||||||
|
if (this._event) {
|
||||||
|
this._userPanned = true;
|
||||||
|
this._updateContainerStyle();
|
||||||
|
this._drawMarker();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute whether the map is following the user location with pan and zoom.
|
||||||
|
*/
|
||||||
|
_isFollowing: function() {
|
||||||
|
if (!this._active) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.setView === 'always') {
|
||||||
|
return true;
|
||||||
|
} else if (this.options.setView === 'untilPan') {
|
||||||
|
return !this._userPanned;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if location is in map bounds
|
||||||
|
*/
|
||||||
|
_isOutsideMapBounds: function() {
|
||||||
|
if (this._event === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this._map.options.maxBounds &&
|
||||||
|
!this._map.options.maxBounds.contains(this._event.latlng);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles button class between following and active.
|
||||||
|
*/
|
||||||
|
_updateContainerStyle: function() {
|
||||||
|
if (!this._container) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._active && !this._event) {
|
||||||
|
// active but don't have a location yet
|
||||||
|
this._setClasses('requesting');
|
||||||
|
} else if (this._isFollowing()) {
|
||||||
|
this._setClasses('following');
|
||||||
|
} else if (this._active) {
|
||||||
|
this._setClasses('active');
|
||||||
|
} else {
|
||||||
|
this._cleanClasses();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the CSS classes for the state.
|
||||||
|
*/
|
||||||
|
_setClasses: function(state) {
|
||||||
|
if (state == 'requesting') {
|
||||||
|
removeClasses(this._container, "active following");
|
||||||
|
addClasses(this._container, "requesting");
|
||||||
|
|
||||||
|
removeClasses(this._icon, this.options.icon);
|
||||||
|
addClasses(this._icon, this.options.iconLoading);
|
||||||
|
} else if (state == 'active') {
|
||||||
|
removeClasses(this._container, "requesting following");
|
||||||
|
addClasses(this._container, "active");
|
||||||
|
|
||||||
|
removeClasses(this._icon, this.options.iconLoading);
|
||||||
|
addClasses(this._icon, this.options.icon);
|
||||||
|
} else if (state == 'following') {
|
||||||
|
removeClasses(this._container, "requesting");
|
||||||
|
addClasses(this._container, "active following");
|
||||||
|
|
||||||
|
removeClasses(this._icon, this.options.iconLoading);
|
||||||
|
addClasses(this._icon, this.options.icon);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all classes from button.
|
||||||
|
*/
|
||||||
|
_cleanClasses: function() {
|
||||||
|
L.DomUtil.removeClass(this._container, "requesting");
|
||||||
|
L.DomUtil.removeClass(this._container, "active");
|
||||||
|
L.DomUtil.removeClass(this._container, "following");
|
||||||
|
|
||||||
|
removeClasses(this._icon, this.options.iconLoading);
|
||||||
|
addClasses(this._icon, this.options.icon);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reinitializes state variables.
|
||||||
|
*/
|
||||||
|
_resetVariables: function() {
|
||||||
|
// whether locate is active or not
|
||||||
|
this._active = false;
|
||||||
|
|
||||||
|
// true if the control was clicked for the first time
|
||||||
|
// we need this so we can pan and zoom once we have the location
|
||||||
|
this._justClicked = false;
|
||||||
|
|
||||||
|
// true if the user has panned the map after clicking the control
|
||||||
|
this._userPanned = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
L.control.locate = function (options) {
|
||||||
|
return new L.Control.Locate(options);
|
||||||
|
};
|
||||||
|
|
||||||
|
return LocateControl;
|
||||||
|
}, window));
|
||||||
56
frappe/public/js/lib/leaflet/easy-button.css
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
.leaflet-bar button,
|
||||||
|
.leaflet-bar button:hover {
|
||||||
|
background-color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
line-height: 26px;
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-bar button {
|
||||||
|
background-position: 50% 50%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
overflow: hidden;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-bar button:hover {
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-bar button:first-of-type {
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-bar button:last-of-type {
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-bar.disabled,
|
||||||
|
.leaflet-bar button.disabled {
|
||||||
|
cursor: default;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: .4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.easy-button-button .button-state{
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.leaflet-touch .leaflet-bar button {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
370
frappe/public/js/lib/leaflet/easy-button.js
Normal file
|
|
@ -0,0 +1,370 @@
|
||||||
|
(function(){
|
||||||
|
|
||||||
|
// This is for grouping buttons into a bar
|
||||||
|
// takes an array of `L.easyButton`s and
|
||||||
|
// then the usual `.addTo(map)`
|
||||||
|
L.Control.EasyBar = L.Control.extend({
|
||||||
|
|
||||||
|
options: {
|
||||||
|
position: 'topleft', // part of leaflet's defaults
|
||||||
|
id: null, // an id to tag the Bar with
|
||||||
|
leafletClasses: true // use leaflet classes?
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
initialize: function(buttons, options){
|
||||||
|
|
||||||
|
if(options){
|
||||||
|
L.Util.setOptions( this, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
this._buildContainer();
|
||||||
|
this._buttons = [];
|
||||||
|
|
||||||
|
for(var i = 0; i < buttons.length; i++){
|
||||||
|
buttons[i]._bar = this;
|
||||||
|
buttons[i]._container = buttons[i].button;
|
||||||
|
this._buttons.push(buttons[i]);
|
||||||
|
this.container.appendChild(buttons[i].button);
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
_buildContainer: function(){
|
||||||
|
this._container = this.container = L.DomUtil.create('div', '');
|
||||||
|
this.options.leafletClasses && L.DomUtil.addClass(this.container, 'leaflet-bar easy-button-container leaflet-control');
|
||||||
|
this.options.id && (this.container.id = this.options.id);
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
enable: function(){
|
||||||
|
L.DomUtil.addClass(this.container, 'enabled');
|
||||||
|
L.DomUtil.removeClass(this.container, 'disabled');
|
||||||
|
this.container.setAttribute('aria-hidden', 'false');
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
disable: function(){
|
||||||
|
L.DomUtil.addClass(this.container, 'disabled');
|
||||||
|
L.DomUtil.removeClass(this.container, 'enabled');
|
||||||
|
this.container.setAttribute('aria-hidden', 'true');
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
onAdd: function () {
|
||||||
|
return this.container;
|
||||||
|
},
|
||||||
|
|
||||||
|
addTo: function (map) {
|
||||||
|
this._map = map;
|
||||||
|
|
||||||
|
for(var i = 0; i < this._buttons.length; i++){
|
||||||
|
this._buttons[i]._map = map;
|
||||||
|
}
|
||||||
|
|
||||||
|
var container = this._container = this.onAdd(map),
|
||||||
|
pos = this.getPosition(),
|
||||||
|
corner = map._controlCorners[pos];
|
||||||
|
|
||||||
|
L.DomUtil.addClass(container, 'leaflet-control');
|
||||||
|
|
||||||
|
if (pos.indexOf('bottom') !== -1) {
|
||||||
|
corner.insertBefore(container, corner.firstChild);
|
||||||
|
} else {
|
||||||
|
corner.appendChild(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
L.easyBar = function(){
|
||||||
|
var args = [L.Control.EasyBar];
|
||||||
|
for(var i = 0; i < arguments.length; i++){
|
||||||
|
args.push( arguments[i] );
|
||||||
|
}
|
||||||
|
return new (Function.prototype.bind.apply(L.Control.EasyBar, args));
|
||||||
|
};
|
||||||
|
|
||||||
|
// L.EasyButton is the actual buttons
|
||||||
|
// can be called without being grouped into a bar
|
||||||
|
L.Control.EasyButton = L.Control.extend({
|
||||||
|
|
||||||
|
options: {
|
||||||
|
position: 'topleft', // part of leaflet's defaults
|
||||||
|
|
||||||
|
id: null, // an id to tag the button with
|
||||||
|
|
||||||
|
type: 'replace', // [(replace|animate)]
|
||||||
|
// replace swaps out elements
|
||||||
|
// animate changes classes with all elements inserted
|
||||||
|
|
||||||
|
states: [], // state names look like this
|
||||||
|
// {
|
||||||
|
// stateName: 'untracked',
|
||||||
|
// onClick: function(){ handle_nav_manually(); };
|
||||||
|
// title: 'click to make inactive',
|
||||||
|
// icon: 'fa-circle', // wrapped with <a>
|
||||||
|
// }
|
||||||
|
|
||||||
|
leafletClasses: true, // use leaflet styles for the button
|
||||||
|
tagName: 'button',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
initialize: function(icon, onClick, title, id){
|
||||||
|
|
||||||
|
// clear the states manually
|
||||||
|
this.options.states = [];
|
||||||
|
|
||||||
|
// add id to options
|
||||||
|
if(id != null){
|
||||||
|
this.options.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// storage between state functions
|
||||||
|
this.storage = {};
|
||||||
|
|
||||||
|
// is the last item an object?
|
||||||
|
if( typeof arguments[arguments.length-1] === 'object' ){
|
||||||
|
|
||||||
|
// if so, it should be the options
|
||||||
|
L.Util.setOptions( this, arguments[arguments.length-1] );
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there aren't any states in options
|
||||||
|
// use the early params
|
||||||
|
if( this.options.states.length === 0 &&
|
||||||
|
typeof icon === 'string' &&
|
||||||
|
typeof onClick === 'function'){
|
||||||
|
|
||||||
|
// turn the options object into a state
|
||||||
|
this.options.states.push({
|
||||||
|
icon: icon,
|
||||||
|
onClick: onClick,
|
||||||
|
title: typeof title === 'string' ? title : ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// curate and move user's states into
|
||||||
|
// the _states for internal use
|
||||||
|
this._states = [];
|
||||||
|
|
||||||
|
for(var i = 0; i < this.options.states.length; i++){
|
||||||
|
this._states.push( new State(this.options.states[i], this) );
|
||||||
|
}
|
||||||
|
|
||||||
|
this._buildButton();
|
||||||
|
|
||||||
|
this._activateState(this._states[0]);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
_buildButton: function(){
|
||||||
|
|
||||||
|
this.button = L.DomUtil.create(this.options.tagName, '');
|
||||||
|
|
||||||
|
if (this.options.tagName === 'button') {
|
||||||
|
this.button.setAttribute('type', 'button');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.id ){
|
||||||
|
this.button.id = this.options.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.leafletClasses){
|
||||||
|
L.DomUtil.addClass(this.button, 'easy-button-button leaflet-bar-part leaflet-interactive');
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't let double clicks and mousedown get to the map
|
||||||
|
L.DomEvent.addListener(this.button, 'dblclick', L.DomEvent.stop);
|
||||||
|
L.DomEvent.addListener(this.button, 'mousedown', L.DomEvent.stop);
|
||||||
|
|
||||||
|
// take care of normal clicks
|
||||||
|
L.DomEvent.addListener(this.button,'click', function(e){
|
||||||
|
L.DomEvent.stop(e);
|
||||||
|
this._currentState.onClick(this, this._map ? this._map : null );
|
||||||
|
this._map && this._map.getContainer().focus();
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
// prep the contents of the control
|
||||||
|
if(this.options.type == 'replace'){
|
||||||
|
this.button.appendChild(this._currentState.icon);
|
||||||
|
} else {
|
||||||
|
for(var i=0;i<this._states.length;i++){
|
||||||
|
this.button.appendChild(this._states[i].icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
_currentState: {
|
||||||
|
// placeholder content
|
||||||
|
stateName: 'unnamed',
|
||||||
|
icon: (function(){ return document.createElement('span'); })()
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_states: null, // populated on init
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
state: function(newState){
|
||||||
|
|
||||||
|
// activate by name
|
||||||
|
if(typeof newState == 'string'){
|
||||||
|
|
||||||
|
this._activateStateNamed(newState);
|
||||||
|
|
||||||
|
// activate by index
|
||||||
|
} else if (typeof newState == 'number'){
|
||||||
|
|
||||||
|
this._activateState(this._states[newState]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
_activateStateNamed: function(stateName){
|
||||||
|
for(var i = 0; i < this._states.length; i++){
|
||||||
|
if( this._states[i].stateName == stateName ){
|
||||||
|
this._activateState( this._states[i] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_activateState: function(newState){
|
||||||
|
|
||||||
|
if( newState === this._currentState ){
|
||||||
|
|
||||||
|
// don't touch the dom if it'll just be the same after
|
||||||
|
return;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// swap out elements... if you're into that kind of thing
|
||||||
|
if( this.options.type == 'replace' ){
|
||||||
|
this.button.appendChild(newState.icon);
|
||||||
|
this.button.removeChild(this._currentState.icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
if( newState.title ){
|
||||||
|
this.button.title = newState.title;
|
||||||
|
} else {
|
||||||
|
this.button.removeAttribute('title');
|
||||||
|
}
|
||||||
|
|
||||||
|
// update classes for animations
|
||||||
|
for(var i=0;i<this._states.length;i++){
|
||||||
|
L.DomUtil.removeClass(this._states[i].icon, this._currentState.stateName + '-active');
|
||||||
|
L.DomUtil.addClass(this._states[i].icon, newState.stateName + '-active');
|
||||||
|
}
|
||||||
|
|
||||||
|
// update classes for animations
|
||||||
|
L.DomUtil.removeClass(this.button, this._currentState.stateName + '-active');
|
||||||
|
L.DomUtil.addClass(this.button, newState.stateName + '-active');
|
||||||
|
|
||||||
|
// update the record
|
||||||
|
this._currentState = newState;
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
enable: function(){
|
||||||
|
L.DomUtil.addClass(this.button, 'enabled');
|
||||||
|
L.DomUtil.removeClass(this.button, 'disabled');
|
||||||
|
this.button.setAttribute('aria-hidden', 'false');
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
disable: function(){
|
||||||
|
L.DomUtil.addClass(this.button, 'disabled');
|
||||||
|
L.DomUtil.removeClass(this.button, 'enabled');
|
||||||
|
this.button.setAttribute('aria-hidden', 'true');
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
onAdd: function(map){
|
||||||
|
var bar = L.easyBar([this], {
|
||||||
|
position: this.options.position,
|
||||||
|
leafletClasses: this.options.leafletClasses
|
||||||
|
});
|
||||||
|
this._anonymousBar = bar;
|
||||||
|
this._container = bar.container;
|
||||||
|
return this._anonymousBar.container;
|
||||||
|
},
|
||||||
|
|
||||||
|
removeFrom: function (map) {
|
||||||
|
if (this._map === map)
|
||||||
|
this.remove();
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
L.easyButton = function(/* args will pass automatically */){
|
||||||
|
var args = Array.prototype.concat.apply([L.Control.EasyButton],arguments);
|
||||||
|
return new (Function.prototype.bind.apply(L.Control.EasyButton, args));
|
||||||
|
};
|
||||||
|
|
||||||
|
/*************************
|
||||||
|
*
|
||||||
|
* util functions
|
||||||
|
*
|
||||||
|
*************************/
|
||||||
|
|
||||||
|
// constructor for states so only curated
|
||||||
|
// states end up getting called
|
||||||
|
function State(template, easyButton){
|
||||||
|
|
||||||
|
this.title = template.title;
|
||||||
|
this.stateName = template.stateName ? template.stateName : 'unnamed-state';
|
||||||
|
|
||||||
|
// build the wrapper
|
||||||
|
this.icon = L.DomUtil.create('span', '');
|
||||||
|
|
||||||
|
L.DomUtil.addClass(this.icon, 'button-state state-' + this.stateName.replace(/(^\s*|\s*$)/g,''));
|
||||||
|
this.icon.innerHTML = buildIcon(template.icon);
|
||||||
|
this.onClick = L.Util.bind(template.onClick?template.onClick:function(){}, easyButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildIcon(ambiguousIconString) {
|
||||||
|
|
||||||
|
var tmpIcon;
|
||||||
|
|
||||||
|
// does this look like html? (i.e. not a class)
|
||||||
|
if( ambiguousIconString.match(/[&;=<>"']/) ){
|
||||||
|
|
||||||
|
// if so, the user should have put in html
|
||||||
|
// so move forward as such
|
||||||
|
tmpIcon = ambiguousIconString;
|
||||||
|
|
||||||
|
// then it wasn't html, so
|
||||||
|
// it's a class list, figure out what kind
|
||||||
|
} else {
|
||||||
|
ambiguousIconString = ambiguousIconString.replace(/(^\s*|\s*$)/g,'');
|
||||||
|
tmpIcon = L.DomUtil.create('span', '');
|
||||||
|
|
||||||
|
if( ambiguousIconString.indexOf('fa-') === 0 ){
|
||||||
|
L.DomUtil.addClass(tmpIcon, 'fa ' + ambiguousIconString)
|
||||||
|
} else if ( ambiguousIconString.indexOf('glyphicon-') === 0 ) {
|
||||||
|
L.DomUtil.addClass(tmpIcon, 'glyphicon ' + ambiguousIconString)
|
||||||
|
} else {
|
||||||
|
L.DomUtil.addClass(tmpIcon, /*rollwithit*/ ambiguousIconString)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make this a string so that it's easy to set innerHTML below
|
||||||
|
tmpIcon = tmpIcon.outerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmpIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
632
frappe/public/js/lib/leaflet/leaflet.css
Normal file
|
|
@ -0,0 +1,632 @@
|
||||||
|
/* required styles */
|
||||||
|
|
||||||
|
.leaflet-pane,
|
||||||
|
.leaflet-tile,
|
||||||
|
.leaflet-marker-icon,
|
||||||
|
.leaflet-marker-shadow,
|
||||||
|
.leaflet-tile-container,
|
||||||
|
.leaflet-pane > svg,
|
||||||
|
.leaflet-pane > canvas,
|
||||||
|
.leaflet-zoom-box,
|
||||||
|
.leaflet-image-layer,
|
||||||
|
.leaflet-layer {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
.leaflet-container {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.leaflet-tile,
|
||||||
|
.leaflet-marker-icon,
|
||||||
|
.leaflet-marker-shadow {
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-drag: none;
|
||||||
|
}
|
||||||
|
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
|
||||||
|
.leaflet-safari .leaflet-tile {
|
||||||
|
image-rendering: -webkit-optimize-contrast;
|
||||||
|
}
|
||||||
|
/* hack that prevents hw layers "stretching" when loading new tiles */
|
||||||
|
.leaflet-safari .leaflet-tile-container {
|
||||||
|
width: 1600px;
|
||||||
|
height: 1600px;
|
||||||
|
-webkit-transform-origin: 0 0;
|
||||||
|
}
|
||||||
|
.leaflet-marker-icon,
|
||||||
|
.leaflet-marker-shadow {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
|
||||||
|
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
|
||||||
|
.leaflet-container .leaflet-overlay-pane svg,
|
||||||
|
.leaflet-container .leaflet-marker-pane img,
|
||||||
|
.leaflet-container .leaflet-shadow-pane img,
|
||||||
|
.leaflet-container .leaflet-tile-pane img,
|
||||||
|
.leaflet-container img.leaflet-image-layer {
|
||||||
|
max-width: none !important; /* csslint allow: important */
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-container.leaflet-touch-zoom {
|
||||||
|
-ms-touch-action: pan-x pan-y;
|
||||||
|
touch-action: pan-x pan-y;
|
||||||
|
}
|
||||||
|
.leaflet-container.leaflet-touch-drag {
|
||||||
|
-ms-touch-action: pinch-zoom;
|
||||||
|
}
|
||||||
|
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
|
||||||
|
-ms-touch-action: none;
|
||||||
|
touch-action: none;
|
||||||
|
}
|
||||||
|
.leaflet-container {
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
.leaflet-container a {
|
||||||
|
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
|
||||||
|
}
|
||||||
|
.leaflet-tile {
|
||||||
|
filter: inherit;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
.leaflet-tile-loaded {
|
||||||
|
visibility: inherit;
|
||||||
|
}
|
||||||
|
.leaflet-zoom-box {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
z-index: 800;
|
||||||
|
}
|
||||||
|
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
|
||||||
|
.leaflet-overlay-pane svg {
|
||||||
|
-moz-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-pane { z-index: 400; }
|
||||||
|
|
||||||
|
.leaflet-tile-pane { z-index: 200; }
|
||||||
|
.leaflet-overlay-pane { z-index: 400; }
|
||||||
|
.leaflet-shadow-pane { z-index: 500; }
|
||||||
|
.leaflet-marker-pane { z-index: 600; }
|
||||||
|
.leaflet-tooltip-pane { z-index: 650; }
|
||||||
|
.leaflet-popup-pane { z-index: 700; }
|
||||||
|
|
||||||
|
.leaflet-map-pane canvas { z-index: 100; }
|
||||||
|
.leaflet-map-pane svg { z-index: 200; }
|
||||||
|
|
||||||
|
.leaflet-vml-shape {
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
.lvml {
|
||||||
|
behavior: url(#default#VML);
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* control positioning */
|
||||||
|
|
||||||
|
.leaflet-control {
|
||||||
|
position: relative;
|
||||||
|
z-index: 800;
|
||||||
|
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
.leaflet-top,
|
||||||
|
.leaflet-bottom {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1000;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.leaflet-top {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
.leaflet-right {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
.leaflet-bottom {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
.leaflet-left {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.leaflet-control {
|
||||||
|
float: left;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
.leaflet-right .leaflet-control {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.leaflet-top .leaflet-control {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.leaflet-bottom .leaflet-control {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.leaflet-left .leaflet-control {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
.leaflet-right .leaflet-control {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* zoom and fade animations */
|
||||||
|
|
||||||
|
.leaflet-fade-anim .leaflet-tile {
|
||||||
|
will-change: opacity;
|
||||||
|
}
|
||||||
|
.leaflet-fade-anim .leaflet-popup {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transition: opacity 0.2s linear;
|
||||||
|
-moz-transition: opacity 0.2s linear;
|
||||||
|
-o-transition: opacity 0.2s linear;
|
||||||
|
transition: opacity 0.2s linear;
|
||||||
|
}
|
||||||
|
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.leaflet-zoom-animated {
|
||||||
|
-webkit-transform-origin: 0 0;
|
||||||
|
-ms-transform-origin: 0 0;
|
||||||
|
transform-origin: 0 0;
|
||||||
|
}
|
||||||
|
.leaflet-zoom-anim .leaflet-zoom-animated {
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
.leaflet-zoom-anim .leaflet-zoom-animated {
|
||||||
|
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||||
|
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||||
|
-o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||||
|
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||||
|
}
|
||||||
|
.leaflet-zoom-anim .leaflet-tile,
|
||||||
|
.leaflet-pan-anim .leaflet-tile {
|
||||||
|
-webkit-transition: none;
|
||||||
|
-moz-transition: none;
|
||||||
|
-o-transition: none;
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-zoom-anim .leaflet-zoom-hide {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* cursors */
|
||||||
|
|
||||||
|
.leaflet-interactive {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.leaflet-grab {
|
||||||
|
cursor: -webkit-grab;
|
||||||
|
cursor: -moz-grab;
|
||||||
|
}
|
||||||
|
.leaflet-crosshair,
|
||||||
|
.leaflet-crosshair .leaflet-interactive {
|
||||||
|
cursor: crosshair;
|
||||||
|
}
|
||||||
|
.leaflet-popup-pane,
|
||||||
|
.leaflet-control {
|
||||||
|
cursor: auto;
|
||||||
|
}
|
||||||
|
.leaflet-dragging .leaflet-grab,
|
||||||
|
.leaflet-dragging .leaflet-grab .leaflet-interactive,
|
||||||
|
.leaflet-dragging .leaflet-marker-draggable {
|
||||||
|
cursor: move;
|
||||||
|
cursor: -webkit-grabbing;
|
||||||
|
cursor: -moz-grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* marker & overlays interactivity */
|
||||||
|
.leaflet-marker-icon,
|
||||||
|
.leaflet-marker-shadow,
|
||||||
|
.leaflet-image-layer,
|
||||||
|
.leaflet-pane > svg path,
|
||||||
|
.leaflet-tile-container {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-marker-icon.leaflet-interactive,
|
||||||
|
.leaflet-image-layer.leaflet-interactive,
|
||||||
|
.leaflet-pane > svg path.leaflet-interactive {
|
||||||
|
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* visual tweaks */
|
||||||
|
|
||||||
|
.leaflet-container {
|
||||||
|
background: #ddd;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
.leaflet-container a {
|
||||||
|
color: #0078A8;
|
||||||
|
}
|
||||||
|
.leaflet-container a.leaflet-active {
|
||||||
|
outline: 2px solid orange;
|
||||||
|
}
|
||||||
|
.leaflet-zoom-box {
|
||||||
|
border: 2px dotted #38f;
|
||||||
|
background: rgba(255,255,255,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* general typography */
|
||||||
|
.leaflet-container {
|
||||||
|
font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* general toolbar styles */
|
||||||
|
|
||||||
|
.leaflet-bar {
|
||||||
|
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.leaflet-bar a,
|
||||||
|
.leaflet-bar a:hover {
|
||||||
|
background-color: #fff;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
line-height: 26px;
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
.leaflet-bar a,
|
||||||
|
.leaflet-control-layers-toggle {
|
||||||
|
background-position: 50% 50%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.leaflet-bar a:hover {
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
}
|
||||||
|
.leaflet-bar a:first-child {
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
}
|
||||||
|
.leaflet-bar a:last-child {
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.leaflet-bar a.leaflet-disabled {
|
||||||
|
cursor: default;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
color: #bbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-touch .leaflet-bar a {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
.leaflet-touch .leaflet-bar a:first-child {
|
||||||
|
border-top-left-radius: 2px;
|
||||||
|
border-top-right-radius: 2px;
|
||||||
|
}
|
||||||
|
.leaflet-touch .leaflet-bar a:last-child {
|
||||||
|
border-bottom-left-radius: 2px;
|
||||||
|
border-bottom-right-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* zoom control */
|
||||||
|
|
||||||
|
.leaflet-control-zoom-in,
|
||||||
|
.leaflet-control-zoom-out {
|
||||||
|
font: bold 18px 'Lucida Console', Monaco, monospace;
|
||||||
|
text-indent: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* layers control */
|
||||||
|
|
||||||
|
.leaflet-control-layers {
|
||||||
|
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers-toggle {
|
||||||
|
background-image: url(/assets/frappe/images/leaflet/layers.png);
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
.leaflet-retina .leaflet-control-layers-toggle {
|
||||||
|
background-image: url(/assets/frappe/images/leaflet/layers-2x.png);
|
||||||
|
background-size: 26px 26px;
|
||||||
|
}
|
||||||
|
.leaflet-touch .leaflet-control-layers-toggle {
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers .leaflet-control-layers-list,
|
||||||
|
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers-expanded .leaflet-control-layers-list {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers-expanded {
|
||||||
|
padding: 6px 10px 6px 6px;
|
||||||
|
color: #333;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers-scrollbar {
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers-selector {
|
||||||
|
margin-top: 2px;
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers label {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers-separator {
|
||||||
|
height: 0;
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
margin: 5px -10px 5px -6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default icon URLs */
|
||||||
|
.leaflet-default-icon-path {
|
||||||
|
background-image: url(/assets/frappe/images/leaflet/marker-icon.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* attribution and scale controls */
|
||||||
|
|
||||||
|
.leaflet-container .leaflet-control-attribution {
|
||||||
|
background: #fff;
|
||||||
|
background: rgba(255, 255, 255, 0.7);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.leaflet-control-attribution,
|
||||||
|
.leaflet-control-scale-line {
|
||||||
|
padding: 0 5px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.leaflet-control-attribution a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.leaflet-control-attribution a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.leaflet-container .leaflet-control-attribution,
|
||||||
|
.leaflet-container .leaflet-control-scale {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
.leaflet-left .leaflet-control-scale {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
.leaflet-bottom .leaflet-control-scale {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.leaflet-control-scale-line {
|
||||||
|
border: 2px solid #777;
|
||||||
|
border-top: none;
|
||||||
|
line-height: 1.1;
|
||||||
|
padding: 2px 5px 1px;
|
||||||
|
font-size: 11px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
background: #fff;
|
||||||
|
background: rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
.leaflet-control-scale-line:not(:first-child) {
|
||||||
|
border-top: 2px solid #777;
|
||||||
|
border-bottom: none;
|
||||||
|
margin-top: -2px;
|
||||||
|
}
|
||||||
|
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
|
||||||
|
border-bottom: 2px solid #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-touch .leaflet-control-attribution,
|
||||||
|
.leaflet-touch .leaflet-control-layers,
|
||||||
|
.leaflet-touch .leaflet-bar {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.leaflet-touch .leaflet-control-layers,
|
||||||
|
.leaflet-touch .leaflet-bar {
|
||||||
|
border: 2px solid rgba(0,0,0,0.2);
|
||||||
|
background-clip: padding-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* popup */
|
||||||
|
|
||||||
|
.leaflet-popup {
|
||||||
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.leaflet-popup-content-wrapper {
|
||||||
|
padding: 1px;
|
||||||
|
text-align: left;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
.leaflet-popup-content {
|
||||||
|
margin: 13px 19px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
.leaflet-popup-content p {
|
||||||
|
margin: 18px 0;
|
||||||
|
}
|
||||||
|
.leaflet-popup-tip-container {
|
||||||
|
width: 40px;
|
||||||
|
height: 20px;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -20px;
|
||||||
|
overflow: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.leaflet-popup-tip {
|
||||||
|
width: 17px;
|
||||||
|
height: 17px;
|
||||||
|
padding: 1px;
|
||||||
|
|
||||||
|
margin: -10px auto 0;
|
||||||
|
|
||||||
|
-webkit-transform: rotate(45deg);
|
||||||
|
-moz-transform: rotate(45deg);
|
||||||
|
-ms-transform: rotate(45deg);
|
||||||
|
-o-transform: rotate(45deg);
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
.leaflet-popup-content-wrapper,
|
||||||
|
.leaflet-popup-tip {
|
||||||
|
background: white;
|
||||||
|
color: #333;
|
||||||
|
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
|
||||||
|
}
|
||||||
|
.leaflet-container a.leaflet-popup-close-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 4px 4px 0 0;
|
||||||
|
border: none;
|
||||||
|
text-align: center;
|
||||||
|
width: 18px;
|
||||||
|
height: 14px;
|
||||||
|
font: 16px/14px Tahoma, Verdana, sans-serif;
|
||||||
|
color: #c3c3c3;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.leaflet-container a.leaflet-popup-close-button:hover {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.leaflet-popup-scrolled {
|
||||||
|
overflow: auto;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-oldie .leaflet-popup-content-wrapper {
|
||||||
|
zoom: 1;
|
||||||
|
}
|
||||||
|
.leaflet-oldie .leaflet-popup-tip {
|
||||||
|
width: 24px;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
|
||||||
|
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
|
||||||
|
}
|
||||||
|
.leaflet-oldie .leaflet-popup-tip-container {
|
||||||
|
margin-top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-oldie .leaflet-control-zoom,
|
||||||
|
.leaflet-oldie .leaflet-control-layers,
|
||||||
|
.leaflet-oldie .leaflet-popup-content-wrapper,
|
||||||
|
.leaflet-oldie .leaflet-popup-tip {
|
||||||
|
border: 1px solid #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* div icon */
|
||||||
|
|
||||||
|
.leaflet-div-icon {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Tooltip */
|
||||||
|
/* Base styles for the element that has a tooltip */
|
||||||
|
.leaflet-tooltip {
|
||||||
|
position: absolute;
|
||||||
|
padding: 6px;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #fff;
|
||||||
|
border-radius: 3px;
|
||||||
|
color: #222;
|
||||||
|
white-space: nowrap;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
pointer-events: none;
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
|
||||||
|
}
|
||||||
|
.leaflet-tooltip.leaflet-clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-top:before,
|
||||||
|
.leaflet-tooltip-bottom:before,
|
||||||
|
.leaflet-tooltip-left:before,
|
||||||
|
.leaflet-tooltip-right:before {
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
border: 6px solid transparent;
|
||||||
|
background: transparent;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Directions */
|
||||||
|
|
||||||
|
.leaflet-tooltip-bottom {
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-top {
|
||||||
|
margin-top: -6px;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-bottom:before,
|
||||||
|
.leaflet-tooltip-top:before {
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -6px;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-top:before {
|
||||||
|
bottom: 0;
|
||||||
|
margin-bottom: -12px;
|
||||||
|
border-top-color: #fff;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-bottom:before {
|
||||||
|
top: 0;
|
||||||
|
margin-top: -12px;
|
||||||
|
margin-left: -6px;
|
||||||
|
border-bottom-color: #fff;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-left {
|
||||||
|
margin-left: -6px;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-right {
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-left:before,
|
||||||
|
.leaflet-tooltip-right:before {
|
||||||
|
top: 50%;
|
||||||
|
margin-top: -6px;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-left:before {
|
||||||
|
right: 0;
|
||||||
|
margin-right: -12px;
|
||||||
|
border-left-color: #fff;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-right:before {
|
||||||
|
left: 0;
|
||||||
|
margin-left: -12px;
|
||||||
|
border-right-color: #fff;
|
||||||
|
}
|
||||||
10
frappe/public/js/lib/leaflet/leaflet.draw.css
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
.leaflet-draw-section{position:relative}.leaflet-draw-toolbar{margin-top:12px}.leaflet-draw-toolbar-top{margin-top:0}.leaflet-draw-toolbar-notop a:first-child{border-top-right-radius:0}.leaflet-draw-toolbar-nobottom a:last-child{border-bottom-right-radius:0}.leaflet-draw-toolbar a{background-image:url('/assets/frappe/images/leaflet/spritesheet.png');background-image:linear-gradient(transparent,transparent),url('/assets/frappe/images/leaflet/spritesheet.svg');background-repeat:no-repeat;background-size:300px 30px;background-clip:padding-box}.leaflet-retina .leaflet-draw-toolbar a{background-image:url('/assets/frappe/images/leaflet/spritesheet-2x.png');background-image:linear-gradient(transparent,transparent),url('/assets/frappe/images/leaflet/spritesheet.svg')}
|
||||||
|
.leaflet-draw a{display:block;text-align:center;text-decoration:none}.leaflet-draw a .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.leaflet-draw-actions{display:none;list-style:none;margin:0;padding:0;position:absolute;left:26px;top:0;white-space:nowrap}.leaflet-touch .leaflet-draw-actions{left:32px}.leaflet-right .leaflet-draw-actions{right:26px;left:auto}.leaflet-touch .leaflet-right .leaflet-draw-actions{right:32px;left:auto}.leaflet-draw-actions li{display:inline-block}
|
||||||
|
.leaflet-draw-actions li:first-child a{border-left:0}.leaflet-draw-actions li:last-child a{-webkit-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.leaflet-right .leaflet-draw-actions li:last-child a{-webkit-border-radius:0;border-radius:0}.leaflet-right .leaflet-draw-actions li:first-child a{-webkit-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.leaflet-draw-actions a{background-color:#919187;border-left:1px solid #AAA;color:#FFF;font:11px/19px "Helvetica Neue",Arial,Helvetica,sans-serif;line-height:28px;text-decoration:none;padding-left:10px;padding-right:10px;height:28px}
|
||||||
|
.leaflet-touch .leaflet-draw-actions a{font-size:12px;line-height:30px;height:30px}.leaflet-draw-actions-bottom{margin-top:0}.leaflet-draw-actions-top{margin-top:1px}.leaflet-draw-actions-top a,.leaflet-draw-actions-bottom a{height:27px;line-height:27px}.leaflet-draw-actions a:hover{background-color:#a0a098}.leaflet-draw-actions-top.leaflet-draw-actions-bottom a{height:26px;line-height:26px}.leaflet-draw-toolbar .leaflet-draw-draw-polyline{background-position:-2px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polyline{background-position:0 -1px}
|
||||||
|
.leaflet-draw-toolbar .leaflet-draw-draw-polygon{background-position:-31px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polygon{background-position:-29px -1px}.leaflet-draw-toolbar .leaflet-draw-draw-rectangle{background-position:-62px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-rectangle{background-position:-60px -1px}.leaflet-draw-toolbar .leaflet-draw-draw-circle{background-position:-92px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circle{background-position:-90px -1px}
|
||||||
|
.leaflet-draw-toolbar .leaflet-draw-draw-marker{background-position:-122px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-marker{background-position:-120px -1px}.leaflet-draw-toolbar .leaflet-draw-draw-circlemarker{background-position:-273px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circlemarker{background-position:-271px -1px}.leaflet-draw-toolbar .leaflet-draw-edit-edit{background-position:-152px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit{background-position:-150px -1px}
|
||||||
|
.leaflet-draw-toolbar .leaflet-draw-edit-remove{background-position:-182px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove{background-position:-180px -1px}.leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled{background-position:-212px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled{background-position:-210px -1px}.leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled{background-position:-242px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled{background-position:-240px -2px}
|
||||||
|
.leaflet-mouse-marker{background-color:#fff;cursor:crosshair}.leaflet-draw-tooltip{background:#363636;background:rgba(0,0,0,0.5);border:1px solid transparent;-webkit-border-radius:4px;border-radius:4px;color:#fff;font:12px/18px "Helvetica Neue",Arial,Helvetica,sans-serif;margin-left:20px;margin-top:-21px;padding:4px 8px;position:absolute;visibility:hidden;white-space:nowrap;z-index:6}.leaflet-draw-tooltip:before{border-right:6px solid black;border-right-color:rgba(0,0,0,0.5);border-top:6px solid transparent;border-bottom:6px solid transparent;content:"";position:absolute;top:7px;left:-7px}
|
||||||
|
.leaflet-error-draw-tooltip{background-color:#f2dede;border:1px solid #e6b6bd;color:#b94a48}.leaflet-error-draw-tooltip:before{border-right-color:#e6b6bd}.leaflet-draw-tooltip-single{margin-top:-12px}.leaflet-draw-tooltip-subtext{color:#f8d5e4}.leaflet-draw-guide-dash{font-size:1%;opacity:.6;position:absolute;width:5px;height:5px}.leaflet-edit-marker-selected{background-color:rgba(254,87,161,0.1);border:4px dashed rgba(254,87,161,0.6);-webkit-border-radius:4px;border-radius:4px;box-sizing:content-box}
|
||||||
|
.leaflet-edit-move{cursor:move}.leaflet-edit-resize{cursor:pointer}.leaflet-oldie .leaflet-draw-toolbar{border:1px solid #999}
|
||||||
1702
frappe/public/js/lib/leaflet/leaflet.draw.js
Normal file
5
frappe/public/js/lib/leaflet/leaflet.js
Normal file
|
|
@ -8,6 +8,8 @@
|
||||||
{%- elif df.fieldtype in ("Image", "Attach Image", "Attach", "Signature")
|
{%- elif df.fieldtype in ("Image", "Attach Image", "Attach", "Signature")
|
||||||
and (guess_mimetype(doc[df.fieldname])[0] or "").startswith("image/") -%}
|
and (guess_mimetype(doc[df.fieldname])[0] or "").startswith("image/") -%}
|
||||||
{{ render_image(df, doc) }}
|
{{ render_image(df, doc) }}
|
||||||
|
{%- elif df.fieldtype=="Geolocation" -%}
|
||||||
|
{{ render_geolocation(df, doc) }}
|
||||||
{%- else -%}
|
{%- else -%}
|
||||||
{{ render_field_with_label(df, doc) }}
|
{{ render_field_with_label(df, doc) }}
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
|
|
@ -98,6 +100,10 @@ data-fieldname="{{ df.fieldname }}" data-fieldtype="{{ df.fieldtype }}"
|
||||||
{{ print_value(df, doc) }}
|
{{ print_value(df, doc) }}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
{%- macro render_geolocation(df, doc) -%}
|
||||||
|
{{ "" }}
|
||||||
|
{%- endmacro -%}
|
||||||
|
|
||||||
{%- macro print_value(df, doc, parent_doc=None, visible_columns=None) -%}
|
{%- macro print_value(df, doc, parent_doc=None, visible_columns=None) -%}
|
||||||
{% if doc.print_templates and
|
{% if doc.print_templates and
|
||||||
doc.print_templates.get(df.fieldname) %}
|
doc.print_templates.get(df.fieldname) %}
|
||||||
|
|
|
||||||
39
frappe/tests/ui/test_control_geolocation.js
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
QUnit.module('controls');
|
||||||
|
|
||||||
|
QUnit.test("Test ControlGeolocation", function(assert) {
|
||||||
|
assert.expect(1);
|
||||||
|
|
||||||
|
const random_name = frappe.utils.get_random(3).toLowerCase();
|
||||||
|
|
||||||
|
let done = assert.async();
|
||||||
|
|
||||||
|
// geolocation alert dialog suppressed (only secure origins or localhost allowed)
|
||||||
|
window.alert = function() {
|
||||||
|
console.log.apply(console, arguments); //eslint-disable-line
|
||||||
|
};
|
||||||
|
|
||||||
|
frappe.run_serially([
|
||||||
|
() => {
|
||||||
|
return frappe.tests.make('Custom Field', [
|
||||||
|
{dt: 'ToDo'},
|
||||||
|
{fieldtype: 'Geolocation'},
|
||||||
|
{label: random_name},
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
() => frappe.set_route('List', 'ToDo'),
|
||||||
|
() => frappe.new_doc('ToDo'),
|
||||||
|
() => {
|
||||||
|
if (frappe.quick_entry)
|
||||||
|
{
|
||||||
|
frappe.quick_entry.dialog.$wrapper.find('.edit-full').click();
|
||||||
|
return frappe.timeout(1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
const control = $(`.frappe-control[data-fieldname="${random_name}"]`);
|
||||||
|
|
||||||
|
return assert.ok(control.data('fieldtype') === 'Geolocation');
|
||||||
|
},
|
||||||
|
() => done()
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
@ -14,3 +14,4 @@ frappe/desk/doctype/event/test_event.js
|
||||||
frappe/workflow/doctype/workflow/tests/test_workflow_create.js
|
frappe/workflow/doctype/workflow/tests/test_workflow_create.js
|
||||||
frappe/workflow/doctype/workflow/tests/test_workflow_test.js
|
frappe/workflow/doctype/workflow/tests/test_workflow_test.js
|
||||||
frappe/tests/ui/test_control_html.js
|
frappe/tests/ui/test_control_html.js
|
||||||
|
frappe/tests/ui/test_control_geolocation.js
|
||||||
|
|
|
||||||