ekylibre-cartography 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +3 -0
  3. data/Rakefile +10 -0
  4. data/app/assets/javascripts/cartography.coffee +535 -0
  5. data/app/assets/javascripts/cartography/base.coffee +11 -0
  6. data/app/assets/javascripts/cartography/controls.coffee +463 -0
  7. data/app/assets/javascripts/cartography/events.coffee +36 -0
  8. data/app/assets/javascripts/cartography/layers.coffee +127 -0
  9. data/app/assets/javascripts/cartography/layers/simple.coffee +37 -0
  10. data/app/assets/javascripts/cartography/leaflet/controls.coffee +420 -0
  11. data/app/assets/javascripts/cartography/leaflet/handlers.coffee +461 -0
  12. data/app/assets/javascripts/cartography/leaflet/i18n.coffee +31 -0
  13. data/app/assets/javascripts/cartography/leaflet/layers.coffee +60 -0
  14. data/app/assets/javascripts/cartography/leaflet/toolbars.coffee +450 -0
  15. data/app/assets/javascripts/cartography/patches.js +8 -0
  16. data/app/assets/javascripts/cartography/util.coffee +18 -0
  17. data/app/assets/javascripts/main.js +18 -0
  18. data/app/assets/stylesheets/cartography.css +86 -0
  19. data/app/helpers/cartography_helper.rb +55 -0
  20. data/lib/cartography.rb +1 -0
  21. data/lib/cartography/engine.rb +11 -0
  22. data/lib/cartography/version.rb +3 -0
  23. data/vendor/assets/components/d3-array/dist/d3-array.js +590 -0
  24. data/vendor/assets/components/d3-array/dist/d3-array.min.js +2 -0
  25. data/vendor/assets/components/geojson-equality/dist/geojson-equality.js +295 -0
  26. data/vendor/assets/components/geojson-equality/dist/geojson-equality.js.map +21 -0
  27. data/vendor/assets/components/geojson-equality/dist/geojson-equality.min.js +1 -0
  28. data/vendor/assets/components/leaflet-controlpanel/dist/leaflet.controlpanel.css +29 -0
  29. data/vendor/assets/components/leaflet-controlpanel/dist/leaflet.controlpanel.js +269 -0
  30. data/vendor/assets/components/leaflet-draw-cut/dist/leaflet.draw.cut.css +1 -0
  31. data/vendor/assets/components/leaflet-draw-cut/dist/leaflet.draw.cut.js +8 -0
  32. data/vendor/assets/components/leaflet-draw-merge/dist/leaflet.draw.merge.css +0 -0
  33. data/vendor/assets/components/leaflet-draw-merge/dist/leaflet.draw.merge.js +48026 -0
  34. data/vendor/assets/components/leaflet-draw/dist/leaflet.draw-src.css +326 -0
  35. data/vendor/assets/components/leaflet-draw/dist/leaflet.draw-src.js +4653 -0
  36. data/vendor/assets/components/leaflet-draw/dist/leaflet.draw-src.map +1 -0
  37. data/vendor/assets/components/leaflet-draw/dist/leaflet.draw.css +10 -0
  38. data/vendor/assets/components/leaflet-draw/dist/leaflet.draw.js +10 -0
  39. data/vendor/assets/components/leaflet-geographicutil/dist/leaflet.geographicutil.js +3220 -0
  40. data/vendor/assets/components/leaflet-reactive_measure/dist/reactive_measure.css +30 -0
  41. data/vendor/assets/components/leaflet-reactive_measure/dist/reactive_measure.js +3764 -0
  42. data/vendor/assets/components/leaflet/dist/leaflet-src.js +13609 -0
  43. data/vendor/assets/components/leaflet/dist/leaflet-src.js.map +1 -0
  44. data/vendor/assets/components/leaflet/dist/leaflet-src.map +1 -0
  45. data/vendor/assets/components/leaflet/dist/leaflet.css +632 -0
  46. data/vendor/assets/components/leaflet/dist/leaflet.js +5 -0
  47. data/vendor/assets/components/leaflet/dist/leaflet.js.map +1 -0
  48. data/vendor/assets/components/martinez-polygon-clipping/dist/martinez.min.js +9 -0
  49. data/vendor/assets/components/martinez-polygon-clipping/dist/martinez.umd.js +1716 -0
  50. data/vendor/assets/components/martinez-polygon-clipping/dist/martinez.umd.js.map +1 -0
  51. data/vendor/assets/components/polygon-clipping/dist/polygon-clipping.js +279 -0
  52. data/vendor/assets/components/polygon-clipping/dist/polygon-clipping.min.js +1 -0
  53. data/vendor/assets/components/rtree/dist/rtree.js +911 -0
  54. data/vendor/assets/components/rtree/dist/rtree.min.js +1 -0
  55. data/vendor/assets/components/splaytree/dist/splay.es6.js +765 -0
  56. data/vendor/assets/components/splaytree/dist/splay.es6.js.map +1 -0
  57. data/vendor/assets/components/splaytree/dist/splay.js +797 -0
  58. data/vendor/assets/components/splaytree/dist/splay.js.map +1 -0
  59. metadata +156 -0
