foliage 0.1.0

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 (68) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +22 -0
  3. data/README.md +9 -0
  4. data/app/assets/images/.keep +0 -0
  5. data/app/assets/images/map/marker/icon-2x.png +0 -0
  6. data/app/assets/images/map/marker/icon.png +0 -0
  7. data/app/assets/images/map/marker/icon.svg +67 -0
  8. data/app/assets/images/map/marker/shadow.png +0 -0
  9. data/app/assets/javascripts/core_ext.js.coffee +61 -0
  10. data/app/assets/javascripts/foliage.js.coffee +23 -0
  11. data/app/assets/javascripts/foliage/band.js.coffee +99 -0
  12. data/app/assets/javascripts/foliage/bubbles.js.coffee +77 -0
  13. data/app/assets/javascripts/foliage/categories.js.coffee +70 -0
  14. data/app/assets/javascripts/foliage/choropleth.js.coffee +51 -0
  15. data/app/assets/javascripts/foliage/color.js.coffee +39 -0
  16. data/app/assets/javascripts/foliage/gradient.js.coffee +72 -0
  17. data/app/assets/javascripts/foliage/heatmap.js.coffee +49 -0
  18. data/app/assets/javascripts/foliage/leaf.js.coffee +422 -0
  19. data/app/assets/javascripts/foliage/path.js.coffee +76 -0
  20. data/app/assets/javascripts/foliage/paths.js.coffee +131 -0
  21. data/app/assets/javascripts/foliage/point_group.js.coffee +83 -0
  22. data/app/assets/javascripts/foliage/points.js.coffee +79 -0
  23. data/app/assets/javascripts/foliage/simple.js.coffee +35 -0
  24. data/app/assets/javascripts/leaflet/geographic_util.js.coffee +23 -0
  25. data/app/assets/javascripts/leaflet/ghost_label.js.coffee +100 -0
  26. data/app/assets/javascripts/leaflet/ghost_label_cluster.js.coffee +192 -0
  27. data/app/assets/javascripts/leaflet/layers_scheduler.js.coffee +57 -0
  28. data/app/assets/javascripts/leaflet/reactive_measure.js.coffee +414 -0
  29. data/app/assets/stylesheets/all.scss +16 -0
  30. data/app/assets/stylesheets/application.css +15 -0
  31. data/app/assets/stylesheets/compass/reset.scss +3 -0
  32. data/app/assets/stylesheets/compass/reset/utilities.scss +142 -0
  33. data/app/assets/stylesheets/leaflet.scss +1093 -0
  34. data/app/assets/stylesheets/leaflet/label.scss +40 -0
  35. data/app/assets/stylesheets/leaflet/tooltip.scss +42 -0
  36. data/app/assets/stylesheets/mixins.scss +131 -0
  37. data/app/assets/stylesheets/reset.scss +89 -0
  38. data/app/assets/stylesheets/variables.scss +47 -0
  39. data/app/helpers/foliage_helper.rb +23 -0
  40. data/lib/foliage.rb +9 -0
  41. data/lib/foliage/leaf.rb +235 -0
  42. data/lib/foliage/rails.rb +2 -0
  43. data/lib/foliage/rails/engine.rb +7 -0
  44. data/lib/foliage/rails/integration.rb +8 -0
  45. data/lib/foliage/version.rb +3 -0
  46. data/vendor/assets/javascripts/.keep +0 -0
  47. data/vendor/assets/javascripts/autosize.js +211 -0
  48. data/vendor/assets/javascripts/geographiclib.js +3074 -0
  49. data/vendor/assets/javascripts/leaflet.js.erb +9175 -0
  50. data/vendor/assets/javascripts/leaflet/draw.js +3573 -0
  51. data/vendor/assets/javascripts/leaflet/easy-button.js +366 -0
  52. data/vendor/assets/javascripts/leaflet/fullscreen.js +162 -0
  53. data/vendor/assets/javascripts/leaflet/heatmap.js +142 -0
  54. data/vendor/assets/javascripts/leaflet/label.js +545 -0
  55. data/vendor/assets/javascripts/leaflet/measure.js +6966 -0
  56. data/vendor/assets/javascripts/leaflet/modal.js +364 -0
  57. data/vendor/assets/javascripts/leaflet/providers.js +479 -0
  58. data/vendor/assets/javascripts/rbush.js +621 -0
  59. data/vendor/assets/stylesheets/.keep +0 -0
  60. data/vendor/assets/stylesheets/bootstrap/mixins.scss +55 -0
  61. data/vendor/assets/stylesheets/bootstrap/variables.scss +10 -0
  62. data/vendor/assets/stylesheets/leaflet.scss +479 -0
  63. data/vendor/assets/stylesheets/leaflet/draw.scss +282 -0
  64. data/vendor/assets/stylesheets/leaflet/easy-button.scss +56 -0
  65. data/vendor/assets/stylesheets/leaflet/fullscreen.scss +2 -0
  66. data/vendor/assets/stylesheets/leaflet/measure.scss +168 -0
  67. data/vendor/assets/stylesheets/leaflet/modal.scss +85 -0
  68. metadata +171 -0
