leaflet-draw-rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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));