UNPKG

leaflet-routing-machine

Version:
1,790 lines (1,520 loc) 78.1 kB
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),(f.L||(f.L={})).Routing=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ function corslite(url, callback, cors) { var sent = false; if (typeof window.XMLHttpRequest === 'undefined') { return callback(Error('Browser not supported')); } if (typeof cors === 'undefined') { var m = url.match(/^\s*https?:\/\/[^\/]*/); cors = m && (m[0] !== location.protocol + '//' + location.domain + (location.port ? ':' + location.port : '')); } var x = new window.XMLHttpRequest(); function isSuccessful(status) { return status >= 200 && status < 300 || status === 304; } if (cors && !('withCredentials' in x)) { // IE8-9 x = new window.XDomainRequest(); // Ensure callback is never called synchronously, i.e., before // x.send() returns (this has been observed in the wild). // See https://github.com/mapbox/mapbox.js/issues/472 var original = callback; callback = function() { if (sent) { original.apply(this, arguments); } else { var that = this, args = arguments; setTimeout(function() { original.apply(that, args); }, 0); } } } function loaded() { if ( // XDomainRequest x.status === undefined || // modern browsers isSuccessful(x.status)) callback.call(x, null, x); else callback.call(x, x, null); } // Both `onreadystatechange` and `onload` can fire. `onreadystatechange` // has [been supported for longer](http://stackoverflow.com/a/9181508/229001). if ('onload' in x) { x.onload = loaded; } else { x.onreadystatechange = function readystate() { if (x.readyState === 4) { loaded(); } }; } // Call the callback with the XMLHttpRequest object as an error and prevent // it from ever being called again by reassigning it to `noop` x.onerror = function error(evt) { // XDomainRequest provides no evt parameter callback.call(this, evt || true, null); callback = function() { }; }; // IE9 must have onprogress be set to a unique function. x.onprogress = function() { }; x.ontimeout = function(evt) { callback.call(this, evt, null); callback = function() { }; }; x.onabort = function(evt) { callback.call(this, evt, null); callback = function() { }; }; // GET is the only supported HTTP Verb by XDomainRequest and is the // only one supported here. x.open('GET', url, true); // Send the request. Sending data is not supported. x.send(null); sent = true; return x; } if (typeof module !== 'undefined') module.exports = corslite; },{}],2:[function(require,module,exports){ var polyline = {}; // Based off of [the offical Google document](https://developers.google.com/maps/documentation/utilities/polylinealgorithm) // // Some parts from [this implementation](http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/PolylineEncoder.js) // by [Mark McClure](http://facstaff.unca.edu/mcmcclur/) function encode(coordinate, factor) { coordinate = Math.round(coordinate * factor); coordinate <<= 1; if (coordinate < 0) { coordinate = ~coordinate; } var output = ''; while (coordinate >= 0x20) { output += String.fromCharCode((0x20 | (coordinate & 0x1f)) + 63); coordinate >>= 5; } output += String.fromCharCode(coordinate + 63); return output; } // This is adapted from the implementation in Project-OSRM // https://github.com/DennisOSRM/Project-OSRM-Web/blob/master/WebContent/routing/OSRM.RoutingGeometry.js polyline.decode = function(str, precision) { var index = 0, lat = 0, lng = 0, coordinates = [], shift = 0, result = 0, byte = null, latitude_change, longitude_change, factor = Math.pow(10, precision || 5); // Coordinates have variable length when encoded, so just keep // track of whether we've hit the end of the string. In each // loop iteration, a single coordinate is decoded. while (index < str.length) { // Reset shift, result, and byte byte = null; shift = 0; result = 0; do { byte = str.charCodeAt(index++) - 63; result |= (byte & 0x1f) << shift; shift += 5; } while (byte >= 0x20); latitude_change = ((result & 1) ? ~(result >> 1) : (result >> 1)); shift = result = 0; do { byte = str.charCodeAt(index++) - 63; result |= (byte & 0x1f) << shift; shift += 5; } while (byte >= 0x20); longitude_change = ((result & 1) ? ~(result >> 1) : (result >> 1)); lat += latitude_change; lng += longitude_change; coordinates.push([lat / factor, lng / factor]); } return coordinates; }; polyline.encode = function(coordinates, precision) { if (!coordinates.length) return ''; var factor = Math.pow(10, precision || 5), output = encode(coordinates[0][0], factor) + encode(coordinates[0][1], factor); for (var i = 1; i < coordinates.length; i++) { var a = coordinates[i], b = coordinates[i - 1]; output += encode(a[0] - b[0], factor); output += encode(a[1] - b[1], factor); } return output; }; if (typeof module !== undefined) module.exports = polyline; },{}],3:[function(require,module,exports){ (function() { 'use strict'; L.Routing = L.Routing || {}; L.Routing.Autocomplete = L.Class.extend({ options: { timeout: 500, blurTimeout: 100, noResultsMessage: 'No results found.' }, initialize: function(elem, callback, context, options) { L.setOptions(this, options); this._elem = elem; this._resultFn = options.resultFn ? L.Util.bind(options.resultFn, options.resultContext) : null; this._autocomplete = options.autocompleteFn ? L.Util.bind(options.autocompleteFn, options.autocompleteContext) : null; this._selectFn = L.Util.bind(callback, context); this._container = L.DomUtil.create('div', 'leaflet-routing-geocoder-result'); this._resultTable = L.DomUtil.create('table', '', this._container); // TODO: looks a bit like a kludge to register same for input and keypress - // browsers supporting both will get duplicate events; just registering // input will not catch enter, though. L.DomEvent.addListener(this._elem, 'input', this._keyPressed, this); L.DomEvent.addListener(this._elem, 'keypress', this._keyPressed, this); L.DomEvent.addListener(this._elem, 'keydown', this._keyDown, this); L.DomEvent.addListener(this._elem, 'blur', function() { if (this._isOpen) { this.close(); } }, this); }, close: function() { L.DomUtil.removeClass(this._container, 'leaflet-routing-geocoder-result-open'); this._isOpen = false; }, _open: function() { var rect = this._elem.getBoundingClientRect(); if (!this._container.parentElement) { // See notes section under https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollX // This abomination is required to support all flavors of IE var scrollX = (window.pageXOffset !== undefined) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft; var scrollY = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop; this._container.style.left = (rect.left + scrollX) + 'px'; this._container.style.top = (rect.bottom + scrollY) + 'px'; this._container.style.width = (rect.right - rect.left) + 'px'; document.body.appendChild(this._container); } L.DomUtil.addClass(this._container, 'leaflet-routing-geocoder-result-open'); this._isOpen = true; }, _setResults: function(results) { var i, tr, td, text; delete this._selection; this._results = results; while (this._resultTable.firstChild) { this._resultTable.removeChild(this._resultTable.firstChild); } for (i = 0; i < results.length; i++) { tr = L.DomUtil.create('tr', '', this._resultTable); tr.setAttribute('data-result-index', i); td = L.DomUtil.create('td', '', tr); text = document.createTextNode(results[i].name); td.appendChild(text); // mousedown + click because: // http://stackoverflow.com/questions/10652852/jquery-fire-click-before-blur-event L.DomEvent.addListener(td, 'mousedown', L.DomEvent.preventDefault); L.DomEvent.addListener(td, 'click', this._createClickListener(results[i])); } if (!i) { tr = L.DomUtil.create('tr', '', this._resultTable); td = L.DomUtil.create('td', 'leaflet-routing-geocoder-no-results', tr); td.innerHTML = this.options.noResultsMessage; } this._open(); if (results.length > 0) { // Select the first entry this._select(1); } }, _createClickListener: function(r) { var resultSelected = this._resultSelected(r); return L.bind(function() { this._elem.blur(); resultSelected(); }, this); }, _resultSelected: function(r) { return L.bind(function() { this.close(); this._elem.value = r.name; this._lastCompletedText = r.name; this._selectFn(r); }, this); }, _keyPressed: function(e) { var index; if (this._isOpen && e.keyCode === 13 && this._selection) { index = parseInt(this._selection.getAttribute('data-result-index'), 10); this._resultSelected(this._results[index])(); L.DomEvent.preventDefault(e); return; } if (e.keyCode === 13) { this._complete(this._resultFn, true); return; } if (this._autocomplete && document.activeElement === this._elem) { if (this._timer) { clearTimeout(this._timer); } this._timer = setTimeout(L.Util.bind(function() { this._complete(this._autocomplete); }, this), this.options.timeout); return; } this._unselect(); }, _select: function(dir) { var sel = this._selection; if (sel) { L.DomUtil.removeClass(sel.firstChild, 'leaflet-routing-geocoder-selected'); sel = sel[dir > 0 ? 'nextSibling' : 'previousSibling']; } if (!sel) { sel = this._resultTable[dir > 0 ? 'firstChild' : 'lastChild']; } if (sel) { L.DomUtil.addClass(sel.firstChild, 'leaflet-routing-geocoder-selected'); this._selection = sel; } }, _unselect: function() { if (this._selection) { L.DomUtil.removeClass(this._selection.firstChild, 'leaflet-routing-geocoder-selected'); } delete this._selection; }, _keyDown: function(e) { if (this._isOpen) { switch (e.keyCode) { // Escape case 27: this.close(); L.DomEvent.preventDefault(e); return; // Up case 38: this._select(-1); L.DomEvent.preventDefault(e); return; // Down case 40: this._select(1); L.DomEvent.preventDefault(e); return; } } }, _complete: function(completeFn, trySelect) { var v = this._elem.value; function completeResults(results) { this._lastCompletedText = v; if (trySelect && results.length === 1) { this._resultSelected(results[0])(); } else { this._setResults(results); } } if (!v) { return; } if (v !== this._lastCompletedText) { completeFn(v, completeResults, this); } else if (trySelect) { completeResults.call(this, this._results); } } }); })(); },{}],4:[function(require,module,exports){ (function (global){ (function() { 'use strict'; var L = (typeof window !== "undefined" ? window.L : typeof global !== "undefined" ? global.L : null); L.Routing = L.Routing || {}; L.extend(L.Routing, require('./L.Routing.Itinerary')); L.extend(L.Routing, require('./L.Routing.Line')); L.extend(L.Routing, require('./L.Routing.Plan')); L.extend(L.Routing, require('./L.Routing.OSRMv1')); L.extend(L.Routing, require('./L.Routing.Mapbox')); L.extend(L.Routing, require('./L.Routing.ErrorControl')); L.Routing.Control = L.Routing.Itinerary.extend({ options: { fitSelectedRoutes: 'smart', routeLine: function(route, options) { return L.Routing.line(route, options); }, autoRoute: true, routeWhileDragging: false, routeDragInterval: 500, waypointMode: 'connect', showAlternatives: false, defaultErrorHandler: function(e) { console.error('Routing error:', e.error); } }, initialize: function(options) { L.Util.setOptions(this, options); this._router = this.options.router || new L.Routing.OSRMv1(options); this._plan = this.options.plan || L.Routing.plan(this.options.waypoints, options); this._requestCount = 0; L.Routing.Itinerary.prototype.initialize.call(this, options); this.on('routeselected', this._routeSelected, this); if (this.options.defaultErrorHandler) { this.on('routingerror', this.options.defaultErrorHandler); } this._plan.on('waypointschanged', this._onWaypointsChanged, this); if (options.routeWhileDragging) { this._setupRouteDragging(); } if (this.options.autoRoute) { this.route(); } }, onAdd: function(map) { var container = L.Routing.Itinerary.prototype.onAdd.call(this, map); this._map = map; this._map.addLayer(this._plan); this._map.on('zoomend', function() { if (!this._selectedRoute || !this._router.requiresMoreDetail) { return; } var map = this._map; if (this._router.requiresMoreDetail(this._selectedRoute, map.getZoom(), map.getBounds())) { this.route({ callback: L.bind(function(err, routes) { var i; if (!err) { for (i = 0; i < routes.length; i++) { this._routes[i].properties = routes[i].properties; } this._updateLineCallback(err, routes); } }, this), simplifyGeometry: false, geometryOnly: true }); } }, this); if (this._plan.options.geocoder) { container.insertBefore(this._plan.createGeocoders(), container.firstChild); } return container; }, onRemove: function(map) { if (this._line) { map.removeLayer(this._line); } map.removeLayer(this._plan); return L.Routing.Itinerary.prototype.onRemove.call(this, map); }, getWaypoints: function() { return this._plan.getWaypoints(); }, setWaypoints: function(waypoints) { this._plan.setWaypoints(waypoints); return this; }, spliceWaypoints: function() { var removed = this._plan.spliceWaypoints.apply(this._plan, arguments); return removed; }, getPlan: function() { return this._plan; }, getRouter: function() { return this._router; }, _routeSelected: function(e) { var route = this._selectedRoute = e.route, alternatives = this.options.showAlternatives && e.alternatives, fitMode = this.options.fitSelectedRoutes, fitBounds = (fitMode === 'smart' && !this._waypointsVisible()) || (fitMode !== 'smart' && fitMode); this._updateLines({route: route, alternatives: alternatives}); if (fitBounds) { this._map.fitBounds(this._line.getBounds()); } if (this.options.waypointMode === 'snap') { this._plan.off('waypointschanged', this._onWaypointsChanged, this); this.setWaypoints(route.waypoints); this._plan.on('waypointschanged', this._onWaypointsChanged, this); } }, _waypointsVisible: function() { var wps = this.getWaypoints(), mapSize, bounds, boundsSize, i, p; try { mapSize = this._map.getSize(); for (i = 0; i < wps.length; i++) { p = this._map.latLngToLayerPoint(wps[i].latLng); if (bounds) { bounds.extend(p); } else { bounds = L.bounds([p]); } } boundsSize = bounds.getSize(); return (boundsSize.x > mapSize.x / 5 || boundsSize.y > mapSize.y / 5) && this._waypointsInViewport(); } catch (e) { return false; } }, _waypointsInViewport: function() { var wps = this.getWaypoints(), mapBounds, i; try { mapBounds = this._map.getBounds(); } catch (e) { return false; } for (i = 0; i < wps.length; i++) { if (mapBounds.contains(wps[i].latLng)) { return true; } } return false; }, _updateLines: function(routes) { var addWaypoints = this.options.addWaypoints !== undefined ? this.options.addWaypoints : true; this._clearLines(); // add alternatives first so they lie below the main route this._alternatives = []; if (routes.alternatives) routes.alternatives.forEach(function(alt, i) { this._alternatives[i] = this.options.routeLine(alt, L.extend({ isAlternative: true }, this.options.altLineOptions || this.options.lineOptions)); this._alternatives[i].addTo(this._map); this._hookAltEvents(this._alternatives[i]); }, this); this._line = this.options.routeLine(routes.route, L.extend({ addWaypoints: addWaypoints, extendToWaypoints: this.options.waypointMode === 'connect' }, this.options.lineOptions)); this._line.addTo(this._map); this._hookEvents(this._line); }, _hookEvents: function(l) { l.on('linetouched', function(e) { this._plan.dragNewWaypoint(e); }, this); }, _hookAltEvents: function(l) { l.on('linetouched', function(e) { var alts = this._routes.slice(); var selected = alts.splice(e.target._route.routesIndex, 1)[0]; this.fire('routeselected', {route: selected, alternatives: alts}); }, this); }, _onWaypointsChanged: function(e) { if (this.options.autoRoute) { this.route({}); } if (!this._plan.isReady()) { this._clearLines(); this._clearAlts(); } this.fire('waypointschanged', {waypoints: e.waypoints}); }, _setupRouteDragging: function() { var timer = 0, waypoints; this._plan.on('waypointdrag', L.bind(function(e) { waypoints = e.waypoints; if (!timer) { timer = setTimeout(L.bind(function() { this.route({ waypoints: waypoints, geometryOnly: true, callback: L.bind(this._updateLineCallback, this) }); timer = undefined; }, this), this.options.routeDragInterval); } }, this)); this._plan.on('waypointdragend', function() { if (timer) { clearTimeout(timer); timer = undefined; } this.route(); }, this); }, _updateLineCallback: function(err, routes) { if (!err) { routes = routes.slice(); var selected = routes.splice(this._selectedRoute.routesIndex, 1)[0]; this._updateLines({route: selected, alternatives: routes }); } else { this._clearLines(); } }, route: function(options) { var ts = ++this._requestCount, wps; options = options || {}; if (this._plan.isReady()) { if (this.options.useZoomParameter) { options.z = this._map && this._map.getZoom(); } wps = options && options.waypoints || this._plan.getWaypoints(); this.fire('routingstart', {waypoints: wps}); this._router.route(wps, options.callback || function(err, routes) { // Prevent race among multiple requests, // by checking the current request's timestamp // against the last request's; ignore result if // this isn't the latest request. if (ts === this._requestCount) { this._clearLines(); this._clearAlts(); if (err) { this.fire('routingerror', {error: err}); return; } routes.forEach(function(route, i) { route.routesIndex = i; }); if (!options.geometryOnly) { this.fire('routesfound', {waypoints: wps, routes: routes}); this.setAlternatives(routes); } else { var selectedRoute = routes.splice(0,1)[0]; this._routeSelected({route: selectedRoute, alternatives: routes}); } } }, this, options); } }, _clearLines: function() { if (this._line) { this._map.removeLayer(this._line); delete this._line; } if (this._alternatives && this._alternatives.length) { for (var i in this._alternatives) { this._map.removeLayer(this._alternatives[i]); } this._alternatives = []; } } }); L.Routing.control = function(options) { return new L.Routing.Control(options); }; module.exports = L.Routing; })(); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./L.Routing.ErrorControl":5,"./L.Routing.Itinerary":8,"./L.Routing.Line":10,"./L.Routing.Mapbox":12,"./L.Routing.OSRMv1":13,"./L.Routing.Plan":14}],5:[function(require,module,exports){ (function() { 'use strict'; L.Routing = L.Routing || {}; L.Routing.ErrorControl = L.Control.extend({ options: { header: 'Routing error', formatMessage: function(error) { if (error.status < 0) { return 'Calculating the route caused an error. Technical description follows: <code><pre>' + error.message + '</pre></code'; } else { return 'The route could not be calculated. ' + error.message; } } }, initialize: function(routingControl, options) { L.Control.prototype.initialize.call(this, options); routingControl .on('routingerror', L.bind(function(e) { if (this._element) { this._element.children[1].innerHTML = this.options.formatMessage(e.error); this._element.style.visibility = 'visible'; } }, this)) .on('routingstart', L.bind(function() { if (this._element) { this._element.style.visibility = 'hidden'; } }, this)); }, onAdd: function() { var header, message; this._element = L.DomUtil.create('div', 'leaflet-bar leaflet-routing-error'); this._element.style.visibility = 'hidden'; header = L.DomUtil.create('h3', null, this._element); message = L.DomUtil.create('span', null, this._element); header.innerHTML = this.options.header; return this._element; }, onRemove: function() { delete this._element; } }); L.Routing.errorControl = function(routingControl, options) { return new L.Routing.ErrorControl(routingControl, options); }; })(); },{}],6:[function(require,module,exports){ (function (global){ (function() { 'use strict'; var L = (typeof window !== "undefined" ? window.L : typeof global !== "undefined" ? global.L : null); L.Routing = L.Routing || {}; L.extend(L.Routing, require('./L.Routing.Localization')); L.Routing.Formatter = L.Class.extend({ options: { units: 'metric', unitNames: null, language: 'en', roundingSensitivity: 1, distanceTemplate: '{value} {unit}' }, initialize: function(options) { L.setOptions(this, options); var langs = L.Util.isArray(this.options.language) ? this.options.language : [this.options.language, 'en']; this._localization = new L.Routing.Localization(langs); }, formatDistance: function(d /* Number (meters) */, sensitivity) { var un = this.options.unitNames || this._localization.localize('units'), simpleRounding = sensitivity <= 0, round = simpleRounding ? function(v) { return v; } : L.bind(this._round, this), v, yards, data, pow10; if (this.options.units === 'imperial') { yards = d / 0.9144; if (yards >= 1000) { data = { value: round(d / 1609.344, sensitivity), unit: un.miles }; } else { data = { value: round(yards, sensitivity), unit: un.yards }; } } else { v = round(d, sensitivity); data = { value: v >= 1000 ? (v / 1000) : v, unit: v >= 1000 ? un.kilometers : un.meters }; } if (simpleRounding) { data.value = data.value.toFixed(-sensitivity); } return L.Util.template(this.options.distanceTemplate, data); }, _round: function(d, sensitivity) { var s = sensitivity || this.options.roundingSensitivity, pow10 = Math.pow(10, (Math.floor(d / s) + '').length - 1), r = Math.floor(d / pow10), p = (r > 5) ? pow10 : pow10 / 2; return Math.round(d / p) * p; }, formatTime: function(t /* Number (seconds) */) { var un = this.options.unitNames || this._localization.localize('units'); // More than 30 seconds precision looks ridiculous t = Math.round(t / 30) * 30; if (t > 86400) { return Math.round(t / 3600) + ' ' + un.hours; } else if (t > 3600) { return Math.floor(t / 3600) + ' ' + un.hours + ' ' + Math.round((t % 3600) / 60) + ' ' + un.minutes; } else if (t > 300) { return Math.round(t / 60) + ' ' + un.minutes; } else if (t > 60) { return Math.floor(t / 60) + ' ' + un.minutes + (t % 60 !== 0 ? ' ' + (t % 60) + ' ' + un.seconds : ''); } else { return t + ' ' + un.seconds; } }, formatInstruction: function(instr, i) { if (instr.text === undefined) { return this.capitalize(L.Util.template(this._getInstructionTemplate(instr, i), L.extend({}, instr, { exitStr: instr.exit ? this._localization.localize('formatOrder')(instr.exit) : '', dir: this._localization.localize(['directions', instr.direction]), modifier: this._localization.localize(['directions', instr.modifier]) }))); } else { return instr.text; } }, getIconName: function(instr, i) { switch (instr.type) { case 'Head': if (i === 0) { return 'depart'; } break; case 'WaypointReached': return 'via'; case 'Roundabout': return 'enter-roundabout'; case 'DestinationReached': return 'arrive'; } switch (instr.modifier) { case 'Straight': return 'continue'; case 'SlightRight': return 'bear-right'; case 'Right': return 'turn-right'; case 'SharpRight': return 'sharp-right'; case 'TurnAround': case 'Uturn': return 'u-turn'; case 'SharpLeft': return 'sharp-left'; case 'Left': return 'turn-left'; case 'SlightLeft': return 'bear-left'; } }, capitalize: function(s) { return s.charAt(0).toUpperCase() + s.substring(1); }, _getInstructionTemplate: function(instr, i) { var type = instr.type === 'Straight' ? (i === 0 ? 'Head' : 'Continue') : instr.type, strings = this._localization.localize(['instructions', type]); if (!strings) { strings = [ this._localization.localize(['directions', type]), ' ' + this._localization.localize(['instructions', 'Onto']) ]; } return strings[0] + (strings.length > 1 && instr.road ? strings[1] : ''); } }); module.exports = L.Routing; })(); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./L.Routing.Localization":11}],7:[function(require,module,exports){ (function (global){ (function() { 'use strict'; var L = (typeof window !== "undefined" ? window.L : typeof global !== "undefined" ? global.L : null); L.Routing = L.Routing || {}; L.extend(L.Routing, require('./L.Routing.Autocomplete')); function selectInputText(input) { if (input.setSelectionRange) { // On iOS, select() doesn't work input.setSelectionRange(0, 9999); } else { // On at least IE8, setSeleectionRange doesn't exist input.select(); } } L.Routing.GeocoderElement = L.Class.extend({ includes: L.Mixin.Events, options: { createGeocoder: function(i, nWps, options) { var container = L.DomUtil.create('div', 'leaflet-routing-geocoder'), input = L.DomUtil.create('input', '', container), remove = options.addWaypoints ? L.DomUtil.create('span', 'leaflet-routing-remove-waypoint', container) : undefined; input.disabled = !options.addWaypoints; return { container: container, input: input, closeButton: remove }; }, geocoderPlaceholder: function(i, numberWaypoints, geocoderElement) { var l = new L.Routing.Localization(geocoderElement.options.language).localize('ui'); return i === 0 ? l.startPlaceholder : (i < numberWaypoints - 1 ? L.Util.template(l.viaPlaceholder, {viaNumber: i}) : l.endPlaceholder); }, geocoderClass: function() { return ''; }, waypointNameFallback: function(latLng) { var ns = latLng.lat < 0 ? 'S' : 'N', ew = latLng.lng < 0 ? 'W' : 'E', lat = (Math.round(Math.abs(latLng.lat) * 10000) / 10000).toString(), lng = (Math.round(Math.abs(latLng.lng) * 10000) / 10000).toString(); return ns + lat + ', ' + ew + lng; }, maxGeocoderTolerance: 200, autocompleteOptions: {}, language: 'en', }, initialize: function(wp, i, nWps, options) { L.setOptions(this, options); var g = this.options.createGeocoder(i, nWps, this.options), closeButton = g.closeButton, geocoderInput = g.input; geocoderInput.setAttribute('placeholder', this.options.geocoderPlaceholder(i, nWps, this)); geocoderInput.className = this.options.geocoderClass(i, nWps); this._element = g; this._waypoint = wp; this.update(); // This has to be here, or geocoder's value will not be properly // initialized. // TODO: look into why and make _updateWaypointName fix this. geocoderInput.value = wp.name; L.DomEvent.addListener(geocoderInput, 'click', function() { selectInputText(this); }, geocoderInput); if (closeButton) { L.DomEvent.addListener(closeButton, 'click', function() { this.fire('delete', { waypoint: this._waypoint }); }, this); } new L.Routing.Autocomplete(geocoderInput, function(r) { geocoderInput.value = r.name; wp.name = r.name; wp.latLng = r.center; this.fire('geocoded', { waypoint: wp, value: r }); }, this, L.extend({ resultFn: this.options.geocoder.geocode, resultContext: this.options.geocoder, autocompleteFn: this.options.geocoder.suggest, autocompleteContext: this.options.geocoder }, this.options.autocompleteOptions)); }, getContainer: function() { return this._element.container; }, setValue: function(v) { this._element.input.value = v; }, update: function(force) { var wp = this._waypoint, wpCoords; wp.name = wp.name || ''; if (wp.latLng && (force || !wp.name)) { wpCoords = this.options.waypointNameFallback(wp.latLng); if (this.options.geocoder && this.options.geocoder.reverse) { this.options.geocoder.reverse(wp.latLng, 67108864 /* zoom 18 */, function(rs) { if (rs.length > 0 && rs[0].center.distanceTo(wp.latLng) < this.options.maxGeocoderTolerance) { wp.name = rs[0].name; } else { wp.name = wpCoords; } this._update(); }, this); } else { wp.name = wpCoords; this._update(); } } }, focus: function() { var input = this._element.input; input.focus(); selectInputText(input); }, _update: function() { var wp = this._waypoint, value = wp && wp.name ? wp.name : ''; this.setValue(value); this.fire('reversegeocoded', {waypoint: wp, value: value}); } }); L.Routing.geocoderElement = function(wp, i, nWps, plan) { return new L.Routing.GeocoderElement(wp, i, nWps, plan); }; module.exports = L.Routing; })(); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./L.Routing.Autocomplete":3}],8:[function(require,module,exports){ (function (global){ (function() { 'use strict'; var L = (typeof window !== "undefined" ? window.L : typeof global !== "undefined" ? global.L : null); L.Routing = L.Routing || {}; L.extend(L.Routing, require('./L.Routing.Formatter')); L.extend(L.Routing, require('./L.Routing.ItineraryBuilder')); L.Routing.Itinerary = L.Control.extend({ includes: L.Mixin.Events, options: { pointMarkerStyle: { radius: 5, color: '#03f', fillColor: 'white', opacity: 1, fillOpacity: 0.7 }, summaryTemplate: '<h2>{name}</h2><h3>{distance}, {time}</h3>', timeTemplate: '{time}', containerClassName: '', alternativeClassName: '', minimizedClassName: '', itineraryClassName: '', totalDistanceRoundingSensitivity: -1, show: true, collapsible: undefined, collapseBtn: function(itinerary) { var collapseBtn = L.DomUtil.create('span', itinerary.options.collapseBtnClass); L.DomEvent.on(collapseBtn, 'click', itinerary._toggle, itinerary); itinerary._container.insertBefore(collapseBtn, itinerary._container.firstChild); }, collapseBtnClass: 'leaflet-routing-collapse-btn' }, initialize: function(options) { L.setOptions(this, options); this._formatter = this.options.formatter || new L.Routing.Formatter(this.options); this._itineraryBuilder = this.options.itineraryBuilder || new L.Routing.ItineraryBuilder({ containerClassName: this.options.itineraryClassName }); }, onAdd: function(map) { var collapsible = this.options.collapsible; collapsible = collapsible || (collapsible === undefined && map.getSize().x <= 640); this._container = L.DomUtil.create('div', 'leaflet-routing-container leaflet-bar ' + (!this.options.show ? 'leaflet-routing-container-hide ' : '') + (collapsible ? 'leaflet-routing-collapsible ' : '') + this.options.containerClassName); this._altContainer = this.createAlternativesContainer(); this._container.appendChild(this._altContainer); L.DomEvent.disableClickPropagation(this._container); L.DomEvent.addListener(this._container, 'mousewheel', function(e) { L.DomEvent.stopPropagation(e); }); if (collapsible) { this.options.collapseBtn(this); } return this._container; }, onRemove: function() { }, createAlternativesContainer: function() { return L.DomUtil.create('div', 'leaflet-routing-alternatives-container'); }, setAlternatives: function(routes) { var i, alt, altDiv; this._clearAlts(); this._routes = routes; for (i = 0; i < this._routes.length; i++) { alt = this._routes[i]; altDiv = this._createAlternative(alt, i); this._altContainer.appendChild(altDiv); this._altElements.push(altDiv); } this._selectRoute({route: this._routes[0], alternatives: this._routes.slice(1)}); return this; }, show: function() { L.DomUtil.removeClass(this._container, 'leaflet-routing-container-hide'); }, hide: function() { L.DomUtil.addClass(this._container, 'leaflet-routing-container-hide'); }, _toggle: function() { var collapsed = L.DomUtil.hasClass(this._container, 'leaflet-routing-container-hide'); this[collapsed ? 'show' : 'hide'](); }, _createAlternative: function(alt, i) { var altDiv = L.DomUtil.create('div', 'leaflet-routing-alt ' + this.options.alternativeClassName + (i > 0 ? ' leaflet-routing-alt-minimized ' + this.options.minimizedClassName : '')), template = this.options.summaryTemplate, data = L.extend({ name: alt.name, distance: this._formatter.formatDistance(alt.summary.totalDistance, this.options.totalDistanceRoundingSensitivity), time: this._formatter.formatTime(alt.summary.totalTime) }, alt); altDiv.innerHTML = typeof(template) === 'function' ? template(data) : L.Util.template(template, data); L.DomEvent.addListener(altDiv, 'click', this._onAltClicked, this); this.on('routeselected', this._selectAlt, this); altDiv.appendChild(this._createItineraryContainer(alt)); return altDiv; }, _clearAlts: function() { var el = this._altContainer; while (el && el.firstChild) { el.removeChild(el.firstChild); } this._altElements = []; }, _createItineraryContainer: function(r) { var container = this._itineraryBuilder.createContainer(), steps = this._itineraryBuilder.createStepsContainer(), i, instr, step, distance, text, icon; container.appendChild(steps); for (i = 0; i < r.instructions.length; i++) { instr = r.instructions[i]; text = this._formatter.formatInstruction(instr, i); distance = this._formatter.formatDistance(instr.distance); icon = this._formatter.getIconName(instr, i); step = this._itineraryBuilder.createStep(text, distance, icon, steps); this._addRowListeners(step, r.coordinates[instr.index]); } return container; }, _addRowListeners: function(row, coordinate) { L.DomEvent.addListener(row, 'mouseover', function() { this._marker = L.circleMarker(coordinate, this.options.pointMarkerStyle).addTo(this._map); }, this); L.DomEvent.addListener(row, 'mouseout', function() { if (this._marker) { this._map.removeLayer(this._marker); delete this._marker; } }, this); L.DomEvent.addListener(row, 'click', function(e) { this._map.panTo(coordinate); L.DomEvent.stopPropagation(e); }, this); }, _onAltClicked: function(e) { var altElem = e.target || window.event.srcElement; while (!L.DomUtil.hasClass(altElem, 'leaflet-routing-alt')) { altElem = altElem.parentElement; } var j = this._altElements.indexOf(altElem); var alts = this._routes.slice(); var route = alts.splice(j, 1)[0]; this.fire('routeselected', { route: route, alternatives: alts }); }, _selectAlt: function(e) { var altElem, j, n, classFn; altElem = this._altElements[e.route.routesIndex]; if (L.DomUtil.hasClass(altElem, 'leaflet-routing-alt-minimized')) { for (j = 0; j < this._altElements.length; j++) { n = this._altElements[j]; classFn = j === e.route.routesIndex ? 'removeClass' : 'addClass'; L.DomUtil[classFn](n, 'leaflet-routing-alt-minimized'); if (this.options.minimizedClassName) { L.DomUtil[classFn](n, this.options.minimizedClassName); } if (j !== e.route.routesIndex) n.scrollTop = 0; } } L.DomEvent.stop(e); }, _selectRoute: function(routes) { if (this._marker) { this._map.removeLayer(this._marker); delete this._marker; } this.fire('routeselected', routes); } }); L.Routing.itinerary = function(options) { return new L.Routing.Itinerary(options); }; module.exports = L.Routing; })(); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./L.Routing.Formatter":6,"./L.Routing.ItineraryBuilder":9}],9:[function(require,module,exports){ (function (global){ (function() { 'use strict'; var L = (typeof window !== "undefined" ? window.L : typeof global !== "undefined" ? global.L : null); L.Routing = L.Routing || {}; L.Routing.ItineraryBuilder = L.Class.extend({ options: { containerClassName: '' }, initialize: function(options) { L.setOptions(this, options); }, createContainer: function(className) { var table = L.DomUtil.create('table', className || ''), colgroup = L.DomUtil.create('colgroup', '', table); L.DomUtil.create('col', 'leaflet-routing-instruction-icon', colgroup); L.DomUtil.create('col', 'leaflet-routing-instruction-text', colgroup); L.DomUtil.create('col', 'leaflet-routing-instruction-distance', colgroup); return table; }, createStepsContainer: function() { return L.DomUtil.create('tbody', ''); }, createStep: function(text, distance, icon, steps) { var row = L.DomUtil.create('tr', '', steps), span, td; td = L.DomUtil.create('td', '', row); span = L.DomUtil.create('span', 'leaflet-routing-icon leaflet-routing-icon-'+icon, td); td.appendChild(span); td = L.DomUtil.create('td', '', row); td.appendChild(document.createTextNode(text)); td = L.DomUtil.create('td', '', row); td.appendChild(document.createTextNode(distance)); return row; } }); module.exports = L.Routing; })(); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],10:[function(require,module,exports){ (function (global){ (function() { 'use strict'; var L = (typeof window !== "undefined" ? window.L : typeof global !== "undefined" ? global.L : null); L.Routing = L.Routing || {}; L.Routing.Line = L.LayerGroup.extend({ includes: L.Mixin.Events, options: { styles: [ {color: 'black', opacity: 0.15, weight: 9}, {color: 'white', opacity: 0.8, weight: 6}, {color: 'red', opacity: 1, weight: 2} ], missingRouteStyles: [ {color: 'black', opacity: 0.15, weight: 7}, {color: 'white', opacity: 0.6, weight: 4}, {color: 'gray', opacity: 0.8, weight: 2, dashArray: '7,12'} ], addWaypoints: true, extendToWaypoints: true, missingRouteTolerance: 10 }, initialize: function(route, options) { L.setOptions(this, options); L.LayerGroup.prototype.initialize.call(this, options); this._route = route; if (this.options.extendToWaypoints) { this._extendToWaypoints(); } this._addSegment( route.coordinates, this.options.styles, this.options.addWaypoints); }, getBounds: function() { return L.latLngBounds(this._route.coordinates); }, _findWaypointIndices: function() { var wps = this._route.inputWaypoints, indices = [], i; for (i = 0; i < wps.length; i++) { indices.push(this._findClosestRoutePoint(wps[i].latLng)); } return indices; }, _findClosestRoutePoint: function(latlng) { var minDist = Number.MAX_VALUE, minIndex, i, d; for (i = this._route.coordinates.length - 1; i >= 0 ; i--) { // TODO: maybe do this in pixel space instead? d = latlng.distanceTo(this._route.coordinates[i]); if (d < minDist) { minIndex = i; minDist = d; } } return minIndex; }, _extendToWaypoints: function() { var wps = this._route.inputWaypoints, wpIndices = this._getWaypointIndices(), i, wpLatLng, routeCoord; for (i = 0; i < wps.length; i++) { wpLatLng = wps[i].latLng; routeCoord = L.latLng(this._route.coordinates[wpIndices[i]]); if (wpLatLng.distanceTo(routeCoord) > this.options.missingRouteTolerance) { this._addSegment([wpLatLng, routeCoord], this.options.missingRouteStyles); } } }, _addSegment: function(coords, styles, mouselistener) { var i, pl; for (i = 0; i < styles.length; i++) { pl = L.polyline(coords, styles[i]); this.addLayer(pl); if (mouselistener) { pl.on('mousedown', this._onLineTouched, this); } } }, _findNearestWpBefore: function(i) { var wpIndices = this._getWaypointIndices(), j = wpIndices.length - 1; while (j >= 0 && wpIndices[j] > i) { j--; } return j; }, _onLineTouched: function(e) { var afterIndex = this._findNearestWpBefore(this._findClosestRoutePoint(e.latlng)); this.fire('linetouched', { afterIndex: afterIndex, latlng: e.latlng }); }, _getWaypointIndices: function() { if (!this._wpIndices) { this._wpIndices = this._route.waypointIndices || this._findWaypointIndices(); } return this._wpIndices; } }); L.Routing.line = function(route, options) { return new L.Routing.Line(route, options); }; module.exports = L.Routing; })(); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],11:[function(require,module,exports){ (function() { 'use strict'; L.Routing = L.Routing || {}; L.Routing.Localization = L.Class.extend({ initialize: function(langs) { this._langs = L.Util.isArray(langs) ? langs : [langs, 'en']; for (var i = 0, l = this._langs.length; i < l; i++) { if (!L.Routing.Localization[this._langs[i]]) { throw new Error('No localization for language "' + this._langs[i] + '".'); } } }, localize: function(keys) { var dict, key, value; keys = L.Util.isArray(keys) ? keys : [keys]; for (var i = 0, l = this._langs.length; i < l; i++) { dict = L.Routing.Localization[this._langs[i]]; for (var j = 0, nKeys = keys.length; dict && j < nKeys; j++) { key = keys[j]; value = dict[key]; dict = value; } if (value) { return value; } } } }); L.Routing.Localization = L.extend(L.Routing.Localization, { 'en': { directions: { N: 'north', NE: 'northeast', E: 'east', SE: 'southeast', S: 'south', SW: 'southwest', W: 'west', NW: 'northwest', SlightRight: 'slight right', Right: 'right', SharpRight: 'sharp right', SlightLeft: 'slight left', Left: 'left', SharpLeft: 'sharp left', Uturn: 'Turn around' }, instructions: { // instruction, postfix if the road is named 'Head': ['Head {dir}', ' on {road}'], 'Continue': ['Continue {dir}'], 'TurnAround': ['Turn around'], 'WaypointReached': ['Waypoint reached'], 'Roundabout': ['Take the {exitStr} exit in the roundabout', ' onto {road}'], 'DestinationReached': ['Destination reached'], 'Fork': ['At the fork, turn {modifier}', ' onto {road}'], 'Merge': ['Merge {modifier}', ' onto {road}'], 'OnRamp': ['Turn {modifier} on the ramp', ' onto {road}'], 'OffRamp': ['Take the ramp on the {modifier}', ' onto {road}'], 'EndOfRoad': ['Turn {modifier} at the end of the road', ' onto {road}'], 'Onto': 'onto {road}' }, formatOrder: function(n) { var i = n % 10 - 1, suffix = ['st', 'nd', 'rd']; return suffix[i] ? n + suffix[i] : n + 'th'; }, ui: { startPlaceholder: 'Start', viaPlaceholder: 'Via {viaNumber}', endPlaceholder: 'End' }, units: { meters: 'm', kilometers: 'km', yards: 'yd', miles: 'mi', hours: 'h', minutes: 'min', seconds: 's' } }, 'de': { directions: { N: 'Norden', NE: 'Nordosten', E: 'Osten', SE: 'Südosten', S: 'Süden', SW: 'Südwesten', W: 'Westen', NW: 'Nordwesten' }, instructions: { // instruction, postfix if the road is named 'Head': ['Richtung {dir}', ' auf {road}'], 'Continue': ['Geradeaus Richtung {dir}', ' auf {road}'], 'SlightRight': ['Leicht rechts abbiegen', ' auf {road}'], 'Right': ['Rechts abbiegen', ' auf {road}'], 'SharpRight': ['Scharf rechts abbiegen', ' auf {road}'], 'TurnAround': ['Wenden'], 'SharpLeft': ['Scharf links abbiegen', ' auf {road}'], 'Left': ['Links abbiegen', ' auf {road}'], 'SlightLeft': ['Leicht links abbiegen', ' auf {road}'], 'WaypointReached': ['Zwischenhalt erreicht'], 'Roundabout': ['Nehmen Sie die {exitStr} Ausfahrt im Kreisverkehr', ' auf {road}'], 'DestinationReached': ['Sie haben ihr Ziel erreicht'], }, formatOrder: function(n) { return n + '.'; }, ui: { startPlaceholder: 'Start', viaPlaceholder: 'Via {viaNumber}', endPlaceholder: 'Ziel' } }, 'sv': { directions: { N: 'norr', NE: 'nordost', E: 'öst', SE: 'sydost', S: 'syd', SW: 'sydväst', W: 'väst', NW: 'nordväst', SlightRight: 'svagt höger', Right: 'höger', SharpRight: 'skarpt höger', SlightLeft: 'svagt vänster', Left: 'vänster', SharpLeft: 'skarpt vänster', Uturn: 'Vänd' }, instructions: { // instruction, postfix if the road is named 'Head': ['Åk åt {dir}', ' till {road}'], 'Continue': ['Fortsätt {dir}'], 'SlightRight': ['Svagt höger', ' till {road}'], 'Right': ['Sväng höger', ' till {road}'], 'SharpRight': ['Skarpt höger', ' till {road}'], 'TurnAround': ['Vänd'], 'SharpLeft': ['Skarpt vänster', ' till {road}'], 'Left': ['Sväng vänster', ' till {road}'], 'SlightLeft': ['Svagt vänster', ' till {road}'], 'WaypointReached': ['Viapunkt nådd'], 'Roundabout': ['Tag {exitStr} avfarten i rondellen', ' till {road}'], 'DestinationReached': ['Framme vid resans mål'], 'Fork': ['Tag av {modifier}', ' till {road}'], 'Merge': ['Anslut {modifier} ', ' till {road}'], 'OnRamp': ['Tag påfarten {modifier}', ' till {road}'], 'OffRamp': ['Tag avfarten {modifier}', ' till {road}'], 'EndOfRoad': ['Sväng {modifier} vid vägens slut', ' till {road}'], 'Onto': 'till {road}' }, formatOrder: function(n) { return ['första', 'andra', 'tredje', 'fjärde', 'femte', 'sjätte', 'sjunde', 'åttonde', 'nionde', 'tionde' /* Can't possibly be more than ten exits, can there? */][n - 1]; }, ui: { startPlaceholder: 'Från', viaPlaceholder: 'Via {viaNumber}', endPlaceholder: 'Till' } }, 'sp': { directions: { N: 'norte', NE: 'noreste', E: 'este', SE: 'sureste', S: 'sur', SW: 'suroeste', W: 'oeste', NW: 'noroeste' }, instructions: { // instruction, postfix if the road is named 'Head': ['Derecho {dir}', ' sobre {road}'], 'Continue': ['Continuar {dir}', ' en {road}'], 'SlightRight': ['Leve giro a la derecha', ' sobre {road}'], 'Right': ['Derecha', ' sobre {road}'], 'SharpRight': ['Giro pronunciado a la derecha', ' sobre {road}'], 'TurnAround': ['Dar vuelta