ekylibre-cartography 0.0.1

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