@@ -0,0 +1,326 @@
1
+ /* ================================================================== */
2
+ /* Toolbars
3
+ /* ================================================================== */
4
+
5
+ .leaflet-draw-section {
6
+ position: relative;
7
+ }
8
+
9
+ .leaflet-draw-toolbar {
10
+ margin-top: 12px;
11
+ }
12
+
13
+ .leaflet-draw-toolbar-top {
14
+ margin-top: 0;
15
+ }
16
+
17
+ .leaflet-draw-toolbar-notop a:first-child {
18
+ border-top-right-radius: 0;
19
+ }
20
+
21
+ .leaflet-draw-toolbar-nobottom a:last-child {
22
+ border-bottom-right-radius: 0;
23
+ }
24
+
25
+ .leaflet-draw-toolbar a {
26
+ background-image: url('images/spritesheet.png');
27
+ background-image: linear-gradient(transparent, transparent), url('images/spritesheet.svg');
28
+ background-repeat: no-repeat;
29
+ background-size: 300px 30px;
30
+ background-clip: padding-box;
31
+ }
32
+
33
+ .leaflet-retina .leaflet-draw-toolbar a {
34
+ background-image: url('images/spritesheet-2x.png');
35
+ background-image: linear-gradient(transparent, transparent), url('images/spritesheet.svg');
36
+ }
37
+
38
+ .leaflet-draw a {
39
+ display: block;
40
+ text-align: center;
41
+ text-decoration: none;
42
+ }
43
+
44
+ .leaflet-draw a .sr-only {
45
+ position: absolute;
46
+ width: 1px;
47
+ height: 1px;
48
+ padding: 0;
49
+ margin: -1px;
50
+ overflow: hidden;
51
+ clip: rect(0,0,0,0);
52
+ border: 0;
53
+ }
54
+
55
+ /* ================================================================== */
56
+ /* Toolbar actions menu
57
+ /* ================================================================== */
58
+
59
+ .leaflet-draw-actions {
60
+ display: none;
61
+ list-style: none;
62
+ margin: 0;
63
+ padding: 0;
64
+ position: absolute;
65
+ left: 26px; /* leaflet-draw-toolbar.left + leaflet-draw-toolbar.width */
66
+ top: 0;
67
+ white-space: nowrap;
68
+ }
69
+
70
+ .leaflet-touch .leaflet-draw-actions {
71
+ left: 32px;
72
+ }
73
+
74
+ .leaflet-right .leaflet-draw-actions {
75
+ right: 26px;
76
+ left: auto;
77
+ }
78
+
79
+ .leaflet-touch .leaflet-right .leaflet-draw-actions {
80
+ right: 32px;
81
+ left: auto;
82
+ }
83
+
84
+ .leaflet-draw-actions li {
85
+ display: inline-block;
86
+ }
87
+
88
+ .leaflet-draw-actions li:first-child a {
89
+ border-left: none;
90
+ }
91
+
92
+ .leaflet-draw-actions li:last-child a {
93
+ -webkit-border-radius: 0 4px 4px 0;
94
+ border-radius: 0 4px 4px 0;
95
+ }
96
+
97
+ .leaflet-right .leaflet-draw-actions li:last-child a {
98
+ -webkit-border-radius: 0;
99
+ border-radius: 0;
100
+ }
101
+
102
+ .leaflet-right .leaflet-draw-actions li:first-child a {
103
+ -webkit-border-radius: 4px 0 0 4px;
104
+ border-radius: 4px 0 0 4px;
105
+ }
106
+
107
+ .leaflet-draw-actions a {
108
+ background-color: #919187;
109
+ border-left: 1px solid #AAA;
110
+ color: #FFF;
111
+ font: 11px/19px "Helvetica Neue", Arial, Helvetica, sans-serif;
112
+ line-height: 28px;
113
+ text-decoration: none;
114
+ padding-left: 10px;
115
+ padding-right: 10px;
116
+ height: 28px;
117
+ }
118
+
119
+ .leaflet-touch .leaflet-draw-actions a {
120
+ font-size: 12px;
121
+ line-height: 30px;
122
+ height: 30px;
123
+ }
124
+
125
+ .leaflet-draw-actions-bottom {
126
+ margin-top: 0;
127
+ }
128
+
129
+ .leaflet-draw-actions-top {
130
+ margin-top: 1px;
131
+ }
132
+
133
+ .leaflet-draw-actions-top a,
134
+ .leaflet-draw-actions-bottom a {
135
+ height: 27px;
136
+ line-height: 27px;
137
+ }
138
+
139
+ .leaflet-draw-actions a:hover {
140
+ background-color: #A0A098;
141
+ }
142
+
143
+ .leaflet-draw-actions-top.leaflet-draw-actions-bottom a {
144
+ height: 26px;
145
+ line-height: 26px;
146
+ }
147
+
148
+ /* ================================================================== */
149
+ /* Draw toolbar
150
+ /* ================================================================== */
151
+
152
+ .leaflet-draw-toolbar .leaflet-draw-draw-polyline {
153
+ background-position: -2px -2px;
154
+ }
155
+
156
+ .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polyline {
157
+ background-position: 0 -1px;
158
+ }
159
+
160
+ .leaflet-draw-toolbar .leaflet-draw-draw-polygon {
161
+ background-position: -31px -2px;
162
+ }
163
+
164
+ .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polygon {
165
+ background-position: -29px -1px;
166
+ }
167
+
168
+ .leaflet-draw-toolbar .leaflet-draw-draw-rectangle {
169
+ background-position: -62px -2px;
170
+ }
171
+
172
+ .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-rectangle {
173
+ background-position: -60px -1px;
174
+ }
175
+
176
+ .leaflet-draw-toolbar .leaflet-draw-draw-circle {
177
+ background-position: -92px -2px;
178
+ }
179
+
180
+ .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circle {
181
+ background-position: -90px -1px;
182
+ }
183
+
184
+ .leaflet-draw-toolbar .leaflet-draw-draw-marker {
185
+ background-position: -122px -2px;
186
+ }
187
+
188
+ .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-marker {
189
+ background-position: -120px -1px;
190
+ }
191
+
192
+ .leaflet-draw-toolbar .leaflet-draw-draw-circlemarker {
193
+ background-position: -273px -2px;
194
+ }
195
+
196
+ .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circlemarker {
197
+ background-position: -271px -1px;
198
+ }
199
+
200
+
201
+ /* ================================================================== */
202
+ /* Edit toolbar
203
+ /* ================================================================== */
204
+
205
+ .leaflet-draw-toolbar .leaflet-draw-edit-edit {
206
+ background-position: -152px -2px;
207
+ }
208
+
209
+ .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit {
210
+ background-position: -150px -1px;
211
+ }
212
+
213
+ .leaflet-draw-toolbar .leaflet-draw-edit-remove {
214
+ background-position: -182px -2px;
215
+ }
216
+
217
+ .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove {
218
+ background-position: -180px -1px;
219
+ }
220
+
221
+ .leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled {
222
+ background-position: -212px -2px;
223
+ }
224
+
225
+ .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled {
226
+ background-position: -210px -1px;
227
+ }
228
+
229
+ .leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled {
230
+ background-position: -242px -2px;
231
+ }
232
+
233
+ .leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled {
234
+ background-position: -240px -2px;
235
+ }
236
+
237
+ /* ================================================================== */
238
+ /* Drawing styles
239
+ /* ================================================================== */
240
+
241
+ .leaflet-mouse-marker {
242
+ background-color: #fff;
243
+ cursor: crosshair;
244
+ }
245
+
246
+ .leaflet-draw-tooltip {
247
+ background: rgb(54, 54, 54);
248
+ background: rgba(0, 0, 0, 0.5);
249
+ border: 1px solid transparent;
250
+ -webkit-border-radius: 4px;
251
+ border-radius: 4px;
252
+ color: #fff;
253
+ font: 12px/18px "Helvetica Neue", Arial, Helvetica, sans-serif;
254
+ margin-left: 20px;
255
+ margin-top: -21px;
256
+ padding: 4px 8px;
257
+ position: absolute;
258
+ visibility: hidden;
259
+ white-space: nowrap;
260
+ z-index: 6;
261
+ }
262
+
263
+ .leaflet-draw-tooltip:before {
264
+ border-right: 6px solid black;
265
+ border-right-color: rgba(0, 0, 0, 0.5);
266
+ border-top: 6px solid transparent;
267
+ border-bottom: 6px solid transparent;
268
+ content: "";
269
+ position: absolute;
270
+ top: 7px;
271
+ left: -7px;
272
+ }
273
+
274
+ .leaflet-error-draw-tooltip {
275
+ background-color: #F2DEDE;
276
+ border: 1px solid #E6B6BD;
277
+ color: #B94A48;
278
+ }
279
+
280
+ .leaflet-error-draw-tooltip:before {
281
+ border-right-color: #E6B6BD;
282
+ }
283
+
284
+ .leaflet-draw-tooltip-single {
285
+ margin-top: -12px
286
+ }
287
+
288
+ .leaflet-draw-tooltip-subtext {
289
+ color: #f8d5e4;
290
+ }
291
+
292
+ .leaflet-draw-guide-dash {
293
+ font-size: 1%;
294
+ opacity: 0.6;
295
+ position: absolute;
296
+ width: 5px;
297
+ height: 5px;
298
+ }
299
+
300
+ /* ================================================================== */
301
+ /* Edit styles
302
+ /* ================================================================== */
303
+
304
+ .leaflet-edit-marker-selected {
305
+ background-color: rgba(254, 87, 161, 0.1);
306
+ border: 4px dashed rgba(254, 87, 161, 0.6);
307
+ -webkit-border-radius: 4px;
308
+ border-radius: 4px;
309
+ box-sizing: content-box;
310
+ }
311
+
312
+ .leaflet-edit-move {
313
+ cursor: move;
314
+ }
315
+
316
+ .leaflet-edit-resize {
317
+ cursor: pointer;
318
+ }
319
+
320
+ /* ================================================================== */
321
+ /* Old IE styles
322
+ /* ================================================================== */
323
+
324
+ .leaflet-oldie .leaflet-draw-toolbar {
325
+ border: 1px solid #999;
326
+ }
@@ -0,0 +1,4653 @@
1
+ /*
2
+ Leaflet.draw 0.4.12, a plugin that adds drawing and editing tools to Leaflet powered maps.
3
+ (c) 2012-2017, Jacob Toye, Jon West, Smartrak, Leaflet
4
+
5
+ https://github.com/Leaflet/Leaflet.draw
6
+ http://leafletjs.com
7
+ */
8
+ (function (window, document, undefined) {/**
9
+ * Leaflet.draw assumes that you have already included the Leaflet library.
10
+ */
11
+ L.drawVersion = "0.4.12";
12
+ /**
13
+ * @class L.Draw
14
+ * @aka Draw
15
+ *
16
+ *
17
+ * To add the draw toolbar set the option drawControl: true in the map options.
18
+ *
19
+ * @example
20
+ * ```js
21
+ * var map = L.map('map', {drawControl: true}).setView([51.505, -0.09], 13);
22
+ *
23
+ * L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
24
+ * attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
25
+ * }).addTo(map);
26
+ * ```
27
+ *
28
+ * ### Adding the edit toolbar
29
+ * To use the edit toolbar you must initialise the Leaflet.draw control and manually add it to the map.
30
+ *
31
+ * ```js
32
+ * var map = L.map('map').setView([51.505, -0.09], 13);
33
+ *
34
+ * L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
35
+ * attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
36
+ * }).addTo(map);
37
+ *
38
+ * // FeatureGroup is to store editable layers
39
+ * var drawnItems = new L.FeatureGroup();
40
+ * map.addLayer(drawnItems);
41
+ *
42
+ * var drawControl = new L.Control.Draw({
43
+ * edit: {
44
+ * featureGroup: drawnItems
45
+ * }
46
+ * });
47
+ * map.addControl(drawControl);
48
+ * ```
49
+ *
50
+ * The key here is the featureGroup option. This tells the plugin which FeatureGroup contains the layers that
51
+ * should be editable. The featureGroup can contain 0 or more features with geometry types Point, LineString, and Polygon.
52
+ * Leaflet.draw does not work with multigeometry features such as MultiPoint, MultiLineString, MultiPolygon,
53
+ * or GeometryCollection. If you need to add multigeometry features to the draw plugin, convert them to a
54
+ * FeatureCollection of non-multigeometries (Points, LineStrings, or Polygons).
55
+ */
56
+ L.Draw = {};
57
+
58
+ /**
59
+ * @class L.drawLocal
60
+ * @aka L.drawLocal
61
+ *
62
+ * The core toolbar class of the API — it is used to create the toolbar ui
63
+ *
64
+ * @example
65
+ * ```js
66
+ * var modifiedDraw = L.drawLocal.extend({
67
+ * draw: {
68
+ * toolbar: {
69
+ * buttons: {
70
+ * polygon: 'Draw an awesome polygon'
71
+ * }
72
+ * }
73
+ * }
74
+ * });
75
+ * ```
76
+ *
77
+ * The default state for the control is the draw toolbar just below the zoom control.
78
+ * This will allow map users to draw vectors and markers.
79
+ * **Please note the edit toolbar is not enabled by default.**
80
+ */
81
+ L.drawLocal = {
82
+ // format: {
83
+ // numeric: {
84
+ // delimiters: {
85
+ // thousands: ',',
86
+ // decimal: '.'
87
+ // }
88
+ // }
89
+ // },
90
+ draw: {
91
+ toolbar: {
92
+ // #TODO: this should be reorganized where actions are nested in actions
93
+ // ex: actions.undo or actions.cancel
94
+ actions: {
95
+ title: 'Cancel drawing',
96
+ text: 'Cancel'
97
+ },
98
+ finish: {
99
+ title: 'Finish drawing',
100
+ text: 'Finish'
101
+ },
102
+ undo: {
103
+ title: 'Delete last point drawn',
104
+ text: 'Delete last point'
105
+ },
106
+ buttons: {
107
+ polyline: 'Draw a polyline',
108
+ polygon: 'Draw a polygon',
109
+ rectangle: 'Draw a rectangle',
110
+ circle: 'Draw a circle',
111
+ marker: 'Draw a marker',
112
+ circlemarker: 'Draw a circlemarker'
113
+ }
114
+ },
115
+ handlers: {
116
+ circle: {
117
+ tooltip: {
118
+ start: 'Click and drag to draw circle.'
119
+ },
120
+ radius: 'Radius'
121
+ },
122
+ circlemarker: {
123
+ tooltip: {
124
+ start: 'Click map to place circle marker.'
125
+ }
126
+ },
127
+ marker: {
128
+ tooltip: {
129
+ start: 'Click map to place marker.'
130
+ }
131
+ },
132
+ polygon: {
133
+ tooltip: {
134
+ start: 'Click to start drawing shape.',
135
+ cont: 'Click to continue drawing shape.',
136
+ end: 'Click first point to close this shape.'
137
+ }
138
+ },
139
+ polyline: {
140
+ error: '<strong>Error:</strong> shape edges cannot cross!',
141
+ tooltip: {
142
+ start: 'Click to start drawing line.',
143
+ cont: 'Click to continue drawing line.',
144
+ end: 'Click last point to finish line.'
145
+ }
146
+ },
147
+ rectangle: {
148
+ tooltip: {
149
+ start: 'Click and drag to draw rectangle.'
150
+ }
151
+ },
152
+ simpleshape: {
153
+ tooltip: {
154
+ end: 'Release mouse to finish drawing.'
155
+ }
156
+ }
157
+ }
158
+ },
159
+ edit: {
160
+ toolbar: {
161
+ actions: {
162
+ save: {
163
+ title: 'Save changes',
164
+ text: 'Save'
165
+ },
166
+ cancel: {
167
+ title: 'Cancel editing, discards all changes',
168
+ text: 'Cancel'
169
+ },
170
+ clearAll:{
171
+ title: 'Clear all layers',
172
+ text: 'Clear All'
173
+ }
174
+ },
175
+ buttons: {
176
+ edit: 'Edit layers',
177
+ editDisabled: 'No layers to edit',
178
+ remove: 'Delete layers',
179
+ removeDisabled: 'No layers to delete'
180
+ }
181
+ },
182
+ handlers: {
183
+ edit: {
184
+ tooltip: {
185
+ text: 'Drag handles or markers to edit features.',
186
+ subtext: 'Click cancel to undo changes.'
187
+ }
188
+ },
189
+ remove: {
190
+ tooltip: {
191
+ text: 'Click on a feature to remove.'
192
+ }
193
+ }
194
+ }
195
+ }
196
+ };
197
+
198
+
199
+
200
+ /**
201
+ * ### Events
202
+ * Once you have successfully added the Leaflet.draw plugin to your map you will want to respond to the different
203
+ * actions users can initiate. The following events will be triggered on the map:
204
+ *
205
+ * @class L.Draw.Event
206
+ * @aka Draw.Event
207
+ *
208
+ * Use `L.Draw.Event.EVENTNAME` constants to ensure events are correct.
209
+ *
210
+ * @example
211
+ * ```js
212
+ * map.on(L.Draw.Event.CREATED; function (e) {
213
+ * var type = e.layerType,
214
+ * layer = e.layer;
215
+ *
216
+ * if (type === 'marker') {
217
+ * // Do marker specific actions
218
+ * }
219
+ *
220
+ * // Do whatever else you need to. (save to db; add to map etc)
221
+ * map.addLayer(layer);
222
+ *});
223
+ * ```
224
+ */
225
+ L.Draw.Event = {};
226
+ /**
227
+ * @event draw:created: PolyLine; Polygon; Rectangle; Circle; Marker | String
228
+ *
229
+ * Layer that was just created.
230
+ * The type of layer this is. One of: `polyline`; `polygon`; `rectangle`; `circle`; `marker`
231
+ * Triggered when a new vector or marker has been created.
232
+ *
233
+ */
234
+ L.Draw.Event.CREATED = 'draw:created';
235
+
236
+ /**
237
+ * @event draw:edited: LayerGroup
238
+ *
239
+ * List of all layers just edited on the map.
240
+ *
241
+ *
242
+ * Triggered when layers in the FeatureGroup; initialised with the plugin; have been edited and saved.
243
+ *
244
+ * @example
245
+ * ```js
246
+ * map.on('draw:edited', function (e) {
247
+ * var layers = e.layers;
248
+ * layers.eachLayer(function (layer) {
249
+ * //do whatever you want; most likely save back to db
250
+ * });
251
+ * });
252
+ * ```
253
+ */
254
+ L.Draw.Event.EDITED = 'draw:edited';
255
+
256
+ /**
257
+ * @event draw:deleted: LayerGroup
258
+ *
259
+ * List of all layers just removed from the map.
260
+ *
261
+ * Triggered when layers have been removed (and saved) from the FeatureGroup.
262
+ */
263
+ L.Draw.Event.DELETED = 'draw:deleted';
264
+
265
+ /**
266
+ * @event draw:drawstart: String
267
+ *
268
+ * The type of layer this is. One of:`polyline`; `polygon`; `rectangle`; `circle`; `marker`
269
+ *
270
+ * Triggered when the user has chosen to draw a particular vector or marker.
271
+ */
272
+ L.Draw.Event.DRAWSTART = 'draw:drawstart';
273
+
274
+ /**
275
+ * @event draw:drawstop: String
276
+ *
277
+ * The type of layer this is. One of: `polyline`; `polygon`; `rectangle`; `circle`; `marker`
278
+ *
279
+ * Triggered when the user has finished a particular vector or marker.
280
+ */
281
+
282
+ L.Draw.Event.DRAWSTOP = 'draw:drawstop';
283
+
284
+ /**
285
+ * @event draw:drawvertex: LayerGroup
286
+ *
287
+ * List of all layers just being added from the map.
288
+ *
289
+ * Triggered when a vertex is created on a polyline or polygon.
290
+ */
291
+ L.Draw.Event.DRAWVERTEX = 'draw:drawvertex';
292
+
293
+ /**
294
+ * @event draw:editstart: String
295
+ *
296
+ * The type of edit this is. One of: `edit`
297
+ *
298
+ * Triggered when the user starts edit mode by clicking the edit tool button.
299
+ */
300
+
301
+ L.Draw.Event.EDITSTART = 'draw:editstart';
302
+
303
+ /**
304
+ * @event draw:editmove: ILayer
305
+ *
306
+ * Layer that was just moved.
307
+ *
308
+ * Triggered as the user moves a rectangle; circle or marker.
309
+ */
310
+ L.Draw.Event.EDITMOVE = 'draw:editmove';
311
+
312
+ /**
313
+ * @event draw:editresize: ILayer
314
+ *
315
+ * Layer that was just moved.
316
+ *
317
+ * Triggered as the user resizes a rectangle or circle.
318
+ */
319
+ L.Draw.Event.EDITRESIZE = 'draw:editresize';
320
+
321
+ /**
322
+ * @event draw:editvertex: LayerGroup
323
+ *
324
+ * List of all layers just being edited from the map.
325
+ *
326
+ * Triggered when a vertex is edited on a polyline or polygon.
327
+ */
328
+ L.Draw.Event.EDITVERTEX = 'draw:editvertex';
329
+
330
+ /**
331
+ * @event draw:editstop: String
332
+ *
333
+ * The type of edit this is. One of: `edit`
334
+ *
335
+ * Triggered when the user has finshed editing (edit mode) and saves edits.
336
+ */
337
+ L.Draw.Event.EDITSTOP = 'draw:editstop';
338
+
339
+ /**
340
+ * @event draw:deletestart: String
341
+ *
342
+ * The type of edit this is. One of: `remove`
343
+ *
344
+ * Triggered when the user starts remove mode by clicking the remove tool button.
345
+ */
346
+ L.Draw.Event.DELETESTART = 'draw:deletestart';
347
+
348
+ /**
349
+ * @event draw:deletestop: String
350
+ *
351
+ * The type of edit this is. One of: `remove`
352
+ *
353
+ * Triggered when the user has finished removing shapes (remove mode) and saves.
354
+ */
355
+ L.Draw.Event.DELETESTOP = 'draw:deletestop';
356
+
357
+
358
+
359
+ L.Draw = L.Draw || {};
360
+
361
+ /**
362
+ * @class L.Draw.Feature
363
+ * @aka Draw.Feature
364
+ */
365
+ L.Draw.Feature = L.Handler.extend({
366
+ includes: L.Mixin.Events,
367
+
368
+ // @method initialize(): void
369
+ initialize: function (map, options) {
370
+ this._map = map;
371
+ this._container = map._container;
372
+ this._overlayPane = map._panes.overlayPane;
373
+ this._popupPane = map._panes.popupPane;
374
+
375
+ // Merge default shapeOptions options with custom shapeOptions
376
+ if (options && options.shapeOptions) {
377
+ options.shapeOptions = L.Util.extend({}, this.options.shapeOptions, options.shapeOptions);
378
+ }
379
+ L.setOptions(this, options);
380
+ },
381
+
382
+ // @method enable(): void
383
+ // Enables this handler
384
+ enable: function () {
385
+ if (this._enabled) {
386
+ return;
387
+ }
388
+
389
+ L.Handler.prototype.enable.call(this);
390
+
391
+ this.fire('enabled', { handler: this.type });
392
+
393
+ this._map.fire(L.Draw.Event.DRAWSTART, { layerType: this.type });
394
+ },
395
+
396
+ // @method disable(): void
397
+ disable: function () {
398
+ if (!this._enabled) {
399
+ return;
400
+ }
401
+
402
+ L.Handler.prototype.disable.call(this);
403
+
404
+ this._map.fire(L.Draw.Event.DRAWSTOP, { layerType: this.type });
405
+
406
+ this.fire('disabled', { handler: this.type });
407
+ },
408
+
409
+ // @method addHooks(): void
410
+ // Add's event listeners to this handler
411
+ addHooks: function () {
412
+ var map = this._map;
413
+
414
+ if (map) {
415
+ L.DomUtil.disableTextSelection();
416
+
417
+ map.getContainer().focus();
418
+
419
+ this._tooltip = new L.Draw.Tooltip(this._map);
420
+
421
+ L.DomEvent.on(this._container, 'keyup', this._cancelDrawing, this);
422
+ }
423
+ },
424
+
425
+ // @method removeHooks(): void
426
+ // Removes event listeners from this handler
427
+ removeHooks: function () {
428
+ if (this._map) {
429
+ L.DomUtil.enableTextSelection();
430
+
431
+ this._tooltip.dispose();
432
+ this._tooltip = null;
433
+
434
+ L.DomEvent.off(this._container, 'keyup', this._cancelDrawing, this);
435
+ }
436
+ },
437
+
438
+ // @method setOptions(object): void
439
+ // Sets new options to this handler
440
+ setOptions: function (options) {
441
+ L.setOptions(this, options);
442
+ },
443
+
444
+ _fireCreatedEvent: function (layer) {
445
+ this._map.fire(L.Draw.Event.CREATED, { layer: layer, layerType: this.type });
446
+ },
447
+
448
+ // Cancel drawing when the escape key is pressed
449
+ _cancelDrawing: function (e) {
450
+ if (e.keyCode === 27) {
451
+ this._map.fire('draw:canceled', { layerType: this.type });
452
+ this.disable();
453
+ }
454
+ }
455
+ });
456
+
457
+
458
+
459
+ /**
460
+ * @class L.Draw.Polyline
461
+ * @aka Draw.Polyline
462
+ * @inherits L.Draw.Feature
463
+ */
464
+ L.Draw.Polyline = L.Draw.Feature.extend({
465
+ statics: {
466
+ TYPE: 'polyline'
467
+ },
468
+
469
+ Poly: L.Polyline,
470
+
471
+ options: {
472
+ allowIntersection: true,
473
+ repeatMode: false,
474
+ drawError: {
475
+ color: '#b00b00',
476
+ timeout: 2500
477
+ },
478
+ icon: new L.DivIcon({
479
+ iconSize: new L.Point(8, 8),
480
+ className: 'leaflet-div-icon leaflet-editing-icon'
481
+ }),
482
+ touchIcon: new L.DivIcon({
483
+ iconSize: new L.Point(20, 20),
484
+ className: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon'
485
+ }),
486
+ guidelineDistance: 20,
487
+ maxGuideLineLength: 4000,
488
+ shapeOptions: {
489
+ stroke: true,
490
+ color: '#3388ff',
491
+ weight: 4,
492
+ opacity: 0.5,
493
+ fill: false,
494
+ clickable: true
495
+ },
496
+ metric: true, // Whether to use the metric measurement system or imperial
497
+ feet: true, // When not metric, to use feet instead of yards for display.
498
+ nautic: false, // When not metric, not feet use nautic mile for display
499
+ showLength: true, // Whether to display distance in the tooltip
500
+ zIndexOffset: 2000, // This should be > than the highest z-index any map layers
501
+ factor: 1 // To change distance calculation
502
+ },
503
+
504
+ // @method initialize(): void
505
+ initialize: function (map, options) {
506
+ // if touch, switch to touch icon
507
+ if (L.Browser.touch) {
508
+ this.options.icon = this.options.touchIcon;
509
+ }
510
+
511
+ // Need to set this here to ensure the correct message is used.
512
+ this.options.drawError.message = L.drawLocal.draw.handlers.polyline.error;
513
+
514
+ // Merge default drawError options with custom options
515
+ if (options && options.drawError) {
516
+ options.drawError = L.Util.extend({}, this.options.drawError, options.drawError);
517
+ }
518
+
519
+ // Save the type so super can fire, need to do this as cannot do this.TYPE :(
520
+ this.type = L.Draw.Polyline.TYPE;
521
+
522
+ L.Draw.Feature.prototype.initialize.call(this, map, options);
523
+ },
524
+
525
+ // @method addHooks(): void
526
+ // Add listener hooks to this handler
527
+ addHooks: function () {
528
+ L.Draw.Feature.prototype.addHooks.call(this);
529
+ if (this._map) {
530
+ this._markers = [];
531
+
532
+ this._markerGroup = new L.LayerGroup();
533
+ this._map.addLayer(this._markerGroup);
534
+
535
+ this._poly = new L.Polyline([], this.options.shapeOptions);
536
+
537
+ this._tooltip.updateContent(this._getTooltipText());
538
+
539
+ // Make a transparent marker that will used to catch click events. These click
540
+ // events will create the vertices. We need to do this so we can ensure that
541
+ // we can create vertices over other map layers (markers, vector layers). We
542
+ // also do not want to trigger any click handlers of objects we are clicking on
543
+ // while drawing.
544
+ if (!this._mouseMarker) {
545
+ this._mouseMarker = L.marker(this._map.getCenter(), {
546
+ icon: L.divIcon({
547
+ className: 'leaflet-mouse-marker',
548
+ iconAnchor: [20, 20],
549
+ iconSize: [40, 40]
550
+ }),
551
+ opacity: 0,
552
+ zIndexOffset: this.options.zIndexOffset
553
+ });
554
+ }
555
+
556
+ this._mouseMarker
557
+ .on('mouseout', this._onMouseOut, this)
558
+ .on('mousemove', this._onMouseMove, this) // Necessary to prevent 0.8 stutter
559
+ .on('mousedown', this._onMouseDown, this)
560
+ .on('mouseup', this._onMouseUp, this) // Necessary for 0.8 compatibility
561
+ .addTo(this._map);
562
+
563
+ this._map
564
+ .on('mouseup', this._onMouseUp, this) // Necessary for 0.7 compatibility
565
+ .on('mousemove', this._onMouseMove, this)
566
+ .on('zoomlevelschange', this._onZoomEnd, this)
567
+ .on('touchstart', this._onTouch, this)
568
+ .on('zoomend', this._onZoomEnd, this);
569
+
570
+ }
571
+ },
572
+
573
+ // @method removeHooks(): void
574
+ // Remove listener hooks from this handler.
575
+ removeHooks: function () {
576
+ L.Draw.Feature.prototype.removeHooks.call(this);
577
+
578
+ this._clearHideErrorTimeout();
579
+
580
+ this._cleanUpShape();
581
+
582
+ // remove markers from map
583
+ this._map.removeLayer(this._markerGroup);
584
+ delete this._markerGroup;
585
+ delete this._markers;
586
+
587
+ this._map.removeLayer(this._poly);
588
+ delete this._poly;
589
+
590
+ this._mouseMarker
591
+ .off('mousedown', this._onMouseDown, this)
592
+ .off('mouseout', this._onMouseOut, this)
593
+ .off('mouseup', this._onMouseUp, this)
594
+ .off('mousemove', this._onMouseMove, this);
595
+ this._map.removeLayer(this._mouseMarker);
596
+ delete this._mouseMarker;
597
+
598
+ // clean up DOM
599
+ this._clearGuides();
600
+
601
+ this._map
602
+ .off('mouseup', this._onMouseUp, this)
603
+ .off('mousemove', this._onMouseMove, this)
604
+ .off('zoomlevelschange', this._onZoomEnd, this)
605
+ .off('zoomend', this._onZoomEnd, this)
606
+ .off('touchstart', this._onTouch, this)
607
+ .off('click', this._onTouch, this);
608
+ },
609
+
610
+ // @method deleteLastVertex(): void
611
+ // Remove the last vertex from the polyline, removes polyline from map if only one point exists.
612
+ deleteLastVertex: function () {
613
+ if (this._markers.length <= 1) {
614
+ return;
615
+ }
616
+
617
+ var lastMarker = this._markers.pop(),
618
+ poly = this._poly,
619
+ // Replaces .spliceLatLngs()
620
+ latlngs = poly.getLatLngs(),
621
+ latlng = latlngs.splice(-1, 1)[0];
622
+ this._poly.setLatLngs(latlngs);
623
+
624
+ this._markerGroup.removeLayer(lastMarker);
625
+
626
+ if (poly.getLatLngs().length < 2) {
627
+ this._map.removeLayer(poly);
628
+ }
629
+
630
+ this._vertexChanged(latlng, false);
631
+ },
632
+
633
+ // @method addVertex(): void
634
+ // Add a vertex to the end of the polyline
635
+ addVertex: function (latlng) {
636
+ var markersLength = this._markers.length;
637
+ // markersLength must be greater than or equal to 2 before intersections can occur
638
+ if (markersLength >= 2 && !this.options.allowIntersection && this._poly.newLatLngIntersects(latlng)) {
639
+ this._showErrorTooltip();
640
+ return;
641
+ }
642
+ else if (this._errorShown) {
643
+ this._hideErrorTooltip();
644
+ }
645
+
646
+ this._markers.push(this._createMarker(latlng));
647
+
648
+ this._poly.addLatLng(latlng);
649
+
650
+ if (this._poly.getLatLngs().length === 2) {
651
+ this._map.addLayer(this._poly);
652
+ }
653
+
654
+ this._vertexChanged(latlng, true);
655
+ },
656
+
657
+ // @method completeShape(): void
658
+ // Closes the polyline between the first and last points
659
+ completeShape: function () {
660
+ if (this._markers.length <= 1) {
661
+ return;
662
+ }
663
+
664
+ this._fireCreatedEvent();
665
+ this.disable();
666
+
667
+ if (this.options.repeatMode) {
668
+ this.enable();
669
+ }
670
+ },
671
+
672
+ _finishShape: function () {
673
+ var latlngs = this._poly._defaultShape ? this._poly._defaultShape() : this._poly.getLatLngs();
674
+ var intersects = this._poly.newLatLngIntersects(latlngs[latlngs.length - 1]);
675
+
676
+ if ((!this.options.allowIntersection && intersects) || !this._shapeIsValid()) {
677
+ this._showErrorTooltip();
678
+ return;
679
+ }
680
+
681
+ this._fireCreatedEvent();
682
+ this.disable();
683
+ if (this.options.repeatMode) {
684
+ this.enable();
685
+ }
686
+ },
687
+
688
+ // Called to verify the shape is valid when the user tries to finish it
689
+ // Return false if the shape is not valid
690
+ _shapeIsValid: function () {
691
+ return true;
692
+ },
693
+
694
+ _onZoomEnd: function () {
695
+ if (this._markers !== null) {
696
+ this._updateGuide();
697
+ }
698
+ },
699
+
700
+ _onMouseMove: function (e) {
701
+ var newPos = this._map.mouseEventToLayerPoint(e.originalEvent);
702
+ var latlng = this._map.layerPointToLatLng(newPos);
703
+
704
+ // Save latlng
705
+ // should this be moved to _updateGuide() ?
706
+ this._currentLatLng = latlng;
707
+
708
+ this._updateTooltip(latlng);
709
+
710
+ // Update the guide line
711
+ this._updateGuide(newPos);
712
+
713
+ // Update the mouse marker position
714
+ this._mouseMarker.setLatLng(latlng);
715
+
716
+ L.DomEvent.preventDefault(e.originalEvent);
717
+ },
718
+
719
+ _vertexChanged: function (latlng, added) {
720
+ this._map.fire(L.Draw.Event.DRAWVERTEX, { layers: this._markerGroup });
721
+ this._updateFinishHandler();
722
+
723
+ this._updateRunningMeasure(latlng, added);
724
+
725
+ this._clearGuides();
726
+
727
+ this._updateTooltip();
728
+ },
729
+
730
+ _onMouseDown: function (e) {
731
+ if (!this._clickHandled && !this._touchHandled && !this._disableMarkers) {
732
+ this._onMouseMove(e);
733
+ this._clickHandled = true;
734
+ this._disableNewMarkers();
735
+ var originalEvent = e.originalEvent;
736
+ var clientX = originalEvent.clientX;
737
+ var clientY = originalEvent.clientY;
738
+ this._startPoint.call(this, clientX, clientY);
739
+ }
740
+ },
741
+
742
+ _startPoint: function (clientX, clientY) {
743
+ this._mouseDownOrigin = L.point(clientX, clientY);
744
+ },
745
+
746
+ _onMouseUp: function (e) {
747
+ var originalEvent = e.originalEvent;
748
+ var clientX = originalEvent.clientX;
749
+ var clientY = originalEvent.clientY;
750
+ this._endPoint.call(this, clientX, clientY, e);
751
+ this._clickHandled = null;
752
+ },
753
+
754
+ _endPoint: function (clientX, clientY, e) {
755
+ if (this._mouseDownOrigin) {
756
+ var dragCheckDistance = L.point(clientX, clientY)
757
+ .distanceTo(this._mouseDownOrigin);
758
+ var lastPtDistance = this._calculateFinishDistance(e.latlng);
759
+ if (lastPtDistance < 10 && L.Browser.touch) {
760
+ this._finishShape();
761
+ } else if (Math.abs(dragCheckDistance) < 9 * (window.devicePixelRatio || 1)) {
762
+ this.addVertex(e.latlng);
763
+ }
764
+ this._enableNewMarkers(); // after a short pause, enable new markers
765
+ }
766
+ this._mouseDownOrigin = null;
767
+ },
768
+
769
+ // ontouch prevented by clickHandled flag because some browsers fire both click/touch events,
770
+ // causing unwanted behavior
771
+ _onTouch: function (e) {
772
+ var originalEvent = e.originalEvent;
773
+ var clientX;
774
+ var clientY;
775
+ if (originalEvent.touches && originalEvent.touches[0] && !this._clickHandled && !this._touchHandled && !this._disableMarkers) {
776
+ clientX = originalEvent.touches[0].clientX;
777
+ clientY = originalEvent.touches[0].clientY;
778
+ this._disableNewMarkers();
779
+ this._touchHandled = true;
780
+ this._startPoint.call(this, clientX, clientY);
781
+ this._endPoint.call(this, clientX, clientY, e);
782
+ this._touchHandled = null;
783
+ }
784
+ this._clickHandled = null;
785
+ },
786
+
787
+ _onMouseOut: function () {
788
+ if (this._tooltip) {
789
+ this._tooltip._onMouseOut.call(this._tooltip);
790
+ }
791
+ },
792
+
793
+ // calculate if we are currently within close enough distance
794
+ // of the closing point (first point for shapes, last point for lines)
795
+ // this is semi-ugly code but the only reliable way i found to get the job done
796
+ // note: calculating point.distanceTo between mouseDownOrigin and last marker did NOT work
797
+ _calculateFinishDistance: function (potentialLatLng) {
798
+ var lastPtDistance
799
+ if (this._markers.length > 0) {
800
+ var finishMarker;
801
+ if (this.type === L.Draw.Polyline.TYPE) {
802
+ finishMarker = this._markers[this._markers.length - 1];
803
+ } else if (this.type === L.Draw.Polygon.TYPE) {
804
+ finishMarker = this._markers[0];
805
+ } else {
806
+ return Infinity;
807
+ }
808
+ var lastMarkerPoint = this._map.latLngToContainerPoint(finishMarker.getLatLng()),
809
+ potentialMarker = new L.Marker(potentialLatLng, {
810
+ icon: this.options.icon,
811
+ zIndexOffset: this.options.zIndexOffset * 2
812
+ });
813
+ var potentialMarkerPint = this._map.latLngToContainerPoint(potentialMarker.getLatLng());
814
+ lastPtDistance = lastMarkerPoint.distanceTo(potentialMarkerPint);
815
+ } else {
816
+ lastPtDistance = Infinity;
817
+ }
818
+ return lastPtDistance;
819
+ },
820
+
821
+ _updateFinishHandler: function () {
822
+ var markerCount = this._markers.length;
823
+ // The last marker should have a click handler to close the polyline
824
+ if (markerCount > 1) {
825
+ this._markers[markerCount - 1].on('click', this._finishShape, this);
826
+ }
827
+
828
+ // Remove the old marker click handler (as only the last point should close the polyline)
829
+ if (markerCount > 2) {
830
+ this._markers[markerCount - 2].off('click', this._finishShape, this);
831
+ }
832
+ },
833
+
834
+ _createMarker: function (latlng) {
835
+ var marker = new L.Marker(latlng, {
836
+ icon: this.options.icon,
837
+ zIndexOffset: this.options.zIndexOffset * 2
838
+ });
839
+
840
+ this._markerGroup.addLayer(marker);
841
+
842
+ return marker;
843
+ },
844
+
845
+ _updateGuide: function (newPos) {
846
+ var markerCount = this._markers ? this._markers.length : 0;
847
+
848
+ if (markerCount > 0) {
849
+ newPos = newPos || this._map.latLngToLayerPoint(this._currentLatLng);
850
+
851
+ // draw the guide line
852
+ this._clearGuides();
853
+ this._drawGuide(
854
+ this._map.latLngToLayerPoint(this._markers[markerCount - 1].getLatLng()),
855
+ newPos
856
+ );
857
+ }
858
+ },
859
+
860
+ _updateTooltip: function (latLng) {
861
+ var text = this._getTooltipText();
862
+
863
+ if (latLng) {
864
+ this._tooltip.updatePosition(latLng);
865
+ }
866
+
867
+ if (!this._errorShown) {
868
+ this._tooltip.updateContent(text);
869
+ }
870
+ },
871
+
872
+ _drawGuide: function (pointA, pointB) {
873
+ var length = Math.floor(Math.sqrt(Math.pow((pointB.x - pointA.x), 2) + Math.pow((pointB.y - pointA.y), 2))),
874
+ guidelineDistance = this.options.guidelineDistance,
875
+ maxGuideLineLength = this.options.maxGuideLineLength,
876
+ // Only draw a guideline with a max length
877
+ i = length > maxGuideLineLength ? length - maxGuideLineLength : guidelineDistance,
878
+ fraction,
879
+ dashPoint,
880
+ dash;
881
+
882
+ //create the guides container if we haven't yet
883
+ if (!this._guidesContainer) {
884
+ this._guidesContainer = L.DomUtil.create('div', 'leaflet-draw-guides', this._overlayPane);
885
+ }
886
+
887
+ //draw a dash every GuildeLineDistance
888
+ for (; i < length; i += this.options.guidelineDistance) {
889
+ //work out fraction along line we are
890
+ fraction = i / length;
891
+
892
+ //calculate new x,y point
893
+ dashPoint = {
894
+ x: Math.floor((pointA.x * (1 - fraction)) + (fraction * pointB.x)),
895
+ y: Math.floor((pointA.y * (1 - fraction)) + (fraction * pointB.y))
896
+ };
897
+
898
+ //add guide dash to guide container
899
+ dash = L.DomUtil.create('div', 'leaflet-draw-guide-dash', this._guidesContainer);
900
+ dash.style.backgroundColor =
901
+ !this._errorShown ? this.options.shapeOptions.color : this.options.drawError.color;
902
+
903
+ L.DomUtil.setPosition(dash, dashPoint);
904
+ }
905
+ },
906
+
907
+ _updateGuideColor: function (color) {
908
+ if (this._guidesContainer) {
909
+ for (var i = 0, l = this._guidesContainer.childNodes.length; i < l; i++) {
910
+ this._guidesContainer.childNodes[i].style.backgroundColor = color;
911
+ }
912
+ }
913
+ },
914
+
915
+ // removes all child elements (guide dashes) from the guides container
916
+ _clearGuides: function () {
917
+ if (this._guidesContainer) {
918
+ while (this._guidesContainer.firstChild) {
919
+ this._guidesContainer.removeChild(this._guidesContainer.firstChild);
920
+ }
921
+ }
922
+ },
923
+
924
+ _getTooltipText: function () {
925
+ var showLength = this.options.showLength,
926
+ labelText, distanceStr;
927
+ if (L.Browser.touch) {
928
+ showLength = false; // if there's a better place to put this, feel free to move it
929
+ }
930
+ if (this._markers.length === 0) {
931
+ labelText = {
932
+ text: L.drawLocal.draw.handlers.polyline.tooltip.start
933
+ };
934
+ } else {
935
+ distanceStr = showLength ? this._getMeasurementString() : '';
936
+
937
+ if (this._markers.length === 1) {
938
+ labelText = {
939
+ text: L.drawLocal.draw.handlers.polyline.tooltip.cont,
940
+ subtext: distanceStr
941
+ };
942
+ } else {
943
+ labelText = {
944
+ text: L.drawLocal.draw.handlers.polyline.tooltip.end,
945
+ subtext: distanceStr
946
+ };
947
+ }
948
+ }
949
+ return labelText;
950
+ },
951
+
952
+ _updateRunningMeasure: function (latlng, added) {
953
+ var markersLength = this._markers.length,
954
+ previousMarkerIndex, distance;
955
+
956
+ if (this._markers.length === 1) {
957
+ this._measurementRunningTotal = 0;
958
+ } else {
959
+ previousMarkerIndex = markersLength - (added ? 2 : 1);
960
+ distance = this._map.distance(latlng, this._markers[previousMarkerIndex].getLatLng()) * (this.options.factor || 1);
961
+
962
+ this._measurementRunningTotal += distance * (added ? 1 : -1);
963
+ }
964
+ },
965
+
966
+ _getMeasurementString: function () {
967
+ var currentLatLng = this._currentLatLng,
968
+ previousLatLng = this._markers[this._markers.length - 1].getLatLng(),
969
+ distance;
970
+
971
+ // calculate the distance from the last fixed point to the mouse position
972
+ distance = previousLatLng && currentLatLng ? this._measurementRunningTotal + this._map.distance(currentLatLng, previousLatLng) * (this.options.factor || 1) : this._measurementRunningTotal || 0 ;
973
+
974
+ return L.GeometryUtil.readableDistance(distance, this.options.metric, this.options.feet, this.options.nautic, this.options.precision);
975
+ },
976
+
977
+ _showErrorTooltip: function () {
978
+ this._errorShown = true;
979
+
980
+ // Update tooltip
981
+ this._tooltip
982
+ .showAsError()
983
+ .updateContent({ text: this.options.drawError.message });
984
+
985
+ // Update shape
986
+ this._updateGuideColor(this.options.drawError.color);
987
+ this._poly.setStyle({ color: this.options.drawError.color });
988
+
989
+ // Hide the error after 2 seconds
990
+ this._clearHideErrorTimeout();
991
+ this._hideErrorTimeout = setTimeout(L.Util.bind(this._hideErrorTooltip, this), this.options.drawError.timeout);
992
+ },
993
+
994
+ _hideErrorTooltip: function () {
995
+ this._errorShown = false;
996
+
997
+ this._clearHideErrorTimeout();
998
+
999
+ // Revert tooltip
1000
+ this._tooltip
1001
+ .removeError()
1002
+ .updateContent(this._getTooltipText());
1003
+
1004
+ // Revert shape
1005
+ this._updateGuideColor(this.options.shapeOptions.color);
1006
+ this._poly.setStyle({ color: this.options.shapeOptions.color });
1007
+ },
1008
+
1009
+ _clearHideErrorTimeout: function () {
1010
+ if (this._hideErrorTimeout) {
1011
+ clearTimeout(this._hideErrorTimeout);
1012
+ this._hideErrorTimeout = null;
1013
+ }
1014
+ },
1015
+
1016
+ // disable new markers temporarily;
1017
+ // this is to prevent duplicated touch/click events in some browsers
1018
+ _disableNewMarkers: function () {
1019
+ this._disableMarkers = true;
1020
+ },
1021
+
1022
+ // see _disableNewMarkers
1023
+ _enableNewMarkers: function () {
1024
+ setTimeout(function() {
1025
+ this._disableMarkers = false;
1026
+ }.bind(this), 50);
1027
+ },
1028
+
1029
+ _cleanUpShape: function () {
1030
+ if (this._markers.length > 1) {
1031
+ this._markers[this._markers.length - 1].off('click', this._finishShape, this);
1032
+ }
1033
+ },
1034
+
1035
+ _fireCreatedEvent: function () {
1036
+ var poly = new this.Poly(this._poly.getLatLngs(), this.options.shapeOptions);
1037
+ L.Draw.Feature.prototype._fireCreatedEvent.call(this, poly);
1038
+ }
1039
+ });
1040
+
1041
+
1042
+
1043
+ /**
1044
+ * @class L.Draw.Polygon
1045
+ * @aka Draw.Polygon
1046
+ * @inherits L.Draw.Polyline
1047
+ */
1048
+ L.Draw.Polygon = L.Draw.Polyline.extend({
1049
+ statics: {
1050
+ TYPE: 'polygon'
1051
+ },
1052
+
1053
+ Poly: L.Polygon,
1054
+
1055
+ options: {
1056
+ showArea: false,
1057
+ showLength: false,
1058
+ shapeOptions: {
1059
+ stroke: true,
1060
+ color: '#3388ff',
1061
+ weight: 4,
1062
+ opacity: 0.5,
1063
+ fill: true,
1064
+ fillColor: null, //same as color by default
1065
+ fillOpacity: 0.2,
1066
+ clickable: true
1067
+ },
1068
+ // Whether to use the metric measurement system (truthy) or not (falsy).
1069
+ // Also defines the units to use for the metric system as an array of
1070
+ // strings (e.g. `['ha', 'm']`).
1071
+ metric: true,
1072
+ feet: true, // When not metric, to use feet instead of yards for display.
1073
+ nautic: false, // When not metric, not feet use nautic mile for display
1074
+ // Defines the precision for each type of unit (e.g. {km: 2, ft: 0}
1075
+ precision: {}
1076
+ },
1077
+
1078
+ // @method initialize(): void
1079
+ initialize: function (map, options) {
1080
+ L.Draw.Polyline.prototype.initialize.call(this, map, options);
1081
+
1082
+ // Save the type so super can fire, need to do this as cannot do this.TYPE :(
1083
+ this.type = L.Draw.Polygon.TYPE;
1084
+ },
1085
+
1086
+ _updateFinishHandler: function () {
1087
+ var markerCount = this._markers.length;
1088
+
1089
+ // The first marker should have a click handler to close the polygon
1090
+ if (markerCount === 1) {
1091
+ this._markers[0].on('click', this._finishShape, this);
1092
+ }
1093
+
1094
+ // Add and update the double click handler
1095
+ if (markerCount > 2) {
1096
+ this._markers[markerCount - 1].on('dblclick', this._finishShape, this);
1097
+ // Only need to remove handler if has been added before
1098
+ if (markerCount > 3) {
1099
+ this._markers[markerCount - 2].off('dblclick', this._finishShape, this);
1100
+ }
1101
+ }
1102
+ },
1103
+
1104
+ _getTooltipText: function () {
1105
+ var text, subtext;
1106
+
1107
+ if (this._markers.length === 0) {
1108
+ text = L.drawLocal.draw.handlers.polygon.tooltip.start;
1109
+ } else if (this._markers.length < 3) {
1110
+ text = L.drawLocal.draw.handlers.polygon.tooltip.cont;
1111
+ subtext = this._getMeasurementString();
1112
+ } else {
1113
+ text = L.drawLocal.draw.handlers.polygon.tooltip.end;
1114
+ subtext = this._getMeasurementString();
1115
+ }
1116
+
1117
+ return {
1118
+ text: text,
1119
+ subtext: subtext
1120
+ };
1121
+ },
1122
+
1123
+ _getMeasurementString: function () {
1124
+ var area = this._area,
1125
+ measurementString = '';
1126
+
1127
+
1128
+ if (!area && !this.options.showLength) {
1129
+ return null;
1130
+ }
1131
+
1132
+ if (this.options.showLength) {
1133
+ measurementString = L.Draw.Polyline.prototype._getMeasurementString.call(this);
1134
+ }
1135
+
1136
+ if (area) {
1137
+ measurementString += '<br>' + L.GeometryUtil.readableArea(area, this.options.metric, this.options.precision);
1138
+ }
1139
+
1140
+ return measurementString;
1141
+ },
1142
+
1143
+ _shapeIsValid: function () {
1144
+ return this._markers.length >= 3;
1145
+ },
1146
+
1147
+ _vertexChanged: function (latlng, added) {
1148
+ var latLngs;
1149
+
1150
+ // Check to see if we should show the area
1151
+ if (!this.options.allowIntersection && this.options.showArea) {
1152
+ latLngs = this._poly.getLatLngs();
1153
+
1154
+ this._area = L.GeometryUtil.geodesicArea(latLngs);
1155
+ }
1156
+
1157
+ L.Draw.Polyline.prototype._vertexChanged.call(this, latlng, added);
1158
+ },
1159
+
1160
+ _cleanUpShape: function () {
1161
+ var markerCount = this._markers.length;
1162
+
1163
+ if (markerCount > 0) {
1164
+ this._markers[0].off('click', this._finishShape, this);
1165
+
1166
+ if (markerCount > 2) {
1167
+ this._markers[markerCount - 1].off('dblclick', this._finishShape, this);
1168
+ }
1169
+ }
1170
+ }
1171
+ });
1172
+
1173
+
1174
+
1175
+ L.SimpleShape = {};
1176
+ /**
1177
+ * @class L.Draw.SimpleShape
1178
+ * @aka Draw.SimpleShape
1179
+ * @inherits L.Draw.Feature
1180
+ */
1181
+ L.Draw.SimpleShape = L.Draw.Feature.extend({
1182
+ options: {
1183
+ repeatMode: false
1184
+ },
1185
+
1186
+ // @method initialize(): void
1187
+ initialize: function (map, options) {
1188
+ this._endLabelText = L.drawLocal.draw.handlers.simpleshape.tooltip.end;
1189
+
1190
+ L.Draw.Feature.prototype.initialize.call(this, map, options);
1191
+ },
1192
+
1193
+ // @method addHooks(): void
1194
+ // Add listener hooks to this handler.
1195
+ addHooks: function () {
1196
+ L.Draw.Feature.prototype.addHooks.call(this);
1197
+ if (this._map) {
1198
+ this._mapDraggable = this._map.dragging.enabled();
1199
+
1200
+ if (this._mapDraggable) {
1201
+ this._map.dragging.disable();
1202
+ }
1203
+
1204
+ //TODO refactor: move cursor to styles
1205
+ this._container.style.cursor = 'crosshair';
1206
+
1207
+ this._tooltip.updateContent({ text: this._initialLabelText });
1208
+
1209
+ this._map
1210
+ .on('mousedown', this._onMouseDown, this)
1211
+ .on('mousemove', this._onMouseMove, this)
1212
+ .on('touchstart', this._onMouseDown, this)
1213
+ .on('touchmove', this._onMouseMove, this);
1214
+ }
1215
+ },
1216
+
1217
+ // @method removeHooks(): void
1218
+ // Remove listener hooks from this handler.
1219
+ removeHooks: function () {
1220
+ L.Draw.Feature.prototype.removeHooks.call(this);
1221
+ if (this._map) {
1222
+ if (this._mapDraggable) {
1223
+ this._map.dragging.enable();
1224
+ }
1225
+
1226
+ //TODO refactor: move cursor to styles
1227
+ this._container.style.cursor = '';
1228
+
1229
+ this._map
1230
+ .off('mousedown', this._onMouseDown, this)
1231
+ .off('mousemove', this._onMouseMove, this)
1232
+ .off('touchstart', this._onMouseDown, this)
1233
+ .off('touchmove', this._onMouseMove, this);
1234
+
1235
+ L.DomEvent.off(document, 'mouseup', this._onMouseUp, this);
1236
+ L.DomEvent.off(document, 'touchend', this._onMouseUp, this);
1237
+
1238
+ // If the box element doesn't exist they must not have moved the mouse, so don't need to destroy/return
1239
+ if (this._shape) {
1240
+ this._map.removeLayer(this._shape);
1241
+ delete this._shape;
1242
+ }
1243
+ }
1244
+ this._isDrawing = false;
1245
+ },
1246
+
1247
+ _getTooltipText: function () {
1248
+ return {
1249
+ text: this._endLabelText
1250
+ };
1251
+ },
1252
+
1253
+ _onMouseDown: function (e) {
1254
+ this._isDrawing = true;
1255
+ this._startLatLng = e.latlng;
1256
+
1257
+ L.DomEvent
1258
+ .on(document, 'mouseup', this._onMouseUp, this)
1259
+ .on(document, 'touchend', this._onMouseUp, this)
1260
+ .preventDefault(e.originalEvent);
1261
+ },
1262
+
1263
+ _onMouseMove: function (e) {
1264
+ var latlng = e.latlng;
1265
+
1266
+ this._tooltip.updatePosition(latlng);
1267
+ if (this._isDrawing) {
1268
+ this._tooltip.updateContent(this._getTooltipText());
1269
+ this._drawShape(latlng);
1270
+ }
1271
+ },
1272
+
1273
+ _onMouseUp: function () {
1274
+ if (this._shape) {
1275
+ this._fireCreatedEvent();
1276
+ }
1277
+
1278
+ this.disable();
1279
+ if (this.options.repeatMode) {
1280
+ this.enable();
1281
+ }
1282
+ }
1283
+ });
1284
+
1285
+
1286
+ /**
1287
+ * @class L.Draw.Rectangle
1288
+ * @aka Draw.Rectangle
1289
+ * @inherits L.Draw.SimpleShape
1290
+ */
1291
+ L.Draw.Rectangle = L.Draw.SimpleShape.extend({
1292
+ statics: {
1293
+ TYPE: 'rectangle'
1294
+ },
1295
+
1296
+ options: {
1297
+ shapeOptions: {
1298
+ stroke: true,
1299
+ color: '#3388ff',
1300
+ weight: 4,
1301
+ opacity: 0.5,
1302
+ fill: true,
1303
+ fillColor: null, //same as color by default
1304
+ fillOpacity: 0.2,
1305
+ showArea: true,
1306
+ clickable: true
1307
+ },
1308
+ metric: true // Whether to use the metric measurement system or imperial
1309
+ },
1310
+
1311
+ // @method initialize(): void
1312
+ initialize: function (map, options) {
1313
+ // Save the type so super can fire, need to do this as cannot do this.TYPE :(
1314
+ this.type = L.Draw.Rectangle.TYPE;
1315
+
1316
+ this._initialLabelText = L.drawLocal.draw.handlers.rectangle.tooltip.start;
1317
+
1318
+ L.Draw.SimpleShape.prototype.initialize.call(this, map, options);
1319
+ },
1320
+
1321
+ // @method disable(): void
1322
+ disable: function () {
1323
+ if (!this._enabled) {
1324
+ return;
1325
+ }
1326
+
1327
+ this._isCurrentlyTwoClickDrawing = false;
1328
+ L.Draw.SimpleShape.prototype.disable.call(this);
1329
+ },
1330
+
1331
+ _onMouseUp: function (e) {
1332
+ if (!this._shape && !this._isCurrentlyTwoClickDrawing) {
1333
+ this._isCurrentlyTwoClickDrawing = true;
1334
+ return;
1335
+ }
1336
+
1337
+ // Make sure closing click is on map
1338
+ if (this._isCurrentlyTwoClickDrawing && !_hasAncestor(e.target, 'leaflet-pane')) {
1339
+ return;
1340
+ }
1341
+
1342
+ L.Draw.SimpleShape.prototype._onMouseUp.call(this);
1343
+ },
1344
+
1345
+ _drawShape: function (latlng) {
1346
+ if (!this._shape) {
1347
+ this._shape = new L.Rectangle(new L.LatLngBounds(this._startLatLng, latlng), this.options.shapeOptions);
1348
+ this._map.addLayer(this._shape);
1349
+ } else {
1350
+ this._shape.setBounds(new L.LatLngBounds(this._startLatLng, latlng));
1351
+ }
1352
+ },
1353
+
1354
+ _fireCreatedEvent: function () {
1355
+ var rectangle = new L.Rectangle(this._shape.getBounds(), this.options.shapeOptions);
1356
+ L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, rectangle);
1357
+ },
1358
+
1359
+ _getTooltipText: function () {
1360
+ var tooltipText = L.Draw.SimpleShape.prototype._getTooltipText.call(this),
1361
+ shape = this._shape,
1362
+ showArea = this.options.showArea,
1363
+ latLngs, area, subtext;
1364
+
1365
+ if (shape) {
1366
+ latLngs = this._shape._defaultShape ? this._shape._defaultShape() : this._shape.getLatLngs();
1367
+ area = L.GeometryUtil.geodesicArea(latLngs);
1368
+ subtext = showArea ? L.GeometryUtil.readableArea(area, this.options.metric) : ''
1369
+ }
1370
+
1371
+ return {
1372
+ text: tooltipText.text,
1373
+ subtext: subtext
1374
+ };
1375
+ }
1376
+ });
1377
+
1378
+ function _hasAncestor (el, cls) {
1379
+ while ((el = el.parentElement) && !el.classList.contains(cls));
1380
+ return el;
1381
+ }
1382
+
1383
+
1384
+
1385
+ /**
1386
+ * @class L.Draw.Marker
1387
+ * @aka Draw.Marker
1388
+ * @inherits L.Draw.Feature
1389
+ */
1390
+ L.Draw.Marker = L.Draw.Feature.extend({
1391
+ statics: {
1392
+ TYPE: 'marker'
1393
+ },
1394
+
1395
+ options: {
1396
+ icon: new L.Icon.Default(),
1397
+ repeatMode: false,
1398
+ zIndexOffset: 2000 // This should be > than the highest z-index any markers
1399
+ },
1400
+
1401
+ // @method initialize(): void
1402
+ initialize: function (map, options) {
1403
+ // Save the type so super can fire, need to do this as cannot do this.TYPE :(
1404
+ this.type = L.Draw.Marker.TYPE;
1405
+
1406
+ this._initialLabelText = L.drawLocal.draw.handlers.marker.tooltip.start;
1407
+
1408
+ L.Draw.Feature.prototype.initialize.call(this, map, options);
1409
+ },
1410
+
1411
+ // @method addHooks(): void
1412
+ // Add listener hooks to this handler.
1413
+ addHooks: function () {
1414
+ L.Draw.Feature.prototype.addHooks.call(this);
1415
+
1416
+ if (this._map) {
1417
+ this._tooltip.updateContent({ text: this._initialLabelText });
1418
+
1419
+ // Same mouseMarker as in Draw.Polyline
1420
+ if (!this._mouseMarker) {
1421
+ this._mouseMarker = L.marker(this._map.getCenter(), {
1422
+ icon: L.divIcon({
1423
+ className: 'leaflet-mouse-marker',
1424
+ iconAnchor: [20, 20],
1425
+ iconSize: [40, 40]
1426
+ }),
1427
+ opacity: 0,
1428
+ zIndexOffset: this.options.zIndexOffset
1429
+ });
1430
+ }
1431
+
1432
+ this._mouseMarker
1433
+ .on('click', this._onClick, this)
1434
+ .addTo(this._map);
1435
+
1436
+ this._map.on('mousemove', this._onMouseMove, this);
1437
+ this._map.on('click', this._onTouch, this);
1438
+ }
1439
+ },
1440
+
1441
+ // @method removeHooks(): void
1442
+ // Remove listener hooks from this handler.
1443
+ removeHooks: function () {
1444
+ L.Draw.Feature.prototype.removeHooks.call(this);
1445
+
1446
+ if (this._map) {
1447
+ if (this._marker) {
1448
+ this._marker.off('click', this._onClick, this);
1449
+ this._map
1450
+ .off('click', this._onClick, this)
1451
+ .off('click', this._onTouch, this)
1452
+ .removeLayer(this._marker);
1453
+ delete this._marker;
1454
+ }
1455
+
1456
+ this._mouseMarker.off('click', this._onClick, this);
1457
+ this._map.removeLayer(this._mouseMarker);
1458
+ delete this._mouseMarker;
1459
+
1460
+ this._map.off('mousemove', this._onMouseMove, this);
1461
+ }
1462
+ },
1463
+
1464
+ _onMouseMove: function (e) {
1465
+ var latlng = e.latlng;
1466
+
1467
+ this._tooltip.updatePosition(latlng);
1468
+ this._mouseMarker.setLatLng(latlng);
1469
+
1470
+ if (!this._marker) {
1471
+ this._marker = this._createMarker(latlng);
1472
+ // Bind to both marker and map to make sure we get the click event.
1473
+ this._marker.on('click', this._onClick, this);
1474
+ this._map
1475
+ .on('click', this._onClick, this)
1476
+ .addLayer(this._marker);
1477
+ }
1478
+ else {
1479
+ latlng = this._mouseMarker.getLatLng();
1480
+ this._marker.setLatLng(latlng);
1481
+ }
1482
+ },
1483
+
1484
+ _createMarker: function (latlng) {
1485
+ return new L.Marker(latlng, {
1486
+ icon: this.options.icon,
1487
+ zIndexOffset: this.options.zIndexOffset
1488
+ });
1489
+ },
1490
+
1491
+ _onClick: function () {
1492
+ this._fireCreatedEvent();
1493
+
1494
+ this.disable();
1495
+ if (this.options.repeatMode) {
1496
+ this.enable();
1497
+ }
1498
+ },
1499
+
1500
+ _onTouch: function (e) {
1501
+ // called on click & tap, only really does any thing on tap
1502
+ this._onMouseMove(e); // creates & places marker
1503
+ this._onClick(); // permanently places marker & ends interaction
1504
+ },
1505
+
1506
+ _fireCreatedEvent: function () {
1507
+ var marker = new L.Marker.Touch(this._marker.getLatLng(), { icon: this.options.icon });
1508
+ L.Draw.Feature.prototype._fireCreatedEvent.call(this, marker);
1509
+ }
1510
+ });
1511
+
1512
+
1513
+
1514
+ /**
1515
+ * @class L.Draw.CircleMarker
1516
+ * @aka Draw.CircleMarker
1517
+ * @inherits L.Draw.Marker
1518
+ */
1519
+ L.Draw.CircleMarker = L.Draw.Marker.extend({
1520
+ statics: {
1521
+ TYPE: 'circlemarker'
1522
+ },
1523
+
1524
+ options: {
1525
+ stroke: true,
1526
+ color: '#3388ff',
1527
+ weight: 4,
1528
+ opacity: 0.5,
1529
+ fill: true,
1530
+ fillColor: null, //same as color by default
1531
+ fillOpacity: 0.2,
1532
+ clickable: true,
1533
+ zIndexOffset: 2000 // This should be > than the highest z-index any markers
1534
+ },
1535
+
1536
+ // @method initialize(): void
1537
+ initialize: function (map, options) {
1538
+ // Save the type so super can fire, need to do this as cannot do this.TYPE :(
1539
+ this.type = L.Draw.CircleMarker.TYPE;
1540
+
1541
+ this._initialLabelText = L.drawLocal.draw.handlers.circlemarker.tooltip.start;
1542
+
1543
+ L.Draw.Feature.prototype.initialize.call(this, map, options);
1544
+ },
1545
+
1546
+
1547
+ _fireCreatedEvent: function () {
1548
+ var circleMarker = new L.CircleMarker(this._marker.getLatLng(), this.options);
1549
+ L.Draw.Feature.prototype._fireCreatedEvent.call(this, circleMarker);
1550
+ },
1551
+
1552
+ _createMarker: function (latlng) {
1553
+ return new L.CircleMarker(latlng, this.options);
1554
+ }
1555
+ });
1556
+
1557
+
1558
+
1559
+ /**
1560
+ * @class L.Draw.Circle
1561
+ * @aka Draw.Circle
1562
+ * @inherits L.Draw.SimpleShape
1563
+ */
1564
+ L.Draw.Circle = L.Draw.SimpleShape.extend({
1565
+ statics: {
1566
+ TYPE: 'circle'
1567
+ },
1568
+
1569
+ options: {
1570
+ shapeOptions: {
1571
+ stroke: true,
1572
+ color: '#3388ff',
1573
+ weight: 4,
1574
+ opacity: 0.5,
1575
+ fill: true,
1576
+ fillColor: null, //same as color by default
1577
+ fillOpacity: 0.2,
1578
+ clickable: true
1579
+ },
1580
+ showRadius: true,
1581
+ metric: true, // Whether to use the metric measurement system or imperial
1582
+ feet: true, // When not metric, use feet instead of yards for display
1583
+ nautic: false // When not metric, not feet use nautic mile for display
1584
+ },
1585
+
1586
+ // @method initialize(): void
1587
+ initialize: function (map, options) {
1588
+ // Save the type so super can fire, need to do this as cannot do this.TYPE :(
1589
+ this.type = L.Draw.Circle.TYPE;
1590
+
1591
+ this._initialLabelText = L.drawLocal.draw.handlers.circle.tooltip.start;
1592
+
1593
+ L.Draw.SimpleShape.prototype.initialize.call(this, map, options);
1594
+ },
1595
+
1596
+ _drawShape: function (latlng) {
1597
+ var distance = this._map.distance(this._startLatLng, latlng);
1598
+ if (!this._shape) {
1599
+ this._shape = new L.Circle(this._startLatLng, distance, this.options.shapeOptions);
1600
+ this._map.addLayer(this._shape);
1601
+ } else {
1602
+ this._shape.setRadius(distance);
1603
+ }
1604
+ },
1605
+
1606
+ _fireCreatedEvent: function () {
1607
+ var circle = new L.Circle(this._startLatLng, this._shape.getRadius(), this.options.shapeOptions);
1608
+ L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, circle);
1609
+ },
1610
+
1611
+ _onMouseMove: function (e) {
1612
+ var latlng = e.latlng,
1613
+ showRadius = this.options.showRadius,
1614
+ useMetric = this.options.metric,
1615
+ radius;
1616
+
1617
+ this._tooltip.updatePosition(latlng);
1618
+ if (this._isDrawing) {
1619
+ this._drawShape(latlng);
1620
+
1621
+ // Get the new radius (rounded to 1 dp)
1622
+ radius = this._shape.getRadius().toFixed(1);
1623
+
1624
+ var subtext = '';
1625
+ if (showRadius) {
1626
+ subtext = L.drawLocal.draw.handlers.circle.radius + ': ' +
1627
+ L.GeometryUtil.readableDistance(radius, useMetric, this.options.feet, this.options.nautic);
1628
+ }
1629
+ this._tooltip.updateContent({
1630
+ text: this._endLabelText,
1631
+ subtext: subtext
1632
+ });
1633
+ }
1634
+ }
1635
+ });
1636
+
1637
+
1638
+
1639
+ L.Edit = L.Edit || {};
1640
+
1641
+ /**
1642
+ * @class L.Edit.Marker
1643
+ * @aka Edit.Marker
1644
+ */
1645
+ L.Edit.Marker = L.Handler.extend({
1646
+ // @method initialize(): void
1647
+ initialize: function (marker, options) {
1648
+ this._marker = marker;
1649
+ L.setOptions(this, options);
1650
+ },
1651
+
1652
+ // @method addHooks(): void
1653
+ // Add listener hooks to this handler
1654
+ addHooks: function () {
1655
+ var marker = this._marker;
1656
+
1657
+ marker.dragging.enable();
1658
+ marker.on('dragend', this._onDragEnd, marker);
1659
+ this._toggleMarkerHighlight();
1660
+ },
1661
+
1662
+ // @method removeHooks(): void
1663
+ // Remove listener hooks from this handler
1664
+ removeHooks: function () {
1665
+ var marker = this._marker;
1666
+
1667
+ marker.dragging.disable();
1668
+ marker.off('dragend', this._onDragEnd, marker);
1669
+ this._toggleMarkerHighlight();
1670
+ },
1671
+
1672
+ _onDragEnd: function (e) {
1673
+ var layer = e.target;
1674
+ layer.edited = true;
1675
+ this._map.fire(L.Draw.Event.EDITMOVE, { layer: layer });
1676
+ },
1677
+
1678
+ _toggleMarkerHighlight: function () {
1679
+ var icon = this._marker._icon;
1680
+
1681
+ // Don't do anything if this layer is a marker but doesn't have an icon. Markers
1682
+ // should usually have icons. If using Leaflet.draw with Leaflet.markercluster there
1683
+ // is a chance that a marker doesn't.
1684
+ if (!icon) {
1685
+ return;
1686
+ }
1687
+
1688
+ // This is quite naughty, but I don't see another way of doing it. (short of setting a new icon)
1689
+ icon.style.display = 'none';
1690
+
1691
+ if (L.DomUtil.hasClass(icon, 'leaflet-edit-marker-selected')) {
1692
+ L.DomUtil.removeClass(icon, 'leaflet-edit-marker-selected');
1693
+ // Offset as the border will make the icon move.
1694
+ this._offsetMarker(icon, -4);
1695
+
1696
+ } else {
1697
+ L.DomUtil.addClass(icon, 'leaflet-edit-marker-selected');
1698
+ // Offset as the border will make the icon move.
1699
+ this._offsetMarker(icon, 4);
1700
+ }
1701
+
1702
+ icon.style.display = '';
1703
+ },
1704
+
1705
+ _offsetMarker: function (icon, offset) {
1706
+ var iconMarginTop = parseInt(icon.style.marginTop, 10) - offset,
1707
+ iconMarginLeft = parseInt(icon.style.marginLeft, 10) - offset;
1708
+
1709
+ icon.style.marginTop = iconMarginTop + 'px';
1710
+ icon.style.marginLeft = iconMarginLeft + 'px';
1711
+ }
1712
+ });
1713
+
1714
+ L.Marker.addInitHook(function () {
1715
+ if (L.Edit.Marker) {
1716
+ this.editing = new L.Edit.Marker(this);
1717
+
1718
+ if (this.options.editable) {
1719
+ this.editing.enable();
1720
+ }
1721
+ }
1722
+ });
1723
+
1724
+
1725
+
1726
+ L.Edit = L.Edit || {};
1727
+
1728
+ /**
1729
+ * @class L.Edit.Polyline
1730
+ * @aka L.Edit.Poly
1731
+ * @aka Edit.Poly
1732
+ */
1733
+ L.Edit.Poly = L.Handler.extend({
1734
+ options: {},
1735
+
1736
+ // @method initialize(): void
1737
+ initialize: function (poly, options) {
1738
+
1739
+ this.latlngs = [poly._latlngs];
1740
+ if (poly._holes) {
1741
+ this.latlngs = this.latlngs.concat(poly._holes);
1742
+ }
1743
+
1744
+ this._poly = poly;
1745
+ L.setOptions(this, options);
1746
+
1747
+ this._poly.on('revert-edited', this._updateLatLngs, this);
1748
+ },
1749
+
1750
+ // Compatibility method to normalize Poly* objects
1751
+ // between 0.7.x and 1.0+
1752
+ _defaultShape: function () {
1753
+ if (!L.Polyline._flat) {
1754
+ return this._poly._latlngs;
1755
+ }
1756
+ return L.Polyline._flat(this._poly._latlngs) ? this._poly._latlngs : this._poly._latlngs[0];
1757
+ },
1758
+
1759
+ _eachVertexHandler: function (callback) {
1760
+ for (var i = 0; i < this._verticesHandlers.length; i++) {
1761
+ callback(this._verticesHandlers[i]);
1762
+ }
1763
+ },
1764
+
1765
+ // @method addHooks(): void
1766
+ // Add listener hooks to this handler
1767
+ addHooks: function () {
1768
+ this._initHandlers();
1769
+ this._eachVertexHandler(function (handler) {
1770
+ handler.addHooks();
1771
+ });
1772
+ },
1773
+
1774
+ // @method removeHooks(): void
1775
+ // Remove listener hooks from this handler
1776
+ removeHooks: function () {
1777
+ this._eachVertexHandler(function (handler) {
1778
+ handler.removeHooks();
1779
+ });
1780
+ },
1781
+
1782
+ // @method updateMarkers(): void
1783
+ // Fire an update for each vertex handler
1784
+ updateMarkers: function () {
1785
+ this._eachVertexHandler(function (handler) {
1786
+ handler.updateMarkers();
1787
+ });
1788
+ },
1789
+
1790
+ _initHandlers: function () {
1791
+ this._verticesHandlers = [];
1792
+ for (var i = 0; i < this.latlngs.length; i++) {
1793
+ this._verticesHandlers.push(new L.Edit.PolyVerticesEdit(this._poly, this.latlngs[i], this.options));
1794
+ }
1795
+ },
1796
+
1797
+ _updateLatLngs: function (e) {
1798
+ this.latlngs = [e.layer._latlngs];
1799
+ if (e.layer._holes) {
1800
+ this.latlngs = this.latlngs.concat(e.layer._holes);
1801
+ }
1802
+ }
1803
+
1804
+ });
1805
+
1806
+ /**
1807
+ * @class L.Edit.PolyVerticesEdit
1808
+ * @aka Edit.PolyVerticesEdit
1809
+ */
1810
+ L.Edit.PolyVerticesEdit = L.Handler.extend({
1811
+ options: {
1812
+ icon: new L.DivIcon({
1813
+ iconSize: new L.Point(8, 8),
1814
+ className: 'leaflet-div-icon leaflet-editing-icon'
1815
+ }),
1816
+ touchIcon: new L.DivIcon({
1817
+ iconSize: new L.Point(20, 20),
1818
+ className: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon'
1819
+ }),
1820
+ drawError: {
1821
+ color: '#b00b00',
1822
+ timeout: 1000
1823
+ }
1824
+
1825
+
1826
+ },
1827
+
1828
+ // @method intialize(): void
1829
+ initialize: function (poly, latlngs, options) {
1830
+ // if touch, switch to touch icon
1831
+ if (L.Browser.touch) {
1832
+ this.options.icon = this.options.touchIcon;
1833
+ }
1834
+ this._poly = poly;
1835
+
1836
+ if (options && options.drawError) {
1837
+ options.drawError = L.Util.extend({}, this.options.drawError, options.drawError);
1838
+ }
1839
+
1840
+ this._latlngs = latlngs;
1841
+
1842
+ L.setOptions(this, options);
1843
+ },
1844
+
1845
+ // Compatibility method to normalize Poly* objects
1846
+ // between 0.7.x and 1.0+
1847
+ _defaultShape: function () {
1848
+ if (!L.Polyline._flat) {
1849
+ return this._latlngs;
1850
+ }
1851
+ return L.Polyline._flat(this._latlngs) ? this._latlngs : this._latlngs[0];
1852
+ },
1853
+
1854
+ // @method addHooks(): void
1855
+ // Add listener hooks to this handler.
1856
+ addHooks: function () {
1857
+ var poly = this._poly;
1858
+
1859
+ if (!(poly instanceof L.Polygon)) {
1860
+ poly.options.fill = false;
1861
+ if (poly.options.editing) {
1862
+ poly.options.editing.fill = false;
1863
+ }
1864
+ }
1865
+
1866
+ poly.setStyle(poly.options.editing);
1867
+
1868
+ if (this._poly._map) {
1869
+
1870
+ this._map = this._poly._map; // Set map
1871
+
1872
+ if (!this._markerGroup) {
1873
+ this._initMarkers();
1874
+ }
1875
+ this._poly._map.addLayer(this._markerGroup);
1876
+ }
1877
+ },
1878
+
1879
+ // @method removeHooks(): void
1880
+ // Remove listener hooks from this handler.
1881
+ removeHooks: function () {
1882
+ var poly = this._poly;
1883
+
1884
+ poly.setStyle(poly.options.original);
1885
+
1886
+ if (poly._map) {
1887
+ poly._map.removeLayer(this._markerGroup);
1888
+ delete this._markerGroup;
1889
+ delete this._markers;
1890
+ }
1891
+ },
1892
+
1893
+ // @method updateMarkers(): void
1894
+ // Clear markers and update their location
1895
+ updateMarkers: function () {
1896
+ this._markerGroup.clearLayers();
1897
+ this._initMarkers();
1898
+ },
1899
+
1900
+ _initMarkers: function () {
1901
+ if (!this._markerGroup) {
1902
+ this._markerGroup = new L.LayerGroup();
1903
+ }
1904
+ this._markers = [];
1905
+
1906
+ var latlngs = this._defaultShape(),
1907
+ i, j, len, marker;
1908
+
1909
+ for (i = 0, len = latlngs.length; i < len; i++) {
1910
+
1911
+ marker = this._createMarker(latlngs[i], i);
1912
+ marker.on('click', this._onMarkerClick, this);
1913
+ this._markers.push(marker);
1914
+ }
1915
+
1916
+ var markerLeft, markerRight;
1917
+
1918
+ for (i = 0, j = len - 1; i < len; j = i++) {
1919
+ if (i === 0 && !(L.Polygon && (this._poly instanceof L.Polygon))) {
1920
+ continue;
1921
+ }
1922
+
1923
+ markerLeft = this._markers[j];
1924
+ markerRight = this._markers[i];
1925
+
1926
+ this._createMiddleMarker(markerLeft, markerRight);
1927
+ this._updatePrevNext(markerLeft, markerRight);
1928
+ }
1929
+ },
1930
+
1931
+ _createMarker: function (latlng, index) {
1932
+ // Extending L.Marker in TouchEvents.js to include touch.
1933
+ var marker = new L.Marker.Touch(latlng, {
1934
+ draggable: true,
1935
+ icon: this.options.icon,
1936
+ });
1937
+
1938
+ marker._origLatLng = latlng;
1939
+ marker._index = index;
1940
+
1941
+ marker
1942
+ .on('dragstart', this._onMarkerDragStart, this)
1943
+ .on('drag', this._onMarkerDrag, this)
1944
+ .on('dragend', this._fireEdit, this)
1945
+ .on('touchmove', this._onTouchMove, this)
1946
+ .on('touchend', this._fireEdit, this)
1947
+ .on('MSPointerMove', this._onTouchMove, this)
1948
+ .on('MSPointerUp', this._fireEdit, this);
1949
+
1950
+ this._markerGroup.addLayer(marker);
1951
+
1952
+ return marker;
1953
+ },
1954
+
1955
+ _onMarkerDragStart: function () {
1956
+ this._poly.fire('editstart');
1957
+ },
1958
+
1959
+ _spliceLatLngs: function () {
1960
+ var latlngs = this._defaultShape();
1961
+ var removed = [].splice.apply(latlngs, arguments);
1962
+ this._poly._convertLatLngs(latlngs, true);
1963
+ this._poly.redraw();
1964
+ return removed;
1965
+ },
1966
+
1967
+ _removeMarker: function (marker) {
1968
+ var i = marker._index;
1969
+
1970
+ this._markerGroup.removeLayer(marker);
1971
+ this._markers.splice(i, 1);
1972
+ this._spliceLatLngs(i, 1);
1973
+ this._updateIndexes(i, -1);
1974
+
1975
+ marker
1976
+ .off('dragstart', this._onMarkerDragStart, this)
1977
+ .off('drag', this._onMarkerDrag, this)
1978
+ .off('dragend', this._fireEdit, this)
1979
+ .off('touchmove', this._onMarkerDrag, this)
1980
+ .off('touchend', this._fireEdit, this)
1981
+ .off('click', this._onMarkerClick, this)
1982
+ .off('MSPointerMove', this._onTouchMove, this)
1983
+ .off('MSPointerUp', this._fireEdit, this);
1984
+ },
1985
+
1986
+ _fireEdit: function () {
1987
+ this._poly.edited = true;
1988
+ this._poly.fire('edit');
1989
+ this._poly._map.fire(L.Draw.Event.EDITVERTEX, { layers: this._markerGroup, poly: this._poly });
1990
+ },
1991
+
1992
+ _onMarkerDrag: function (e) {
1993
+ var marker = e.target;
1994
+ var poly = this._poly;
1995
+
1996
+ L.extend(marker._origLatLng, marker._latlng);
1997
+
1998
+ if (marker._middleLeft) {
1999
+ marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker));
2000
+ }
2001
+ if (marker._middleRight) {
2002
+ marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next));
2003
+ }
2004
+
2005
+ if (poly.options.poly) {
2006
+ var tooltip = poly._map._editTooltip; // Access the tooltip
2007
+
2008
+ // If we don't allow intersections and the polygon intersects
2009
+ if (!poly.options.poly.allowIntersection && poly.intersects()) {
2010
+
2011
+ var originalColor = poly.options.color;
2012
+ poly.setStyle({ color: this.options.drawError.color });
2013
+
2014
+ // Manually trigger 'dragend' behavior on marker we are about to remove
2015
+ // WORKAROUND: introduced in 1.0.0-rc2, may be related to #4484
2016
+ if (L.version.indexOf('0.7') !== 0) {
2017
+ marker.dragging._draggable._onUp(e);
2018
+ }
2019
+ this._onMarkerClick(e); // Remove violating marker
2020
+ // FIXME: Reset the marker to it's original position (instead of remove)
2021
+
2022
+ if (tooltip) {
2023
+ tooltip.updateContent({
2024
+ text: L.drawLocal.draw.handlers.polyline.error
2025
+ });
2026
+ }
2027
+
2028
+ // Reset everything back to normal after a second
2029
+ setTimeout(function () {
2030
+ poly.setStyle({ color: originalColor });
2031
+ if (tooltip) {
2032
+ tooltip.updateContent({
2033
+ text: L.drawLocal.edit.handlers.edit.tooltip.text,
2034
+ subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext
2035
+ });
2036
+ }
2037
+ }, 1000);
2038
+ }
2039
+ }
2040
+
2041
+ this._poly.redraw();
2042
+ this._poly.fire('editdrag');
2043
+ },
2044
+
2045
+ _onMarkerClick: function (e) {
2046
+
2047
+ var minPoints = L.Polygon && (this._poly instanceof L.Polygon) ? 4 : 3,
2048
+ marker = e.target;
2049
+
2050
+ // If removing this point would create an invalid polyline/polygon don't remove
2051
+ if (this._defaultShape().length < minPoints) {
2052
+ return;
2053
+ }
2054
+
2055
+ // remove the marker
2056
+ this._removeMarker(marker);
2057
+
2058
+ // update prev/next links of adjacent markers
2059
+ this._updatePrevNext(marker._prev, marker._next);
2060
+
2061
+ // remove ghost markers near the removed marker
2062
+ if (marker._middleLeft) {
2063
+ this._markerGroup.removeLayer(marker._middleLeft);
2064
+ }
2065
+ if (marker._middleRight) {
2066
+ this._markerGroup.removeLayer(marker._middleRight);
2067
+ }
2068
+
2069
+ // create a ghost marker in place of the removed one
2070
+ if (marker._prev && marker._next) {
2071
+ this._createMiddleMarker(marker._prev, marker._next);
2072
+
2073
+ } else if (!marker._prev) {
2074
+ marker._next._middleLeft = null;
2075
+
2076
+ } else if (!marker._next) {
2077
+ marker._prev._middleRight = null;
2078
+ }
2079
+
2080
+ this._fireEdit();
2081
+ },
2082
+
2083
+ _onTouchMove: function (e) {
2084
+
2085
+ var layerPoint = this._map.mouseEventToLayerPoint(e.originalEvent.touches[0]),
2086
+ latlng = this._map.layerPointToLatLng(layerPoint),
2087
+ marker = e.target;
2088
+
2089
+ L.extend(marker._origLatLng, latlng);
2090
+
2091
+ if (marker._middleLeft) {
2092
+ marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker));
2093
+ }
2094
+ if (marker._middleRight) {
2095
+ marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next));
2096
+ }
2097
+
2098
+ this._poly.redraw();
2099
+ this.updateMarkers();
2100
+ },
2101
+
2102
+ _updateIndexes: function (index, delta) {
2103
+ this._markerGroup.eachLayer(function (marker) {
2104
+ if (marker._index > index) {
2105
+ marker._index += delta;
2106
+ }
2107
+ });
2108
+ },
2109
+
2110
+ _createMiddleMarker: function (marker1, marker2) {
2111
+ var latlng = this._getMiddleLatLng(marker1, marker2),
2112
+ marker = this._createMarker(latlng),
2113
+ onClick,
2114
+ onDragStart,
2115
+ onDragEnd;
2116
+
2117
+ marker.setOpacity(0.6);
2118
+
2119
+ marker1._middleRight = marker2._middleLeft = marker;
2120
+
2121
+ onDragStart = function () {
2122
+ marker.off('touchmove', onDragStart, this);
2123
+ var i = marker2._index;
2124
+
2125
+ marker._index = i;
2126
+
2127
+ marker
2128
+ .off('click', onClick, this)
2129
+ .on('click', this._onMarkerClick, this);
2130
+
2131
+ latlng.lat = marker.getLatLng().lat;
2132
+ latlng.lng = marker.getLatLng().lng;
2133
+ this._spliceLatLngs(i, 0, latlng);
2134
+ this._markers.splice(i, 0, marker);
2135
+
2136
+ marker.setOpacity(1);
2137
+
2138
+ this._updateIndexes(i, 1);
2139
+ marker2._index++;
2140
+ this._updatePrevNext(marker1, marker);
2141
+ this._updatePrevNext(marker, marker2);
2142
+
2143
+ this._poly.fire('editstart');
2144
+ };
2145
+
2146
+ onDragEnd = function () {
2147
+ marker.off('dragstart', onDragStart, this);
2148
+ marker.off('dragend', onDragEnd, this);
2149
+ marker.off('touchmove', onDragStart, this);
2150
+
2151
+ this._createMiddleMarker(marker1, marker);
2152
+ this._createMiddleMarker(marker, marker2);
2153
+ };
2154
+
2155
+ onClick = function () {
2156
+ onDragStart.call(this);
2157
+ onDragEnd.call(this);
2158
+ this._fireEdit();
2159
+ };
2160
+
2161
+ marker
2162
+ .on('click', onClick, this)
2163
+ .on('dragstart', onDragStart, this)
2164
+ .on('dragend', onDragEnd, this)
2165
+ .on('touchmove', onDragStart, this);
2166
+
2167
+ this._markerGroup.addLayer(marker);
2168
+ },
2169
+
2170
+ _updatePrevNext: function (marker1, marker2) {
2171
+ if (marker1) {
2172
+ marker1._next = marker2;
2173
+ }
2174
+ if (marker2) {
2175
+ marker2._prev = marker1;
2176
+ }
2177
+ },
2178
+
2179
+ _getMiddleLatLng: function (marker1, marker2) {
2180
+ var map = this._poly._map,
2181
+ p1 = map.project(marker1.getLatLng()),
2182
+ p2 = map.project(marker2.getLatLng());
2183
+
2184
+ return map.unproject(p1._add(p2)._divideBy(2));
2185
+ }
2186
+ });
2187
+
2188
+ L.Polyline.addInitHook(function () {
2189
+
2190
+ // Check to see if handler has already been initialized. This is to support versions of Leaflet that still have L.Handler.PolyEdit
2191
+ if (this.editing) {
2192
+ return;
2193
+ }
2194
+
2195
+ if (L.Edit.Poly) {
2196
+
2197
+ this.editing = new L.Edit.Poly(this, this.options.poly);
2198
+
2199
+ if (this.options.editable) {
2200
+ this.editing.enable();
2201
+ }
2202
+ }
2203
+
2204
+ this.on('add', function () {
2205
+ if (this.editing && this.editing.enabled()) {
2206
+ this.editing.addHooks();
2207
+ }
2208
+ });
2209
+
2210
+ this.on('remove', function () {
2211
+ if (this.editing && this.editing.enabled()) {
2212
+ this.editing.removeHooks();
2213
+ }
2214
+ });
2215
+ });
2216
+
2217
+
2218
+
2219
+ L.Edit = L.Edit || {};
2220
+ /**
2221
+ * @class L.Edit.SimpleShape
2222
+ * @aka Edit.SimpleShape
2223
+ */
2224
+ L.Edit.SimpleShape = L.Handler.extend({
2225
+ options: {
2226
+ moveIcon: new L.DivIcon({
2227
+ iconSize: new L.Point(8, 8),
2228
+ className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-move'
2229
+ }),
2230
+ resizeIcon: new L.DivIcon({
2231
+ iconSize: new L.Point(8, 8),
2232
+ className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-resize'
2233
+ }),
2234
+ touchMoveIcon: new L.DivIcon({
2235
+ iconSize: new L.Point(20, 20),
2236
+ className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-move leaflet-touch-icon'
2237
+ }),
2238
+ touchResizeIcon: new L.DivIcon({
2239
+ iconSize: new L.Point(20, 20),
2240
+ className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-resize leaflet-touch-icon'
2241
+ }),
2242
+ },
2243
+
2244
+ // @method intialize(): void
2245
+ initialize: function (shape, options) {
2246
+ // if touch, switch to touch icon
2247
+ if (L.Browser.touch) {
2248
+ this.options.moveIcon = this.options.touchMoveIcon;
2249
+ this.options.resizeIcon = this.options.touchResizeIcon;
2250
+ }
2251
+
2252
+ this._shape = shape;
2253
+ L.Util.setOptions(this, options);
2254
+ },
2255
+
2256
+ // @method addHooks(): void
2257
+ // Add listener hooks to this handler
2258
+ addHooks: function () {
2259
+ var shape = this._shape;
2260
+ if (this._shape._map) {
2261
+ this._map = this._shape._map;
2262
+ shape.setStyle(shape.options.editing);
2263
+
2264
+ if (shape._map) {
2265
+ this._map = shape._map;
2266
+ if (!this._markerGroup) {
2267
+ this._initMarkers();
2268
+ }
2269
+ this._map.addLayer(this._markerGroup);
2270
+ }
2271
+ }
2272
+ },
2273
+
2274
+ // @method removeHooks(): void
2275
+ // Remove listener hooks from this handler
2276
+ removeHooks: function () {
2277
+ var shape = this._shape;
2278
+
2279
+ shape.setStyle(shape.options.original);
2280
+
2281
+ if (shape._map) {
2282
+ this._unbindMarker(this._moveMarker);
2283
+
2284
+ for (var i = 0, l = this._resizeMarkers.length; i < l; i++) {
2285
+ this._unbindMarker(this._resizeMarkers[i]);
2286
+ }
2287
+ this._resizeMarkers = null;
2288
+
2289
+ this._map.removeLayer(this._markerGroup);
2290
+ delete this._markerGroup;
2291
+ }
2292
+
2293
+ this._map = null;
2294
+ },
2295
+
2296
+ // @method updateMarkers(): void
2297
+ // Remove the edit markers from this layer
2298
+ updateMarkers: function () {
2299
+ this._markerGroup.clearLayers();
2300
+ this._initMarkers();
2301
+ },
2302
+
2303
+ _initMarkers: function () {
2304
+ if (!this._markerGroup) {
2305
+ this._markerGroup = new L.LayerGroup();
2306
+ }
2307
+
2308
+ // Create center marker
2309
+ this._createMoveMarker();
2310
+
2311
+ // Create edge marker
2312
+ this._createResizeMarker();
2313
+ },
2314
+
2315
+ _createMoveMarker: function () {
2316
+ // Children override
2317
+ },
2318
+
2319
+ _createResizeMarker: function () {
2320
+ // Children override
2321
+ },
2322
+
2323
+ _createMarker: function (latlng, icon) {
2324
+ // Extending L.Marker in TouchEvents.js to include touch.
2325
+ var marker = new L.Marker.Touch(latlng, {
2326
+ draggable: true,
2327
+ icon: icon,
2328
+ zIndexOffset: 10
2329
+ });
2330
+
2331
+ this._bindMarker(marker);
2332
+
2333
+ this._markerGroup.addLayer(marker);
2334
+
2335
+ return marker;
2336
+ },
2337
+
2338
+ _bindMarker: function (marker) {
2339
+ marker
2340
+ .on('dragstart', this._onMarkerDragStart, this)
2341
+ .on('drag', this._onMarkerDrag, this)
2342
+ .on('dragend', this._onMarkerDragEnd, this)
2343
+ .on('touchstart', this._onTouchStart, this)
2344
+ .on('touchmove', this._onTouchMove, this)
2345
+ .on('MSPointerMove', this._onTouchMove, this)
2346
+ .on('touchend', this._onTouchEnd, this)
2347
+ .on('MSPointerUp', this._onTouchEnd, this);
2348
+ },
2349
+
2350
+ _unbindMarker: function (marker) {
2351
+ marker
2352
+ .off('dragstart', this._onMarkerDragStart, this)
2353
+ .off('drag', this._onMarkerDrag, this)
2354
+ .off('dragend', this._onMarkerDragEnd, this)
2355
+ .off('touchstart', this._onTouchStart, this)
2356
+ .off('touchmove', this._onTouchMove, this)
2357
+ .off('MSPointerMove', this._onTouchMove, this)
2358
+ .off('touchend', this._onTouchEnd, this)
2359
+ .off('MSPointerUp', this._onTouchEnd, this);
2360
+ },
2361
+
2362
+ _onMarkerDragStart: function (e) {
2363
+ var marker = e.target;
2364
+ marker.setOpacity(0);
2365
+
2366
+ this._shape.fire('editstart');
2367
+ },
2368
+
2369
+ _fireEdit: function () {
2370
+ this._shape.edited = true;
2371
+ this._shape.fire('edit');
2372
+ },
2373
+
2374
+ _onMarkerDrag: function (e) {
2375
+ var marker = e.target,
2376
+ latlng = marker.getLatLng();
2377
+
2378
+ if (marker === this._moveMarker) {
2379
+ this._move(latlng);
2380
+ } else {
2381
+ this._resize(latlng);
2382
+ }
2383
+
2384
+ this._shape.redraw();
2385
+ this._shape.fire('editdrag');
2386
+ },
2387
+
2388
+ _onMarkerDragEnd: function (e) {
2389
+ var marker = e.target;
2390
+ marker.setOpacity(1);
2391
+
2392
+ this._fireEdit();
2393
+ },
2394
+
2395
+ _onTouchStart: function (e) {
2396
+ L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, e);
2397
+
2398
+ if (typeof(this._getCorners) === 'function') {
2399
+ // Save a reference to the opposite point
2400
+ var corners = this._getCorners(),
2401
+ marker = e.target,
2402
+ currentCornerIndex = marker._cornerIndex;
2403
+
2404
+ marker.setOpacity(0);
2405
+
2406
+ // Copyed from Edit.Rectangle.js line 23 _onMarkerDragStart()
2407
+ // Latlng is null otherwise.
2408
+ this._oppositeCorner = corners[(currentCornerIndex + 2) % 4];
2409
+ this._toggleCornerMarkers(0, currentCornerIndex);
2410
+ }
2411
+
2412
+ this._shape.fire('editstart');
2413
+ },
2414
+
2415
+ _onTouchMove: function (e) {
2416
+ var layerPoint = this._map.mouseEventToLayerPoint(e.originalEvent.touches[0]),
2417
+ latlng = this._map.layerPointToLatLng(layerPoint),
2418
+ marker = e.target;
2419
+
2420
+ if (marker === this._moveMarker) {
2421
+ this._move(latlng);
2422
+ } else {
2423
+ this._resize(latlng);
2424
+ }
2425
+
2426
+ this._shape.redraw();
2427
+
2428
+ // prevent touchcancel in IOS
2429
+ // e.preventDefault();
2430
+ return false;
2431
+ },
2432
+
2433
+ _onTouchEnd: function (e) {
2434
+ var marker = e.target;
2435
+ marker.setOpacity(1);
2436
+ this.updateMarkers();
2437
+ this._fireEdit();
2438
+ },
2439
+
2440
+ _move: function () {
2441
+ // Children override
2442
+ },
2443
+
2444
+ _resize: function () {
2445
+ // Children override
2446
+ }
2447
+ });
2448
+
2449
+
2450
+
2451
+ L.Edit = L.Edit || {};
2452
+ /**
2453
+ * @class L.Edit.Rectangle
2454
+ * @aka Edit.Rectangle
2455
+ * @inherits L.Edit.SimpleShape
2456
+ */
2457
+ L.Edit.Rectangle = L.Edit.SimpleShape.extend({
2458
+ _createMoveMarker: function () {
2459
+ var bounds = this._shape.getBounds(),
2460
+ center = bounds.getCenter();
2461
+
2462
+ this._moveMarker = this._createMarker(center, this.options.moveIcon);
2463
+ },
2464
+
2465
+ _createResizeMarker: function () {
2466
+ var corners = this._getCorners();
2467
+
2468
+ this._resizeMarkers = [];
2469
+
2470
+ for (var i = 0, l = corners.length; i < l; i++) {
2471
+ this._resizeMarkers.push(this._createMarker(corners[i], this.options.resizeIcon));
2472
+ // Monkey in the corner index as we will need to know this for dragging
2473
+ this._resizeMarkers[i]._cornerIndex = i;
2474
+ }
2475
+ },
2476
+
2477
+ _onMarkerDragStart: function (e) {
2478
+ L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, e);
2479
+
2480
+ // Save a reference to the opposite point
2481
+ var corners = this._getCorners(),
2482
+ marker = e.target,
2483
+ currentCornerIndex = marker._cornerIndex;
2484
+
2485
+ this._oppositeCorner = corners[(currentCornerIndex + 2) % 4];
2486
+
2487
+ this._toggleCornerMarkers(0, currentCornerIndex);
2488
+ },
2489
+
2490
+ _onMarkerDragEnd: function (e) {
2491
+ var marker = e.target,
2492
+ bounds, center;
2493
+
2494
+ // Reset move marker position to the center
2495
+ if (marker === this._moveMarker) {
2496
+ bounds = this._shape.getBounds();
2497
+ center = bounds.getCenter();
2498
+
2499
+ marker.setLatLng(center);
2500
+ }
2501
+
2502
+ this._toggleCornerMarkers(1);
2503
+
2504
+ this._repositionCornerMarkers();
2505
+
2506
+ L.Edit.SimpleShape.prototype._onMarkerDragEnd.call(this, e);
2507
+ },
2508
+
2509
+ _move: function (newCenter) {
2510
+ var latlngs = this._shape._defaultShape ? this._shape._defaultShape() : this._shape.getLatLngs(),
2511
+ bounds = this._shape.getBounds(),
2512
+ center = bounds.getCenter(),
2513
+ offset, newLatLngs = [];
2514
+
2515
+ // Offset the latlngs to the new center
2516
+ for (var i = 0, l = latlngs.length; i < l; i++) {
2517
+ offset = [latlngs[i].lat - center.lat, latlngs[i].lng - center.lng];
2518
+ newLatLngs.push([newCenter.lat + offset[0], newCenter.lng + offset[1]]);
2519
+ }
2520
+
2521
+ this._shape.setLatLngs(newLatLngs);
2522
+
2523
+ // Reposition the resize markers
2524
+ this._repositionCornerMarkers();
2525
+
2526
+ this._map.fire(L.Draw.Event.EDITMOVE, { layer: this._shape });
2527
+ },
2528
+
2529
+ _resize: function (latlng) {
2530
+ var bounds;
2531
+
2532
+ // Update the shape based on the current position of this corner and the opposite point
2533
+ this._shape.setBounds(L.latLngBounds(latlng, this._oppositeCorner));
2534
+
2535
+ // Reposition the move marker
2536
+ bounds = this._shape.getBounds();
2537
+ this._moveMarker.setLatLng(bounds.getCenter());
2538
+
2539
+ this._map.fire(L.Draw.Event.EDITRESIZE, { layer: this._shape });
2540
+ },
2541
+
2542
+ _getCorners: function () {
2543
+ var bounds = this._shape.getBounds(),
2544
+ nw = bounds.getNorthWest(),
2545
+ ne = bounds.getNorthEast(),
2546
+ se = bounds.getSouthEast(),
2547
+ sw = bounds.getSouthWest();
2548
+
2549
+ return [nw, ne, se, sw];
2550
+ },
2551
+
2552
+ _toggleCornerMarkers: function (opacity) {
2553
+ for (var i = 0, l = this._resizeMarkers.length; i < l; i++) {
2554
+ this._resizeMarkers[i].setOpacity(opacity);
2555
+ }
2556
+ },
2557
+
2558
+ _repositionCornerMarkers: function () {
2559
+ var corners = this._getCorners();
2560
+
2561
+ for (var i = 0, l = this._resizeMarkers.length; i < l; i++) {
2562
+ this._resizeMarkers[i].setLatLng(corners[i]);
2563
+ }
2564
+ }
2565
+ });
2566
+
2567
+ L.Rectangle.addInitHook(function () {
2568
+ if (L.Edit.Rectangle) {
2569
+ this.editing = new L.Edit.Rectangle(this);
2570
+
2571
+ if (this.options.editable) {
2572
+ this.editing.enable();
2573
+ }
2574
+ }
2575
+ });
2576
+
2577
+
2578
+
2579
+ L.Edit = L.Edit || {};
2580
+ /**
2581
+ * @class L.Edit.CircleMarker
2582
+ * @aka Edit.Circle
2583
+ * @inherits L.Edit.SimpleShape
2584
+ */
2585
+ L.Edit.CircleMarker = L.Edit.SimpleShape.extend({
2586
+ _createMoveMarker: function () {
2587
+ var center = this._shape.getLatLng();
2588
+
2589
+ this._moveMarker = this._createMarker(center, this.options.moveIcon);
2590
+ },
2591
+
2592
+ _createResizeMarker: function () {
2593
+ // To avoid an undefined check in L.Edit.SimpleShape.removeHooks
2594
+ this._resizeMarkers = [];
2595
+ },
2596
+
2597
+ _move: function (latlng) {
2598
+ if (this._resizeMarkers.length) {
2599
+ var resizemarkerPoint = this._getResizeMarkerPoint(latlng);
2600
+ // Move the resize marker
2601
+ this._resizeMarkers[0].setLatLng(resizemarkerPoint);
2602
+ }
2603
+
2604
+ // Move the circle
2605
+ this._shape.setLatLng(latlng);
2606
+
2607
+ this._map.fire(L.Draw.Event.EDITMOVE, { layer: this._shape });
2608
+ },
2609
+ });
2610
+
2611
+ L.CircleMarker.addInitHook(function () {
2612
+ if (L.Edit.CircleMarker) {
2613
+ this.editing = new L.Edit.CircleMarker(this);
2614
+
2615
+ if (this.options.editable) {
2616
+ this.editing.enable();
2617
+ }
2618
+ }
2619
+
2620
+ this.on('add', function () {
2621
+ if (this.editing && this.editing.enabled()) {
2622
+ this.editing.addHooks();
2623
+ }
2624
+ });
2625
+
2626
+ this.on('remove', function () {
2627
+ if (this.editing && this.editing.enabled()) {
2628
+ this.editing.removeHooks();
2629
+ }
2630
+ });
2631
+ });
2632
+
2633
+
2634
+
2635
+ L.Edit = L.Edit || {};
2636
+ /**
2637
+ * @class L.Edit.Circle
2638
+ * @aka Edit.Circle
2639
+ * @inherits L.Edit.CircleMarker
2640
+ */
2641
+ L.Edit.Circle = L.Edit.CircleMarker.extend({
2642
+
2643
+ _createResizeMarker: function () {
2644
+ var center = this._shape.getLatLng(),
2645
+ resizemarkerPoint = this._getResizeMarkerPoint(center);
2646
+
2647
+ this._resizeMarkers = [];
2648
+ this._resizeMarkers.push(this._createMarker(resizemarkerPoint, this.options.resizeIcon));
2649
+ },
2650
+
2651
+ _getResizeMarkerPoint: function (latlng) {
2652
+ // From L.shape.getBounds()
2653
+ var delta = this._shape._radius * Math.cos(Math.PI / 4),
2654
+ point = this._map.project(latlng);
2655
+ return this._map.unproject([point.x + delta, point.y - delta]);
2656
+ },
2657
+
2658
+ _resize: function (latlng) {
2659
+ var moveLatLng = this._moveMarker.getLatLng(),
2660
+ radius = this._map.distance(moveLatLng, latlng);
2661
+
2662
+ this._shape.setRadius(radius);
2663
+
2664
+ this._map.fire(L.Draw.Event.EDITRESIZE, { layer: this._shape });
2665
+ }
2666
+ });
2667
+
2668
+ L.Circle.addInitHook(function () {
2669
+ if (L.Edit.Circle) {
2670
+ this.editing = new L.Edit.Circle(this);
2671
+
2672
+ if (this.options.editable) {
2673
+ this.editing.enable();
2674
+ }
2675
+ }
2676
+
2677
+ this.on('add', function () {
2678
+ if (this.editing && this.editing.enabled()) {
2679
+ this.editing.addHooks();
2680
+ }
2681
+ });
2682
+
2683
+ this.on('remove', function () {
2684
+ if (this.editing && this.editing.enabled()) {
2685
+ this.editing.removeHooks();
2686
+ }
2687
+ });
2688
+ });
2689
+
2690
+
2691
+
2692
+ L.Map.mergeOptions({
2693
+ touchExtend: true
2694
+ });
2695
+
2696
+ /**
2697
+ * @class L.Map.TouchExtend
2698
+ * @aka TouchExtend
2699
+ */
2700
+ L.Map.TouchExtend = L.Handler.extend({
2701
+
2702
+ // @method initialize(): void
2703
+ // Sets TouchExtend private accessor variables
2704
+ initialize: function (map) {
2705
+ this._map = map;
2706
+ this._container = map._container;
2707
+ this._pane = map._panes.overlayPane;
2708
+ },
2709
+
2710
+ // @method addHooks(): void
2711
+ // Adds dom listener events to the map container
2712
+ addHooks: function () {
2713
+ L.DomEvent.on(this._container, 'touchstart', this._onTouchStart, this);
2714
+ L.DomEvent.on(this._container, 'touchend', this._onTouchEnd, this);
2715
+ L.DomEvent.on(this._container, 'touchmove', this._onTouchMove, this);
2716
+ if (this._detectIE()) {
2717
+ L.DomEvent.on(this._container, 'MSPointerDown', this._onTouchStart, this);
2718
+ L.DomEvent.on(this._container, 'MSPointerUp', this._onTouchEnd, this);
2719
+ L.DomEvent.on(this._container, 'MSPointerMove', this._onTouchMove, this);
2720
+ L.DomEvent.on(this._container, 'MSPointerCancel', this._onTouchCancel, this);
2721
+
2722
+ } else {
2723
+ L.DomEvent.on(this._container, 'touchcancel', this._onTouchCancel, this);
2724
+ L.DomEvent.on(this._container, 'touchleave', this._onTouchLeave, this);
2725
+ }
2726
+ },
2727
+
2728
+ // @method removeHooks(): void
2729
+ // Removes dom listener events from the map container
2730
+ removeHooks: function () {
2731
+ L.DomEvent.off(this._container, 'touchstart', this._onTouchStart);
2732
+ L.DomEvent.off(this._container, 'touchend', this._onTouchEnd);
2733
+ L.DomEvent.off(this._container, 'touchmove', this._onTouchMove);
2734
+ if (this._detectIE()) {
2735
+ L.DomEvent.off(this._container, 'MSPointerDowm', this._onTouchStart);
2736
+ L.DomEvent.off(this._container, 'MSPointerUp', this._onTouchEnd);
2737
+ L.DomEvent.off(this._container, 'MSPointerMove', this._onTouchMove);
2738
+ L.DomEvent.off(this._container, 'MSPointerCancel', this._onTouchCancel);
2739
+ } else {
2740
+ L.DomEvent.off(this._container, 'touchcancel', this._onTouchCancel);
2741
+ L.DomEvent.off(this._container, 'touchleave', this._onTouchLeave);
2742
+ }
2743
+ },
2744
+
2745
+ _touchEvent: function (e, type) {
2746
+ // #TODO: fix the pageX error that is do a bug in Android where a single touch triggers two click events
2747
+ // _filterClick is what leaflet uses as a workaround.
2748
+ // This is a problem with more things than just android. Another problem is touchEnd has no touches in
2749
+ // its touch list.
2750
+ var touchEvent = {};
2751
+ if (typeof e.touches !== 'undefined') {
2752
+ if (!e.touches.length) {
2753
+ return;
2754
+ }
2755
+ touchEvent = e.touches[0];
2756
+ } else if (e.pointerType === 'touch') {
2757
+ touchEvent = e;
2758
+ if (!this._filterClick(e)) {
2759
+ return;
2760
+ }
2761
+ } else {
2762
+ return;
2763
+ }
2764
+
2765
+ var containerPoint = this._map.mouseEventToContainerPoint(touchEvent),
2766
+ layerPoint = this._map.mouseEventToLayerPoint(touchEvent),
2767
+ latlng = this._map.layerPointToLatLng(layerPoint);
2768
+
2769
+ this._map.fire(type, {
2770
+ latlng: latlng,
2771
+ layerPoint: layerPoint,
2772
+ containerPoint: containerPoint,
2773
+ pageX: touchEvent.pageX,
2774
+ pageY: touchEvent.pageY,
2775
+ originalEvent: e
2776
+ });
2777
+ },
2778
+
2779
+ /** Borrowed from Leaflet and modified for bool ops **/
2780
+ _filterClick: function (e) {
2781
+ var timeStamp = (e.timeStamp || e.originalEvent.timeStamp),
2782
+ elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick);
2783
+
2784
+ // are they closer together than 500ms yet more than 100ms?
2785
+ // Android typically triggers them ~300ms apart while multiple listeners
2786
+ // on the same event should be triggered far faster;
2787
+ // or check if click is simulated on the element, and if it is, reject any non-simulated events
2788
+ if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
2789
+ L.DomEvent.stop(e);
2790
+ return false;
2791
+ }
2792
+ L.DomEvent._lastClick = timeStamp;
2793
+ return true;
2794
+ },
2795
+
2796
+ _onTouchStart: function (e) {
2797
+ if (!this._map._loaded) {
2798
+ return;
2799
+ }
2800
+
2801
+ var type = 'touchstart';
2802
+ this._touchEvent(e, type);
2803
+
2804
+ },
2805
+
2806
+ _onTouchEnd: function (e) {
2807
+ if (!this._map._loaded) {
2808
+ return;
2809
+ }
2810
+
2811
+ var type = 'touchend';
2812
+ this._touchEvent(e, type);
2813
+ },
2814
+
2815
+ _onTouchCancel: function (e) {
2816
+ if (!this._map._loaded) {
2817
+ return;
2818
+ }
2819
+
2820
+ var type = 'touchcancel';
2821
+ if (this._detectIE()) {
2822
+ type = 'pointercancel';
2823
+ }
2824
+ this._touchEvent(e, type);
2825
+ },
2826
+
2827
+ _onTouchLeave: function (e) {
2828
+ if (!this._map._loaded) {
2829
+ return;
2830
+ }
2831
+
2832
+ var type = 'touchleave';
2833
+ this._touchEvent(e, type);
2834
+ },
2835
+
2836
+ _onTouchMove: function (e) {
2837
+ if (!this._map._loaded) {
2838
+ return;
2839
+ }
2840
+
2841
+ var type = 'touchmove';
2842
+ this._touchEvent(e, type);
2843
+ },
2844
+
2845
+ _detectIE: function () {
2846
+ var ua = window.navigator.userAgent;
2847
+
2848
+ var msie = ua.indexOf('MSIE ');
2849
+ if (msie > 0) {
2850
+ // IE 10 or older => return version number
2851
+ return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
2852
+ }
2853
+
2854
+ var trident = ua.indexOf('Trident/');
2855
+ if (trident > 0) {
2856
+ // IE 11 => return version number
2857
+ var rv = ua.indexOf('rv:');
2858
+ return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
2859
+ }
2860
+
2861
+ var edge = ua.indexOf('Edge/');
2862
+ if (edge > 0) {
2863
+ // IE 12 => return version number
2864
+ return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
2865
+ }
2866
+
2867
+ // other browser
2868
+ return false;
2869
+ }
2870
+ });
2871
+
2872
+ L.Map.addInitHook('addHandler', 'touchExtend', L.Map.TouchExtend);
2873
+
2874
+
2875
+ /**
2876
+ * @class L.Marker.Touch
2877
+ * @aka Marker.Touch
2878
+ *
2879
+ * This isn't full Touch support. This is just to get markers to also support dom touch events after creation
2880
+ * #TODO: find a better way of getting markers to support touch.
2881
+ */
2882
+ L.Marker.Touch = L.Marker.extend({
2883
+
2884
+ _initInteraction: function () {
2885
+ if (!this.addInteractiveTarget) {
2886
+ // 0.7.x support
2887
+ return this._initInteractionLegacy();
2888
+ }
2889
+ // TODO this may need be updated to re-add touch events for 1.0+
2890
+ return L.Marker.prototype._initInteraction.apply(this);
2891
+ },
2892
+
2893
+ // This is an exact copy of https://github.com/Leaflet/Leaflet/blob/v0.7/src/layer/marker/Marker.js
2894
+ // with the addition of the touch events
2895
+ _initInteractionLegacy: function () {
2896
+
2897
+ if (!this.options.clickable) {
2898
+ return;
2899
+ }
2900
+
2901
+ // TODO refactor into something shared with Map/Path/etc. to DRY it up
2902
+
2903
+ var icon = this._icon,
2904
+ events = ['dblclick',
2905
+ 'mousedown',
2906
+ 'mouseover',
2907
+ 'mouseout',
2908
+ 'contextmenu',
2909
+ 'touchstart',
2910
+ 'touchend',
2911
+ 'touchmove'];
2912
+ if (this._detectIE) {
2913
+ events.concat(['MSPointerDown',
2914
+ 'MSPointerUp',
2915
+ 'MSPointerMove',
2916
+ 'MSPointerCancel']);
2917
+ } else {
2918
+ events.concat(['touchcancel']);
2919
+ }
2920
+
2921
+ L.DomUtil.addClass(icon, 'leaflet-clickable');
2922
+ L.DomEvent.on(icon, 'click', this._onMouseClick, this);
2923
+ L.DomEvent.on(icon, 'keypress', this._onKeyPress, this);
2924
+
2925
+ for (var i = 0; i < events.length; i++) {
2926
+ L.DomEvent.on(icon, events[i], this._fireMouseEvent, this);
2927
+ }
2928
+
2929
+ if (L.Handler.MarkerDrag) {
2930
+ this.dragging = new L.Handler.MarkerDrag(this);
2931
+
2932
+ if (this.options.draggable) {
2933
+ this.dragging.enable();
2934
+ }
2935
+ }
2936
+ },
2937
+
2938
+ _detectIE: function () {
2939
+ var ua = window.navigator.userAgent;
2940
+
2941
+ var msie = ua.indexOf('MSIE ');
2942
+ if (msie > 0) {
2943
+ // IE 10 or older => return version number
2944
+ return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
2945
+ }
2946
+
2947
+ var trident = ua.indexOf('Trident/');
2948
+ if (trident > 0) {
2949
+ // IE 11 => return version number
2950
+ var rv = ua.indexOf('rv:');
2951
+ return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
2952
+ }
2953
+
2954
+ var edge = ua.indexOf('Edge/');
2955
+ if (edge > 0) {
2956
+ // IE 12 => return version number
2957
+ return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
2958
+ }
2959
+
2960
+ // other browser
2961
+ return false;
2962
+ }
2963
+ });
2964
+
2965
+
2966
+
2967
+ /**
2968
+ * @class L.LatLngUtil
2969
+ * @aka LatLngUtil
2970
+ */
2971
+ L.LatLngUtil = {
2972
+ // Clones a LatLngs[], returns [][]
2973
+
2974
+ // @method cloneLatLngs(LatLngs[]): L.LatLngs[]
2975
+ // Clone the latLng point or points or nested points and return an array with those points
2976
+ cloneLatLngs: function (latlngs) {
2977
+ var clone = [];
2978
+ for (var i = 0, l = latlngs.length; i < l; i++) {
2979
+ // Check for nested array (Polyline/Polygon)
2980
+ if (Array.isArray(latlngs[i])) {
2981
+ clone.push(L.LatLngUtil.cloneLatLngs(latlngs[i]));
2982
+ } else {
2983
+ clone.push(this.cloneLatLng(latlngs[i]));
2984
+ }
2985
+ }
2986
+ return clone;
2987
+ },
2988
+
2989
+ // @method cloneLatLng(LatLng): L.LatLng
2990
+ // Clone the latLng and return a new LatLng object.
2991
+ cloneLatLng: function (latlng) {
2992
+ return L.latLng(latlng.lat, latlng.lng);
2993
+ }
2994
+ };
2995
+
2996
+
2997
+
2998
+ (function() {
2999
+
3000
+ var defaultPrecision = {
3001
+ km: 2,
3002
+ ha: 2,
3003
+ m: 0,
3004
+ mi: 2,
3005
+ ac: 2,
3006
+ yd: 0,
3007
+ ft: 0,
3008
+ nm: 2
3009
+ };
3010
+
3011
+
3012
+ /**
3013
+ * @class L.GeometryUtil
3014
+ * @aka GeometryUtil
3015
+ */
3016
+ L.GeometryUtil = L.extend(L.GeometryUtil || {}, {
3017
+ // Ported from the OpenLayers implementation. See https://github.com/openlayers/openlayers/blob/master/lib/OpenLayers/Geometry/LinearRing.js#L270
3018
+
3019
+ // @method geodesicArea(): number
3020
+ geodesicArea: function (latLngs) {
3021
+ var pointsCount = latLngs.length,
3022
+ area = 0.0,
3023
+ d2r = Math.PI / 180,
3024
+ p1, p2;
3025
+
3026
+ if (pointsCount > 2) {
3027
+ for (var i = 0; i < pointsCount; i++) {
3028
+ p1 = latLngs[i];
3029
+ p2 = latLngs[(i + 1) % pointsCount];
3030
+ area += ((p2.lng - p1.lng) * d2r) *
3031
+ (2 + Math.sin(p1.lat * d2r) + Math.sin(p2.lat * d2r));
3032
+ }
3033
+ area = area * 6378137.0 * 6378137.0 / 2.0;
3034
+ }
3035
+
3036
+ return Math.abs(area);
3037
+ },
3038
+
3039
+ // @method formattedNumber(n, precision): string
3040
+ // Returns n in specified number format (if defined) and precision
3041
+ formattedNumber: function (n, precision) {
3042
+ var formatted = parseFloat(n).toFixed(precision),
3043
+ format = L.drawLocal.format && L.drawLocal.format.numeric,
3044
+ delimiters = format && format.delimiters,
3045
+ thousands = delimiters && delimiters.thousands,
3046
+ decimal = delimiters && delimiters.decimal;
3047
+
3048
+ if (thousands || decimal) {
3049
+ var splitValue = formatted.split('.');
3050
+ formatted = thousands ? splitValue[0].replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + thousands) : splitValue[0];
3051
+ decimal = decimal || '.';
3052
+ if (splitValue.length > 1) {
3053
+ formatted = formatted + decimal + splitValue[1];
3054
+ }
3055
+ }
3056
+
3057
+ return formatted;
3058
+ },
3059
+
3060
+ // @method readableArea(area, isMetric, precision): string
3061
+ // Returns a readable area string in yards or metric.
3062
+ // The value will be rounded as defined by the precision option object.
3063
+ readableArea: function (area, isMetric, precision) {
3064
+ var areaStr,
3065
+ units,
3066
+ precision = L.Util.extend({}, defaultPrecision, precision);
3067
+
3068
+ if (isMetric) {
3069
+ units = ['ha', 'm'];
3070
+ type = typeof isMetric;
3071
+ if (type === 'string') {
3072
+ units = [isMetric];
3073
+ } else if (type !== 'boolean') {
3074
+ units = isMetric;
3075
+ }
3076
+
3077
+ if (area >= 1000000 && units.indexOf('km') !== -1) {
3078
+ areaStr = L.GeometryUtil.formattedNumber(area * 0.000001, precision['km']) + ' km²';
3079
+ } else if (area >= 10000 && units.indexOf('ha') !== -1) {
3080
+ areaStr = L.GeometryUtil.formattedNumber(area * 0.0001, precision['ha']) + ' ha';
3081
+ } else {
3082
+ areaStr = L.GeometryUtil.formattedNumber(area, precision['m']) + ' m²';
3083
+ }
3084
+ } else {
3085
+ area /= 0.836127; // Square yards in 1 meter
3086
+
3087
+ if (area >= 3097600) { //3097600 square yards in 1 square mile
3088
+ areaStr = L.GeometryUtil.formattedNumber(area / 3097600, precision['mi']) + ' mi²';
3089
+ } else if (area >= 4840) { //4840 square yards in 1 acre
3090
+ areaStr = L.GeometryUtil.formattedNumber(area / 4840, precision['ac']) + ' acres';
3091
+ } else {
3092
+ areaStr = L.GeometryUtil.formattedNumber(area, precision['yd']) + ' yd²';
3093
+ }
3094
+ }
3095
+
3096
+ return areaStr;
3097
+ },
3098
+
3099
+ // @method readableDistance(distance, units): string
3100
+ // Converts a metric distance to one of [ feet, nauticalMile, metric or yards ] string
3101
+ //
3102
+ // @alternative
3103
+ // @method readableDistance(distance, isMetric, useFeet, isNauticalMile, precision): string
3104
+ // Converts metric distance to distance string.
3105
+ // The value will be rounded as defined by the precision option object.
3106
+ readableDistance: function (distance, isMetric, isFeet, isNauticalMile, precision) {
3107
+ var distanceStr,
3108
+ units,
3109
+ precision = L.Util.extend({}, defaultPrecision, precision);
3110
+
3111
+ if (isMetric) {
3112
+ units = typeof isMetric == 'string' ? isMetric : 'metric';
3113
+ } else if (isFeet) {
3114
+ units = 'feet';
3115
+ } else if (isNauticalMile) {
3116
+ units = 'nauticalMile';
3117
+ } else {
3118
+ units = 'yards';
3119
+ }
3120
+
3121
+ switch (units) {
3122
+ case 'metric':
3123
+ // show metres when distance is < 1km, then show km
3124
+ if (distance > 1000) {
3125
+ distanceStr = L.GeometryUtil.formattedNumber(distance / 1000, precision['km']) + ' km';
3126
+ } else {
3127
+ distanceStr = L.GeometryUtil.formattedNumber(distance, precision['m']) + ' m';
3128
+ }
3129
+ break;
3130
+ case 'feet':
3131
+ distance *= 1.09361 * 3;
3132
+ distanceStr = L.GeometryUtil.formattedNumber(distance, precision['ft']) + ' ft';
3133
+
3134
+ break;
3135
+ case 'nauticalMile':
3136
+ distance *= 0.53996;
3137
+ distanceStr = L.GeometryUtil.formattedNumber(distance / 1000, precision['nm']) + ' nm';
3138
+ break;
3139
+ case 'yards':
3140
+ default:
3141
+ distance *= 1.09361;
3142
+
3143
+ if (distance > 1760) {
3144
+ distanceStr = L.GeometryUtil.formattedNumber(distance / 1760, precision['mi']) + ' miles';
3145
+ } else {
3146
+ distanceStr = L.GeometryUtil.formattedNumber(distance, precision['yd']) + ' yd';
3147
+ }
3148
+ break;
3149
+ }
3150
+ return distanceStr;
3151
+ }
3152
+ });
3153
+
3154
+ })();
3155
+
3156
+
3157
+
3158
+ /**
3159
+ * @class L.LineUtil
3160
+ * @aka Util
3161
+ * @aka L.Utils
3162
+ */
3163
+ L.Util.extend(L.LineUtil, {
3164
+
3165
+ // @method segmentsIntersect(): boolean
3166
+ // Checks to see if two line segments intersect. Does not handle degenerate cases.
3167
+ // http://compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf
3168
+ segmentsIntersect: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2, /*Point*/ p3) {
3169
+ return this._checkCounterclockwise(p, p2, p3) !==
3170
+ this._checkCounterclockwise(p1, p2, p3) &&
3171
+ this._checkCounterclockwise(p, p1, p2) !==
3172
+ this._checkCounterclockwise(p, p1, p3);
3173
+ },
3174
+
3175
+ // check to see if points are in counterclockwise order
3176
+ _checkCounterclockwise: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
3177
+ return (p2.y - p.y) * (p1.x - p.x) > (p1.y - p.y) * (p2.x - p.x);
3178
+ }
3179
+ });
3180
+
3181
+
3182
+ /**
3183
+ * @class L.Polyline
3184
+ * @aka Polyline
3185
+ */
3186
+ L.Polyline.include({
3187
+
3188
+ // @method intersects(): boolean
3189
+ // Check to see if this polyline has any linesegments that intersect.
3190
+ // NOTE: does not support detecting intersection for degenerate cases.
3191
+ intersects: function () {
3192
+ var points = this._getProjectedPoints(),
3193
+ len = points ? points.length : 0,
3194
+ i, p, p1;
3195
+
3196
+ if (this._tooFewPointsForIntersection()) {
3197
+ return false;
3198
+ }
3199
+
3200
+ for (i = len - 1; i >= 3; i--) {
3201
+ p = points[i - 1];
3202
+ p1 = points[i];
3203
+
3204
+
3205
+ if (this._lineSegmentsIntersectsRange(p, p1, i - 2)) {
3206
+ return true;
3207
+ }
3208
+ }
3209
+
3210
+ return false;
3211
+ },
3212
+
3213
+ // @method newLatLngIntersects(): boolean
3214
+ // Check for intersection if new latlng was added to this polyline.
3215
+ // NOTE: does not support detecting intersection for degenerate cases.
3216
+ newLatLngIntersects: function (latlng, skipFirst) {
3217
+ // Cannot check a polyline for intersecting lats/lngs when not added to the map
3218
+ if (!this._map) {
3219
+ return false;
3220
+ }
3221
+
3222
+ return this.newPointIntersects(this._map.latLngToLayerPoint(latlng), skipFirst);
3223
+ },
3224
+
3225
+ // @method newPointIntersects(): boolean
3226
+ // Check for intersection if new point was added to this polyline.
3227
+ // newPoint must be a layer point.
3228
+ // NOTE: does not support detecting intersection for degenerate cases.
3229
+ newPointIntersects: function (newPoint, skipFirst) {
3230
+ var points = this._getProjectedPoints(),
3231
+ len = points ? points.length : 0,
3232
+ lastPoint = points ? points[len - 1] : null,
3233
+ // The previous previous line segment. Previous line segment doesn't need testing.
3234
+ maxIndex = len - 2;
3235
+
3236
+ if (this._tooFewPointsForIntersection(1)) {
3237
+ return false;
3238
+ }
3239
+
3240
+ return this._lineSegmentsIntersectsRange(lastPoint, newPoint, maxIndex, skipFirst ? 1 : 0);
3241
+ },
3242
+
3243
+ // Polylines with 2 sides can only intersect in cases where points are collinear (we don't support detecting these).
3244
+ // Cannot have intersection when < 3 line segments (< 4 points)
3245
+ _tooFewPointsForIntersection: function (extraPoints) {
3246
+ var points = this._getProjectedPoints(),
3247
+ len = points ? points.length : 0;
3248
+ // Increment length by extraPoints if present
3249
+ len += extraPoints || 0;
3250
+
3251
+ return !points || len <= 3;
3252
+ },
3253
+
3254
+ // Checks a line segment intersections with any line segments before its predecessor.
3255
+ // Don't need to check the predecessor as will never intersect.
3256
+ _lineSegmentsIntersectsRange: function (p, p1, maxIndex, minIndex) {
3257
+ var points = this._getProjectedPoints(),
3258
+ p2, p3;
3259
+
3260
+ minIndex = minIndex || 0;
3261
+
3262
+ // Check all previous line segments (beside the immediately previous) for intersections
3263
+ for (var j = maxIndex; j > minIndex; j--) {
3264
+ p2 = points[j - 1];
3265
+ p3 = points[j];
3266
+
3267
+ if (L.LineUtil.segmentsIntersect(p, p1, p2, p3)) {
3268
+ return true;
3269
+ }
3270
+ }
3271
+
3272
+ return false;
3273
+ },
3274
+
3275
+ _getProjectedPoints: function () {
3276
+ if (!this._defaultShape) {
3277
+ return this._originalPoints;
3278
+ }
3279
+ var points = [],
3280
+ _shape = this._defaultShape();
3281
+
3282
+ for (var i = 0; i < _shape.length; i++) {
3283
+ points.push(this._map.latLngToLayerPoint(_shape[i]));
3284
+ }
3285
+ return points;
3286
+ }
3287
+ });
3288
+
3289
+
3290
+
3291
+ /**
3292
+ * @class L.Polygon
3293
+ * @aka Polygon
3294
+ */
3295
+ L.Polygon.include({
3296
+
3297
+ // @method intersects(): boolean
3298
+ // Checks a polygon for any intersecting line segments. Ignores holes.
3299
+ intersects: function () {
3300
+ var polylineIntersects,
3301
+ points = this._getProjectedPoints(),
3302
+ len, firstPoint, lastPoint, maxIndex;
3303
+
3304
+ if (this._tooFewPointsForIntersection()) {
3305
+ return false;
3306
+ }
3307
+
3308
+ polylineIntersects = L.Polyline.prototype.intersects.call(this);
3309
+
3310
+ // If already found an intersection don't need to check for any more.
3311
+ if (polylineIntersects) {
3312
+ return true;
3313
+ }
3314
+
3315
+ len = points.length;
3316
+ firstPoint = points[0];
3317
+ lastPoint = points[len - 1];
3318
+ maxIndex = len - 2;
3319
+
3320
+ // Check the line segment between last and first point. Don't need to check the first line segment (minIndex = 1)
3321
+ return this._lineSegmentsIntersectsRange(lastPoint, firstPoint, maxIndex, 1);
3322
+ }
3323
+ });
3324
+
3325
+
3326
+
3327
+ /**
3328
+ * @class L.Control.Draw
3329
+ * @aka L.Draw
3330
+ */
3331
+ L.Control.Draw = L.Control.extend({
3332
+
3333
+ // Options
3334
+ options: {
3335
+ position: 'topleft',
3336
+ draw: {},
3337
+ edit: false
3338
+ },
3339
+
3340
+ // @method initialize(): void
3341
+ // Initializes draw control, toolbars from the options
3342
+ initialize: function (options) {
3343
+ if (L.version < '0.7') {
3344
+ throw new Error('Leaflet.draw 0.2.3+ requires Leaflet 0.7.0+. Download latest from https://github.com/Leaflet/Leaflet/');
3345
+ }
3346
+
3347
+ L.Control.prototype.initialize.call(this, options);
3348
+
3349
+ var toolbar;
3350
+
3351
+ this._toolbars = {};
3352
+
3353
+ // Initialize toolbars
3354
+ if (L.DrawToolbar && this.options.draw) {
3355
+ toolbar = new L.DrawToolbar(this.options.draw);
3356
+
3357
+ this._toolbars[L.DrawToolbar.TYPE] = toolbar;
3358
+
3359
+ // Listen for when toolbar is enabled
3360
+ this._toolbars[L.DrawToolbar.TYPE].on('enable', this._toolbarEnabled, this);
3361
+ }
3362
+
3363
+ if (L.EditToolbar && this.options.edit) {
3364
+ toolbar = new L.EditToolbar(this.options.edit);
3365
+
3366
+ this._toolbars[L.EditToolbar.TYPE] = toolbar;
3367
+
3368
+ // Listen for when toolbar is enabled
3369
+ this._toolbars[L.EditToolbar.TYPE].on('enable', this._toolbarEnabled, this);
3370
+ }
3371
+ L.toolbar = this; //set global var for editing the toolbar
3372
+ },
3373
+
3374
+ // @method onAdd(): container
3375
+ // Adds the toolbar container to the map
3376
+ onAdd: function (map) {
3377
+ var container = L.DomUtil.create('div', 'leaflet-draw'),
3378
+ addedTopClass = false,
3379
+ topClassName = 'leaflet-draw-toolbar-top',
3380
+ toolbarContainer;
3381
+
3382
+ for (var toolbarId in this._toolbars) {
3383
+ if (this._toolbars.hasOwnProperty(toolbarId)) {
3384
+ toolbarContainer = this._toolbars[toolbarId].addToolbar(map);
3385
+
3386
+ if (toolbarContainer) {
3387
+ // Add class to the first toolbar to remove the margin
3388
+ if (!addedTopClass) {
3389
+ if (!L.DomUtil.hasClass(toolbarContainer, topClassName)) {
3390
+ L.DomUtil.addClass(toolbarContainer.childNodes[0], topClassName);
3391
+ }
3392
+ addedTopClass = true;
3393
+ }
3394
+
3395
+ container.appendChild(toolbarContainer);
3396
+ }
3397
+ }
3398
+ }
3399
+
3400
+ return container;
3401
+ },
3402
+
3403
+ // @method onRemove(): void
3404
+ // Removes the toolbars from the map toolbar container
3405
+ onRemove: function () {
3406
+ for (var toolbarId in this._toolbars) {
3407
+ if (this._toolbars.hasOwnProperty(toolbarId)) {
3408
+ this._toolbars[toolbarId].removeToolbar();
3409
+ }
3410
+ }
3411
+ },
3412
+
3413
+ // @method setDrawingOptions(options): void
3414
+ // Sets options to all toolbar instances
3415
+ setDrawingOptions: function (options) {
3416
+ for (var toolbarId in this._toolbars) {
3417
+ if (this._toolbars[toolbarId] instanceof L.DrawToolbar) {
3418
+ this._toolbars[toolbarId].setOptions(options);
3419
+ }
3420
+ }
3421
+ },
3422
+
3423
+ _toolbarEnabled: function (e) {
3424
+ var enabledToolbar = e.target;
3425
+
3426
+ for (var toolbarId in this._toolbars) {
3427
+ if (this._toolbars[toolbarId] !== enabledToolbar) {
3428
+ this._toolbars[toolbarId].disable();
3429
+ }
3430
+ }
3431
+ }
3432
+ });
3433
+
3434
+ L.Map.mergeOptions({
3435
+ drawControlTooltips: true,
3436
+ drawControl: false
3437
+ });
3438
+
3439
+ L.Map.addInitHook(function () {
3440
+ if (this.options.drawControl) {
3441
+ this.drawControl = new L.Control.Draw();
3442
+ this.addControl(this.drawControl);
3443
+ }
3444
+ });
3445
+
3446
+
3447
+
3448
+ /**
3449
+ * @class L.Draw.Toolbar
3450
+ * @aka Toolbar
3451
+ *
3452
+ * The toolbar class of the API — it is used to create the ui
3453
+ * This will be depreciated
3454
+ *
3455
+ * @example
3456
+ *
3457
+ * ```js
3458
+ * var toolbar = L.Toolbar();
3459
+ * toolbar.addToolbar(map);
3460
+ * ```
3461
+ *
3462
+ * ### Disabling a toolbar
3463
+ *
3464
+ * If you do not want a particular toolbar in your app you can turn it off by setting the toolbar to false.
3465
+ *
3466
+ * ```js
3467
+ * var drawControl = new L.Control.Draw({
3468
+ * draw: false,
3469
+ * edit: {
3470
+ * featureGroup: editableLayers
3471
+ * }
3472
+ * });
3473
+ * ```
3474
+ *
3475
+ * ### Disabling a toolbar item
3476
+ *
3477
+ * If you want to turn off a particular toolbar item, set it to false. The following disables drawing polygons and
3478
+ * markers. It also turns off the ability to edit layers.
3479
+ *
3480
+ * ```js
3481
+ * var drawControl = new L.Control.Draw({
3482
+ * draw: {
3483
+ * polygon: false,
3484
+ * marker: false
3485
+ * },
3486
+ * edit: {
3487
+ * featureGroup: editableLayers,
3488
+ * edit: false
3489
+ * }
3490
+ * });
3491
+ * ```
3492
+ */
3493
+ L.Toolbar = L.Class.extend({
3494
+ includes: [L.Mixin.Events],
3495
+
3496
+ // @section Methods for modifying the toolbar
3497
+
3498
+ // @method initialize(options): void
3499
+ // Toolbar constructor
3500
+ initialize: function (options) {
3501
+ L.setOptions(this, options);
3502
+
3503
+ this._modes = {};
3504
+ this._actionButtons = [];
3505
+ this._activeMode = null;
3506
+ },
3507
+
3508
+ // @method enabled(): boolean
3509
+ // Gets a true/false of whether the toolbar is enabled
3510
+ enabled: function () {
3511
+ return this._activeMode !== null;
3512
+ },
3513
+
3514
+ // @method disable(): void
3515
+ // Disables the toolbar
3516
+ disable: function () {
3517
+ if (!this.enabled()) {
3518
+ return;
3519
+ }
3520
+
3521
+ this._activeMode.handler.disable();
3522
+ },
3523
+
3524
+ // @method addToolbar(map): L.DomUtil
3525
+ // Adds the toolbar to the map and returns the toolbar dom element
3526
+ addToolbar: function (map) {
3527
+ var container = L.DomUtil.create('div', 'leaflet-draw-section'),
3528
+ buttonIndex = 0,
3529
+ buttonClassPrefix = this._toolbarClass || '',
3530
+ modeHandlers = this.getModeHandlers(map),
3531
+ i;
3532
+
3533
+ this._toolbarContainer = L.DomUtil.create('div', 'leaflet-draw-toolbar leaflet-bar');
3534
+ this._map = map;
3535
+
3536
+ for (i = 0; i < modeHandlers.length; i++) {
3537
+ if (modeHandlers[i].enabled) {
3538
+ this._initModeHandler(
3539
+ modeHandlers[i].handler,
3540
+ this._toolbarContainer,
3541
+ buttonIndex++,
3542
+ buttonClassPrefix,
3543
+ modeHandlers[i].title
3544
+ );
3545
+ }
3546
+ }
3547
+
3548
+ // if no buttons were added, do not add the toolbar
3549
+ if (!buttonIndex) {
3550
+ return;
3551
+ }
3552
+
3553
+ // Save button index of the last button, -1 as we would have ++ after the last button
3554
+ this._lastButtonIndex = --buttonIndex;
3555
+
3556
+ // Create empty actions part of the toolbar
3557
+ this._actionsContainer = L.DomUtil.create('ul', 'leaflet-draw-actions');
3558
+
3559
+ // Add draw and cancel containers to the control container
3560
+ container.appendChild(this._toolbarContainer);
3561
+ container.appendChild(this._actionsContainer);
3562
+
3563
+ return container;
3564
+ },
3565
+
3566
+ // @method removeToolbar(): void
3567
+ // Removes the toolbar and drops the handler event listeners
3568
+ removeToolbar: function () {
3569
+ // Dispose each handler
3570
+ for (var handlerId in this._modes) {
3571
+ if (this._modes.hasOwnProperty(handlerId)) {
3572
+ // Unbind handler button
3573
+ this._disposeButton(
3574
+ this._modes[handlerId].button,
3575
+ this._modes[handlerId].handler.enable,
3576
+ this._modes[handlerId].handler
3577
+ );
3578
+
3579
+ // Make sure is disabled
3580
+ this._modes[handlerId].handler.disable();
3581
+
3582
+ // Unbind handler
3583
+ this._modes[handlerId].handler
3584
+ .off('enabled', this._handlerActivated, this)
3585
+ .off('disabled', this._handlerDeactivated, this);
3586
+ }
3587
+ }
3588
+ this._modes = {};
3589
+
3590
+ // Dispose the actions toolbar
3591
+ for (var i = 0, l = this._actionButtons.length; i < l; i++) {
3592
+ this._disposeButton(
3593
+ this._actionButtons[i].button,
3594
+ this._actionButtons[i].callback,
3595
+ this
3596
+ );
3597
+ }
3598
+ this._actionButtons = [];
3599
+ this._actionsContainer = null;
3600
+ },
3601
+
3602
+ _initModeHandler: function (handler, container, buttonIndex, classNamePredix, buttonTitle) {
3603
+ var type = handler.type;
3604
+
3605
+ this._modes[type] = {};
3606
+
3607
+ this._modes[type].handler = handler;
3608
+
3609
+ this._modes[type].button = this._createButton({
3610
+ type: type,
3611
+ title: buttonTitle,
3612
+ className: classNamePredix + '-' + type,
3613
+ container: container,
3614
+ callback: this._modes[type].handler.enable,
3615
+ context: this._modes[type].handler
3616
+ });
3617
+
3618
+ this._modes[type].buttonIndex = buttonIndex;
3619
+
3620
+ this._modes[type].handler
3621
+ .on('enabled', this._handlerActivated, this)
3622
+ .on('disabled', this._handlerDeactivated, this);
3623
+ },
3624
+
3625
+ /* Detect iOS based on browser User Agent, based on:
3626
+ * http://stackoverflow.com/a/9039885 */
3627
+ _detectIOS: function () {
3628
+ var iOS = (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream);
3629
+ return iOS;
3630
+ },
3631
+
3632
+ _createButton: function (options) {
3633
+
3634
+ var link = L.DomUtil.create('a', options.className || '', options.container);
3635
+ // Screen reader tag
3636
+ var sr = L.DomUtil.create('span', 'sr-only', options.container);
3637
+
3638
+ link.href = '#';
3639
+ link.appendChild(sr);
3640
+
3641
+ if (options.title) {
3642
+ link.title = options.title;
3643
+ sr.innerHTML = options.title;
3644
+ }
3645
+
3646
+ if (options.text) {
3647
+ link.innerHTML = options.text;
3648
+ sr.innerHTML = options.text;
3649
+ }
3650
+
3651
+ /* iOS does not use click events */
3652
+ var buttonEvent = this._detectIOS() ? 'touchstart' : 'click';
3653
+
3654
+ L.DomEvent
3655
+ .on(link, 'click', L.DomEvent.stopPropagation)
3656
+ .on(link, 'mousedown', L.DomEvent.stopPropagation)
3657
+ .on(link, 'dblclick', L.DomEvent.stopPropagation)
3658
+ .on(link, 'touchstart', L.DomEvent.stopPropagation)
3659
+ .on(link, 'click', L.DomEvent.preventDefault)
3660
+ .on(link, buttonEvent, options.callback, options.context);
3661
+
3662
+ return link;
3663
+ },
3664
+
3665
+ _disposeButton: function (button, callback) {
3666
+ /* iOS does not use click events */
3667
+ var buttonEvent = this._detectIOS() ? 'touchstart' : 'click';
3668
+
3669
+ L.DomEvent
3670
+ .off(button, 'click', L.DomEvent.stopPropagation)
3671
+ .off(button, 'mousedown', L.DomEvent.stopPropagation)
3672
+ .off(button, 'dblclick', L.DomEvent.stopPropagation)
3673
+ .off(button, 'touchstart', L.DomEvent.stopPropagation)
3674
+ .off(button, 'click', L.DomEvent.preventDefault)
3675
+ .off(button, buttonEvent, callback);
3676
+ },
3677
+
3678
+ _handlerActivated: function (e) {
3679
+ // Disable active mode (if present)
3680
+ this.disable();
3681
+
3682
+ // Cache new active feature
3683
+ this._activeMode = this._modes[e.handler];
3684
+
3685
+ L.DomUtil.addClass(this._activeMode.button, 'leaflet-draw-toolbar-button-enabled');
3686
+
3687
+ this._showActionsToolbar();
3688
+
3689
+ this.fire('enable');
3690
+ },
3691
+
3692
+ _handlerDeactivated: function () {
3693
+ this._hideActionsToolbar();
3694
+
3695
+ L.DomUtil.removeClass(this._activeMode.button, 'leaflet-draw-toolbar-button-enabled');
3696
+
3697
+ this._activeMode = null;
3698
+
3699
+ this.fire('disable');
3700
+ },
3701
+
3702
+ _createActions: function (handler) {
3703
+ var container = this._actionsContainer,
3704
+ buttons = this.getActions(handler),
3705
+ l = buttons.length,
3706
+ li, di, dl, button;
3707
+
3708
+ // Dispose the actions toolbar (todo: dispose only not used buttons)
3709
+ for (di = 0, dl = this._actionButtons.length; di < dl; di++) {
3710
+ this._disposeButton(this._actionButtons[di].button, this._actionButtons[di].callback);
3711
+ }
3712
+ this._actionButtons = [];
3713
+
3714
+ // Remove all old buttons
3715
+ while (container.firstChild) {
3716
+ container.removeChild(container.firstChild);
3717
+ }
3718
+
3719
+ for (var i = 0; i < l; i++) {
3720
+ if ('enabled' in buttons[i] && !buttons[i].enabled) {
3721
+ continue;
3722
+ }
3723
+
3724
+ li = L.DomUtil.create('li', '', container);
3725
+
3726
+ button = this._createButton({
3727
+ title: buttons[i].title,
3728
+ text: buttons[i].text,
3729
+ container: li,
3730
+ callback: buttons[i].callback,
3731
+ context: buttons[i].context
3732
+ });
3733
+
3734
+ this._actionButtons.push({
3735
+ button: button,
3736
+ callback: buttons[i].callback
3737
+ });
3738
+ }
3739
+ },
3740
+
3741
+ _showActionsToolbar: function () {
3742
+ var buttonIndex = this._activeMode.buttonIndex,
3743
+ lastButtonIndex = this._lastButtonIndex,
3744
+ toolbarPosition = this._activeMode.button.offsetTop - 1;
3745
+
3746
+ // Recreate action buttons on every click
3747
+ this._createActions(this._activeMode.handler);
3748
+
3749
+ // Correctly position the cancel button
3750
+ this._actionsContainer.style.top = toolbarPosition + 'px';
3751
+
3752
+ if (buttonIndex === 0) {
3753
+ L.DomUtil.addClass(this._toolbarContainer, 'leaflet-draw-toolbar-notop');
3754
+ L.DomUtil.addClass(this._actionsContainer, 'leaflet-draw-actions-top');
3755
+ }
3756
+
3757
+ if (buttonIndex === lastButtonIndex) {
3758
+ L.DomUtil.addClass(this._toolbarContainer, 'leaflet-draw-toolbar-nobottom');
3759
+ L.DomUtil.addClass(this._actionsContainer, 'leaflet-draw-actions-bottom');
3760
+ }
3761
+
3762
+ this._actionsContainer.style.display = 'block';
3763
+ },
3764
+
3765
+ _hideActionsToolbar: function () {
3766
+ this._actionsContainer.style.display = 'none';
3767
+
3768
+ L.DomUtil.removeClass(this._toolbarContainer, 'leaflet-draw-toolbar-notop');
3769
+ L.DomUtil.removeClass(this._toolbarContainer, 'leaflet-draw-toolbar-nobottom');
3770
+ L.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-top');
3771
+ L.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-bottom');
3772
+ }
3773
+ });
3774
+
3775
+
3776
+
3777
+ L.Draw = L.Draw || {};
3778
+ /**
3779
+ * @class L.Draw.Tooltip
3780
+ * @aka Tooltip
3781
+ *
3782
+ * The tooltip class — it is used to display the tooltip while drawing
3783
+ * This will be depreciated
3784
+ *
3785
+ * @example
3786
+ *
3787
+ * ```js
3788
+ * var tooltip = L.Draw.Tooltip();
3789
+ * ```
3790
+ *
3791
+ */
3792
+ L.Draw.Tooltip = L.Class.extend({
3793
+
3794
+ // @section Methods for modifying draw state
3795
+
3796
+ // @method initialize(map): void
3797
+ // Tooltip constructor
3798
+ initialize: function (map) {
3799
+ this._map = map;
3800
+ this._popupPane = map._panes.popupPane;
3801
+ this._visible = false;
3802
+
3803
+ this._container = map.options.drawControlTooltips ?
3804
+ L.DomUtil.create('div', 'leaflet-draw-tooltip', this._popupPane) : null;
3805
+ this._singleLineLabel = false;
3806
+
3807
+ this._map.on('mouseout', this._onMouseOut, this);
3808
+ },
3809
+
3810
+ // @method dispose(): void
3811
+ // Remove Tooltip DOM and unbind events
3812
+ dispose: function () {
3813
+ this._map.off('mouseout', this._onMouseOut, this);
3814
+
3815
+ if (this._container) {
3816
+ this._popupPane.removeChild(this._container);
3817
+ this._container = null;
3818
+ }
3819
+ },
3820
+
3821
+ // @method updateContent(labelText): this
3822
+ // Changes the tooltip text to string in function call
3823
+ updateContent: function (labelText) {
3824
+ if (!this._container) {
3825
+ return this;
3826
+ }
3827
+ labelText.subtext = labelText.subtext || '';
3828
+
3829
+ // update the vertical position (only if changed)
3830
+ if (labelText.subtext.length === 0 && !this._singleLineLabel) {
3831
+ L.DomUtil.addClass(this._container, 'leaflet-draw-tooltip-single');
3832
+ this._singleLineLabel = true;
3833
+ }
3834
+ else if (labelText.subtext.length > 0 && this._singleLineLabel) {
3835
+ L.DomUtil.removeClass(this._container, 'leaflet-draw-tooltip-single');
3836
+ this._singleLineLabel = false;
3837
+ }
3838
+
3839
+ this._container.innerHTML =
3840
+ (labelText.subtext.length > 0 ?
3841
+ '<span class="leaflet-draw-tooltip-subtext">' + labelText.subtext + '</span>' + '<br />' : '') +
3842
+ '<span>' + labelText.text + '</span>';
3843
+
3844
+ if (!labelText.text && !labelText.subtext) {
3845
+ this._visible = false;
3846
+ this._container.style.visibility = 'hidden';
3847
+ } else {
3848
+ this._visible = true;
3849
+ this._container.style.visibility = 'inherit';
3850
+ }
3851
+
3852
+ return this;
3853
+ },
3854
+
3855
+ // @method updatePosition(latlng): this
3856
+ // Changes the location of the tooltip
3857
+ updatePosition: function (latlng) {
3858
+ var pos = this._map.latLngToLayerPoint(latlng),
3859
+ tooltipContainer = this._container;
3860
+
3861
+ if (this._container) {
3862
+ if (this._visible) {
3863
+ tooltipContainer.style.visibility = 'inherit';
3864
+ }
3865
+ L.DomUtil.setPosition(tooltipContainer, pos);
3866
+ }
3867
+
3868
+ return this;
3869
+ },
3870
+
3871
+ // @method showAsError(): this
3872
+ // Applies error class to tooltip
3873
+ showAsError: function () {
3874
+ if (this._container) {
3875
+ L.DomUtil.addClass(this._container, 'leaflet-error-draw-tooltip');
3876
+ }
3877
+ return this;
3878
+ },
3879
+
3880
+ // @method removeError(): this
3881
+ // Removes the error class from the tooltip
3882
+ removeError: function () {
3883
+ if (this._container) {
3884
+ L.DomUtil.removeClass(this._container, 'leaflet-error-draw-tooltip');
3885
+ }
3886
+ return this;
3887
+ },
3888
+
3889
+ _onMouseOut: function () {
3890
+ if (this._container) {
3891
+ this._container.style.visibility = 'hidden';
3892
+ }
3893
+ }
3894
+ });
3895
+
3896
+
3897
+
3898
+ /**
3899
+ * @class L.DrawToolbar
3900
+ * @aka Toolbar
3901
+ */
3902
+ L.DrawToolbar = L.Toolbar.extend({
3903
+
3904
+ statics: {
3905
+ TYPE: 'draw'
3906
+ },
3907
+
3908
+ options: {
3909
+ polyline: {},
3910
+ polygon: {},
3911
+ rectangle: {},
3912
+ circle: {},
3913
+ marker: {},
3914
+ circlemarker: {}
3915
+ },
3916
+
3917
+ // @method initialize(): void
3918
+ initialize: function (options) {
3919
+ // Ensure that the options are merged correctly since L.extend is only shallow
3920
+ for (var type in this.options) {
3921
+ if (this.options.hasOwnProperty(type)) {
3922
+ if (options[type]) {
3923
+ options[type] = L.extend({}, this.options[type], options[type]);
3924
+ }
3925
+ }
3926
+ }
3927
+
3928
+ this._toolbarClass = 'leaflet-draw-draw';
3929
+ L.Toolbar.prototype.initialize.call(this, options);
3930
+ },
3931
+
3932
+ // @method getModeHandlers(): object
3933
+ // Get mode handlers information
3934
+ getModeHandlers: function (map) {
3935
+ return [
3936
+ {
3937
+ enabled: this.options.polyline,
3938
+ handler: new L.Draw.Polyline(map, this.options.polyline),
3939
+ title: L.drawLocal.draw.toolbar.buttons.polyline
3940
+ },
3941
+ {
3942
+ enabled: this.options.polygon,
3943
+ handler: new L.Draw.Polygon(map, this.options.polygon),
3944
+ title: L.drawLocal.draw.toolbar.buttons.polygon
3945
+ },
3946
+ {
3947
+ enabled: this.options.rectangle,
3948
+ handler: new L.Draw.Rectangle(map, this.options.rectangle),
3949
+ title: L.drawLocal.draw.toolbar.buttons.rectangle
3950
+ },
3951
+ {
3952
+ enabled: this.options.circle,
3953
+ handler: new L.Draw.Circle(map, this.options.circle),
3954
+ title: L.drawLocal.draw.toolbar.buttons.circle
3955
+ },
3956
+ {
3957
+ enabled: this.options.marker,
3958
+ handler: new L.Draw.Marker(map, this.options.marker),
3959
+ title: L.drawLocal.draw.toolbar.buttons.marker
3960
+ },
3961
+ {
3962
+ enabled: this.options.circlemarker,
3963
+ handler: new L.Draw.CircleMarker(map, this.options.circlemarker),
3964
+ title: L.drawLocal.draw.toolbar.buttons.circlemarker
3965
+ }
3966
+ ];
3967
+ },
3968
+
3969
+ // @method getActions(): object
3970
+ // Get action information
3971
+ getActions: function (handler) {
3972
+ return [
3973
+ {
3974
+ enabled: handler.completeShape,
3975
+ title: L.drawLocal.draw.toolbar.finish.title,
3976
+ text: L.drawLocal.draw.toolbar.finish.text,
3977
+ callback: handler.completeShape,
3978
+ context: handler
3979
+ },
3980
+ {
3981
+ enabled: handler.deleteLastVertex,
3982
+ title: L.drawLocal.draw.toolbar.undo.title,
3983
+ text: L.drawLocal.draw.toolbar.undo.text,
3984
+ callback: handler.deleteLastVertex,
3985
+ context: handler
3986
+ },
3987
+ {
3988
+ title: L.drawLocal.draw.toolbar.actions.title,
3989
+ text: L.drawLocal.draw.toolbar.actions.text,
3990
+ callback: this.disable,
3991
+ context: this
3992
+ }
3993
+ ];
3994
+ },
3995
+
3996
+ // @method setOptions(): void
3997
+ // Sets the options to the toolbar
3998
+ setOptions: function (options) {
3999
+ L.setOptions(this, options);
4000
+
4001
+ for (var type in this._modes) {
4002
+ if (this._modes.hasOwnProperty(type) && options.hasOwnProperty(type)) {
4003
+ this._modes[type].handler.setOptions(options[type]);
4004
+ }
4005
+ }
4006
+ }
4007
+ });
4008
+
4009
+
4010
+
4011
+ /*L.Map.mergeOptions({
4012
+ editControl: true
4013
+ });*/
4014
+ /**
4015
+ * @class L.EditToolbar
4016
+ * @aka EditToolbar
4017
+ */
4018
+ L.EditToolbar = L.Toolbar.extend({
4019
+ statics: {
4020
+ TYPE: 'edit'
4021
+ },
4022
+
4023
+ options: {
4024
+ edit: {
4025
+ selectedPathOptions: {
4026
+ dashArray: '10, 10',
4027
+
4028
+ fill: true,
4029
+ fillColor: '#fe57a1',
4030
+ fillOpacity: 0.1,
4031
+
4032
+ // Whether to user the existing layers color
4033
+ maintainColor: false
4034
+ }
4035
+ },
4036
+ remove: {},
4037
+ poly: null,
4038
+ featureGroup: null /* REQUIRED! TODO: perhaps if not set then all layers on the map are selectable? */
4039
+ },
4040
+
4041
+ // @method intialize(): void
4042
+ initialize: function (options) {
4043
+ // Need to set this manually since null is an acceptable value here
4044
+ if (options.edit) {
4045
+ if (typeof options.edit.selectedPathOptions === 'undefined') {
4046
+ options.edit.selectedPathOptions = this.options.edit.selectedPathOptions;
4047
+ }
4048
+ options.edit.selectedPathOptions = L.extend({}, this.options.edit.selectedPathOptions, options.edit.selectedPathOptions);
4049
+ }
4050
+
4051
+ if (options.remove) {
4052
+ options.remove = L.extend({}, this.options.remove, options.remove);
4053
+ }
4054
+
4055
+ if (options.poly) {
4056
+ options.poly = L.extend({}, this.options.poly, options.poly);
4057
+ }
4058
+
4059
+ this._toolbarClass = 'leaflet-draw-edit';
4060
+ L.Toolbar.prototype.initialize.call(this, options);
4061
+
4062
+ this._selectedFeatureCount = 0;
4063
+ },
4064
+
4065
+ // @method getModeHandlers(): object
4066
+ // Get mode handlers information
4067
+ getModeHandlers: function (map) {
4068
+ var featureGroup = this.options.featureGroup;
4069
+ return [
4070
+ {
4071
+ enabled: this.options.edit,
4072
+ handler: new L.EditToolbar.Edit(map, {
4073
+ featureGroup: featureGroup,
4074
+ selectedPathOptions: this.options.edit.selectedPathOptions,
4075
+ poly: this.options.poly
4076
+ }),
4077
+ title: L.drawLocal.edit.toolbar.buttons.edit
4078
+ },
4079
+ {
4080
+ enabled: this.options.remove,
4081
+ handler: new L.EditToolbar.Delete(map, {
4082
+ featureGroup: featureGroup
4083
+ }),
4084
+ title: L.drawLocal.edit.toolbar.buttons.remove
4085
+ }
4086
+ ];
4087
+ },
4088
+
4089
+ // @method getActions(): object
4090
+ // Get actions information
4091
+ getActions: function (handler) {
4092
+ var actions = [
4093
+ {
4094
+ title: L.drawLocal.edit.toolbar.actions.save.title,
4095
+ text: L.drawLocal.edit.toolbar.actions.save.text,
4096
+ callback: this._save,
4097
+ context: this
4098
+ },
4099
+ {
4100
+ title: L.drawLocal.edit.toolbar.actions.cancel.title,
4101
+ text: L.drawLocal.edit.toolbar.actions.cancel.text,
4102
+ callback: this.disable,
4103
+ context: this
4104
+ }
4105
+ ];
4106
+
4107
+ if (handler.removeAllLayers) {
4108
+ actions.push({
4109
+ title: L.drawLocal.edit.toolbar.actions.clearAll.title,
4110
+ text: L.drawLocal.edit.toolbar.actions.clearAll.text,
4111
+ callback: this._clearAllLayers,
4112
+ context: this
4113
+ });
4114
+ }
4115
+
4116
+ return actions;
4117
+ },
4118
+
4119
+ // @method addToolbar(map): L.DomUtil
4120
+ // Adds the toolbar to the map
4121
+ addToolbar: function (map) {
4122
+ var container = L.Toolbar.prototype.addToolbar.call(this, map);
4123
+
4124
+ this._checkDisabled();
4125
+
4126
+ this.options.featureGroup.on('layeradd layerremove', this._checkDisabled, this);
4127
+
4128
+ return container;
4129
+ },
4130
+
4131
+ // @method removeToolbar(): void
4132
+ // Removes the toolbar from the map
4133
+ removeToolbar: function () {
4134
+ this.options.featureGroup.off('layeradd layerremove', this._checkDisabled, this);
4135
+
4136
+ L.Toolbar.prototype.removeToolbar.call(this);
4137
+ },
4138
+
4139
+ // @method disable(): void
4140
+ // Disables the toolbar
4141
+ disable: function () {
4142
+ if (!this.enabled()) {
4143
+ return;
4144
+ }
4145
+
4146
+ this._activeMode.handler.revertLayers();
4147
+
4148
+ L.Toolbar.prototype.disable.call(this);
4149
+ },
4150
+
4151
+ _save: function () {
4152
+ this._activeMode.handler.save();
4153
+ if (this._activeMode) {
4154
+ this._activeMode.handler.disable();
4155
+ }
4156
+ },
4157
+
4158
+ _clearAllLayers:function(){
4159
+ this._activeMode.handler.removeAllLayers();
4160
+ if (this._activeMode) {
4161
+ this._activeMode.handler.disable();
4162
+ }
4163
+ },
4164
+
4165
+ _checkDisabled: function () {
4166
+ var featureGroup = this.options.featureGroup,
4167
+ hasLayers = featureGroup.getLayers().length !== 0,
4168
+ button;
4169
+
4170
+ if (this.options.edit) {
4171
+ button = this._modes[L.EditToolbar.Edit.TYPE].button;
4172
+
4173
+ if (hasLayers) {
4174
+ L.DomUtil.removeClass(button, 'leaflet-disabled');
4175
+ } else {
4176
+ L.DomUtil.addClass(button, 'leaflet-disabled');
4177
+ }
4178
+
4179
+ button.setAttribute(
4180
+ 'title',
4181
+ hasLayers ?
4182
+ L.drawLocal.edit.toolbar.buttons.edit
4183
+ : L.drawLocal.edit.toolbar.buttons.editDisabled
4184
+ );
4185
+ }
4186
+
4187
+ if (this.options.remove) {
4188
+ button = this._modes[L.EditToolbar.Delete.TYPE].button;
4189
+
4190
+ if (hasLayers) {
4191
+ L.DomUtil.removeClass(button, 'leaflet-disabled');
4192
+ } else {
4193
+ L.DomUtil.addClass(button, 'leaflet-disabled');
4194
+ }
4195
+
4196
+ button.setAttribute(
4197
+ 'title',
4198
+ hasLayers ?
4199
+ L.drawLocal.edit.toolbar.buttons.remove
4200
+ : L.drawLocal.edit.toolbar.buttons.removeDisabled
4201
+ );
4202
+ }
4203
+ }
4204
+ });
4205
+
4206
+
4207
+
4208
+ /**
4209
+ * @class L.EditToolbar.Edit
4210
+ * @aka EditToolbar.Edit
4211
+ */
4212
+ L.EditToolbar.Edit = L.Handler.extend({
4213
+ statics: {
4214
+ TYPE: 'edit'
4215
+ },
4216
+
4217
+ includes: L.Mixin.Events,
4218
+
4219
+ // @method intialize(): void
4220
+ initialize: function (map, options) {
4221
+ L.Handler.prototype.initialize.call(this, map);
4222
+
4223
+ L.setOptions(this, options);
4224
+
4225
+ // Store the selectable layer group for ease of access
4226
+ this._featureGroup = options.featureGroup;
4227
+
4228
+ if (!(this._featureGroup instanceof L.FeatureGroup)) {
4229
+ throw new Error('options.featureGroup must be a L.FeatureGroup');
4230
+ }
4231
+
4232
+ this._uneditedLayerProps = {};
4233
+
4234
+ // Save the type so super can fire, need to do this as cannot do this.TYPE :(
4235
+ this.type = L.EditToolbar.Edit.TYPE;
4236
+ },
4237
+
4238
+ // @method enable(): void
4239
+ // Enable the edit toolbar
4240
+ enable: function () {
4241
+ if (this._enabled || !this._hasAvailableLayers()) {
4242
+ return;
4243
+ }
4244
+ this.fire('enabled', { handler: this.type });
4245
+ //this disable other handlers
4246
+
4247
+ this._map.fire(L.Draw.Event.EDITSTART, { handler: this.type });
4248
+ //allow drawLayer to be updated before beginning edition.
4249
+
4250
+ L.Handler.prototype.enable.call(this);
4251
+ this._featureGroup
4252
+ .on('layeradd', this._enableLayerEdit, this)
4253
+ .on('layerremove', this._disableLayerEdit, this);
4254
+ },
4255
+
4256
+ // @method disable(): void
4257
+ // Disable the edit toolbar
4258
+ disable: function () {
4259
+ if (!this._enabled) {
4260
+ return;
4261
+ }
4262
+ this._featureGroup
4263
+ .off('layeradd', this._enableLayerEdit, this)
4264
+ .off('layerremove', this._disableLayerEdit, this);
4265
+ L.Handler.prototype.disable.call(this);
4266
+ this._map.fire(L.Draw.Event.EDITSTOP, { handler: this.type });
4267
+ this.fire('disabled', { handler: this.type });
4268
+ },
4269
+
4270
+ // @method addHooks(): void
4271
+ // Add listener hooks for this handler
4272
+ addHooks: function () {
4273
+ var map = this._map;
4274
+
4275
+ if (map) {
4276
+ map.getContainer().focus();
4277
+
4278
+ this._featureGroup.eachLayer(this._enableLayerEdit, this);
4279
+
4280
+ this._tooltip = new L.Draw.Tooltip(this._map);
4281
+ this._tooltip.updateContent({
4282
+ text: L.drawLocal.edit.handlers.edit.tooltip.text,
4283
+ subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext
4284
+ });
4285
+
4286
+ // Quickly access the tooltip to update for intersection checking
4287
+ map._editTooltip = this._tooltip;
4288
+
4289
+ this._updateTooltip();
4290
+
4291
+ this._map
4292
+ .on('mousemove', this._onMouseMove, this)
4293
+ .on('touchmove', this._onMouseMove, this)
4294
+ .on('MSPointerMove', this._onMouseMove, this)
4295
+ .on(L.Draw.Event.EDITVERTEX, this._updateTooltip, this);
4296
+ }
4297
+ },
4298
+
4299
+ // @method removeHooks(): void
4300
+ // Remove listener hooks for this handler
4301
+ removeHooks: function () {
4302
+ if (this._map) {
4303
+ // Clean up selected layers.
4304
+ this._featureGroup.eachLayer(this._disableLayerEdit, this);
4305
+
4306
+ // Clear the backups of the original layers
4307
+ this._uneditedLayerProps = {};
4308
+
4309
+ this._tooltip.dispose();
4310
+ this._tooltip = null;
4311
+
4312
+ this._map
4313
+ .off('mousemove', this._onMouseMove, this)
4314
+ .off('touchmove', this._onMouseMove, this)
4315
+ .off('MSPointerMove', this._onMouseMove, this)
4316
+ .off(L.Draw.Event.EDITVERTEX, this._updateTooltip, this);
4317
+ }
4318
+ },
4319
+
4320
+ // @method revertLayers(): void
4321
+ // Revert each layer's geometry changes
4322
+ revertLayers: function () {
4323
+ this._featureGroup.eachLayer(function (layer) {
4324
+ this._revertLayer(layer);
4325
+ }, this);
4326
+ },
4327
+
4328
+ // @method save(): void
4329
+ // Save the layer geometries
4330
+ save: function () {
4331
+ var editedLayers = new L.LayerGroup();
4332
+ this._featureGroup.eachLayer(function (layer) {
4333
+ if (layer.edited) {
4334
+ editedLayers.addLayer(layer);
4335
+ layer.edited = false;
4336
+ }
4337
+ });
4338
+ this._map.fire(L.Draw.Event.EDITED, { layers: editedLayers });
4339
+ },
4340
+
4341
+ _backupLayer: function (layer) {
4342
+ var id = L.Util.stamp(layer);
4343
+
4344
+ if (!this._uneditedLayerProps[id]) {
4345
+ // Polyline, Polygon or Rectangle
4346
+ if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) {
4347
+ this._uneditedLayerProps[id] = {
4348
+ latlngs: L.LatLngUtil.cloneLatLngs(layer.getLatLngs())
4349
+ };
4350
+ } else if (layer instanceof L.Circle) {
4351
+ this._uneditedLayerProps[id] = {
4352
+ latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng()),
4353
+ radius: layer.getRadius()
4354
+ };
4355
+ } else if (layer instanceof L.Marker || layer instanceof L.CircleMarker) { // Marker
4356
+ this._uneditedLayerProps[id] = {
4357
+ latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng())
4358
+ };
4359
+ }
4360
+ }
4361
+ },
4362
+
4363
+ _getTooltipText: function () {
4364
+ return ({
4365
+ text: L.drawLocal.edit.handlers.edit.tooltip.text,
4366
+ subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext
4367
+ });
4368
+ },
4369
+
4370
+ _updateTooltip: function () {
4371
+ this._tooltip.updateContent(this._getTooltipText());
4372
+ },
4373
+
4374
+ _revertLayer: function (layer) {
4375
+ var id = L.Util.stamp(layer);
4376
+ layer.edited = false;
4377
+ if (this._uneditedLayerProps.hasOwnProperty(id)) {
4378
+ // Polyline, Polygon or Rectangle
4379
+ if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) {
4380
+ layer.setLatLngs(this._uneditedLayerProps[id].latlngs);
4381
+ } else if (layer instanceof L.Circle) {
4382
+ layer.setLatLng(this._uneditedLayerProps[id].latlng);
4383
+ layer.setRadius(this._uneditedLayerProps[id].radius);
4384
+ } else if (layer instanceof L.Marker || layer instanceof L.CircleMarker) { // Marker or CircleMarker
4385
+ layer.setLatLng(this._uneditedLayerProps[id].latlng);
4386
+ }
4387
+
4388
+ layer.fire('revert-edited', { layer: layer });
4389
+ }
4390
+ },
4391
+
4392
+ _enableLayerEdit: function (e) {
4393
+ var layer = e.layer || e.target || e,
4394
+ pathOptions, poly;
4395
+
4396
+ // Back up this layer (if haven't before)
4397
+ this._backupLayer(layer);
4398
+
4399
+ if (this.options.poly) {
4400
+ poly = L.Util.extend({}, this.options.poly);
4401
+ layer.options.poly = poly;
4402
+ }
4403
+
4404
+ // Set different style for editing mode
4405
+ if (this.options.selectedPathOptions) {
4406
+ pathOptions = L.Util.extend({}, this.options.selectedPathOptions);
4407
+
4408
+ // Use the existing color of the layer
4409
+ if (pathOptions.maintainColor) {
4410
+ pathOptions.color = layer.options.color;
4411
+ pathOptions.fillColor = layer.options.fillColor;
4412
+ }
4413
+
4414
+ layer.options.original = L.extend({}, layer.options);
4415
+ layer.options.editing = pathOptions;
4416
+
4417
+ }
4418
+
4419
+ if (layer instanceof L.Marker) {
4420
+ if (layer.editing) {
4421
+ layer.editing.enable();
4422
+ }
4423
+ layer.dragging.enable();
4424
+ layer
4425
+ .on('dragend', this._onMarkerDragEnd)
4426
+ // #TODO: remove when leaflet finally fixes their draggable so it's touch friendly again.
4427
+ .on('touchmove', this._onTouchMove, this)
4428
+ .on('MSPointerMove', this._onTouchMove, this)
4429
+ .on('touchend', this._onMarkerDragEnd, this)
4430
+ .on('MSPointerUp', this._onMarkerDragEnd, this);
4431
+ } else {
4432
+ layer.editing.enable();
4433
+ }
4434
+ },
4435
+
4436
+ _disableLayerEdit: function (e) {
4437
+ var layer = e.layer || e.target || e;
4438
+
4439
+ layer.edited = false;
4440
+ if (layer.editing) {
4441
+ layer.editing.disable();
4442
+ }
4443
+
4444
+ delete layer.options.editing;
4445
+ delete layer.options.original;
4446
+ // Reset layer styles to that of before select
4447
+ if (this._selectedPathOptions) {
4448
+ if (layer instanceof L.Marker) {
4449
+ this._toggleMarkerHighlight(layer);
4450
+ } else {
4451
+ // reset the layer style to what is was before being selected
4452
+ layer.setStyle(layer.options.previousOptions);
4453
+ // remove the cached options for the layer object
4454
+ delete layer.options.previousOptions;
4455
+ }
4456
+ }
4457
+
4458
+ if (layer instanceof L.Marker) {
4459
+ layer.dragging.disable();
4460
+ layer
4461
+ .off('dragend', this._onMarkerDragEnd, this)
4462
+ .off('touchmove', this._onTouchMove, this)
4463
+ .off('MSPointerMove', this._onTouchMove, this)
4464
+ .off('touchend', this._onMarkerDragEnd, this)
4465
+ .off('MSPointerUp', this._onMarkerDragEnd, this);
4466
+ } else {
4467
+ layer.editing.disable();
4468
+ }
4469
+ },
4470
+
4471
+ _onMouseMove: function (e) {
4472
+ this._tooltip.updatePosition(e.latlng);
4473
+ },
4474
+
4475
+ _onMarkerDragEnd: function (e) {
4476
+ var layer = e.target;
4477
+ layer.edited = true;
4478
+ this._map.fire(L.Draw.Event.EDITMOVE, { layer: layer });
4479
+ },
4480
+
4481
+ _onTouchMove: function (e) {
4482
+ var touchEvent = e.originalEvent.changedTouches[0],
4483
+ layerPoint = this._map.mouseEventToLayerPoint(touchEvent),
4484
+ latlng = this._map.layerPointToLatLng(layerPoint);
4485
+ e.target.setLatLng(latlng);
4486
+ },
4487
+
4488
+ _hasAvailableLayers: function () {
4489
+ return this._featureGroup.getLayers().length !== 0;
4490
+ }
4491
+ });
4492
+
4493
+
4494
+
4495
+ /**
4496
+ * @class L.EditToolbar.Delete
4497
+ * @aka EditToolbar.Delete
4498
+ */
4499
+ L.EditToolbar.Delete = L.Handler.extend({
4500
+ statics: {
4501
+ TYPE: 'remove' // not delete as delete is reserved in js
4502
+ },
4503
+
4504
+ includes: L.Mixin.Events,
4505
+
4506
+ // @method intialize(): void
4507
+ initialize: function (map, options) {
4508
+ L.Handler.prototype.initialize.call(this, map);
4509
+
4510
+ L.Util.setOptions(this, options);
4511
+
4512
+ // Store the selectable layer group for ease of access
4513
+ this._deletableLayers = this.options.featureGroup;
4514
+
4515
+ if (!(this._deletableLayers instanceof L.FeatureGroup)) {
4516
+ throw new Error('options.featureGroup must be a L.FeatureGroup');
4517
+ }
4518
+
4519
+ // Save the type so super can fire, need to do this as cannot do this.TYPE :(
4520
+ this.type = L.EditToolbar.Delete.TYPE;
4521
+ },
4522
+
4523
+ // @method enable(): void
4524
+ // Enable the delete toolbar
4525
+ enable: function () {
4526
+ if (this._enabled || !this._hasAvailableLayers()) {
4527
+ return;
4528
+ }
4529
+ this.fire('enabled', { handler: this.type });
4530
+
4531
+ this._map.fire(L.Draw.Event.DELETESTART, { handler: this.type });
4532
+
4533
+ L.Handler.prototype.enable.call(this);
4534
+
4535
+ this._deletableLayers
4536
+ .on('layeradd', this._enableLayerDelete, this)
4537
+ .on('layerremove', this._disableLayerDelete, this);
4538
+ },
4539
+
4540
+ // @method disable(): void
4541
+ // Disable the delete toolbar
4542
+ disable: function () {
4543
+ if (!this._enabled) {
4544
+ return;
4545
+ }
4546
+
4547
+ this._deletableLayers
4548
+ .off('layeradd', this._enableLayerDelete, this)
4549
+ .off('layerremove', this._disableLayerDelete, this);
4550
+
4551
+ L.Handler.prototype.disable.call(this);
4552
+
4553
+ this._map.fire(L.Draw.Event.DELETESTOP, { handler: this.type });
4554
+
4555
+ this.fire('disabled', { handler: this.type });
4556
+ },
4557
+
4558
+ // @method addHooks(): void
4559
+ // Add listener hooks to this handler
4560
+ addHooks: function () {
4561
+ var map = this._map;
4562
+
4563
+ if (map) {
4564
+ map.getContainer().focus();
4565
+
4566
+ this._deletableLayers.eachLayer(this._enableLayerDelete, this);
4567
+ this._deletedLayers = new L.LayerGroup();
4568
+
4569
+ this._tooltip = new L.Draw.Tooltip(this._map);
4570
+ this._tooltip.updateContent({ text: L.drawLocal.edit.handlers.remove.tooltip.text });
4571
+
4572
+ this._map.on('mousemove', this._onMouseMove, this);
4573
+ }
4574
+ },
4575
+
4576
+ // @method removeHooks(): void
4577
+ // Remove listener hooks from this handler
4578
+ removeHooks: function () {
4579
+ if (this._map) {
4580
+ this._deletableLayers.eachLayer(this._disableLayerDelete, this);
4581
+ this._deletedLayers = null;
4582
+
4583
+ this._tooltip.dispose();
4584
+ this._tooltip = null;
4585
+
4586
+ this._map.off('mousemove', this._onMouseMove, this);
4587
+ }
4588
+ },
4589
+
4590
+ // @method revertLayers(): void
4591
+ // Revert the deleted layers back to their prior state.
4592
+ revertLayers: function () {
4593
+ // Iterate of the deleted layers and add them back into the featureGroup
4594
+ this._deletedLayers.eachLayer(function (layer) {
4595
+ this._deletableLayers.addLayer(layer);
4596
+ layer.fire('revert-deleted', { layer: layer });
4597
+ }, this);
4598
+ },
4599
+
4600
+ // @method save(): void
4601
+ // Save deleted layers
4602
+ save: function () {
4603
+ this._map.fire(L.Draw.Event.DELETED, { layers: this._deletedLayers });
4604
+ },
4605
+
4606
+ // @method removeAllLayers(): void
4607
+ // Remove all delateable layers
4608
+ removeAllLayers: function(){
4609
+ // Iterate of the delateable layers and add remove them
4610
+ this._deletableLayers.eachLayer(function (layer) {
4611
+ this._removeLayer({layer:layer});
4612
+ }, this);
4613
+ this.save();
4614
+ },
4615
+
4616
+ _enableLayerDelete: function (e) {
4617
+ var layer = e.layer || e.target || e;
4618
+
4619
+ layer.on('click', this._removeLayer, this);
4620
+ },
4621
+
4622
+ _disableLayerDelete: function (e) {
4623
+ var layer = e.layer || e.target || e;
4624
+
4625
+ layer.off('click', this._removeLayer, this);
4626
+
4627
+ // Remove from the deleted layers so we can't accidentally revert if the user presses cancel
4628
+ this._deletedLayers.removeLayer(layer);
4629
+ },
4630
+
4631
+ _removeLayer: function (e) {
4632
+ var layer = e.layer || e.target || e;
4633
+
4634
+ this._deletableLayers.removeLayer(layer);
4635
+
4636
+ this._deletedLayers.addLayer(layer);
4637
+
4638
+ layer.fire('deleted');
4639
+ },
4640
+
4641
+ _onMouseMove: function (e) {
4642
+ this._tooltip.updatePosition(e.latlng);
4643
+ },
4644
+
4645
+ _hasAvailableLayers: function () {
4646
+ return this._deletableLayers.getLayers().length !== 0;
4647
+ }
4648
+ });
4649
+
4650
+
4651
+
4652
+ }(window, document));
4653
+ //# sourceMappingURL=leaflet.draw-src.map