leaflet-draw-rails 0.0.1

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