@@ -0,0 +1,3573 @@
1
+ /*
2
+ Leaflet.draw, a plugin that adds drawing and editing tools to Leaflet powered maps.
3
+ (c) 2012-2016, Jacob Toye, 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
+
12
+ L.drawVersion = '0.3.0-dev';
13
+
14
+ L.drawLocal = {
15
+ draw: {
16
+ toolbar: {
17
+ // #TODO: this should be reorganized where actions are nested in actions
18
+ // ex: actions.undo or actions.cancel
19
+ actions: {
20
+ title: 'Cancel drawing',
21
+ text: 'Cancel'
22
+ },
23
+ finish: {
24
+ title: 'Finish drawing',
25
+ text: 'Finish'
26
+ },
27
+ undo: {
28
+ title: 'Delete last point drawn',
29
+ text: 'Delete last point'
30
+ },
31
+ buttons: {
32
+ polyline: 'Draw a polyline',
33
+ polygon: 'Draw a polygon',
34
+ rectangle: 'Draw a rectangle',
35
+ circle: 'Draw a circle',
36
+ marker: 'Draw a marker'
37
+ }
38
+ },
39
+ handlers: {
40
+ circle: {
41
+ tooltip: {
42
+ start: 'Click and drag to draw circle.'
43
+ },
44
+ radius: 'Radius'
45
+ },
46
+ marker: {
47
+ tooltip: {
48
+ start: 'Click map to place marker.'
49
+ }
50
+ },
51
+ polygon: {
52
+ tooltip: {
53
+ start: 'Click to start drawing shape.',
54
+ cont: 'Click to continue drawing shape.',
55
+ end: 'Click first point to close this shape.'
56
+ }
57
+ },
58
+ polyline: {
59
+ error: '<strong>Error:</strong> shape edges cannot cross!',
60
+ tooltip: {
61
+ start: 'Click to start drawing line.',
62
+ cont: 'Click to continue drawing line.',
63
+ end: 'Click last point to finish line.'
64
+ }
65
+ },
66
+ rectangle: {
67
+ tooltip: {
68
+ start: 'Click and drag to draw rectangle.'
69
+ }
70
+ },
71
+ simpleshape: {
72
+ tooltip: {
73
+ end: 'Release mouse to finish drawing.'
74
+ }
75
+ }
76
+ }
77
+ },
78
+ edit: {
79
+ toolbar: {
80
+ actions: {
81
+ save: {
82
+ title: 'Save changes.',
83
+ text: 'Save'
84
+ },
85
+ cancel: {
86
+ title: 'Cancel editing, discards all changes.',
87
+ text: 'Cancel'
88
+ }
89
+ },
90
+ buttons: {
91
+ edit: 'Edit layers.',
92
+ editDisabled: 'No layers to edit.',
93
+ remove: 'Delete layers.',
94
+ removeDisabled: 'No layers to delete.'
95
+ }
96
+ },
97
+ handlers: {
98
+ edit: {
99
+ tooltip: {
100
+ text: 'Drag handles, or marker to edit feature.',
101
+ subtext: 'Click cancel to undo changes.'
102
+ }
103
+ },
104
+ remove: {
105
+ tooltip: {
106
+ text: 'Click on a feature to remove'
107
+ }
108
+ }
109
+ }
110
+ }
111
+ };
112
+
113
+
114
+ L.Draw = {};
115
+
116
+ L.Draw.Feature = L.Handler.extend({
117
+ includes: L.Mixin.Events,
118
+
119
+ initialize: function (map, options) {
120
+ this._map = map;
121
+ this._container = map._container;
122
+ this._overlayPane = map._panes.overlayPane;
123
+ this._popupPane = map._panes.popupPane;
124
+
125
+ // Merge default shapeOptions options with custom shapeOptions
126
+ if (options && options.shapeOptions) {
127
+ options.shapeOptions = L.Util.extend({}, this.options.shapeOptions, options.shapeOptions);
128
+ }
129
+ L.setOptions(this, options);
130
+ },
131
+
132
+ enable: function () {
133
+ if (this._enabled) { return; }
134
+
135
+ L.Handler.prototype.enable.call(this);
136
+
137
+ this.fire('enabled', { handler: this.type });
138
+
139
+ this._map.fire('draw:drawstart', { layerType: this.type });
140
+ },
141
+
142
+ disable: function () {
143
+ if (!this._enabled) { return; }
144
+
145
+ L.Handler.prototype.disable.call(this);
146
+
147
+ this._map.fire('draw:drawstop', { layerType: this.type });
148
+
149
+ this.fire('disabled', { handler: this.type });
150
+ },
151
+
152
+ addHooks: function () {
153
+ var map = this._map;
154
+
155
+ if (map) {
156
+ L.DomUtil.disableTextSelection();
157
+
158
+ map.getContainer().focus();
159
+
160
+ this._tooltip = new L.Tooltip(this._map);
161
+
162
+ L.DomEvent.on(this._container, 'keyup', this._cancelDrawing, this);
163
+ }
164
+ },
165
+
166
+ removeHooks: function () {
167
+ if (this._map) {
168
+ L.DomUtil.enableTextSelection();
169
+
170
+ this._tooltip.dispose();
171
+ this._tooltip = null;
172
+
173
+ L.DomEvent.off(this._container, 'keyup', this._cancelDrawing, this);
174
+ }
175
+ },
176
+
177
+ setOptions: function (options) {
178
+ L.setOptions(this, options);
179
+ },
180
+
181
+ _fireCreatedEvent: function (layer) {
182
+ this._map.fire('draw:created', { layer: layer, layerType: this.type });
183
+ },
184
+
185
+ // Cancel drawing when the escape key is pressed
186
+ _cancelDrawing: function (e) {
187
+ if (e.keyCode === 27) {
188
+ this.disable();
189
+ }
190
+ }
191
+ });
192
+
193
+ L.Draw.Polyline = L.Draw.Feature.extend({
194
+ statics: {
195
+ TYPE: 'polyline'
196
+ },
197
+
198
+ Poly: L.Polyline,
199
+
200
+ options: {
201
+ allowIntersection: true,
202
+ repeatMode: false,
203
+ drawError: {
204
+ color: '#b00b00',
205
+ timeout: 2500
206
+ },
207
+ icon: new L.DivIcon({
208
+ iconSize: new L.Point(8, 8),
209
+ className: 'leaflet-div-icon leaflet-editing-icon'
210
+ }),
211
+ touchIcon: new L.DivIcon({
212
+ iconSize: new L.Point(20, 20),
213
+ className: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon'
214
+ }),
215
+ guidelineDistance: 20,
216
+ maxGuideLineLength: 4000,
217
+ shapeOptions: {
218
+ stroke: true,
219
+ color: '#f06eaa',
220
+ weight: 4,
221
+ opacity: 0.5,
222
+ fill: false,
223
+ clickable: true
224
+ },
225
+ metric: true, // Whether to use the metric meaurement system or imperial
226
+ feet: true, // When not metric, to use feet instead of yards for display.
227
+ showLength: true, // Whether to display distance in the tooltip
228
+ zIndexOffset: 2000 // This should be > than the highest z-index any map layers
229
+ },
230
+
231
+ initialize: function (map, options) {
232
+ // if touch, switch to touch icon
233
+ if (L.Browser.touch) {
234
+ this.options.icon = this.options.touchIcon;
235
+ }
236
+
237
+ // Need to set this here to ensure the correct message is used.
238
+ this.options.drawError.message = L.drawLocal.draw.handlers.polyline.error;
239
+
240
+ // Merge default drawError options with custom options
241
+ if (options && options.drawError) {
242
+ options.drawError = L.Util.extend({}, this.options.drawError, options.drawError);
243
+ }
244
+
245
+ // Save the type so super can fire, need to do this as cannot do this.TYPE :(
246
+ this.type = L.Draw.Polyline.TYPE;
247
+
248
+ L.Draw.Feature.prototype.initialize.call(this, map, options);
249
+ },
250
+
251
+ addHooks: function () {
252
+ L.Draw.Feature.prototype.addHooks.call(this);
253
+ if (this._map) {
254
+ this._markers = [];
255
+
256
+ this._markerGroup = new L.LayerGroup();
257
+ this._map.addLayer(this._markerGroup);
258
+
259
+ this._poly = new L.Polyline([], this.options.shapeOptions);
260
+
261
+ this._tooltip.updateContent(this._getTooltipText());
262
+
263
+ // Make a transparent marker that will used to catch click events. These click
264
+ // events will create the vertices. We need to do this so we can ensure that
265
+ // we can create vertices over other map layers (markers, vector layers). We
266
+ // also do not want to trigger any click handlers of objects we are clicking on
267
+ // while drawing.
268
+ if (!this._mouseMarker) {
269
+ this._mouseMarker = L.marker(this._map.getCenter(), {
270
+ icon: L.divIcon({
271
+ className: 'leaflet-mouse-marker',
272
+ iconAnchor: [20, 20],
273
+ iconSize: [40, 40]
274
+ }),
275
+ opacity: 0,
276
+ zIndexOffset: this.options.zIndexOffset
277
+ });
278
+ }
279
+
280
+ this._mouseMarker
281
+ .on('mousedown', this._onMouseDown, this)
282
+ .on('mouseout', this._onMouseOut, this)
283
+ .on('mouseup', this._onMouseUp, this) // Necessary for 0.8 compatibility
284
+ .on('mousemove', this._onMouseMove, this) // Necessary to prevent 0.8 stutter
285
+ .addTo(this._map);
286
+
287
+ if(!L.Browser.touch){
288
+ this._map.on('mouseup', this._onMouseUp, this); // Necessary for 0.7 compatibility
289
+ }
290
+
291
+ this._map
292
+ // .on('mouseup', this._onMouseUp, this) // Necessary for 0.7 compatibility
293
+ .on('mousemove', this._onMouseMove, this)
294
+ .on('zoomlevelschange', this._onZoomEnd, this)
295
+ .on('click', this._onTouch, this)
296
+ .on('zoomend', this._onZoomEnd, this);
297
+ }
298
+ },
299
+
300
+ removeHooks: function () {
301
+ L.Draw.Feature.prototype.removeHooks.call(this);
302
+
303
+ this._clearHideErrorTimeout();
304
+
305
+ this._cleanUpShape();
306
+
307
+ // remove markers from map
308
+ this._map.removeLayer(this._markerGroup);
309
+ delete this._markerGroup;
310
+ delete this._markers;
311
+
312
+ this._map.removeLayer(this._poly);
313
+ delete this._poly;
314
+
315
+ this._mouseMarker
316
+ .off('mousedown', this._onMouseDown, this)
317
+ .off('mouseout', this._onMouseOut, this)
318
+ .off('mouseup', this._onMouseUp, this)
319
+ .off('mousemove', this._onMouseMove, this);
320
+ this._map.removeLayer(this._mouseMarker);
321
+ delete this._mouseMarker;
322
+
323
+ // clean up DOM
324
+ this._clearGuides();
325
+
326
+ this._map
327
+ .off('mouseup', this._onMouseUp, this)
328
+ .off('mousemove', this._onMouseMove, this)
329
+ .off('mouseup', this._onMouseUp, this)
330
+ .off('zoomend', this._onZoomEnd, this)
331
+ .off('click', this._onTouch, this);
332
+ },
333
+
334
+ deleteLastVertex: function () {
335
+ if (this._markers.length <= 1) {
336
+ return;
337
+ }
338
+
339
+ var lastMarker = this._markers.pop(),
340
+ poly = this._poly,
341
+ latlng = this._poly.spliceLatLngs(poly.getLatLngs().length - 1, 1)[0];
342
+
343
+ this._markerGroup.removeLayer(lastMarker);
344
+
345
+ if (poly.getLatLngs().length < 2) {
346
+ this._map.removeLayer(poly);
347
+ }
348
+
349
+ this._vertexChanged(latlng, false);
350
+ },
351
+
352
+ addVertex: function (latlng) {
353
+ var markersLength = this._markers.length;
354
+
355
+ if (markersLength > 0 && !this.options.allowIntersection && this._poly.newLatLngIntersects(latlng)) {
356
+ this._showErrorTooltip();
357
+ return;
358
+ }
359
+ else if (this._errorShown) {
360
+ this._hideErrorTooltip();
361
+ }
362
+
363
+ this._markers.push(this._createMarker(latlng));
364
+
365
+ this._poly.addLatLng(latlng);
366
+
367
+ if (this._poly.getLatLngs().length === 2) {
368
+ this._map.addLayer(this._poly);
369
+ }
370
+
371
+ this._vertexChanged(latlng, true);
372
+ },
373
+
374
+ completeShape: function () {
375
+ if (this._markers.length <= 1) {
376
+ return;
377
+ }
378
+
379
+ this._fireCreatedEvent();
380
+ this.disable();
381
+
382
+ if (this.options.repeatMode) {
383
+ this.enable();
384
+ }
385
+ },
386
+
387
+ _finishShape: function () {
388
+ var intersects = this._poly.newLatLngIntersects(this._poly.getLatLngs()[this._poly.getLatLngs().length - 1]);
389
+
390
+ if ((!this.options.allowIntersection && intersects) || !this._shapeIsValid()) {
391
+ this._showErrorTooltip();
392
+ return;
393
+ }
394
+
395
+ this._fireCreatedEvent();
396
+ this.disable();
397
+ if (this.options.repeatMode) {
398
+ this.enable();
399
+ }
400
+ },
401
+
402
+ //Called to verify the shape is valid when the user tries to finish it
403
+ //Return false if the shape is not valid
404
+ _shapeIsValid: function () {
405
+ return true;
406
+ },
407
+
408
+ _onZoomEnd: function () {
409
+ this._updateGuide();
410
+ },
411
+
412
+ _onMouseMove: function (e) {
413
+ var newPos = this._map.mouseEventToLayerPoint(e.originalEvent);
414
+ var latlng = this._map.layerPointToLatLng(newPos);
415
+
416
+ // Save latlng
417
+ // should this be moved to _updateGuide() ?
418
+ this._currentLatLng = latlng;
419
+
420
+ this._updateTooltip(latlng);
421
+
422
+ // Update the guide line
423
+ this._updateGuide(newPos);
424
+
425
+ // Update the mouse marker position
426
+ this._mouseMarker.setLatLng(latlng);
427
+
428
+ L.DomEvent.preventDefault(e.originalEvent);
429
+ },
430
+
431
+ _vertexChanged: function (latlng, added) {
432
+ this._map.fire('draw:drawvertex', { layers: this._markerGroup });
433
+ this._updateFinishHandler();
434
+
435
+ this._updateRunningMeasure(latlng, added);
436
+
437
+ this._clearGuides();
438
+
439
+ this._updateTooltip();
440
+ },
441
+
442
+ _onMouseDown: function (e) {
443
+ var originalEvent = e.originalEvent;
444
+ this._mouseDownOrigin = L.point(originalEvent.clientX, originalEvent.clientY);
445
+ },
446
+
447
+ _onMouseUp: function (e) {
448
+ if (this._mouseDownOrigin) {
449
+ // We detect clicks within a certain tolerance, otherwise let it
450
+ // be interpreted as a drag by the map
451
+ var distance = L.point(e.originalEvent.clientX, e.originalEvent.clientY)
452
+ .distanceTo(this._mouseDownOrigin);
453
+ if (Math.abs(distance) < 9 * (window.devicePixelRatio || 1)) {
454
+ this.addVertex(e.latlng);
455
+ }
456
+ }
457
+ this._mouseDownOrigin = null;
458
+ },
459
+
460
+ _onTouch: function (e) {
461
+ // #TODO: use touchstart and touchend vs using click(touch start & end).
462
+ if (L.Browser.touch) { // #TODO: get rid of this once leaflet fixes their click/touch.
463
+ this._onMouseDown(e);
464
+ this._onMouseUp(e);
465
+ }
466
+ },
467
+
468
+ _onMouseOut: function () {
469
+ if (this._tooltip) {
470
+ this._tooltip._onMouseOut.call(this._tooltip);
471
+ }
472
+ },
473
+
474
+ _updateFinishHandler: function () {
475
+ var markerCount = this._markers.length;
476
+ // The last marker should have a click handler to close the polyline
477
+ if (markerCount > 1) {
478
+ this._markers[markerCount - 1].on('click', this._finishShape, this);
479
+ }
480
+
481
+ // Remove the old marker click handler (as only the last point should close the polyline)
482
+ if (markerCount > 2) {
483
+ this._markers[markerCount - 2].off('click', this._finishShape, this);
484
+ }
485
+ },
486
+
487
+ _createMarker: function (latlng) {
488
+ var marker = new L.Marker(latlng, {
489
+ icon: this.options.icon,
490
+ zIndexOffset: this.options.zIndexOffset * 2
491
+ });
492
+
493
+ this._markerGroup.addLayer(marker);
494
+
495
+ return marker;
496
+ },
497
+
498
+ _updateGuide: function (newPos) {
499
+ var markerCount = this._markers.length;
500
+
501
+ if (markerCount > 0) {
502
+ newPos = newPos || this._map.latLngToLayerPoint(this._currentLatLng);
503
+
504
+ // draw the guide line
505
+ this._clearGuides();
506
+ this._drawGuide(
507
+ this._map.latLngToLayerPoint(this._markers[markerCount - 1].getLatLng()),
508
+ newPos
509
+ );
510
+ }
511
+ },
512
+
513
+ _updateTooltip: function (latLng) {
514
+ var text = this._getTooltipText();
515
+
516
+ if (latLng) {
517
+ this._tooltip.updatePosition(latLng);
518
+ }
519
+
520
+ if (!this._errorShown) {
521
+ this._tooltip.updateContent(text);
522
+ }
523
+ },
524
+
525
+ _drawGuide: function (pointA, pointB) {
526
+ var length = Math.floor(Math.sqrt(Math.pow((pointB.x - pointA.x), 2) + Math.pow((pointB.y - pointA.y), 2))),
527
+ guidelineDistance = this.options.guidelineDistance,
528
+ maxGuideLineLength = this.options.maxGuideLineLength,
529
+ // Only draw a guideline with a max length
530
+ i = length > maxGuideLineLength ? length - maxGuideLineLength : guidelineDistance,
531
+ fraction,
532
+ dashPoint,
533
+ dash;
534
+
535
+ //create the guides container if we haven't yet
536
+ if (!this._guidesContainer) {
537
+ this._guidesContainer = L.DomUtil.create('div', 'leaflet-draw-guides', this._overlayPane);
538
+ }
539
+
540
+ //draw a dash every GuildeLineDistance
541
+ for (; i < length; i += this.options.guidelineDistance) {
542
+ //work out fraction along line we are
543
+ fraction = i / length;
544
+
545
+ //calculate new x,y point
546
+ dashPoint = {
547
+ x: Math.floor((pointA.x * (1 - fraction)) + (fraction * pointB.x)),
548
+ y: Math.floor((pointA.y * (1 - fraction)) + (fraction * pointB.y))
549
+ };
550
+
551
+ //add guide dash to guide container
552
+ dash = L.DomUtil.create('div', 'leaflet-draw-guide-dash', this._guidesContainer);
553
+ dash.style.backgroundColor =
554
+ !this._errorShown ? this.options.shapeOptions.color : this.options.drawError.color;
555
+
556
+ L.DomUtil.setPosition(dash, dashPoint);
557
+ }
558
+ },
559
+
560
+ _updateGuideColor: function (color) {
561
+ if (this._guidesContainer) {
562
+ for (var i = 0, l = this._guidesContainer.childNodes.length; i < l; i++) {
563
+ this._guidesContainer.childNodes[i].style.backgroundColor = color;
564
+ }
565
+ }
566
+ },
567
+
568
+ // removes all child elements (guide dashes) from the guides container
569
+ _clearGuides: function () {
570
+ if (this._guidesContainer) {
571
+ while (this._guidesContainer.firstChild) {
572
+ this._guidesContainer.removeChild(this._guidesContainer.firstChild);
573
+ }
574
+ }
575
+ },
576
+
577
+ _getTooltipText: function () {
578
+ var showLength = this.options.showLength,
579
+ labelText, distanceStr;
580
+
581
+ if (this._markers.length === 0) {
582
+ labelText = {
583
+ text: L.drawLocal.draw.handlers.polyline.tooltip.start
584
+ };
585
+ } else {
586
+ distanceStr = showLength ? this._getMeasurementString() : '';
587
+
588
+ if (this._markers.length === 1) {
589
+ labelText = {
590
+ text: L.drawLocal.draw.handlers.polyline.tooltip.cont,
591
+ subtext: distanceStr
592
+ };
593
+ } else {
594
+ labelText = {
595
+ text: L.drawLocal.draw.handlers.polyline.tooltip.end,
596
+ subtext: distanceStr
597
+ };
598
+ }
599
+ }
600
+ return labelText;
601
+ },
602
+
603
+ _updateRunningMeasure: function (latlng, added) {
604
+ var markersLength = this._markers.length,
605
+ previousMarkerIndex, distance;
606
+
607
+ if (this._markers.length === 1) {
608
+ this._measurementRunningTotal = 0;
609
+ } else {
610
+ previousMarkerIndex = markersLength - (added ? 2 : 1);
611
+ distance = latlng.distanceTo(this._markers[previousMarkerIndex].getLatLng());
612
+
613
+ this._measurementRunningTotal += distance * (added ? 1 : -1);
614
+ }
615
+ },
616
+
617
+ _getMeasurementString: function () {
618
+ var currentLatLng = this._currentLatLng,
619
+ previousLatLng = this._markers[this._markers.length - 1].getLatLng(),
620
+ distance;
621
+
622
+ // calculate the distance from the last fixed point to the mouse position
623
+ distance = this._measurementRunningTotal + currentLatLng.distanceTo(previousLatLng);
624
+
625
+ return L.GeometryUtil.readableDistance(distance, this.options.metric, this.options.feet);
626
+ },
627
+
628
+ _showErrorTooltip: function () {
629
+ this._errorShown = true;
630
+
631
+ // Update tooltip
632
+ this._tooltip
633
+ .showAsError()
634
+ .updateContent({ text: this.options.drawError.message });
635
+
636
+ // Update shape
637
+ this._updateGuideColor(this.options.drawError.color);
638
+ this._poly.setStyle({ color: this.options.drawError.color });
639
+
640
+ // Hide the error after 2 seconds
641
+ this._clearHideErrorTimeout();
642
+ this._hideErrorTimeout = setTimeout(L.Util.bind(this._hideErrorTooltip, this), this.options.drawError.timeout);
643
+ },
644
+
645
+ _hideErrorTooltip: function () {
646
+ this._errorShown = false;
647
+
648
+ this._clearHideErrorTimeout();
649
+
650
+ // Revert tooltip
651
+ this._tooltip
652
+ .removeError()
653
+ .updateContent(this._getTooltipText());
654
+
655
+ // Revert shape
656
+ this._updateGuideColor(this.options.shapeOptions.color);
657
+ this._poly.setStyle({ color: this.options.shapeOptions.color });
658
+ },
659
+
660
+ _clearHideErrorTimeout: function () {
661
+ if (this._hideErrorTimeout) {
662
+ clearTimeout(this._hideErrorTimeout);
663
+ this._hideErrorTimeout = null;
664
+ }
665
+ },
666
+
667
+ _cleanUpShape: function () {
668
+ if (this._markers.length > 1) {
669
+ this._markers[this._markers.length - 1].off('click', this._finishShape, this);
670
+ }
671
+ },
672
+
673
+ _fireCreatedEvent: function () {
674
+ var poly = new this.Poly(this._poly.getLatLngs(), this.options.shapeOptions);
675
+ L.Draw.Feature.prototype._fireCreatedEvent.call(this, poly);
676
+ }
677
+ });
678
+
679
+
680
+ L.Draw.Polygon = L.Draw.Polyline.extend({
681
+ statics: {
682
+ TYPE: 'polygon'
683
+ },
684
+
685
+ Poly: L.Polygon,
686
+
687
+ options: {
688
+ showArea: false,
689
+ shapeOptions: {
690
+ stroke: true,
691
+ color: '#f06eaa',
692
+ weight: 4,
693
+ opacity: 0.5,
694
+ fill: true,
695
+ fillColor: null, //same as color by default
696
+ fillOpacity: 0.2,
697
+ clickable: true
698
+ }
699
+ },
700
+
701
+ initialize: function (map, options) {
702
+ L.Draw.Polyline.prototype.initialize.call(this, map, options);
703
+
704
+ // Save the type so super can fire, need to do this as cannot do this.TYPE :(
705
+ this.type = L.Draw.Polygon.TYPE;
706
+ },
707
+
708
+ _updateFinishHandler: function () {
709
+ var markerCount = this._markers.length;
710
+
711
+ // The first marker should have a click handler to close the polygon
712
+ if (markerCount === 1) {
713
+ this._markers[0].on('click', this._finishShape, this);
714
+ }
715
+
716
+ // Add and update the double click handler
717
+ if (markerCount > 2) {
718
+ this._markers[markerCount - 1].on('dblclick', this._finishShape, this);
719
+ // Only need to remove handler if has been added before
720
+ if (markerCount > 3) {
721
+ this._markers[markerCount - 2].off('dblclick', this._finishShape, this);
722
+ }
723
+ }
724
+ },
725
+
726
+ _getTooltipText: function () {
727
+ var text, subtext;
728
+
729
+ if (this._markers.length === 0) {
730
+ text = L.drawLocal.draw.handlers.polygon.tooltip.start;
731
+ } else if (this._markers.length < 3) {
732
+ text = L.drawLocal.draw.handlers.polygon.tooltip.cont;
733
+ } else {
734
+ text = L.drawLocal.draw.handlers.polygon.tooltip.end;
735
+ subtext = this._getMeasurementString();
736
+ }
737
+
738
+ return {
739
+ text: text,
740
+ subtext: subtext
741
+ };
742
+ },
743
+
744
+ _getMeasurementString: function () {
745
+ var area = this._area;
746
+
747
+ if (!area) {
748
+ return null;
749
+ }
750
+
751
+ return L.GeometryUtil.readableArea(area, this.options.metric);
752
+ },
753
+
754
+ _shapeIsValid: function () {
755
+ return this._markers.length >= 3;
756
+ },
757
+
758
+ _vertexChanged: function (latlng, added) {
759
+ var latLngs;
760
+
761
+ // Check to see if we should show the area
762
+ if (!this.options.allowIntersection && this.options.showArea) {
763
+ latLngs = this._poly.getLatLngs();
764
+
765
+ this._area = L.GeometryUtil.geodesicArea(latLngs);
766
+ }
767
+
768
+ L.Draw.Polyline.prototype._vertexChanged.call(this, latlng, added);
769
+ },
770
+
771
+ _cleanUpShape: function () {
772
+ var markerCount = this._markers.length;
773
+
774
+ if (markerCount > 0) {
775
+ this._markers[0].off('click', this._finishShape, this);
776
+
777
+ if (markerCount > 2) {
778
+ this._markers[markerCount - 1].off('dblclick', this._finishShape, this);
779
+ }
780
+ }
781
+ }
782
+ });
783
+
784
+
785
+ L.SimpleShape = {};
786
+
787
+ L.Draw.SimpleShape = L.Draw.Feature.extend({
788
+ options: {
789
+ repeatMode: false
790
+ },
791
+
792
+ initialize: function (map, options) {
793
+ this._endLabelText = L.drawLocal.draw.handlers.simpleshape.tooltip.end;
794
+
795
+ L.Draw.Feature.prototype.initialize.call(this, map, options);
796
+ },
797
+
798
+ addHooks: function () {
799
+ L.Draw.Feature.prototype.addHooks.call(this);
800
+ if (this._map) {
801
+ this._mapDraggable = this._map.dragging.enabled();
802
+
803
+ if (this._mapDraggable) {
804
+ this._map.dragging.disable();
805
+ }
806
+
807
+ //TODO refactor: move cursor to styles
808
+ this._container.style.cursor = 'crosshair';
809
+
810
+ this._tooltip.updateContent({ text: this._initialLabelText });
811
+
812
+ this._map
813
+ .on('mousedown', this._onMouseDown, this)
814
+ .on('mousemove', this._onMouseMove, this)
815
+ .on('touchstart', this._onMouseDown, this)
816
+ .on('touchmove', this._onMouseMove, this);
817
+ }
818
+ },
819
+
820
+ removeHooks: function () {
821
+ L.Draw.Feature.prototype.removeHooks.call(this);
822
+ if (this._map) {
823
+ if (this._mapDraggable) {
824
+ this._map.dragging.enable();
825
+ }
826
+
827
+ //TODO refactor: move cursor to styles
828
+ this._container.style.cursor = '';
829
+
830
+ this._map
831
+ .off('mousedown', this._onMouseDown, this)
832
+ .off('mousemove', this._onMouseMove, this)
833
+ .off('touchstart', this._onMouseDown, this)
834
+ .off('touchmove', this._onMouseMove, this);
835
+
836
+ L.DomEvent.off(document, 'mouseup', this._onMouseUp, this);
837
+ L.DomEvent.off(document, 'touchend', this._onMouseUp, this);
838
+
839
+ // If the box element doesn't exist they must not have moved the mouse, so don't need to destroy/return
840
+ if (this._shape) {
841
+ this._map.removeLayer(this._shape);
842
+ delete this._shape;
843
+ }
844
+ }
845
+ this._isDrawing = false;
846
+ },
847
+
848
+ _getTooltipText: function () {
849
+ return {
850
+ text: this._endLabelText
851
+ };
852
+ },
853
+
854
+ _onMouseDown: function (e) {
855
+ this._isDrawing = true;
856
+ this._startLatLng = e.latlng;
857
+
858
+ L.DomEvent
859
+ .on(document, 'mouseup', this._onMouseUp, this)
860
+ .on(document, 'touchend', this._onMouseUp, this)
861
+ .preventDefault(e.originalEvent);
862
+ },
863
+
864
+ _onMouseMove: function (e) {
865
+ var latlng = e.latlng;
866
+
867
+ this._tooltip.updatePosition(latlng);
868
+ if (this._isDrawing) {
869
+ this._tooltip.updateContent(this._getTooltipText());
870
+ this._drawShape(latlng);
871
+ }
872
+ },
873
+
874
+ _onMouseUp: function () {
875
+ if (this._shape) {
876
+ this._fireCreatedEvent();
877
+ }
878
+
879
+ this.disable();
880
+ if (this.options.repeatMode) {
881
+ this.enable();
882
+ }
883
+ }
884
+ });
885
+
886
+ L.Draw.Rectangle = L.Draw.SimpleShape.extend({
887
+ statics: {
888
+ TYPE: 'rectangle'
889
+ },
890
+
891
+ options: {
892
+ shapeOptions: {
893
+ stroke: true,
894
+ color: '#f06eaa',
895
+ weight: 4,
896
+ opacity: 0.5,
897
+ fill: true,
898
+ fillColor: null, //same as color by default
899
+ fillOpacity: 0.2,
900
+ clickable: true
901
+ },
902
+ metric: true // Whether to use the metric measurement system or imperial
903
+ },
904
+
905
+ initialize: function (map, options) {
906
+ // Save the type so super can fire, need to do this as cannot do this.TYPE :(
907
+ this.type = L.Draw.Rectangle.TYPE;
908
+
909
+ this._initialLabelText = L.drawLocal.draw.handlers.rectangle.tooltip.start;
910
+
911
+ L.Draw.SimpleShape.prototype.initialize.call(this, map, options);
912
+ },
913
+
914
+ _drawShape: function (latlng) {
915
+ if (!this._shape) {
916
+ this._shape = new L.Rectangle(new L.LatLngBounds(this._startLatLng, latlng), this.options.shapeOptions);
917
+ this._map.addLayer(this._shape);
918
+ } else {
919
+ this._shape.setBounds(new L.LatLngBounds(this._startLatLng, latlng));
920
+ }
921
+ },
922
+
923
+ _fireCreatedEvent: function () {
924
+ var rectangle = new L.Rectangle(this._shape.getBounds(), this.options.shapeOptions);
925
+ L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, rectangle);
926
+ },
927
+
928
+ _getTooltipText: function () {
929
+ var tooltipText = L.Draw.SimpleShape.prototype._getTooltipText.call(this),
930
+ shape = this._shape,
931
+ latLngs, area, subtext;
932
+
933
+ if (shape) {
934
+ latLngs = this._shape.getLatLngs();
935
+ area = L.GeometryUtil.geodesicArea(latLngs);
936
+ subtext = L.GeometryUtil.readableArea(area, this.options.metric);
937
+ }
938
+
939
+ return {
940
+ text: tooltipText.text,
941
+ subtext: subtext
942
+ };
943
+ }
944
+ });
945
+
946
+
947
+ L.Draw.Circle = L.Draw.SimpleShape.extend({
948
+ statics: {
949
+ TYPE: 'circle'
950
+ },
951
+
952
+ options: {
953
+ shapeOptions: {
954
+ stroke: true,
955
+ color: '#f06eaa',
956
+ weight: 4,
957
+ opacity: 0.5,
958
+ fill: true,
959
+ fillColor: null, //same as color by default
960
+ fillOpacity: 0.2,
961
+ clickable: true
962
+ },
963
+ showRadius: true,
964
+ metric: true, // Whether to use the metric meaurement system or imperial
965
+ feet: true // When not metric, use feet instead of yards for display
966
+ },
967
+
968
+ initialize: function (map, options) {
969
+ // Save the type so super can fire, need to do this as cannot do this.TYPE :(
970
+ this.type = L.Draw.Circle.TYPE;
971
+
972
+ this._initialLabelText = L.drawLocal.draw.handlers.circle.tooltip.start;
973
+
974
+ L.Draw.SimpleShape.prototype.initialize.call(this, map, options);
975
+ },
976
+
977
+ _drawShape: function (latlng) {
978
+ if (!this._shape) {
979
+ this._shape = new L.Circle(this._startLatLng, this._startLatLng.distanceTo(latlng), this.options.shapeOptions);
980
+ this._map.addLayer(this._shape);
981
+ } else {
982
+ this._shape.setRadius(this._startLatLng.distanceTo(latlng));
983
+ }
984
+ },
985
+
986
+ _fireCreatedEvent: function () {
987
+ var circle = new L.Circle(this._startLatLng, this._shape.getRadius(), this.options.shapeOptions);
988
+ L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, circle);
989
+ },
990
+
991
+ _onMouseMove: function (e) {
992
+ var latlng = e.latlng,
993
+ showRadius = this.options.showRadius,
994
+ useMetric = this.options.metric,
995
+ radius;
996
+
997
+ this._tooltip.updatePosition(latlng);
998
+ if (this._isDrawing) {
999
+ this._drawShape(latlng);
1000
+
1001
+ // Get the new radius (rounded to 1 dp)
1002
+ radius = this._shape.getRadius().toFixed(1);
1003
+
1004
+ this._tooltip.updateContent({
1005
+ text: this._endLabelText,
1006
+ subtext: showRadius ? L.drawLocal.draw.handlers.circle.radius + ': ' +
1007
+ L.GeometryUtil.readableDistance(radius, useMetric, this.options.feet) : ''
1008
+ });
1009
+ }
1010
+ }
1011
+ });
1012
+
1013
+
1014
+ L.Draw.Marker = L.Draw.Feature.extend({
1015
+ statics: {
1016
+ TYPE: 'marker'
1017
+ },
1018
+
1019
+ options: {
1020
+ icon: new L.Icon.Default(),
1021
+ repeatMode: false,
1022
+ zIndexOffset: 2000 // This should be > than the highest z-index any markers
1023
+ },
1024
+
1025
+ initialize: function (map, options) {
1026
+ // Save the type so super can fire, need to do this as cannot do this.TYPE :(
1027
+ this.type = L.Draw.Marker.TYPE;
1028
+
1029
+ L.Draw.Feature.prototype.initialize.call(this, map, options);
1030
+ },
1031
+
1032
+ addHooks: function () {
1033
+ L.Draw.Feature.prototype.addHooks.call(this);
1034
+
1035
+ if (this._map) {
1036
+ this._tooltip.updateContent({ text: L.drawLocal.draw.handlers.marker.tooltip.start });
1037
+
1038
+ // Same mouseMarker as in Draw.Polyline
1039
+ if (!this._mouseMarker) {
1040
+ this._mouseMarker = L.marker(this._map.getCenter(), {
1041
+ icon: L.divIcon({
1042
+ className: 'leaflet-mouse-marker',
1043
+ iconAnchor: [20, 20],
1044
+ iconSize: [40, 40]
1045
+ }),
1046
+ opacity: 0,
1047
+ zIndexOffset: this.options.zIndexOffset
1048
+ });
1049
+ }
1050
+
1051
+ this._mouseMarker
1052
+ .on('click', this._onClick, this)
1053
+ .addTo(this._map);
1054
+
1055
+ this._map.on('mousemove', this._onMouseMove, this);
1056
+ this._map.on('click', this._onTouch, this);
1057
+ }
1058
+ },
1059
+
1060
+ removeHooks: function () {
1061
+ L.Draw.Feature.prototype.removeHooks.call(this);
1062
+
1063
+ if (this._map) {
1064
+ if (this._marker) {
1065
+ this._marker.off('click', this._onClick, this);
1066
+ this._map
1067
+ .off('click', this._onClick, this)
1068
+ .off('click', this._onTouch, this)
1069
+ .removeLayer(this._marker);
1070
+ delete this._marker;
1071
+ }
1072
+
1073
+ this._mouseMarker.off('click', this._onClick, this);
1074
+ this._map.removeLayer(this._mouseMarker);
1075
+ delete this._mouseMarker;
1076
+
1077
+ this._map.off('mousemove', this._onMouseMove, this);
1078
+ }
1079
+ },
1080
+
1081
+ _onMouseMove: function (e) {
1082
+ var latlng = e.latlng;
1083
+
1084
+ this._tooltip.updatePosition(latlng);
1085
+ this._mouseMarker.setLatLng(latlng);
1086
+
1087
+ if (!this._marker) {
1088
+ this._marker = new L.Marker(latlng, {
1089
+ icon: this.options.icon,
1090
+ zIndexOffset: this.options.zIndexOffset
1091
+ });
1092
+ // Bind to both marker and map to make sure we get the click event.
1093
+ this._marker.on('click', this._onClick, this);
1094
+ this._map
1095
+ .on('click', this._onClick, this)
1096
+ .addLayer(this._marker);
1097
+ }
1098
+ else {
1099
+ latlng = this._mouseMarker.getLatLng();
1100
+ this._marker.setLatLng(latlng);
1101
+ }
1102
+ },
1103
+
1104
+ _onClick: function () {
1105
+ this._fireCreatedEvent();
1106
+
1107
+ this.disable();
1108
+ if (this.options.repeatMode) {
1109
+ this.enable();
1110
+ }
1111
+ },
1112
+
1113
+ _onTouch: function (e) {
1114
+ // called on click & tap, only really does any thing on tap
1115
+ this._onMouseMove(e); // creates & places marker
1116
+ this._onClick(); // permanently places marker & ends interaction
1117
+ },
1118
+
1119
+ _fireCreatedEvent: function () {
1120
+ var marker = new L.Marker.Touch(this._marker.getLatLng(), { icon: this.options.icon });
1121
+ L.Draw.Feature.prototype._fireCreatedEvent.call(this, marker);
1122
+ }
1123
+ });
1124
+
1125
+
1126
+ L.Edit = L.Edit || {};
1127
+
1128
+ L.Edit.Marker = L.Handler.extend({
1129
+ initialize: function (marker, options) {
1130
+ this._marker = marker;
1131
+ L.setOptions(this, options);
1132
+ },
1133
+
1134
+ addHooks: function () {
1135
+ var marker = this._marker;
1136
+
1137
+ marker.dragging.enable();
1138
+ marker.on('dragend', this._onDragEnd, marker);
1139
+ this._toggleMarkerHighlight();
1140
+ },
1141
+
1142
+ removeHooks: function () {
1143
+ var marker = this._marker;
1144
+
1145
+ marker.dragging.disable();
1146
+ marker.off('dragend', this._onDragEnd, marker);
1147
+ this._toggleMarkerHighlight();
1148
+ },
1149
+
1150
+ _onDragEnd: function (e) {
1151
+ var layer = e.target;
1152
+ layer.edited = true;
1153
+ },
1154
+
1155
+ _toggleMarkerHighlight: function () {
1156
+ var icon = this._marker._icon;
1157
+
1158
+
1159
+ // Don't do anything if this layer is a marker but doesn't have an icon. Markers
1160
+ // should usually have icons. If using Leaflet.draw with Leaflet.markercluster there
1161
+ // is a chance that a marker doesn't.
1162
+ if (!icon) {
1163
+ return;
1164
+ }
1165
+
1166
+ // This is quite naughty, but I don't see another way of doing it. (short of setting a new icon)
1167
+ icon.style.display = 'none';
1168
+
1169
+ if (L.DomUtil.hasClass(icon, 'leaflet-edit-marker-selected')) {
1170
+ L.DomUtil.removeClass(icon, 'leaflet-edit-marker-selected');
1171
+ // Offset as the border will make the icon move.
1172
+ this._offsetMarker(icon, -4);
1173
+
1174
+ } else {
1175
+ L.DomUtil.addClass(icon, 'leaflet-edit-marker-selected');
1176
+ // Offset as the border will make the icon move.
1177
+ this._offsetMarker(icon, 4);
1178
+ }
1179
+
1180
+ icon.style.display = '';
1181
+ },
1182
+
1183
+ _offsetMarker: function (icon, offset) {
1184
+ var iconMarginTop = parseInt(icon.style.marginTop, 10) - offset,
1185
+ iconMarginLeft = parseInt(icon.style.marginLeft, 10) - offset;
1186
+
1187
+ icon.style.marginTop = iconMarginTop + 'px';
1188
+ icon.style.marginLeft = iconMarginLeft + 'px';
1189
+ }
1190
+ });
1191
+
1192
+ L.Marker.addInitHook(function () {
1193
+ if (L.Edit.Marker) {
1194
+ this.editing = new L.Edit.Marker(this);
1195
+
1196
+ if (this.options.editable) {
1197
+ this.editing.enable();
1198
+ }
1199
+ }
1200
+ });
1201
+
1202
+
1203
+ L.Edit = L.Edit || {};
1204
+
1205
+ /*
1206
+ * L.Edit.Poly is an editing handler for polylines and polygons.
1207
+ */
1208
+ L.Edit.Poly = L.Handler.extend({
1209
+ options: {},
1210
+
1211
+ initialize: function (poly, options) {
1212
+
1213
+ this.latlngs = [poly._latlngs];
1214
+ if (poly._holes) {
1215
+ this.latlngs = this.latlngs.concat(poly._holes);
1216
+ }
1217
+
1218
+ this._verticesHandlers = [];
1219
+ for (var i = 0; i < this.latlngs.length; i++) {
1220
+ this._verticesHandlers.push(new L.Edit.PolyVerticesEdit(poly, this.latlngs[i], options));
1221
+ }
1222
+
1223
+ this._poly = poly;
1224
+ L.setOptions(this, options);
1225
+ },
1226
+
1227
+ _eachVertexHandler: function (callback) {
1228
+ for (var i = 0; i < this._verticesHandlers.length; i++) {
1229
+ callback(this._verticesHandlers[i]);
1230
+ }
1231
+ },
1232
+
1233
+ addHooks: function () {
1234
+ this._eachVertexHandler(function (handler) {
1235
+ handler.addHooks();
1236
+ });
1237
+ },
1238
+
1239
+ removeHooks: function () {
1240
+ this._eachVertexHandler(function (handler) {
1241
+ handler.removeHooks();
1242
+ });
1243
+ },
1244
+
1245
+ updateMarkers: function () {
1246
+ this._eachVertexHandler(function (handler) {
1247
+ handler.updateMarkers();
1248
+ });
1249
+ }
1250
+
1251
+ });
1252
+
1253
+ L.Edit.PolyVerticesEdit = L.Handler.extend({
1254
+ options: {
1255
+ icon: new L.DivIcon({
1256
+ iconSize: new L.Point(8, 8),
1257
+ className: 'leaflet-div-icon leaflet-editing-icon'
1258
+ }),
1259
+ touchIcon: new L.DivIcon({
1260
+ iconSize: new L.Point(20, 20),
1261
+ className: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon'
1262
+ }),
1263
+ drawError: {
1264
+ color: '#b00b00',
1265
+ timeout: 1000
1266
+ }
1267
+
1268
+
1269
+ },
1270
+
1271
+ initialize: function (poly, latlngs, options) {
1272
+ // if touch, switch to touch icon
1273
+ if (L.Browser.touch) {
1274
+ this.options.icon = this.options.touchIcon;
1275
+ }
1276
+ this._poly = poly;
1277
+
1278
+ if (options && options.drawError) {
1279
+ options.drawError = L.Util.extend({}, this.options.drawError, options.drawError);
1280
+ }
1281
+
1282
+ this._latlngs = latlngs;
1283
+
1284
+ L.setOptions(this, options);
1285
+ },
1286
+
1287
+ addHooks: function () {
1288
+ var poly = this._poly;
1289
+
1290
+ if (!(poly instanceof L.Polygon)) {
1291
+ poly.options.editing.fill = false;
1292
+ }
1293
+
1294
+ poly.setStyle(poly.options.editing);
1295
+
1296
+ if (this._poly._map) {
1297
+
1298
+ this._map = this._poly._map; // Set map
1299
+
1300
+ if (!this._markerGroup) {
1301
+ this._initMarkers();
1302
+ }
1303
+ this._poly._map.addLayer(this._markerGroup);
1304
+ }
1305
+ },
1306
+
1307
+ removeHooks: function () {
1308
+ var poly = this._poly;
1309
+
1310
+ poly.setStyle(poly.options.original);
1311
+
1312
+ if (poly._map) {
1313
+ poly._map.removeLayer(this._markerGroup);
1314
+ delete this._markerGroup;
1315
+ delete this._markers;
1316
+ }
1317
+ },
1318
+
1319
+ updateMarkers: function () {
1320
+ this._markerGroup.clearLayers();
1321
+ this._initMarkers();
1322
+ },
1323
+
1324
+ _initMarkers: function () {
1325
+ if (!this._markerGroup) {
1326
+ this._markerGroup = new L.LayerGroup();
1327
+ }
1328
+ this._markers = [];
1329
+
1330
+ var latlngs = this._latlngs,
1331
+ i, j, len, marker;
1332
+
1333
+ for (i = 0, len = latlngs.length; i < len; i++) {
1334
+
1335
+ marker = this._createMarker(latlngs[i], i);
1336
+ marker.on('click', this._onMarkerClick, this);
1337
+ this._markers.push(marker);
1338
+ }
1339
+
1340
+ var markerLeft, markerRight;
1341
+
1342
+ for (i = 0, j = len - 1; i < len; j = i++) {
1343
+ if (i === 0 && !(L.Polygon && (this._poly instanceof L.Polygon))) {
1344
+ continue;
1345
+ }
1346
+
1347
+ markerLeft = this._markers[j];
1348
+ markerRight = this._markers[i];
1349
+
1350
+ this._createMiddleMarker(markerLeft, markerRight);
1351
+ this._updatePrevNext(markerLeft, markerRight);
1352
+ }
1353
+ },
1354
+
1355
+ _createMarker: function (latlng, index) {
1356
+ // Extending L.Marker in TouchEvents.js to include touch.
1357
+ var marker = new L.Marker.Touch(latlng, {
1358
+ draggable: true,
1359
+ icon: this.options.icon,
1360
+ });
1361
+
1362
+ marker._origLatLng = latlng;
1363
+ marker._index = index;
1364
+
1365
+ marker
1366
+ .on('dragstart', this._onMarkerDragStart, this)
1367
+ .on('drag', this._onMarkerDrag, this)
1368
+ .on('dragend', this._fireEdit, this)
1369
+ .on('touchmove', this._onTouchMove, this)
1370
+ .on('MSPointerMove', this._onTouchMove, this)
1371
+ .on('touchend', this._fireEdit, this)
1372
+ .on('MSPointerUp', this._fireEdit, this);
1373
+
1374
+ this._markerGroup.addLayer(marker);
1375
+
1376
+ return marker;
1377
+ },
1378
+
1379
+ _onMarkerDragStart: function () {
1380
+ this._poly.fire('editstart');
1381
+ },
1382
+
1383
+ _spliceLatLngs: function () {
1384
+ var removed = [].splice.apply(this._latlngs, arguments);
1385
+ this._poly._convertLatLngs(this._latlngs, true);
1386
+ this._poly.redraw();
1387
+ return removed;
1388
+ },
1389
+
1390
+ _removeMarker: function (marker) {
1391
+ var i = marker._index;
1392
+
1393
+ this._markerGroup.removeLayer(marker);
1394
+ this._markers.splice(i, 1);
1395
+ this._spliceLatLngs(i, 1);
1396
+ this._updateIndexes(i, -1);
1397
+
1398
+ marker
1399
+ .off('dragstart', this._onMarkerDragStart, this)
1400
+ .off('drag', this._onMarkerDrag, this)
1401
+ .off('dragend', this._fireEdit, this)
1402
+ .off('touchmove', this._onMarkerDrag, this)
1403
+ .off('touchend', this._fireEdit, this)
1404
+ .off('click', this._onMarkerClick, this);
1405
+ },
1406
+
1407
+ _fireEdit: function () {
1408
+ this._poly.edited = true;
1409
+ this._poly.fire('edit');
1410
+ this._poly._map.fire('draw:editvertex', { layers: this._markerGroup });
1411
+ },
1412
+
1413
+ _onMarkerDrag: function (e) {
1414
+ var marker = e.target;
1415
+ var poly = this._poly;
1416
+
1417
+ L.extend(marker._origLatLng, marker._latlng);
1418
+
1419
+ if (marker._middleLeft) {
1420
+ marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker));
1421
+ }
1422
+ if (marker._middleRight) {
1423
+ marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next));
1424
+ }
1425
+
1426
+ if (poly.options.poly) {
1427
+ var tooltip = poly._map._editTooltip; // Access the tooltip
1428
+
1429
+ // If we don't allow intersections and the polygon intersects
1430
+ if (!poly.options.poly.allowIntersection && poly.intersects()) {
1431
+
1432
+ var originalColor = poly.options.color;
1433
+ poly.setStyle({ color: this.options.drawError.color });
1434
+
1435
+ if (tooltip) {
1436
+ tooltip.updateContent({
1437
+ text: L.drawLocal.draw.handlers.polyline.error
1438
+ });
1439
+ }
1440
+
1441
+ // Reset everything back to normal after a second
1442
+ setTimeout(function () {
1443
+ poly.setStyle({ color: originalColor });
1444
+ if (tooltip) {
1445
+ tooltip.updateContent({
1446
+ text: L.drawLocal.edit.handlers.edit.tooltip.text,
1447
+ subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext
1448
+ });
1449
+ }
1450
+ }, 1000);
1451
+ this._onMarkerClick(e); // Reset the marker to it's original position
1452
+ }
1453
+ }
1454
+
1455
+ this._poly.redraw();
1456
+ this._poly.fire('editdrag');
1457
+ },
1458
+
1459
+ _onMarkerClick: function (e) {
1460
+
1461
+ var minPoints = L.Polygon && (this._poly instanceof L.Polygon) ? 4 : 3,
1462
+ marker = e.target;
1463
+
1464
+ // If removing this point would create an invalid polyline/polygon don't remove
1465
+ if (this._latlngs.length < minPoints) {
1466
+ return;
1467
+ }
1468
+
1469
+ // remove the marker
1470
+ this._removeMarker(marker);
1471
+
1472
+ // update prev/next links of adjacent markers
1473
+ this._updatePrevNext(marker._prev, marker._next);
1474
+
1475
+ // remove ghost markers near the removed marker
1476
+ if (marker._middleLeft) {
1477
+ this._markerGroup.removeLayer(marker._middleLeft);
1478
+ }
1479
+ if (marker._middleRight) {
1480
+ this._markerGroup.removeLayer(marker._middleRight);
1481
+ }
1482
+
1483
+ // create a ghost marker in place of the removed one
1484
+ if (marker._prev && marker._next) {
1485
+ this._createMiddleMarker(marker._prev, marker._next);
1486
+
1487
+ } else if (!marker._prev) {
1488
+ marker._next._middleLeft = null;
1489
+
1490
+ } else if (!marker._next) {
1491
+ marker._prev._middleRight = null;
1492
+ }
1493
+
1494
+ this._fireEdit();
1495
+ },
1496
+
1497
+ _onTouchMove: function (e) {
1498
+
1499
+ var layerPoint = this._map.mouseEventToLayerPoint(e.originalEvent.touches[0]),
1500
+ latlng = this._map.layerPointToLatLng(layerPoint),
1501
+ marker = e.target;
1502
+
1503
+ L.extend(marker._origLatLng, latlng);
1504
+
1505
+ if (marker._middleLeft) {
1506
+ marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker));
1507
+ }
1508
+ if (marker._middleRight) {
1509
+ marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next));
1510
+ }
1511
+
1512
+ this._poly.redraw();
1513
+ this.updateMarkers();
1514
+ },
1515
+
1516
+ _updateIndexes: function (index, delta) {
1517
+ this._markerGroup.eachLayer(function (marker) {
1518
+ if (marker._index > index) {
1519
+ marker._index += delta;
1520
+ }
1521
+ });
1522
+ },
1523
+
1524
+ _createMiddleMarker: function (marker1, marker2) {
1525
+ var latlng = this._getMiddleLatLng(marker1, marker2),
1526
+ marker = this._createMarker(latlng),
1527
+ onClick,
1528
+ onDragStart,
1529
+ onDragEnd;
1530
+
1531
+ marker.setOpacity(0.6);
1532
+
1533
+ marker1._middleRight = marker2._middleLeft = marker;
1534
+
1535
+ onDragStart = function () {
1536
+ var i = marker2._index;
1537
+
1538
+ marker._index = i;
1539
+
1540
+ marker
1541
+ .off('click', onClick, this)
1542
+ .on('click', this._onMarkerClick, this);
1543
+
1544
+ latlng.lat = marker.getLatLng().lat;
1545
+ latlng.lng = marker.getLatLng().lng;
1546
+ this._spliceLatLngs(i, 0, latlng);
1547
+ this._markers.splice(i, 0, marker);
1548
+
1549
+ marker.setOpacity(1);
1550
+
1551
+ this._updateIndexes(i, 1);
1552
+ marker2._index++;
1553
+ this._updatePrevNext(marker1, marker);
1554
+ this._updatePrevNext(marker, marker2);
1555
+
1556
+ this._poly.fire('editstart');
1557
+ };
1558
+
1559
+ onDragEnd = function () {
1560
+
1561
+ marker.off('dragstart', onDragStart, this);
1562
+ marker.off('dragend', onDragEnd, this);
1563
+ marker.off('touchmove', onDragStart, this);
1564
+
1565
+ this._createMiddleMarker(marker1, marker);
1566
+ this._createMiddleMarker(marker, marker2);
1567
+
1568
+ };
1569
+
1570
+ onClick = function () {
1571
+ onDragStart.call(this);
1572
+ onDragEnd.call(this);
1573
+ this._fireEdit();
1574
+ };
1575
+
1576
+ marker
1577
+ .on('click', onClick, this)
1578
+ .on('dragstart', onDragStart, this)
1579
+ .on('dragend', onDragEnd, this)
1580
+ .on('touchmove', onDragStart, this);
1581
+
1582
+ this._markerGroup.addLayer(marker);
1583
+ },
1584
+
1585
+ _updatePrevNext: function (marker1, marker2) {
1586
+ if (marker1) {
1587
+ marker1._next = marker2;
1588
+ }
1589
+ if (marker2) {
1590
+ marker2._prev = marker1;
1591
+ }
1592
+ },
1593
+
1594
+ _getMiddleLatLng: function (marker1, marker2) {
1595
+ var map = this._poly._map,
1596
+ p1 = map.project(marker1.getLatLng()),
1597
+ p2 = map.project(marker2.getLatLng());
1598
+
1599
+ return map.unproject(p1._add(p2)._divideBy(2));
1600
+ }
1601
+ });
1602
+
1603
+ L.Polyline.addInitHook(function () {
1604
+
1605
+ // Check to see if handler has already been initialized. This is to support versions of Leaflet that still have L.Handler.PolyEdit
1606
+ if (this.editing) {
1607
+ return;
1608
+ }
1609
+
1610
+ if (L.Edit.Poly) {
1611
+
1612
+ this.editing = new L.Edit.Poly(this, this.options.poly);
1613
+
1614
+ if (this.options.editable) {
1615
+ this.editing.enable();
1616
+ }
1617
+ }
1618
+
1619
+ this.on('add', function () {
1620
+ if (this.editing && this.editing.enabled()) {
1621
+ this.editing.addHooks();
1622
+ }
1623
+ });
1624
+
1625
+ this.on('remove', function () {
1626
+ if (this.editing && this.editing.enabled()) {
1627
+ this.editing.removeHooks();
1628
+ }
1629
+ });
1630
+ });
1631
+
1632
+
1633
+ L.Edit = L.Edit || {};
1634
+
1635
+ L.Edit.SimpleShape = L.Handler.extend({
1636
+ options: {
1637
+ moveIcon: new L.DivIcon({
1638
+ iconSize: new L.Point(8, 8),
1639
+ className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-move'
1640
+ }),
1641
+ resizeIcon: new L.DivIcon({
1642
+ iconSize: new L.Point(8, 8),
1643
+ className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-resize'
1644
+ }),
1645
+ touchMoveIcon: new L.DivIcon({
1646
+ iconSize: new L.Point(20, 20),
1647
+ className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-move leaflet-touch-icon'
1648
+ }),
1649
+ touchResizeIcon: new L.DivIcon({
1650
+ iconSize: new L.Point(20, 20),
1651
+ className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-resize leaflet-touch-icon'
1652
+ }),
1653
+ },
1654
+
1655
+ initialize: function (shape, options) {
1656
+ // if touch, switch to touch icon
1657
+ if (L.Browser.touch) {
1658
+ this.options.moveIcon = this.options.touchMoveIcon;
1659
+ this.options.resizeIcon = this.options.touchResizeIcon;
1660
+ }
1661
+
1662
+ this._shape = shape;
1663
+ L.Util.setOptions(this, options);
1664
+ },
1665
+
1666
+ addHooks: function () {
1667
+ var shape = this._shape;
1668
+ if (this._shape._map) {
1669
+ this._map = this._shape._map;
1670
+ shape.setStyle(shape.options.editing);
1671
+
1672
+ if (shape._map) {
1673
+ this._map = shape._map;
1674
+ if (!this._markerGroup) {
1675
+ this._initMarkers();
1676
+ }
1677
+ this._map.addLayer(this._markerGroup);
1678
+ }
1679
+ }
1680
+ },
1681
+
1682
+ removeHooks: function () {
1683
+ var shape = this._shape;
1684
+
1685
+ shape.setStyle(shape.options.original);
1686
+
1687
+ if (shape._map) {
1688
+ this._unbindMarker(this._moveMarker);
1689
+
1690
+ for (var i = 0, l = this._resizeMarkers.length; i < l; i++) {
1691
+ this._unbindMarker(this._resizeMarkers[i]);
1692
+ }
1693
+ this._resizeMarkers = null;
1694
+
1695
+ this._map.removeLayer(this._markerGroup);
1696
+ delete this._markerGroup;
1697
+ }
1698
+
1699
+ this._map = null;
1700
+ },
1701
+
1702
+ updateMarkers: function () {
1703
+ this._markerGroup.clearLayers();
1704
+ this._initMarkers();
1705
+ },
1706
+
1707
+ _initMarkers: function () {
1708
+ if (!this._markerGroup) {
1709
+ this._markerGroup = new L.LayerGroup();
1710
+ }
1711
+
1712
+ // Create center marker
1713
+ this._createMoveMarker();
1714
+
1715
+ // Create edge marker
1716
+ this._createResizeMarker();
1717
+ },
1718
+
1719
+ _createMoveMarker: function () {
1720
+ // Children override
1721
+ },
1722
+
1723
+ _createResizeMarker: function () {
1724
+ // Children override
1725
+ },
1726
+
1727
+ _createMarker: function (latlng, icon) {
1728
+ // Extending L.Marker in TouchEvents.js to include touch.
1729
+ var marker = new L.Marker.Touch(latlng, {
1730
+ draggable: true,
1731
+ icon: icon,
1732
+ zIndexOffset: 10
1733
+ });
1734
+
1735
+ this._bindMarker(marker);
1736
+
1737
+ this._markerGroup.addLayer(marker);
1738
+
1739
+ return marker;
1740
+ },
1741
+
1742
+ _bindMarker: function (marker) {
1743
+ marker
1744
+ .on('dragstart', this._onMarkerDragStart, this)
1745
+ .on('drag', this._onMarkerDrag, this)
1746
+ .on('dragend', this._onMarkerDragEnd, this)
1747
+ .on('touchstart', this._onTouchStart, this)
1748
+ .on('touchmove', this._onTouchMove, this)
1749
+ .on('MSPointerMove', this._onTouchMove, this)
1750
+ .on('touchend', this._onTouchEnd, this)
1751
+ .on('MSPointerUp', this._onTouchEnd, this);
1752
+ },
1753
+
1754
+ _unbindMarker: function (marker) {
1755
+ marker
1756
+ .off('dragstart', this._onMarkerDragStart, this)
1757
+ .off('drag', this._onMarkerDrag, this)
1758
+ .off('dragend', this._onMarkerDragEnd, this)
1759
+ .off('touchstart', this._onTouchStart, this)
1760
+ .off('touchmove', this._onTouchMove, this)
1761
+ .off('MSPointerMove', this._onTouchMove, this)
1762
+ .off('touchend', this._onTouchEnd, this)
1763
+ .off('MSPointerUp', this._onTouchEnd, this);
1764
+ },
1765
+
1766
+ _onMarkerDragStart: function (e) {
1767
+ var marker = e.target;
1768
+ marker.setOpacity(0);
1769
+
1770
+ this._shape.fire('editstart');
1771
+ },
1772
+
1773
+ _fireEdit: function () {
1774
+ this._shape.edited = true;
1775
+ this._shape.fire('edit');
1776
+ },
1777
+
1778
+ _onMarkerDrag: function (e) {
1779
+ var marker = e.target,
1780
+ latlng = marker.getLatLng();
1781
+
1782
+ if (marker === this._moveMarker) {
1783
+ this._move(latlng);
1784
+ } else {
1785
+ this._resize(latlng);
1786
+ }
1787
+
1788
+ this._shape.redraw();
1789
+ this._shape.fire('editdrag');
1790
+ },
1791
+
1792
+ _onMarkerDragEnd: function (e) {
1793
+ var marker = e.target;
1794
+ marker.setOpacity(1);
1795
+
1796
+ this._fireEdit();
1797
+ },
1798
+
1799
+ _onTouchStart: function (e) {
1800
+ L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, e);
1801
+
1802
+ if (typeof(this._getCorners) === 'function') {
1803
+ // Save a reference to the opposite point
1804
+ var corners = this._getCorners(),
1805
+ marker = e.target,
1806
+ currentCornerIndex = marker._cornerIndex;
1807
+
1808
+ marker.setOpacity(0);
1809
+
1810
+ // Copyed from Edit.Rectangle.js line 23 _onMarkerDragStart()
1811
+ // Latlng is null otherwise.
1812
+ this._oppositeCorner = corners[(currentCornerIndex + 2) % 4];
1813
+ this._toggleCornerMarkers(0, currentCornerIndex);
1814
+ }
1815
+
1816
+ this._shape.fire('editstart');
1817
+ },
1818
+
1819
+ _onTouchMove: function (e) {
1820
+ var layerPoint = this._map.mouseEventToLayerPoint(e.originalEvent.touches[0]),
1821
+ latlng = this._map.layerPointToLatLng(layerPoint),
1822
+ marker = e.target;
1823
+
1824
+ if (marker === this._moveMarker) {
1825
+ this._move(latlng);
1826
+ } else {
1827
+ this._resize(latlng);
1828
+ }
1829
+
1830
+ this._shape.redraw();
1831
+
1832
+ // prevent touchcancel in IOS
1833
+ // e.preventDefault();
1834
+ return false;
1835
+ },
1836
+
1837
+ _onTouchEnd: function (e) {
1838
+ var marker = e.target;
1839
+ marker.setOpacity(1);
1840
+ this.updateMarkers();
1841
+ this._fireEdit();
1842
+ },
1843
+
1844
+ _move: function () {
1845
+ // Children override
1846
+ },
1847
+
1848
+ _resize: function () {
1849
+ // Children override
1850
+ }
1851
+ });
1852
+
1853
+
1854
+ L.Edit = L.Edit || {};
1855
+
1856
+ L.Edit.Rectangle = L.Edit.SimpleShape.extend({
1857
+ _createMoveMarker: function () {
1858
+ var bounds = this._shape.getBounds(),
1859
+ center = bounds.getCenter();
1860
+
1861
+ this._moveMarker = this._createMarker(center, this.options.moveIcon);
1862
+ },
1863
+
1864
+ _createResizeMarker: function () {
1865
+ var corners = this._getCorners();
1866
+
1867
+ this._resizeMarkers = [];
1868
+
1869
+ for (var i = 0, l = corners.length; i < l; i++) {
1870
+ this._resizeMarkers.push(this._createMarker(corners[i], this.options.resizeIcon));
1871
+ // Monkey in the corner index as we will need to know this for dragging
1872
+ this._resizeMarkers[i]._cornerIndex = i;
1873
+ }
1874
+ },
1875
+
1876
+ _onMarkerDragStart: function (e) {
1877
+ L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, e);
1878
+
1879
+ // Save a reference to the opposite point
1880
+ var corners = this._getCorners(),
1881
+ marker = e.target,
1882
+ currentCornerIndex = marker._cornerIndex;
1883
+
1884
+ this._oppositeCorner = corners[(currentCornerIndex + 2) % 4];
1885
+
1886
+ this._toggleCornerMarkers(0, currentCornerIndex);
1887
+ },
1888
+
1889
+ _onMarkerDragEnd: function (e) {
1890
+ var marker = e.target,
1891
+ bounds, center;
1892
+
1893
+ // Reset move marker position to the center
1894
+ if (marker === this._moveMarker) {
1895
+ bounds = this._shape.getBounds();
1896
+ center = bounds.getCenter();
1897
+
1898
+ marker.setLatLng(center);
1899
+ }
1900
+
1901
+ this._toggleCornerMarkers(1);
1902
+
1903
+ this._repositionCornerMarkers();
1904
+
1905
+ L.Edit.SimpleShape.prototype._onMarkerDragEnd.call(this, e);
1906
+ },
1907
+
1908
+ _move: function (newCenter) {
1909
+ var latlngs = this._shape.getLatLngs(),
1910
+ bounds = this._shape.getBounds(),
1911
+ center = bounds.getCenter(),
1912
+ offset, newLatLngs = [];
1913
+
1914
+ // Offset the latlngs to the new center
1915
+ for (var i = 0, l = latlngs.length; i < l; i++) {
1916
+ offset = [latlngs[i].lat - center.lat, latlngs[i].lng - center.lng];
1917
+ newLatLngs.push([newCenter.lat + offset[0], newCenter.lng + offset[1]]);
1918
+ }
1919
+
1920
+ this._shape.setLatLngs(newLatLngs);
1921
+
1922
+ // Reposition the resize markers
1923
+ this._repositionCornerMarkers();
1924
+ },
1925
+
1926
+ _resize: function (latlng) {
1927
+ var bounds;
1928
+
1929
+ // Update the shape based on the current position of this corner and the opposite point
1930
+ this._shape.setBounds(L.latLngBounds(latlng, this._oppositeCorner));
1931
+
1932
+ // Reposition the move marker
1933
+ bounds = this._shape.getBounds();
1934
+ this._moveMarker.setLatLng(bounds.getCenter());
1935
+ },
1936
+
1937
+ _getCorners: function () {
1938
+ var bounds = this._shape.getBounds(),
1939
+ nw = bounds.getNorthWest(),
1940
+ ne = bounds.getNorthEast(),
1941
+ se = bounds.getSouthEast(),
1942
+ sw = bounds.getSouthWest();
1943
+
1944
+ return [nw, ne, se, sw];
1945
+ },
1946
+
1947
+ _toggleCornerMarkers: function (opacity) {
1948
+ for (var i = 0, l = this._resizeMarkers.length; i < l; i++) {
1949
+ this._resizeMarkers[i].setOpacity(opacity);
1950
+ }
1951
+ },
1952
+
1953
+ _repositionCornerMarkers: function () {
1954
+ var corners = this._getCorners();
1955
+
1956
+ for (var i = 0, l = this._resizeMarkers.length; i < l; i++) {
1957
+ this._resizeMarkers[i].setLatLng(corners[i]);
1958
+ }
1959
+ }
1960
+ });
1961
+
1962
+ L.Rectangle.addInitHook(function () {
1963
+ if (L.Edit.Rectangle) {
1964
+ this.editing = new L.Edit.Rectangle(this);
1965
+
1966
+ if (this.options.editable) {
1967
+ this.editing.enable();
1968
+ }
1969
+ }
1970
+ });
1971
+
1972
+
1973
+ L.Edit = L.Edit || {};
1974
+
1975
+ L.Edit.Circle = L.Edit.SimpleShape.extend({
1976
+ _createMoveMarker: function () {
1977
+ var center = this._shape.getLatLng();
1978
+
1979
+ this._moveMarker = this._createMarker(center, this.options.moveIcon);
1980
+ },
1981
+
1982
+ _createResizeMarker: function () {
1983
+ var center = this._shape.getLatLng(),
1984
+ resizemarkerPoint = this._getResizeMarkerPoint(center);
1985
+
1986
+ this._resizeMarkers = [];
1987
+ this._resizeMarkers.push(this._createMarker(resizemarkerPoint, this.options.resizeIcon));
1988
+ },
1989
+
1990
+ _getResizeMarkerPoint: function (latlng) {
1991
+ // From L.shape.getBounds()
1992
+ var delta = this._shape._radius * Math.cos(Math.PI / 4),
1993
+ point = this._map.project(latlng);
1994
+ return this._map.unproject([point.x + delta, point.y - delta]);
1995
+ },
1996
+
1997
+ _move: function (latlng) {
1998
+ var resizemarkerPoint = this._getResizeMarkerPoint(latlng);
1999
+
2000
+ // Move the resize marker
2001
+ this._resizeMarkers[0].setLatLng(resizemarkerPoint);
2002
+
2003
+ // Move the circle
2004
+ this._shape.setLatLng(latlng);
2005
+ },
2006
+
2007
+ _resize: function (latlng) {
2008
+ var moveLatLng = this._moveMarker.getLatLng(),
2009
+ radius = moveLatLng.distanceTo(latlng);
2010
+
2011
+ this._shape.setRadius(radius);
2012
+ }
2013
+ });
2014
+
2015
+ L.Circle.addInitHook(function () {
2016
+ if (L.Edit.Circle) {
2017
+ this.editing = new L.Edit.Circle(this);
2018
+
2019
+ if (this.options.editable) {
2020
+ this.editing.enable();
2021
+ }
2022
+ }
2023
+
2024
+ this.on('add', function () {
2025
+ if (this.editing && this.editing.enabled()) {
2026
+ this.editing.addHooks();
2027
+ }
2028
+ });
2029
+
2030
+ this.on('remove', function () {
2031
+ if (this.editing && this.editing.enabled()) {
2032
+ this.editing.removeHooks();
2033
+ }
2034
+ });
2035
+ });
2036
+
2037
+ L.Map.mergeOptions({
2038
+ touchExtend: true
2039
+ });
2040
+
2041
+ L.Map.TouchExtend = L.Handler.extend({
2042
+
2043
+ initialize: function (map) {
2044
+ this._map = map;
2045
+ this._container = map._container;
2046
+ this._pane = map._panes.overlayPane;
2047
+ },
2048
+
2049
+ addHooks: function () {
2050
+ L.DomEvent.on(this._container, 'touchstart', this._onTouchStart, this);
2051
+ L.DomEvent.on(this._container, 'touchend', this._onTouchEnd, this);
2052
+ L.DomEvent.on(this._container, 'touchmove', this._onTouchMove, this);
2053
+ if (this._detectIE()) {
2054
+ L.DomEvent.on(this._container, 'MSPointerDown', this._onTouchStart, this);
2055
+ L.DomEvent.on(this._container, 'MSPointerUp', this._onTouchEnd, this);
2056
+ L.DomEvent.on(this._container, 'MSPointerMove', this._onTouchMove, this);
2057
+ L.DomEvent.on(this._container, 'MSPointerCancel', this._onTouchCancel, this);
2058
+
2059
+ } else {
2060
+ L.DomEvent.on(this._container, 'touchcancel', this._onTouchCancel, this);
2061
+ L.DomEvent.on(this._container, 'touchleave', this._onTouchLeave, this);
2062
+ }
2063
+ },
2064
+
2065
+ removeHooks: function () {
2066
+ L.DomEvent.off(this._container, 'touchstart', this._onTouchStart);
2067
+ L.DomEvent.off(this._container, 'touchend', this._onTouchEnd);
2068
+ L.DomEvent.off(this._container, 'touchmove', this._onTouchMove);
2069
+ if (this._detectIE()) {
2070
+ L.DomEvent.off(this._container, 'MSPointerDowm', this._onTouchStart);
2071
+ L.DomEvent.off(this._container, 'MSPointerUp', this._onTouchEnd);
2072
+ L.DomEvent.off(this._container, 'MSPointerMove', this._onTouchMove);
2073
+ L.DomEvent.off(this._container, 'MSPointerCancel', this._onTouchCancel);
2074
+ } else {
2075
+ L.DomEvent.off(this._container, 'touchcancel', this._onTouchCancel);
2076
+ L.DomEvent.off(this._container, 'touchleave', this._onTouchLeave);
2077
+ }
2078
+ },
2079
+
2080
+ _touchEvent: function (e, type) {
2081
+ // #TODO: fix the pageX error that is do a bug in Android where a single touch triggers two click events
2082
+ // _filterClick is what leaflet uses as a workaround.
2083
+ // This is a problem with more things than just android. Another problem is touchEnd has no touches in
2084
+ // its touch list.
2085
+ var touchEvent = {};
2086
+ if (typeof e.touches !== 'undefined') {
2087
+ if (!e.touches.length) {
2088
+ return;
2089
+ }
2090
+ touchEvent = e.touches[0];
2091
+ } else if (e.pointerType === 'touch') {
2092
+ touchEvent = e;
2093
+ if (!this._filterClick(e)) {
2094
+ return;
2095
+ }
2096
+ } else {
2097
+ return;
2098
+ }
2099
+
2100
+ var containerPoint = this._map.mouseEventToContainerPoint(touchEvent),
2101
+ layerPoint = this._map.mouseEventToLayerPoint(touchEvent),
2102
+ latlng = this._map.layerPointToLatLng(layerPoint);
2103
+
2104
+ this._map.fire(type, {
2105
+ latlng: latlng,
2106
+ layerPoint: layerPoint,
2107
+ containerPoint: containerPoint,
2108
+ pageX: touchEvent.pageX,
2109
+ pageY: touchEvent.pageY,
2110
+ originalEvent: e
2111
+ });
2112
+ },
2113
+
2114
+ /** Borrowed from Leaflet and modified for bool ops **/
2115
+ _filterClick: function (e) {
2116
+ var timeStamp = (e.timeStamp || e.originalEvent.timeStamp),
2117
+ elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick);
2118
+
2119
+ // are they closer together than 500ms yet more than 100ms?
2120
+ // Android typically triggers them ~300ms apart while multiple listeners
2121
+ // on the same event should be triggered far faster;
2122
+ // or check if click is simulated on the element, and if it is, reject any non-simulated events
2123
+ if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
2124
+ L.DomEvent.stop(e);
2125
+ return false;
2126
+ }
2127
+ L.DomEvent._lastClick = timeStamp;
2128
+ return true;
2129
+ },
2130
+
2131
+ _onTouchStart: function (e) {
2132
+ if (!this._map._loaded) {
2133
+ return;
2134
+ }
2135
+
2136
+ var type = 'touchstart';
2137
+ this._touchEvent(e, type);
2138
+
2139
+ },
2140
+
2141
+ _onTouchEnd: function (e) {
2142
+ if (!this._map._loaded) {
2143
+ return;
2144
+ }
2145
+
2146
+ var type = 'touchend';
2147
+ this._touchEvent(e, type);
2148
+ },
2149
+
2150
+ _onTouchCancel: function (e) {
2151
+ if (!this._map._loaded) {
2152
+ return;
2153
+ }
2154
+
2155
+ var type = 'touchcancel';
2156
+ if (this._detectIE()) {
2157
+ type = 'pointercancel';
2158
+ }
2159
+ this._touchEvent(e, type);
2160
+ },
2161
+
2162
+ _onTouchLeave: function (e) {
2163
+ if (!this._map._loaded) {
2164
+ return;
2165
+ }
2166
+
2167
+ var type = 'touchleave';
2168
+ this._touchEvent(e, type);
2169
+ },
2170
+
2171
+ _onTouchMove: function (e) {
2172
+ if (!this._map._loaded) {
2173
+ return;
2174
+ }
2175
+
2176
+ var type = 'touchmove';
2177
+ this._touchEvent(e, type);
2178
+ },
2179
+
2180
+ _detectIE: function () {
2181
+ var ua = window.navigator.userAgent;
2182
+
2183
+ var msie = ua.indexOf('MSIE ');
2184
+ if (msie > 0) {
2185
+ // IE 10 or older => return version number
2186
+ return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
2187
+ }
2188
+
2189
+ var trident = ua.indexOf('Trident/');
2190
+ if (trident > 0) {
2191
+ // IE 11 => return version number
2192
+ var rv = ua.indexOf('rv:');
2193
+ return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
2194
+ }
2195
+
2196
+ var edge = ua.indexOf('Edge/');
2197
+ if (edge > 0) {
2198
+ // IE 12 => return version number
2199
+ return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
2200
+ }
2201
+
2202
+ // other browser
2203
+ return false;
2204
+ }
2205
+ });
2206
+
2207
+ L.Map.addInitHook('addHandler', 'touchExtend', L.Map.TouchExtend);
2208
+
2209
+ // This isn't full Touch support. This is just to get makers to also support dom touch events after creation
2210
+ // #TODO: find a better way of getting markers to support touch.
2211
+ L.Marker.Touch = L.Marker.extend({
2212
+
2213
+ // This is an exact copy of https://github.com/Leaflet/Leaflet/blob/v0.7/src/layer/marker/Marker.js
2214
+ // with the addition of the touch event son line 15.
2215
+ _initInteraction: function () {
2216
+
2217
+ if (!this.options.clickable) {
2218
+ return;
2219
+ }
2220
+
2221
+ // TODO refactor into something shared with Map/Path/etc. to DRY it up
2222
+
2223
+ var icon = this._icon,
2224
+ events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu', 'touchstart', 'touchend', 'touchmove'];
2225
+ if (this._detectIE) {
2226
+ events.concat(['MSPointerDown', 'MSPointerUp', 'MSPointerMove', 'MSPointerCancel']);
2227
+ } else {
2228
+ events.concat(['touchcancel']);
2229
+ }
2230
+
2231
+ L.DomUtil.addClass(icon, 'leaflet-clickable');
2232
+ L.DomEvent.on(icon, 'click', this._onMouseClick, this);
2233
+ L.DomEvent.on(icon, 'keypress', this._onKeyPress, this);
2234
+
2235
+ for (var i = 0; i < events.length; i++) {
2236
+ L.DomEvent.on(icon, events[i], this._fireMouseEvent, this);
2237
+ }
2238
+
2239
+ if (L.Handler.MarkerDrag) {
2240
+ this.dragging = new L.Handler.MarkerDrag(this);
2241
+
2242
+ if (this.options.draggable) {
2243
+ this.dragging.enable();
2244
+ }
2245
+ }
2246
+ },
2247
+ _detectIE: function () {
2248
+ var ua = window.navigator.userAgent;
2249
+
2250
+ var msie = ua.indexOf('MSIE ');
2251
+ if (msie > 0) {
2252
+ // IE 10 or older => return version number
2253
+ return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
2254
+ }
2255
+
2256
+ var trident = ua.indexOf('Trident/');
2257
+ if (trident > 0) {
2258
+ // IE 11 => return version number
2259
+ var rv = ua.indexOf('rv:');
2260
+ return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
2261
+ }
2262
+
2263
+ var edge = ua.indexOf('Edge/');
2264
+ if (edge > 0) {
2265
+ // IE 12 => return version number
2266
+ return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
2267
+ }
2268
+
2269
+ // other browser
2270
+ return false;
2271
+ }
2272
+ });
2273
+
2274
+
2275
+ /*
2276
+ * L.LatLngUtil contains different utility functions for LatLngs.
2277
+ */
2278
+
2279
+ L.LatLngUtil = {
2280
+ // Clones a LatLngs[], returns [][]
2281
+ cloneLatLngs: function (latlngs) {
2282
+ var clone = [];
2283
+ for (var i = 0, l = latlngs.length; i < l; i++) {
2284
+ clone.push(this.cloneLatLng(latlngs[i]));
2285
+ }
2286
+ return clone;
2287
+ },
2288
+
2289
+ cloneLatLng: function (latlng) {
2290
+ return L.latLng(latlng.lat, latlng.lng);
2291
+ }
2292
+ };
2293
+
2294
+ L.GeometryUtil = L.extend(L.GeometryUtil || {}, {
2295
+ // Ported from the OpenLayers implementation. See https://github.com/openlayers/openlayers/blob/master/lib/OpenLayers/Geometry/LinearRing.js#L270
2296
+ geodesicArea: function (latLngs) {
2297
+ var pointsCount = latLngs.length,
2298
+ area = 0.0,
2299
+ d2r = L.LatLng.DEG_TO_RAD,
2300
+ p1, p2;
2301
+
2302
+ if (pointsCount > 2) {
2303
+ for (var i = 0; i < pointsCount; i++) {
2304
+ p1 = latLngs[i];
2305
+ p2 = latLngs[(i + 1) % pointsCount];
2306
+ area += ((p2.lng - p1.lng) * d2r) *
2307
+ (2 + Math.sin(p1.lat * d2r) + Math.sin(p2.lat * d2r));
2308
+ }
2309
+ area = area * 6378137.0 * 6378137.0 / 2.0;
2310
+ }
2311
+
2312
+ return Math.abs(area);
2313
+ },
2314
+
2315
+ readableArea: function (area, isMetric) {
2316
+ var areaStr;
2317
+
2318
+ if (isMetric) {
2319
+ if (area >= 10000) {
2320
+ areaStr = (area * 0.0001).toFixed(2) + ' ha';
2321
+ } else {
2322
+ areaStr = area.toFixed(2) + ' m&sup2;';
2323
+ }
2324
+ } else {
2325
+ area /= 0.836127; // Square yards in 1 meter
2326
+
2327
+ if (area >= 3097600) { //3097600 square yards in 1 square mile
2328
+ areaStr = (area / 3097600).toFixed(2) + ' mi&sup2;';
2329
+ } else if (area >= 4840) {//48040 square yards in 1 acre
2330
+ areaStr = (area / 4840).toFixed(2) + ' acres';
2331
+ } else {
2332
+ areaStr = Math.ceil(area) + ' yd&sup2;';
2333
+ }
2334
+ }
2335
+
2336
+ return areaStr;
2337
+ },
2338
+
2339
+ readableDistance: function (distance, isMetric, useFeet) {
2340
+ var distanceStr;
2341
+
2342
+ if (isMetric) {
2343
+ // show metres when distance is < 1km, then show km
2344
+ if (distance > 1000) {
2345
+ distanceStr = (distance / 1000).toFixed(2) + ' km';
2346
+ } else {
2347
+ distanceStr = Math.ceil(distance) + ' m';
2348
+ }
2349
+ } else {
2350
+ distance *= 1.09361;
2351
+
2352
+ if (distance > 1760) {
2353
+ distanceStr = (distance / 1760).toFixed(2) + ' miles';
2354
+ } else {
2355
+ var suffix = ' yd';
2356
+ if (useFeet) {
2357
+ distance = distance * 3;
2358
+ suffix = ' ft';
2359
+ }
2360
+ distanceStr = Math.ceil(distance) + suffix;
2361
+ }
2362
+ }
2363
+
2364
+ return distanceStr;
2365
+ }
2366
+ });
2367
+
2368
+
2369
+ L.Util.extend(L.LineUtil, {
2370
+ // Checks to see if two line segments intersect. Does not handle degenerate cases.
2371
+ // http://compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf
2372
+ segmentsIntersect: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2, /*Point*/ p3) {
2373
+ return this._checkCounterclockwise(p, p2, p3) !==
2374
+ this._checkCounterclockwise(p1, p2, p3) &&
2375
+ this._checkCounterclockwise(p, p1, p2) !==
2376
+ this._checkCounterclockwise(p, p1, p3);
2377
+ },
2378
+
2379
+ // check to see if points are in counterclockwise order
2380
+ _checkCounterclockwise: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
2381
+ return (p2.y - p.y) * (p1.x - p.x) > (p1.y - p.y) * (p2.x - p.x);
2382
+ }
2383
+ });
2384
+
2385
+ L.Polyline.include({
2386
+ // Check to see if this polyline has any linesegments that intersect.
2387
+ // NOTE: does not support detecting intersection for degenerate cases.
2388
+ intersects: function () {
2389
+ var points = this._originalPoints,
2390
+ len = points ? points.length : 0,
2391
+ i, p, p1;
2392
+
2393
+ if (this._tooFewPointsForIntersection()) {
2394
+ return false;
2395
+ }
2396
+
2397
+ for (i = len - 1; i >= 3; i--) {
2398
+ p = points[i - 1];
2399
+ p1 = points[i];
2400
+
2401
+
2402
+ if (this._lineSegmentsIntersectsRange(p, p1, i - 2)) {
2403
+ return true;
2404
+ }
2405
+ }
2406
+
2407
+ return false;
2408
+ },
2409
+
2410
+ // Check for intersection if new latlng was added to this polyline.
2411
+ // NOTE: does not support detecting intersection for degenerate cases.
2412
+ newLatLngIntersects: function (latlng, skipFirst) {
2413
+ // Cannot check a polyline for intersecting lats/lngs when not added to the map
2414
+ if (!this._map) {
2415
+ return false;
2416
+ }
2417
+
2418
+ return this.newPointIntersects(this._map.latLngToLayerPoint(latlng), skipFirst);
2419
+ },
2420
+
2421
+ // Check for intersection if new point was added to this polyline.
2422
+ // newPoint must be a layer point.
2423
+ // NOTE: does not support detecting intersection for degenerate cases.
2424
+ newPointIntersects: function (newPoint, skipFirst) {
2425
+ var points = this._originalPoints,
2426
+ len = points ? points.length : 0,
2427
+ lastPoint = points ? points[len - 1] : null,
2428
+ // The previous previous line segment. Previous line segment doesn't need testing.
2429
+ maxIndex = len - 2;
2430
+
2431
+ if (this._tooFewPointsForIntersection(1)) {
2432
+ return false;
2433
+ }
2434
+
2435
+ return this._lineSegmentsIntersectsRange(lastPoint, newPoint, maxIndex, skipFirst ? 1 : 0);
2436
+ },
2437
+
2438
+ // Polylines with 2 sides can only intersect in cases where points are collinear (we don't support detecting these).
2439
+ // Cannot have intersection when < 3 line segments (< 4 points)
2440
+ _tooFewPointsForIntersection: function (extraPoints) {
2441
+ var points = this._originalPoints,
2442
+ len = points ? points.length : 0;
2443
+ // Increment length by extraPoints if present
2444
+ len += extraPoints || 0;
2445
+
2446
+ return !this._originalPoints || len <= 3;
2447
+ },
2448
+
2449
+ // Checks a line segment intersections with any line segments before its predecessor.
2450
+ // Don't need to check the predecessor as will never intersect.
2451
+ _lineSegmentsIntersectsRange: function (p, p1, maxIndex, minIndex) {
2452
+ var points = this._originalPoints,
2453
+ p2, p3;
2454
+
2455
+ minIndex = minIndex || 0;
2456
+
2457
+ // Check all previous line segments (beside the immediately previous) for intersections
2458
+ for (var j = maxIndex; j > minIndex; j--) {
2459
+ p2 = points[j - 1];
2460
+ p3 = points[j];
2461
+
2462
+ if (L.LineUtil.segmentsIntersect(p, p1, p2, p3)) {
2463
+ return true;
2464
+ }
2465
+ }
2466
+
2467
+ return false;
2468
+ }
2469
+ });
2470
+
2471
+
2472
+ L.Polygon.include({
2473
+ // Checks a polygon for any intersecting line segments. Ignores holes.
2474
+ intersects: function () {
2475
+ var polylineIntersects,
2476
+ points = this._originalPoints,
2477
+ len, firstPoint, lastPoint, maxIndex;
2478
+
2479
+ if (this._tooFewPointsForIntersection()) {
2480
+ return false;
2481
+ }
2482
+
2483
+ polylineIntersects = L.Polyline.prototype.intersects.call(this);
2484
+
2485
+ // If already found an intersection don't need to check for any more.
2486
+ if (polylineIntersects) {
2487
+ return true;
2488
+ }
2489
+
2490
+ len = points.length;
2491
+ firstPoint = points[0];
2492
+ lastPoint = points[len - 1];
2493
+ maxIndex = len - 2;
2494
+
2495
+ // Check the line segment between last and first point. Don't need to check the first line segment (minIndex = 1)
2496
+ return this._lineSegmentsIntersectsRange(lastPoint, firstPoint, maxIndex, 1);
2497
+ }
2498
+ });
2499
+
2500
+ L.Control.Draw = L.Control.extend({
2501
+
2502
+ options: {
2503
+ position: 'topleft',
2504
+ draw: {},
2505
+ edit: false
2506
+ },
2507
+
2508
+ initialize: function (options) {
2509
+ if (L.version < '0.7') {
2510
+ throw new Error('Leaflet.draw 0.2.3+ requires Leaflet 0.7.0+. Download latest from https://github.com/Leaflet/Leaflet/');
2511
+ }
2512
+
2513
+ L.Control.prototype.initialize.call(this, options);
2514
+
2515
+ var toolbar;
2516
+
2517
+ this._toolbars = {};
2518
+
2519
+ // Initialize toolbars
2520
+ if (L.DrawToolbar && this.options.draw) {
2521
+ toolbar = new L.DrawToolbar(this.options.draw);
2522
+
2523
+ this._toolbars[L.DrawToolbar.TYPE] = toolbar;
2524
+
2525
+ // Listen for when toolbar is enabled
2526
+ this._toolbars[L.DrawToolbar.TYPE].on('enable', this._toolbarEnabled, this);
2527
+ }
2528
+
2529
+ if (L.EditToolbar && this.options.edit) {
2530
+ toolbar = new L.EditToolbar(this.options.edit);
2531
+
2532
+ this._toolbars[L.EditToolbar.TYPE] = toolbar;
2533
+
2534
+ // Listen for when toolbar is enabled
2535
+ this._toolbars[L.EditToolbar.TYPE].on('enable', this._toolbarEnabled, this);
2536
+ }
2537
+ L.toolbar = this; //set global var for editing the toolbar
2538
+ },
2539
+
2540
+ onAdd: function (map) {
2541
+ var container = L.DomUtil.create('div', 'leaflet-draw'),
2542
+ addedTopClass = false,
2543
+ topClassName = 'leaflet-draw-toolbar-top',
2544
+ toolbarContainer;
2545
+
2546
+ for (var toolbarId in this._toolbars) {
2547
+ if (this._toolbars.hasOwnProperty(toolbarId)) {
2548
+ toolbarContainer = this._toolbars[toolbarId].addToolbar(map);
2549
+
2550
+ if (toolbarContainer) {
2551
+ // Add class to the first toolbar to remove the margin
2552
+ if (!addedTopClass) {
2553
+ if (!L.DomUtil.hasClass(toolbarContainer, topClassName)) {
2554
+ L.DomUtil.addClass(toolbarContainer.childNodes[0], topClassName);
2555
+ }
2556
+ addedTopClass = true;
2557
+ }
2558
+
2559
+ container.appendChild(toolbarContainer);
2560
+ }
2561
+ }
2562
+ }
2563
+
2564
+ return container;
2565
+ },
2566
+
2567
+ onRemove: function () {
2568
+ for (var toolbarId in this._toolbars) {
2569
+ if (this._toolbars.hasOwnProperty(toolbarId)) {
2570
+ this._toolbars[toolbarId].removeToolbar();
2571
+ }
2572
+ }
2573
+ },
2574
+
2575
+ setDrawingOptions: function (options) {
2576
+ for (var toolbarId in this._toolbars) {
2577
+ if (this._toolbars[toolbarId] instanceof L.DrawToolbar) {
2578
+ this._toolbars[toolbarId].setOptions(options);
2579
+ }
2580
+ }
2581
+ },
2582
+
2583
+ _toolbarEnabled: function (e) {
2584
+ var enabledToolbar = e.target;
2585
+
2586
+ for (var toolbarId in this._toolbars) {
2587
+ if (this._toolbars[toolbarId] !== enabledToolbar) {
2588
+ this._toolbars[toolbarId].disable();
2589
+ }
2590
+ }
2591
+ }
2592
+ });
2593
+
2594
+ L.Map.mergeOptions({
2595
+ drawControlTooltips: true,
2596
+ drawControl: false
2597
+ });
2598
+
2599
+ L.Map.addInitHook(function () {
2600
+ if (this.options.drawControl) {
2601
+ this.drawControl = new L.Control.Draw();
2602
+ this.addControl(this.drawControl);
2603
+ }
2604
+ });
2605
+
2606
+
2607
+ L.Toolbar = L.Class.extend({
2608
+ includes: [L.Mixin.Events],
2609
+
2610
+ initialize: function (options) {
2611
+ L.setOptions(this, options);
2612
+
2613
+ this._modes = {};
2614
+ this._actionButtons = [];
2615
+ this._activeMode = null;
2616
+ },
2617
+
2618
+ enabled: function () {
2619
+ return this._activeMode !== null;
2620
+ },
2621
+
2622
+ disable: function () {
2623
+ if (!this.enabled()) { return; }
2624
+
2625
+ this._activeMode.handler.disable();
2626
+ },
2627
+
2628
+ addToolbar: function (map) {
2629
+ var container = L.DomUtil.create('div', 'leaflet-draw-section'),
2630
+ buttonIndex = 0,
2631
+ buttonClassPrefix = this._toolbarClass || '',
2632
+ modeHandlers = this.getModeHandlers(map),
2633
+ i;
2634
+
2635
+ this._toolbarContainer = L.DomUtil.create('div', 'leaflet-draw-toolbar leaflet-bar');
2636
+ this._map = map;
2637
+
2638
+ for (i = 0; i < modeHandlers.length; i++) {
2639
+ if (modeHandlers[i].enabled) {
2640
+ this._initModeHandler(
2641
+ modeHandlers[i].handler,
2642
+ this._toolbarContainer,
2643
+ buttonIndex++,
2644
+ buttonClassPrefix,
2645
+ modeHandlers[i].title
2646
+ );
2647
+ }
2648
+ }
2649
+
2650
+ // if no buttons were added, do not add the toolbar
2651
+ if (!buttonIndex) {
2652
+ return;
2653
+ }
2654
+
2655
+ // Save button index of the last button, -1 as we would have ++ after the last button
2656
+ this._lastButtonIndex = --buttonIndex;
2657
+
2658
+ // Create empty actions part of the toolbar
2659
+ this._actionsContainer = L.DomUtil.create('ul', 'leaflet-draw-actions');
2660
+
2661
+ // Add draw and cancel containers to the control container
2662
+ container.appendChild(this._toolbarContainer);
2663
+ container.appendChild(this._actionsContainer);
2664
+
2665
+ return container;
2666
+ },
2667
+
2668
+ removeToolbar: function () {
2669
+ // Dispose each handler
2670
+ for (var handlerId in this._modes) {
2671
+ if (this._modes.hasOwnProperty(handlerId)) {
2672
+ // Unbind handler button
2673
+ this._disposeButton(
2674
+ this._modes[handlerId].button,
2675
+ this._modes[handlerId].handler.enable,
2676
+ this._modes[handlerId].handler
2677
+ );
2678
+
2679
+ // Make sure is disabled
2680
+ this._modes[handlerId].handler.disable();
2681
+
2682
+ // Unbind handler
2683
+ this._modes[handlerId].handler
2684
+ .off('enabled', this._handlerActivated, this)
2685
+ .off('disabled', this._handlerDeactivated, this);
2686
+ }
2687
+ }
2688
+ this._modes = {};
2689
+
2690
+ // Dispose the actions toolbar
2691
+ for (var i = 0, l = this._actionButtons.length; i < l; i++) {
2692
+ this._disposeButton(
2693
+ this._actionButtons[i].button,
2694
+ this._actionButtons[i].callback,
2695
+ this
2696
+ );
2697
+ }
2698
+ this._actionButtons = [];
2699
+ this._actionsContainer = null;
2700
+ },
2701
+
2702
+ _initModeHandler: function (handler, container, buttonIndex, classNamePredix, buttonTitle) {
2703
+ var type = handler.type;
2704
+
2705
+ this._modes[type] = {};
2706
+
2707
+ this._modes[type].handler = handler;
2708
+
2709
+ this._modes[type].button = this._createButton({
2710
+ type: type,
2711
+ title: buttonTitle,
2712
+ className: classNamePredix + '-' + type,
2713
+ container: container,
2714
+ callback: this._modes[type].handler.enable,
2715
+ context: this._modes[type].handler
2716
+ });
2717
+
2718
+ this._modes[type].buttonIndex = buttonIndex;
2719
+
2720
+ this._modes[type].handler
2721
+ .on('enabled', this._handlerActivated, this)
2722
+ .on('disabled', this._handlerDeactivated, this);
2723
+ },
2724
+
2725
+ _createButton: function (options) {
2726
+
2727
+ var link = L.DomUtil.create('a', options.className || '', options.container);
2728
+ link.href = '#';
2729
+
2730
+ if (options.text) {
2731
+ link.innerHTML = options.text;
2732
+ }
2733
+
2734
+ if (options.title) {
2735
+ link.title = options.title;
2736
+ }
2737
+
2738
+ L.DomEvent
2739
+ .on(link, 'click', L.DomEvent.stopPropagation)
2740
+ .on(link, 'mousedown', L.DomEvent.stopPropagation)
2741
+ .on(link, 'dblclick', L.DomEvent.stopPropagation)
2742
+ .on(link, 'click', L.DomEvent.preventDefault)
2743
+ .on(link, 'click', options.callback, options.context);
2744
+
2745
+ return link;
2746
+ },
2747
+
2748
+ _disposeButton: function (button, callback) {
2749
+ L.DomEvent
2750
+ .off(button, 'click', L.DomEvent.stopPropagation)
2751
+ .off(button, 'mousedown', L.DomEvent.stopPropagation)
2752
+ .off(button, 'dblclick', L.DomEvent.stopPropagation)
2753
+ .off(button, 'click', L.DomEvent.preventDefault)
2754
+ .off(button, 'click', callback);
2755
+ },
2756
+
2757
+ _handlerActivated: function (e) {
2758
+ // Disable active mode (if present)
2759
+ this.disable();
2760
+
2761
+ // Cache new active feature
2762
+ this._activeMode = this._modes[e.handler];
2763
+
2764
+ L.DomUtil.addClass(this._activeMode.button, 'leaflet-draw-toolbar-button-enabled');
2765
+
2766
+ this._showActionsToolbar();
2767
+
2768
+ this.fire('enable');
2769
+ },
2770
+
2771
+ _handlerDeactivated: function () {
2772
+ this._hideActionsToolbar();
2773
+
2774
+ L.DomUtil.removeClass(this._activeMode.button, 'leaflet-draw-toolbar-button-enabled');
2775
+
2776
+ this._activeMode = null;
2777
+
2778
+ this.fire('disable');
2779
+ },
2780
+
2781
+ _createActions: function (handler) {
2782
+ var container = this._actionsContainer,
2783
+ buttons = this.getActions(handler),
2784
+ l = buttons.length,
2785
+ li, di, dl, button;
2786
+
2787
+ // Dispose the actions toolbar (todo: dispose only not used buttons)
2788
+ for (di = 0, dl = this._actionButtons.length; di < dl; di++) {
2789
+ this._disposeButton(this._actionButtons[di].button, this._actionButtons[di].callback);
2790
+ }
2791
+ this._actionButtons = [];
2792
+
2793
+ // Remove all old buttons
2794
+ while (container.firstChild) {
2795
+ container.removeChild(container.firstChild);
2796
+ }
2797
+
2798
+ for (var i = 0; i < l; i++) {
2799
+ if ('enabled' in buttons[i] && !buttons[i].enabled) {
2800
+ continue;
2801
+ }
2802
+
2803
+ li = L.DomUtil.create('li', '', container);
2804
+
2805
+ button = this._createButton({
2806
+ title: buttons[i].title,
2807
+ text: buttons[i].text,
2808
+ container: li,
2809
+ callback: buttons[i].callback,
2810
+ context: buttons[i].context
2811
+ });
2812
+
2813
+ this._actionButtons.push({
2814
+ button: button,
2815
+ callback: buttons[i].callback
2816
+ });
2817
+ }
2818
+ },
2819
+
2820
+ _showActionsToolbar: function () {
2821
+ var buttonIndex = this._activeMode.buttonIndex,
2822
+ lastButtonIndex = this._lastButtonIndex,
2823
+ toolbarPosition = this._activeMode.button.offsetTop - 1;
2824
+
2825
+ // Recreate action buttons on every click
2826
+ this._createActions(this._activeMode.handler);
2827
+
2828
+ // Correctly position the cancel button
2829
+ this._actionsContainer.style.top = toolbarPosition + 'px';
2830
+
2831
+ if (buttonIndex === 0) {
2832
+ L.DomUtil.addClass(this._toolbarContainer, 'leaflet-draw-toolbar-notop');
2833
+ L.DomUtil.addClass(this._actionsContainer, 'leaflet-draw-actions-top');
2834
+ }
2835
+
2836
+ if (buttonIndex === lastButtonIndex) {
2837
+ L.DomUtil.addClass(this._toolbarContainer, 'leaflet-draw-toolbar-nobottom');
2838
+ L.DomUtil.addClass(this._actionsContainer, 'leaflet-draw-actions-bottom');
2839
+ }
2840
+
2841
+ this._actionsContainer.style.display = 'block';
2842
+ },
2843
+
2844
+ _hideActionsToolbar: function () {
2845
+ this._actionsContainer.style.display = 'none';
2846
+
2847
+ L.DomUtil.removeClass(this._toolbarContainer, 'leaflet-draw-toolbar-notop');
2848
+ L.DomUtil.removeClass(this._toolbarContainer, 'leaflet-draw-toolbar-nobottom');
2849
+ L.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-top');
2850
+ L.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-bottom');
2851
+ }
2852
+ });
2853
+
2854
+
2855
+ L.Tooltip = L.Class.extend({
2856
+ initialize: function (map) {
2857
+ this._map = map;
2858
+ this._popupPane = map._panes.popupPane;
2859
+
2860
+ this._container = map.options.drawControlTooltips ? L.DomUtil.create('div', 'leaflet-draw-tooltip', this._popupPane) : null;
2861
+ this._singleLineLabel = false;
2862
+
2863
+ this._map.on('mouseout', this._onMouseOut, this);
2864
+ },
2865
+
2866
+ dispose: function () {
2867
+ this._map.off('mouseout', this._onMouseOut, this);
2868
+
2869
+ if (this._container) {
2870
+ this._popupPane.removeChild(this._container);
2871
+ this._container = null;
2872
+ }
2873
+ },
2874
+
2875
+ updateContent: function (labelText) {
2876
+ if (!this._container) {
2877
+ return this;
2878
+ }
2879
+ labelText.subtext = labelText.subtext || '';
2880
+
2881
+ // update the vertical position (only if changed)
2882
+ if (labelText.subtext.length === 0 && !this._singleLineLabel) {
2883
+ L.DomUtil.addClass(this._container, 'leaflet-draw-tooltip-single');
2884
+ this._singleLineLabel = true;
2885
+ }
2886
+ else if (labelText.subtext.length > 0 && this._singleLineLabel) {
2887
+ L.DomUtil.removeClass(this._container, 'leaflet-draw-tooltip-single');
2888
+ this._singleLineLabel = false;
2889
+ }
2890
+
2891
+ this._container.innerHTML =
2892
+ (labelText.subtext.length > 0 ? '<span class="leaflet-draw-tooltip-subtext">' + labelText.subtext + '</span>' + '<br />' : '') +
2893
+ '<span>' + labelText.text + '</span>';
2894
+
2895
+ return this;
2896
+ },
2897
+
2898
+ updatePosition: function (latlng) {
2899
+ var pos = this._map.latLngToLayerPoint(latlng),
2900
+ tooltipContainer = this._container;
2901
+
2902
+ if (this._container) {
2903
+ tooltipContainer.style.visibility = 'inherit';
2904
+ L.DomUtil.setPosition(tooltipContainer, pos);
2905
+ }
2906
+
2907
+ return this;
2908
+ },
2909
+
2910
+ showAsError: function () {
2911
+ if (this._container) {
2912
+ L.DomUtil.addClass(this._container, 'leaflet-error-draw-tooltip');
2913
+ }
2914
+ return this;
2915
+ },
2916
+
2917
+ removeError: function () {
2918
+ if (this._container) {
2919
+ L.DomUtil.removeClass(this._container, 'leaflet-error-draw-tooltip');
2920
+ }
2921
+ return this;
2922
+ },
2923
+
2924
+ _onMouseOut: function () {
2925
+ if (this._container) {
2926
+ this._container.style.visibility = 'hidden';
2927
+ }
2928
+ }
2929
+ });
2930
+
2931
+
2932
+ L.DrawToolbar = L.Toolbar.extend({
2933
+
2934
+ statics: {
2935
+ TYPE: 'draw'
2936
+ },
2937
+
2938
+ options: {
2939
+ polyline: {},
2940
+ polygon: {},
2941
+ rectangle: {},
2942
+ circle: {},
2943
+ marker: {}
2944
+ },
2945
+
2946
+ initialize: function (options) {
2947
+ // Ensure that the options are merged correctly since L.extend is only shallow
2948
+ for (var type in this.options) {
2949
+ if (this.options.hasOwnProperty(type)) {
2950
+ if (options[type]) {
2951
+ options[type] = L.extend({}, this.options[type], options[type]);
2952
+ }
2953
+ }
2954
+ }
2955
+
2956
+ this._toolbarClass = 'leaflet-draw-draw';
2957
+ L.Toolbar.prototype.initialize.call(this, options);
2958
+ },
2959
+
2960
+ getModeHandlers: function (map) {
2961
+ return [
2962
+ {
2963
+ enabled: this.options.polyline,
2964
+ handler: new L.Draw.Polyline(map, this.options.polyline),
2965
+ title: L.drawLocal.draw.toolbar.buttons.polyline
2966
+ },
2967
+ {
2968
+ enabled: this.options.polygon,
2969
+ handler: new L.Draw.Polygon(map, this.options.polygon),
2970
+ title: L.drawLocal.draw.toolbar.buttons.polygon
2971
+ },
2972
+ {
2973
+ enabled: this.options.rectangle,
2974
+ handler: new L.Draw.Rectangle(map, this.options.rectangle),
2975
+ title: L.drawLocal.draw.toolbar.buttons.rectangle
2976
+ },
2977
+ {
2978
+ enabled: this.options.circle,
2979
+ handler: new L.Draw.Circle(map, this.options.circle),
2980
+ title: L.drawLocal.draw.toolbar.buttons.circle
2981
+ },
2982
+ {
2983
+ enabled: this.options.marker,
2984
+ handler: new L.Draw.Marker(map, this.options.marker),
2985
+ title: L.drawLocal.draw.toolbar.buttons.marker
2986
+ }
2987
+ ];
2988
+ },
2989
+
2990
+ // Get the actions part of the toolbar
2991
+ getActions: function (handler) {
2992
+ return [
2993
+ {
2994
+ enabled: handler.completeShape,
2995
+ title: L.drawLocal.draw.toolbar.finish.title,
2996
+ text: L.drawLocal.draw.toolbar.finish.text,
2997
+ callback: handler.completeShape,
2998
+ context: handler
2999
+ },
3000
+ {
3001
+ enabled: handler.deleteLastVertex,
3002
+ title: L.drawLocal.draw.toolbar.undo.title,
3003
+ text: L.drawLocal.draw.toolbar.undo.text,
3004
+ callback: handler.deleteLastVertex,
3005
+ context: handler
3006
+ },
3007
+ {
3008
+ title: L.drawLocal.draw.toolbar.actions.title,
3009
+ text: L.drawLocal.draw.toolbar.actions.text,
3010
+ callback: this.disable,
3011
+ context: this
3012
+ }
3013
+ ];
3014
+ },
3015
+
3016
+ setOptions: function (options) {
3017
+ L.setOptions(this, options);
3018
+
3019
+ for (var type in this._modes) {
3020
+ if (this._modes.hasOwnProperty(type) && options.hasOwnProperty(type)) {
3021
+ this._modes[type].handler.setOptions(options[type]);
3022
+ }
3023
+ }
3024
+ }
3025
+ });
3026
+
3027
+
3028
+ /*L.Map.mergeOptions({
3029
+ editControl: true
3030
+ });*/
3031
+
3032
+ L.EditToolbar = L.Toolbar.extend({
3033
+ statics: {
3034
+ TYPE: 'edit'
3035
+ },
3036
+
3037
+ options: {
3038
+ edit: {
3039
+ selectedPathOptions: {
3040
+ dashArray: '10, 10',
3041
+
3042
+ fill: true,
3043
+ fillColor: '#fe57a1',
3044
+ fillOpacity: 0.1,
3045
+
3046
+ // Whether to user the existing layers color
3047
+ maintainColor: false
3048
+ }
3049
+ },
3050
+ remove: {},
3051
+ poly: null,
3052
+ featureGroup: null /* REQUIRED! TODO: perhaps if not set then all layers on the map are selectable? */
3053
+ },
3054
+
3055
+ initialize: function (options) {
3056
+ // Need to set this manually since null is an acceptable value here
3057
+ if (options.edit) {
3058
+ if (typeof options.edit.selectedPathOptions === 'undefined') {
3059
+ options.edit.selectedPathOptions = this.options.edit.selectedPathOptions;
3060
+ }
3061
+ options.edit.selectedPathOptions = L.extend({}, this.options.edit.selectedPathOptions, options.edit.selectedPathOptions);
3062
+ }
3063
+
3064
+ if (options.remove) {
3065
+ options.remove = L.extend({}, this.options.remove, options.remove);
3066
+ }
3067
+
3068
+ if (options.poly) {
3069
+ options.poly = L.extend({}, this.options.poly, options.poly);
3070
+ }
3071
+
3072
+ this._toolbarClass = 'leaflet-draw-edit';
3073
+ L.Toolbar.prototype.initialize.call(this, options);
3074
+
3075
+ this._selectedFeatureCount = 0;
3076
+ },
3077
+
3078
+ getModeHandlers: function (map) {
3079
+ var featureGroup = this.options.featureGroup;
3080
+ return [
3081
+ {
3082
+ enabled: this.options.edit,
3083
+ handler: new L.EditToolbar.Edit(map, {
3084
+ featureGroup: featureGroup,
3085
+ selectedPathOptions: this.options.edit.selectedPathOptions,
3086
+ poly : this.options.poly
3087
+ }),
3088
+ title: L.drawLocal.edit.toolbar.buttons.edit
3089
+ },
3090
+ {
3091
+ enabled: this.options.remove,
3092
+ handler: new L.EditToolbar.Delete(map, {
3093
+ featureGroup: featureGroup
3094
+ }),
3095
+ title: L.drawLocal.edit.toolbar.buttons.remove
3096
+ }
3097
+ ];
3098
+ },
3099
+
3100
+ getActions: function () {
3101
+ return [
3102
+ {
3103
+ title: L.drawLocal.edit.toolbar.actions.save.title,
3104
+ text: L.drawLocal.edit.toolbar.actions.save.text,
3105
+ callback: this._save,
3106
+ context: this
3107
+ },
3108
+ {
3109
+ title: L.drawLocal.edit.toolbar.actions.cancel.title,
3110
+ text: L.drawLocal.edit.toolbar.actions.cancel.text,
3111
+ callback: this.disable,
3112
+ context: this
3113
+ }
3114
+ ];
3115
+ },
3116
+
3117
+ addToolbar: function (map) {
3118
+ var container = L.Toolbar.prototype.addToolbar.call(this, map);
3119
+
3120
+ this._checkDisabled();
3121
+
3122
+ this.options.featureGroup.on('layeradd layerremove', this._checkDisabled, this);
3123
+
3124
+ return container;
3125
+ },
3126
+
3127
+ removeToolbar: function () {
3128
+ this.options.featureGroup.off('layeradd layerremove', this._checkDisabled, this);
3129
+
3130
+ L.Toolbar.prototype.removeToolbar.call(this);
3131
+ },
3132
+
3133
+ disable: function () {
3134
+ if (!this.enabled()) { return; }
3135
+
3136
+ this._activeMode.handler.revertLayers();
3137
+
3138
+ L.Toolbar.prototype.disable.call(this);
3139
+ },
3140
+
3141
+ _save: function () {
3142
+ this._activeMode.handler.save();
3143
+ this._activeMode.handler.disable();
3144
+ },
3145
+
3146
+ _checkDisabled: function () {
3147
+ var featureGroup = this.options.featureGroup,
3148
+ hasLayers = featureGroup.getLayers().length !== 0,
3149
+ button;
3150
+
3151
+ if (this.options.edit) {
3152
+ button = this._modes[L.EditToolbar.Edit.TYPE].button;
3153
+
3154
+ if (hasLayers) {
3155
+ L.DomUtil.removeClass(button, 'leaflet-disabled');
3156
+ } else {
3157
+ L.DomUtil.addClass(button, 'leaflet-disabled');
3158
+ }
3159
+
3160
+ button.setAttribute(
3161
+ 'title',
3162
+ hasLayers ?
3163
+ L.drawLocal.edit.toolbar.buttons.edit
3164
+ : L.drawLocal.edit.toolbar.buttons.editDisabled
3165
+ );
3166
+ }
3167
+
3168
+ if (this.options.remove) {
3169
+ button = this._modes[L.EditToolbar.Delete.TYPE].button;
3170
+
3171
+ if (hasLayers) {
3172
+ L.DomUtil.removeClass(button, 'leaflet-disabled');
3173
+ } else {
3174
+ L.DomUtil.addClass(button, 'leaflet-disabled');
3175
+ }
3176
+
3177
+ button.setAttribute(
3178
+ 'title',
3179
+ hasLayers ?
3180
+ L.drawLocal.edit.toolbar.buttons.remove
3181
+ : L.drawLocal.edit.toolbar.buttons.removeDisabled
3182
+ );
3183
+ }
3184
+ }
3185
+ });
3186
+
3187
+
3188
+ L.EditToolbar.Edit = L.Handler.extend({
3189
+ statics: {
3190
+ TYPE: 'edit'
3191
+ },
3192
+
3193
+ includes: L.Mixin.Events,
3194
+
3195
+ initialize: function (map, options) {
3196
+ L.Handler.prototype.initialize.call(this, map);
3197
+
3198
+ L.setOptions(this, options);
3199
+
3200
+ // Store the selectable layer group for ease of access
3201
+ this._featureGroup = options.featureGroup;
3202
+
3203
+ if (!(this._featureGroup instanceof L.FeatureGroup)) {
3204
+ throw new Error('options.featureGroup must be a L.FeatureGroup');
3205
+ }
3206
+
3207
+ this._uneditedLayerProps = {};
3208
+
3209
+ // Save the type so super can fire, need to do this as cannot do this.TYPE :(
3210
+ this.type = L.EditToolbar.Edit.TYPE;
3211
+ },
3212
+
3213
+ enable: function () {
3214
+ if (this._enabled || !this._hasAvailableLayers()) {
3215
+ return;
3216
+ }
3217
+ this.fire('enabled', {handler: this.type});
3218
+ //this disable other handlers
3219
+
3220
+ this._map.fire('draw:editstart', { handler: this.type });
3221
+ //allow drawLayer to be updated before beginning edition.
3222
+
3223
+ L.Handler.prototype.enable.call(this);
3224
+ this._featureGroup
3225
+ .on('layeradd', this._enableLayerEdit, this)
3226
+ .on('layerremove', this._disableLayerEdit, this);
3227
+ },
3228
+
3229
+ disable: function () {
3230
+ if (!this._enabled) { return; }
3231
+ this._featureGroup
3232
+ .off('layeradd', this._enableLayerEdit, this)
3233
+ .off('layerremove', this._disableLayerEdit, this);
3234
+ L.Handler.prototype.disable.call(this);
3235
+ this._map.fire('draw:editstop', { handler: this.type });
3236
+ this.fire('disabled', {handler: this.type});
3237
+ },
3238
+
3239
+ addHooks: function () {
3240
+ var map = this._map;
3241
+
3242
+ if (map) {
3243
+ map.getContainer().focus();
3244
+
3245
+ this._featureGroup.eachLayer(this._enableLayerEdit, this);
3246
+
3247
+ this._tooltip = new L.Tooltip(this._map);
3248
+ this._tooltip.updateContent({
3249
+ text: L.drawLocal.edit.handlers.edit.tooltip.text,
3250
+ subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext
3251
+ });
3252
+
3253
+ // Quickly access the tooltip to update for intersection checking
3254
+ map._editTooltip = this._tooltip;
3255
+
3256
+ this._updateTooltip();
3257
+
3258
+ this._map
3259
+ .on('mousemove', this._onMouseMove, this)
3260
+ .on('touchmove', this._onMouseMove, this)
3261
+ .on('MSPointerMove', this._onMouseMove, this)
3262
+ .on('click', this._editStyle, this)
3263
+ .on('draw:editvertex', this._updateTooltip, this);
3264
+ }
3265
+ },
3266
+
3267
+ removeHooks: function () {
3268
+ if (this._map) {
3269
+ // Clean up selected layers.
3270
+ this._featureGroup.eachLayer(this._disableLayerEdit, this);
3271
+
3272
+ // Clear the backups of the original layers
3273
+ this._uneditedLayerProps = {};
3274
+
3275
+ this._tooltip.dispose();
3276
+ this._tooltip = null;
3277
+
3278
+ this._map
3279
+ .off('mousemove', this._onMouseMove, this)
3280
+ .off('touchmove', this._onMouseMove, this)
3281
+ .off('MSPointerMove', this._onMouseMove, this)
3282
+ .off('click', this._editStyle, this)
3283
+ .off('draw:editvertex', this._updateTooltip, this);
3284
+ }
3285
+ },
3286
+
3287
+ revertLayers: function () {
3288
+ this._featureGroup.eachLayer(function (layer) {
3289
+ this._revertLayer(layer);
3290
+ }, this);
3291
+ },
3292
+
3293
+ save: function () {
3294
+ var editedLayers = new L.LayerGroup();
3295
+ this._featureGroup.eachLayer(function (layer) {
3296
+ if (layer.edited) {
3297
+ editedLayers.addLayer(layer);
3298
+ layer.edited = false;
3299
+ }
3300
+ });
3301
+ this._map.fire('draw:edited', {layers: editedLayers});
3302
+ },
3303
+
3304
+ _backupLayer: function (layer) {
3305
+ var id = L.Util.stamp(layer);
3306
+
3307
+ if (!this._uneditedLayerProps[id]) {
3308
+ // Polyline, Polygon or Rectangle
3309
+ if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) {
3310
+ this._uneditedLayerProps[id] = {
3311
+ latlngs: L.LatLngUtil.cloneLatLngs(layer.getLatLngs())
3312
+ };
3313
+ } else if (layer instanceof L.Circle) {
3314
+ this._uneditedLayerProps[id] = {
3315
+ latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng()),
3316
+ radius: layer.getRadius()
3317
+ };
3318
+ } else if (layer instanceof L.Marker) { // Marker
3319
+ this._uneditedLayerProps[id] = {
3320
+ latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng())
3321
+ };
3322
+ }
3323
+ }
3324
+ },
3325
+
3326
+ _getTooltipText: function () {
3327
+ return ({
3328
+ text: L.drawLocal.edit.handlers.edit.tooltip.text,
3329
+ subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext
3330
+ });
3331
+ },
3332
+
3333
+ _updateTooltip: function () {
3334
+ this._tooltip.updateContent(this._getTooltipText());
3335
+ },
3336
+
3337
+ _revertLayer: function (layer) {
3338
+ var id = L.Util.stamp(layer);
3339
+ layer.edited = false;
3340
+ if (this._uneditedLayerProps.hasOwnProperty(id)) {
3341
+ // Polyline, Polygon or Rectangle
3342
+ if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) {
3343
+ layer.setLatLngs(this._uneditedLayerProps[id].latlngs);
3344
+ } else if (layer instanceof L.Circle) {
3345
+ layer.setLatLng(this._uneditedLayerProps[id].latlng);
3346
+ layer.setRadius(this._uneditedLayerProps[id].radius);
3347
+ } else if (layer instanceof L.Marker) { // Marker
3348
+ layer.setLatLng(this._uneditedLayerProps[id].latlng);
3349
+ }
3350
+
3351
+ layer.fire('revert-edited', { layer: layer });
3352
+ }
3353
+ },
3354
+
3355
+ _enableLayerEdit: function (e) {
3356
+ var layer = e.layer || e.target || e,
3357
+ pathOptions, poly;
3358
+
3359
+ // Back up this layer (if haven't before)
3360
+ this._backupLayer(layer);
3361
+
3362
+ if (this.options.poly) {
3363
+ poly = L.Util.extend({}, this.options.poly);
3364
+ layer.options.poly = poly;
3365
+ }
3366
+
3367
+ // Set different style for editing mode
3368
+ if (this.options.selectedPathOptions) {
3369
+ pathOptions = L.Util.extend({}, this.options.selectedPathOptions);
3370
+
3371
+ // Use the existing color of the layer
3372
+ if (pathOptions.maintainColor) {
3373
+ pathOptions.color = layer.options.color;
3374
+ pathOptions.fillColor = layer.options.fillColor;
3375
+ }
3376
+
3377
+ layer.options.original = L.extend({}, layer.options);
3378
+ layer.options.editing = pathOptions;
3379
+
3380
+ }
3381
+
3382
+ if (this.isMarker) {
3383
+ layer.dragging.enable();
3384
+ layer
3385
+ .on('dragend', this._onMarkerDragEnd)
3386
+ // #TODO: remove when leaflet finally fixes their draggable so it's touch friendly again.
3387
+ .on('touchmove', this._onTouchMove, this)
3388
+ .on('MSPointerMove', this._onTouchMove, this)
3389
+ .on('touchend', this._onMarkerDragEnd, this)
3390
+ .on('MSPointerUp', this._onMarkerDragEnd, this);
3391
+ } else {
3392
+ layer.editing.enable();
3393
+ }
3394
+ },
3395
+
3396
+ _disableLayerEdit: function (e) {
3397
+ var layer = e.layer || e.target || e;
3398
+
3399
+ layer.edited = false;
3400
+ layer.editing.disable();
3401
+
3402
+ delete layer.options.editing;
3403
+ delete layer.options.original;
3404
+ // Reset layer styles to that of before select
3405
+ if (this._selectedPathOptions) {
3406
+ if (layer instanceof L.Marker) {
3407
+ this._toggleMarkerHighlight(layer);
3408
+ } else {
3409
+ // reset the layer style to what is was before being selected
3410
+ layer.setStyle(layer.options.previousOptions);
3411
+ // remove the cached options for the layer object
3412
+ delete layer.options.previousOptions;
3413
+ }
3414
+ }
3415
+
3416
+ if (layer instanceof L.Marker) {
3417
+ layer.dragging.disable();
3418
+ layer
3419
+ .off('dragend', this._onMarkerDragEnd, this)
3420
+ .off('touchmove', this._onTouchMove, this)
3421
+ .off('MSPointerMove', this._onTouchMove, this)
3422
+ .off('touchend', this._onMarkerDragEnd, this)
3423
+ .off('MSPointerUp', this._onMarkerDragEnd, this);
3424
+ } else {
3425
+ layer.editing.disable();
3426
+ }
3427
+ },
3428
+
3429
+ _onMouseMove: function (e) {
3430
+ this._tooltip.updatePosition(e.latlng);
3431
+ },
3432
+
3433
+ _onTouchMove: function (e) {
3434
+ var touchEvent = e.originalEvent.changedTouches[0],
3435
+ layerPoint = this._map.mouseEventToLayerPoint(touchEvent),
3436
+ latlng = this._map.layerPointToLatLng(layerPoint);
3437
+ e.target.setLatLng(latlng);
3438
+ },
3439
+
3440
+ _hasAvailableLayers: function () {
3441
+ return this._featureGroup.getLayers().length !== 0;
3442
+ }
3443
+ });
3444
+
3445
+
3446
+ L.EditToolbar.Delete = L.Handler.extend({
3447
+ statics: {
3448
+ TYPE: 'remove' // not delete as delete is reserved in js
3449
+ },
3450
+
3451
+ includes: L.Mixin.Events,
3452
+
3453
+ initialize: function (map, options) {
3454
+ L.Handler.prototype.initialize.call(this, map);
3455
+
3456
+ L.Util.setOptions(this, options);
3457
+
3458
+ // Store the selectable layer group for ease of access
3459
+ this._deletableLayers = this.options.featureGroup;
3460
+
3461
+ if (!(this._deletableLayers instanceof L.FeatureGroup)) {
3462
+ throw new Error('options.featureGroup must be a L.FeatureGroup');
3463
+ }
3464
+
3465
+ // Save the type so super can fire, need to do this as cannot do this.TYPE :(
3466
+ this.type = L.EditToolbar.Delete.TYPE;
3467
+ },
3468
+
3469
+ enable: function () {
3470
+ if (this._enabled || !this._hasAvailableLayers()) {
3471
+ return;
3472
+ }
3473
+ this.fire('enabled', { handler: this.type});
3474
+
3475
+ this._map.fire('draw:deletestart', { handler: this.type });
3476
+
3477
+ L.Handler.prototype.enable.call(this);
3478
+
3479
+ this._deletableLayers
3480
+ .on('layeradd', this._enableLayerDelete, this)
3481
+ .on('layerremove', this._disableLayerDelete, this);
3482
+ },
3483
+
3484
+ disable: function () {
3485
+ if (!this._enabled) { return; }
3486
+
3487
+ this._deletableLayers
3488
+ .off('layeradd', this._enableLayerDelete, this)
3489
+ .off('layerremove', this._disableLayerDelete, this);
3490
+
3491
+ L.Handler.prototype.disable.call(this);
3492
+
3493
+ this._map.fire('draw:deletestop', { handler: this.type });
3494
+
3495
+ this.fire('disabled', { handler: this.type});
3496
+ },
3497
+
3498
+ addHooks: function () {
3499
+ var map = this._map;
3500
+
3501
+ if (map) {
3502
+ map.getContainer().focus();
3503
+
3504
+ this._deletableLayers.eachLayer(this._enableLayerDelete, this);
3505
+ this._deletedLayers = new L.LayerGroup();
3506
+
3507
+ this._tooltip = new L.Tooltip(this._map);
3508
+ this._tooltip.updateContent({ text: L.drawLocal.edit.handlers.remove.tooltip.text });
3509
+
3510
+ this._map.on('mousemove', this._onMouseMove, this);
3511
+ }
3512
+ },
3513
+
3514
+ removeHooks: function () {
3515
+ if (this._map) {
3516
+ this._deletableLayers.eachLayer(this._disableLayerDelete, this);
3517
+ this._deletedLayers = null;
3518
+
3519
+ this._tooltip.dispose();
3520
+ this._tooltip = null;
3521
+
3522
+ this._map.off('mousemove', this._onMouseMove, this);
3523
+ }
3524
+ },
3525
+
3526
+ revertLayers: function () {
3527
+ // Iterate of the deleted layers and add them back into the featureGroup
3528
+ this._deletedLayers.eachLayer(function (layer) {
3529
+ this._deletableLayers.addLayer(layer);
3530
+ layer.fire('revert-deleted', { layer: layer });
3531
+ }, this);
3532
+ },
3533
+
3534
+ save: function () {
3535
+ this._map.fire('draw:deleted', { layers: this._deletedLayers });
3536
+ },
3537
+
3538
+ _enableLayerDelete: function (e) {
3539
+ var layer = e.layer || e.target || e;
3540
+
3541
+ layer.on('click', this._removeLayer, this);
3542
+ },
3543
+
3544
+ _disableLayerDelete: function (e) {
3545
+ var layer = e.layer || e.target || e;
3546
+
3547
+ layer.off('click', this._removeLayer, this);
3548
+
3549
+ // Remove from the deleted layers so we can't accidentally revert if the user presses cancel
3550
+ this._deletedLayers.removeLayer(layer);
3551
+ },
3552
+
3553
+ _removeLayer: function (e) {
3554
+ var layer = e.layer || e.target || e;
3555
+
3556
+ this._deletableLayers.removeLayer(layer);
3557
+
3558
+ this._deletedLayers.addLayer(layer);
3559
+
3560
+ layer.fire('deleted');
3561
+ },
3562
+
3563
+ _onMouseMove: function (e) {
3564
+ this._tooltip.updatePosition(e.latlng);
3565
+ },
3566
+
3567
+ _hasAvailableLayers: function () {
3568
+ return this._deletableLayers.getLayers().length !== 0;
3569
+ }
3570
+ });
3571
+
3572
+
3573
+ }(window, document));