vendor: 17,648 bytes, lines 1-592
1/** 2 * @class L.Draw.Polyline 3 * @aka Draw.Polyline 4 * @inherits L.Draw.Feature 5 */ 6L.Draw.Polyline = L.Draw.Feature.extend({ 7 statics: { 8 TYPE: 'polyline' 9 }, 10 11 Poly: L.Polyline, 12 13 options: { 14 allowIntersection: true, 15 repeatMode: false, 16 drawError: { 17 color: '#b00b00', 18 timeout: 2500 19 }, 20 icon: new L.DivIcon({ 21 iconSize: new L.Point(8, 8), 22 className: 'leaflet-div-icon leaflet-editing-icon' 23 }), 24 touchIcon: new L.DivIcon({ 25 iconSize: new L.Point(20, 20), 26 className: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon' 27 }), 28 guidelineDistance: 20, 29 maxGuideLineLength: 4000, 30 shapeOptions: { 31 stroke: true, 32 color: '#3388ff', 33 weight: 4, 34 opacity: 0.5, 35 fill: false, 36 clickable: true 37 }, 38 metric: true, // Whether to use the metric measurement system or imperial 39 feet: true, // When not metric, to use feet instead of yards for display. 40 nautic: false, // When not metric, not feet use nautic mile for display 41 showLength: true, // Whether to display distance in the tooltip 42 zIndexOffset: 2000, // This should be > than the highest z-index any map layers 43 factor: 1, // To change distance calculation 44 maxPoints: 0 // Once this number of points are placed, finish shape 45 }, 46 47 // @method initialize(): void 48 initialize: function (map, options) { 49 // if touch, switch to touch icon 50 if (L.Browser.touch) { 51 this.options.icon = this.options.touchIcon; 52 } 53 54 // Need to set this here to ensure the correct message is used. 55 this.options.drawError.message = L.drawLocal.draw.handlers.polyline.error; 56 57 // Merge default drawError options with custom options 58 if (options && options.drawError) { 59 options.drawError = L.Util.extend({}, this.options.drawError, options.drawError); 60 } 61 62 // Save the type so super can fire, need to do this as cannot do this.TYPE :( 63 this.type = L.Draw.Polyline.TYPE; 64 65 L.Draw.Feature.prototype.initialize.call(this, map, options); 66 }, 67 68 // @method addHooks(): void 69 // Add listener hooks to this handler 70 addHooks: function () { 71 L.Draw.Feature.prototype.addHooks.call(this); 72 if (this._map) { 73 this._markers = []; 74 75 this._markerGroup = new L.LayerGroup(); 76 this._map.addLayer(this._markerGroup); 77 78 this._poly = new L.Polyline([], this.options.shapeOptions); 79 80 this._tooltip.updateContent(this._getTooltipText()); 81 82 // Make a transparent marker that will used to catch click events. These click 83 // events will create the vertices. We need to do this so we can ensure that 84 // we can create vertices over other map layers (markers, vector layers). We 85 // also do not want to trigger any click handlers of objects we are clicking on 86 // while drawing. 87 if (!this._mouseMarker) { 88 this._mouseMarker = L.marker(this._map.getCenter(), { 89 icon: L.divIcon({ 90 className: 'leaflet-mouse-marker', 91 iconAnchor: [20, 20], 92 iconSize: [40, 40] 93 }), 94 opacity: 0, 95 zIndexOffset: this.options.zIndexOffset 96 }); 97 } 98 99 this._mouseMarker 100 .on('mouseout', this._onMouseOut, this) 101 .on('mousemove', this._onMouseMove, this) // Necessary to prevent 0.8 stutter 102 .on('mousedown', this._onMouseDown, this) 103 .on('mouseup', this._onMouseUp, this) // Necessary for 0.8 compatibility 104 .addTo(this._map); 105 106 this._map 107 .on('mouseup', this._onMouseUp, this) // Necessary for 0.7 compatibility 108 .on('mousemove', this._onMouseMove, this) 109 .on('zoomlevelschange', this._onZoomEnd, this) 110 .on('touchstart', this._onTouch, this) 111 .on('zoomend', this._onZoomEnd, this); 112 113 } 114 }, 115 116 // @method removeHooks(): void 117 // Remove listener hooks from this handler. 118 removeHooks: function () { 119 L.Draw.Feature.prototype.removeHooks.call(this); 120 121 this._clearHideErrorTimeout(); 122 123 this._cleanUpShape(); 124 125 // remove markers from map 126 this._map.removeLayer(this._markerGroup); 127 delete this._markerGroup; 128 delete this._markers; 129 130 this._map.removeLayer(this._poly); 131 delete this._poly; 132 133 this._mouseMarker 134 .off('mousedown', this._onMouseDown, this) 135 .off('mouseout', this._onMouseOut, this) 136 .off('mouseup', this._onMouseUp, this) 137 .off('mousemove', this._onMouseMove, this); 138 this._map.removeLayer(this._mouseMarker); 139 delete this._mouseMarker; 140 141 // clean up DOM 142 this._clearGuides(); 143 144 this._map 145 .off('mouseup', this._onMouseUp, this) 146 .off('mousemove', this._onMouseMove, this) 147 .off('zoomlevelschange', this._onZoomEnd, this) 148 .off('zoomend', this._onZoomEnd, this) 149 .off('touchstart', this._onTouch, this) 150 .off('click', this._onTouch, this); 151 }, 152 153 // @method deleteLastVertex(): void 154 // Remove the last vertex from the polyline, removes polyline from map if only one point exists. 155 deleteLastVertex: function () { 156 if (this._markers.length <= 1) { 157 return; 158 } 159 160 var lastMarker = this._markers.pop(), 161 poly = this._poly, 162 // Replaces .spliceLatLngs() 163 latlngs = poly.getLatLngs(), 164 latlng = latlngs.splice(-1, 1)[0]; 165 this._poly.setLatLngs(latlngs); 166 167 this._markerGroup.removeLayer(lastMarker); 168 169 if (poly.getLatLngs().length < 2) { 170 this._map.removeLayer(poly); 171 } 172 173 this._vertexChanged(latlng, false); 174 }, 175 176 // @method addVertex(): void 177 // Add a vertex to the end of the polyline 178 addVertex: function (latlng) { 179 var markersLength = this._markers.length; 180 // markersLength must be greater than or equal to 2 before intersections can occur 181 if (markersLength >= 2 && !this.options.allowIntersection && this._poly.newLatLngIntersects(latlng)) { 182 this._showErrorTooltip(); 183 return; 184 } 185 else if (this._errorShown) { 186 this._hideErrorTooltip(); 187 } 188 189 this._markers.push(this._createMarker(latlng)); 190 191 this._poly.addLatLng(latlng); 192 193 if (this._poly.getLatLngs().length === 2) { 194 this._map.addLayer(this._poly); 195 } 196 197 this._vertexChanged(latlng, true); 198 }, 199 200 // @method completeShape(): void 201 // Closes the polyline between the first and last points 202 completeShape: function () { 203 if (this._markers.length <= 1 || !this._shapeIsValid()) { 204 return; 205 } 206 207 this._fireCreatedEvent(); 208 this.disable(); 209 210 if (this.options.repeatMode) { 211 this.enable(); 212 } 213 }, 214 215 _finishShape: function () { 216 var latlngs = this._poly._defaultShape ? this._poly._defaultShape() : this._poly.getLatLngs(); 217 var intersects = this._poly.newLatLngIntersects(latlngs[latlngs.length - 1]); 218 219 if ((!this.options.allowIntersection && intersects) || !this._shapeIsValid()) { 220 this._showErrorTooltip(); 221 return; 222 } 223 224 this._fireCreatedEvent(); 225 this.disable(); 226 if (this.options.repeatMode) { 227 this.enable(); 228 } 229 }, 230 231 // Called to verify the shape is valid when the user tries to finish it 232 // Return false if the shape is not valid 233 _shapeIsValid: function () { 234 return true; 235 }, 236 237 _onZoomEnd: function () { 238 if (this._markers !== null) { 239 this._updateGuide(); 240 } 241 }, 242 243 _onMouseMove: function (e) { 244 var newPos = this._map.mouseEventToLayerPoint(e.originalEvent); 245 var latlng = this._map.layerPointToLatLng(newPos); 246 247 // Save latlng 248 // should this be moved to _updateGuide() ? 249 this._currentLatLng = latlng; 250 251 this._updateTooltip(latlng); 252 253 // Update the guide line 254 this._updateGuide(newPos); 255 256 // Update the mouse marker position 257 this._mouseMarker.setLatLng(latlng); 258 259 L.DomEvent.preventDefault(e.originalEvent); 260 }, 261 262 _vertexChanged: function (latlng, added) { 263 this._map.fire(L.Draw.Event.DRAWVERTEX, {layers: this._markerGroup}); 264 this._updateFinishHandler(); 265 266 this._updateRunningMeasure(latlng, added); 267 268 this._clearGuides(); 269 270 this._updateTooltip(); 271 }, 272 273 _onMouseDown: function (e) { 274 if (!this._clickHandled && !this._touchHandled && !this._disableMarkers) { 275 this._onMouseMove(e); 276 this._clickHandled = true; 277 this._disableNewMarkers(); 278 var originalEvent = e.originalEvent; 279 var clientX = originalEvent.clientX; 280 var clientY = originalEvent.clientY; 281 this._startPoint.call(this, clientX, clientY); 282 } 283 }, 284 285 _startPoint: function (clientX, clientY) { 286 this._mouseDownOrigin = L.point(clientX, clientY); 287 }, 288 289 _onMouseUp: function (e) { 290 var originalEvent = e.originalEvent; 291 var clientX = originalEvent.clientX; 292 var clientY = originalEvent.clientY; 293 this._endPoint.call(this, clientX, clientY, e); 294 this._clickHandled = null; 295 }, 296 297 _endPoint: function (clientX, clientY, e) { 298 if (this._mouseDownOrigin) { 299 var dragCheckDistance = L.point(clientX, clientY) 300 .distanceTo(this._mouseDownOrigin); 301 var lastPtDistance = this._calculateFinishDistance(e.latlng); 302 if (this.options.maxPoints > 1 && this.options.maxPoints == this._markers.length + 1) { 303 this.addVertex(e.latlng); 304 this._finishShape(); 305 } else if (lastPtDistance < 10 && L.Browser.touch) { 306 this._finishShape(); 307 } else if (Math.abs(dragCheckDistance) < 9 * (window.devicePixelRatio || 1)) { 308 this.addVertex(e.latlng); 309 } 310 this._enableNewMarkers(); // after a short pause, enable new markers 311 } 312 this._mouseDownOrigin = null; 313 }, 314 315 // ontouch prevented by clickHandled flag because some browsers fire both click/touch events, 316 // causing unwanted behavior 317 _onTouch: function (e) { 318 var originalEvent = e.originalEvent; 319 var clientX; 320 var clientY; 321 if (originalEvent.touches && originalEvent.touches[0] && !this._clickHandled && !this._touchHandled && !this._disableMarkers) { 322 clientX = originalEvent.touches[0].clientX; 323 clientY = originalEvent.touches[0].clientY; 324 this._disableNewMarkers(); 325 this._touchHandled = true; 326 this._startPoint.call(this, clientX, clientY); 327 this._endPoint.call(this, clientX, clientY, e); 328 this._touchHandled = null; 329 } 330 this._clickHandled = null; 331 }, 332 333 _onMouseOut: function () { 334 if (this._tooltip) { 335 this._tooltip._onMouseOut.call(this._tooltip); 336 } 337 }, 338 339 // calculate if we are currently within close enough distance 340 // of the closing point (first point for shapes, last point for lines) 341 // this is semi-ugly code but the only reliable way i found to get the job done 342 // note: calculating point.distanceTo between mouseDownOrigin and last marker did NOT work 343 _calculateFinishDistance: function (potentialLatLng) { 344 var lastPtDistance; 345 if (this._markers.length > 0) { 346 var finishMarker; 347 if (this.type === L.Draw.Polyline.TYPE) { 348 finishMarker = this._markers[this._markers.length - 1]; 349 } else if (this.type === L.Draw.Polygon.TYPE) { 350 finishMarker = this._markers[0]; 351 } else { 352 return Infinity; 353 } 354 var lastMarkerPoint = this._map.latLngToContainerPoint(finishMarker.getLatLng()), 355 potentialMarker = new L.Marker(potentialLatLng, { 356 icon: this.options.icon, 357 zIndexOffset: this.options.zIndexOffset * 2 358 }); 359 var potentialMarkerPint = this._map.latLngToContainerPoint(potentialMarker.getLatLng()); 360 lastPtDistance = lastMarkerPoint.distanceTo(potentialMarkerPint); 361 } else { 362 lastPtDistance = Infinity; 363 } 364 return lastPtDistance; 365 }, 366 367 _updateFinishHandler: function () { 368 var markerCount = this._markers.length; 369 // The last marker should have a click handler to close the polyline 370 if (markerCount > 1) { 371 this._markers[markerCount - 1].on('click', this._finishShape, this); 372 } 373 374 // Remove the old marker click handler (as only the last point should close the polyline) 375 if (markerCount > 2) { 376 this._markers[markerCount - 2].off('click', this._finishShape, this); 377 } 378 }, 379 380 _createMarker: function (latlng) { 381 var marker = new L.Marker(latlng, { 382 icon: this.options.icon, 383 zIndexOffset: this.options.zIndexOffset * 2 384 }); 385 386 this._markerGroup.addLayer(marker); 387 388 return marker; 389 }, 390 391 _updateGuide: function (newPos) { 392 var markerCount = this._markers ? this._markers.length : 0; 393 394 if (markerCount > 0) { 395 newPos = newPos || this._map.latLngToLayerPoint(this._currentLatLng); 396 397 // draw the guide line 398 this._clearGuides(); 399 this._drawGuide( 400 this._map.latLngToLayerPoint(this._markers[markerCount - 1].getLatLng()), 401 newPos 402 ); 403 } 404 }, 405 406 _updateTooltip: function (latLng) { 407 var text = this._getTooltipText(); 408 409 if (latLng) { 410 this._tooltip.updatePosition(latLng); 411 } 412 413 if (!this._errorShown) { 414 this._tooltip.updateContent(text); 415 } 416 }, 417 418 _drawGuide: function (pointA, pointB) { 419 var length = Math.floor(Math.sqrt(Math.pow((pointB.x - pointA.x), 2) + Math.pow((pointB.y - pointA.y), 2))), 420 guidelineDistance = this.options.guidelineDistance, 421 maxGuideLineLength = this.options.maxGuideLineLength, 422 // Only draw a guideline with a max length 423 i = length > maxGuideLineLength ? length - maxGuideLineLength : guidelineDistance, 424 fraction, 425 dashPoint, 426 dash; 427 428 //create the guides container if we haven't yet 429 if (!this._guidesContainer) { 430 this._guidesContainer = L.DomUtil.create('div', 'leaflet-draw-guides', this._overlayPane); 431 } 432 433 //draw a dash every GuildeLineDistance 434 for (; i < length; i += this.options.guidelineDistance) { 435 //work out fraction along line we are 436 fraction = i / length; 437 438 //calculate new x,y point 439 dashPoint = { 440 x: Math.floor((pointA.x * (1 - fraction)) + (fraction * pointB.x)), 441 y: Math.floor((pointA.y * (1 - fraction)) + (fraction * pointB.y)) 442 }; 443 444 //add guide dash to guide container 445 dash = L.DomUtil.create('div', 'leaflet-draw-guide-dash', this._guidesContainer); 446 dash.style.backgroundColor = 447 !this._errorShown ? this.options.shapeOptions.color : this.options.drawError.color; 448 449 L.DomUtil.setPosition(dash, dashPoint); 450 } 451 }, 452 453 _updateGuideColor: function (color) { 454 if (this._guidesContainer) { 455 for (var i = 0, l = this._guidesContainer.childNodes.length; i < l; i++) { 456 this._guidesContainer.childNodes[i].style.backgroundColor = color; 457 } 458 } 459 }, 460 461 // removes all child elements (guide dashes) from the guides container 462 _clearGuides: function () { 463 if (this._guidesContainer) { 464 while (this._guidesContainer.firstChild) { 465 this._guidesContainer.removeChild(this._guidesContainer.firstChild); 466 } 467 } 468 }, 469 470 _getTooltipText: function () { 471 var showLength = this.options.showLength, 472 labelText, distanceStr; 473 if (this._markers.length === 0) { 474 labelText = { 475 text: L.drawLocal.draw.handlers.polyline.tooltip.start 476 }; 477 } else { 478 distanceStr = showLength ? this._getMeasurementString() : ''; 479 480 if (this._markers.length === 1) { 481 labelText = { 482 text: L.drawLocal.draw.handlers.polyline.tooltip.cont, 483 subtext: distanceStr 484 }; 485 } else { 486 labelText = { 487 text: L.drawLocal.draw.handlers.polyline.tooltip.end, 488 subtext: distanceStr 489 }; 490 } 491 } 492 return labelText; 493 }, 494 495 _updateRunningMeasure: function (latlng, added) { 496 var markersLength = this._markers.length, 497 previousMarkerIndex, distance; 498 499 if (this._markers.length === 1) { 500 this._measurementRunningTotal = 0; 501 } else { 502 previousMarkerIndex = markersLength - (added ? 2 : 1); 503 504 // Calculate the distance based on the version 505 if (L.GeometryUtil.isVersion07x()) { 506 distance = latlng.distanceTo(this._markers[previousMarkerIndex].getLatLng()) * (this.options.factor || 1); 507 } else { 508 distance = this._map.distance(latlng, this._markers[previousMarkerIndex].getLatLng()) * (this.options.factor || 1); 509 } 510 511 this._measurementRunningTotal += distance * (added ? 1 : -1); 512 } 513 }, 514 515 _getMeasurementString: function () { 516 var currentLatLng = this._currentLatLng, 517 previousLatLng = this._markers[this._markers.length - 1].getLatLng(), 518 distance; 519 520 // Calculate the distance from the last fixed point to the mouse position based on the version 521 if (L.GeometryUtil.isVersion07x()) { 522 distance = previousLatLng && currentLatLng && currentLatLng.distanceTo ? this._measurementRunningTotal + currentLatLng.distanceTo(previousLatLng) * (this.options.factor || 1) : this._measurementRunningTotal || 0; 523 } else { 524 distance = previousLatLng && currentLatLng ? this._measurementRunningTotal + this._map.distance(currentLatLng, previousLatLng) * (this.options.factor || 1) : this._measurementRunningTotal || 0; 525 } 526 527 return L.GeometryUtil.readableDistance(distance, this.options.metric, this.options.feet, this.options.nautic, this.options.precision); 528 }, 529 530 _showErrorTooltip: function () { 531 this._errorShown = true; 532 533 // Update tooltip 534 this._tooltip 535 .showAsError() 536 .updateContent({text: this.options.drawError.message}); 537 538 // Update shape 539 this._updateGuideColor(this.options.drawError.color); 540 this._poly.setStyle({color: this.options.drawError.color}); 541 542 // Hide the error after 2 seconds 543 this._clearHideErrorTimeout(); 544 this._hideErrorTimeout = setTimeout(L.Util.bind(this._hideErrorTooltip, this), this.options.drawError.timeout); 545 }, 546 547 _hideErrorTooltip: function () { 548 this._errorShown = false; 549 550 this._clearHideErrorTimeout(); 551 552 // Revert tooltip 553 this._tooltip 554 .removeError() 555 .updateContent(this._getTooltipText()); 556 557 // Revert shape 558 this._updateGuideColor(this.options.shapeOptions.color); 559 this._poly.setStyle({color: this.options.shapeOptions.color}); 560 }, 561 562 _clearHideErrorTimeout: function () { 563 if (this._hideErrorTimeout) { 564 clearTimeout(this._hideErrorTimeout); 565 this._hideErrorTimeout = null; 566 } 567 }, 568 569 // disable new markers temporarily; 570 // this is to prevent duplicated touch/click events in some browsers 571 _disableNewMarkers: function () { 572 this._disableMarkers = true; 573 }, 574 575 // see _disableNewMarkers 576 _enableNewMarkers: function () { 577 setTimeout(function () { 578 this._disableMarkers = false; 579 }.bind(this), 50); 580 }, 581 582 _cleanUpShape: function () { 583 if (this._markers.length > 1) { 584 this._markers[this._markers.length - 1].off('click', this._finishShape, this); 585 } 586 }, 587 588 _fireCreatedEvent: function () { 589 var poly = new this.Poly(this._poly.getLatLngs(), this.options.shapeOptions); 590 L.Draw.Feature.prototype._fireCreatedEvent.call(this, poly); 591 } 592});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.