├── .gitignore ├── README.md ├── bower.json ├── dist ├── leaflet.ajax.js └── leaflet.ajax.min.js ├── example ├── colleges.geojson ├── counties.geojson ├── images │ ├── layers-2x.png │ ├── layers.png │ ├── marker-icon-2x.png │ ├── marker-icon.png │ └── marker-shadow.png ├── index.html ├── leaflet-src.js ├── leaflet.css ├── leaflet.spin.js ├── local.html ├── spin.js └── test.geojson ├── license.md ├── package.json ├── server.js └── src ├── ajax.js ├── index.js └── jsonp.js /.gitignore: -------------------------------------------------------------------------------- 1 | .c9revisions 2 | *~ 3 | node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | leaflet-ajax 2 | =========== 3 | 4 | [![js-semistandard-style](https://img.shields.io/badge/code%20style-semistandard-brightgreen.svg?style=flat-square)](https://github.com/Flet/semistandard) 5 | 6 | 7 | Allows you to call JSON via an Ajax call with a jsonp fallback. 8 | 9 | ```javascript 10 | var geojsonLayer = new L.GeoJSON.AJAX("geojson.json"); 11 | ``` 12 | for jsonp add the option "dataType" and set it to "jsonp" 13 | ``` javascript 14 | var geojsonLayer = L.geoJson.ajax("http:webhost.fake/geojson.jsonp",{dataType:"jsonp"}); 15 | ``` 16 | Note that data starts to download when the layer is created NOT when it’s added to the map in order to get a head start. 17 | 18 | You may pass either a url string or an array of url strings if you want to download multiple things (handy 19 | if your downloading data from an ESRI based thing which will have separate line, point, and poly features). 20 | 21 | As you see you can also use lower case methods without creating new objects 22 | 23 | For weirder jsonp you can set "callbackParam" for if you need to change the name of the callback parameter to something besides "callback", e.g. [Mapquest Nominative Open](http://open.mapquestapi.com/nominatim/) uses "json_callback" instead of "callback". 24 | 25 | If you want to be able to load stuff from the file system (with appropriate custom flags set) set local to true. 26 | 27 | If you want to set headers to the XMLHttpRequest set the 'headers' option equal to an object. 28 | 29 | Gives off three events `data:loading`, `data:progress` and `data:loaded`. 30 | 31 | - `data:loading` fires before we start downloading things, note if the constructor is given a url it won't wait to be added to the map 32 | to start downloading the data, but it does do an async wait so you have time to add a listener to it (and so [leaflet.spin](https://github.com/makinacorpus/Leaflet.Spin) will work with it). 33 | - `data:progress` is called each time a file is downloaded and passes the downloaded geojson as event data. 34 | - `data:loaded` is called when all files have downloaded, this is mainly different from `data:progress` when you are downloading multiple things. 35 | 36 | You can also add a middleware function which is called after you download the data but before you add it to leaflet: 37 | 38 | ```javascript 39 | var geojsonLayer = L.geoJson.ajax("route/to/esri.json",{ 40 | middleware:function(data){ 41 | return esri2geoOrSomething(json); 42 | } 43 | }); 44 | ``` 45 | 46 | addUrl does not clear the current layers but adds to the current one, e.g.: 47 | 48 | ```javascript 49 | var geojsonLayer = L.geoJson.ajax("data.json"); 50 | geojsonLayer.addUrl("data2.json");//we now have 2 layers 51 | geojsonLayer.refresh();//redownload the most recent layer 52 | geojsonLayer.refresh("new1.json");//add a new layer replacing whatever is there 53 | ``` 54 | 55 | last but now least we can refilter layers without re adding them 56 | 57 | ```javascript 58 | var geojsonLayer = L.geoJson.ajax("data.json"); 59 | geojsonLayer.refilter(function(feature){ 60 | return feature.properties.key === values; 61 | }); 62 | ``` 63 | 64 | Behind the scenes are two new classes L.Util.ajax = function (url) for same origin requests and L.Util.jsonp = function (url,options) cross origin ones. Both return promises, which have an additional abort method that will abort the ajax request. 65 | 66 | ```js 67 | L.Util.ajax("url/same/origin.xml").then(function(data){ 68 | doStuff(data); 69 | }); 70 | //or 71 | L.Util.jsonp("http://www.dif.ori/gin").then(function(data){ 72 | doStuff(data); 73 | }); 74 | ``` 75 | 76 | In related news `L.Util.Promise` is now a constructor for a promise, it takes one argument, a resolver function. 77 | 78 | Some of the jsonp code inspired by/taken from [this interesting looking plugin](https://github.com/stefanocudini/leaflet-search) that I have failed to make heads nor tails of (the plugin, not the jsonp code) 79 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leaflet-ajax", 3 | "version": "2.1.0", 4 | "homepage": "https://github.com/calvinmetcalf/leaflet-ajax", 5 | "authors": [ 6 | "Calvin Metcalf " 7 | ], 8 | "description": "AJAX and JSONP in Leaflet", 9 | "main": "dist/leaflet.ajax.js", 10 | "keywords": [ 11 | "leaflet", 12 | "ajax", 13 | "jsonp" 14 | ], 15 | "license": "MIT", 16 | "ignore": [ 17 | "**/.*", 18 | "node_modules", 19 | "bower_components", 20 | "test", 21 | "tests" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /dist/leaflet.ajax.js: -------------------------------------------------------------------------------- 1 | (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 element; its readystatechange event will be fired asynchronously once it is inserted 284 | // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called. 285 | var scriptEl = global.document.createElement('script'); 286 | scriptEl.onreadystatechange = function () { 287 | nextTick(); 288 | 289 | scriptEl.onreadystatechange = null; 290 | scriptEl.parentNode.removeChild(scriptEl); 291 | scriptEl = null; 292 | }; 293 | global.document.documentElement.appendChild(scriptEl); 294 | }; 295 | } else { 296 | scheduleDrain = function () { 297 | setTimeout(nextTick, 0); 298 | }; 299 | } 300 | } 301 | 302 | var draining; 303 | var queue = []; 304 | //named nextTick for less confusing stack traces 305 | function nextTick() { 306 | draining = true; 307 | var i, oldQueue; 308 | var len = queue.length; 309 | while (len) { 310 | oldQueue = queue; 311 | queue = []; 312 | i = -1; 313 | while (++i < len) { 314 | oldQueue[i](); 315 | } 316 | len = queue.length; 317 | } 318 | draining = false; 319 | } 320 | 321 | module.exports = immediate; 322 | function immediate(task) { 323 | if (queue.push(task) === 1 && !draining) { 324 | scheduleDrain(); 325 | } 326 | } 327 | 328 | }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 329 | },{}],3:[function(require,module,exports){ 330 | (function (global){ 331 | 'use strict'; 332 | var jsonp = require('./jsonp'); 333 | var Promise = require('lie'); 334 | 335 | module.exports = function (url, options) { 336 | options = options || {}; 337 | if (options.jsonp) { 338 | return jsonp(url, options); 339 | } 340 | var request; 341 | var cancel; 342 | var out = new Promise(function (resolve, reject) { 343 | cancel = reject; 344 | if (global.XMLHttpRequest === undefined) { 345 | reject('XMLHttpRequest is not supported'); 346 | } 347 | var response; 348 | request = new global.XMLHttpRequest(); 349 | request.open('GET', url); 350 | if (options.headers) { 351 | Object.keys(options.headers).forEach(function (key) { 352 | request.setRequestHeader(key, options.headers[key]); 353 | }); 354 | } 355 | request.onreadystatechange = function () { 356 | if (request.readyState === 4) { 357 | if ((request.status < 400 && options.local) || request.status === 200) { 358 | if (global.JSON) { 359 | response = JSON.parse(request.responseText); 360 | } else { 361 | reject(new Error('JSON is not supported')); 362 | } 363 | resolve(response); 364 | } else { 365 | if (!request.status) { 366 | reject('Attempted cross origin request without CORS enabled'); 367 | } else { 368 | reject(request.statusText); 369 | } 370 | } 371 | } 372 | }; 373 | request.send(); 374 | }); 375 | out.catch(function (reason) { 376 | request.abort(); 377 | return reason; 378 | }); 379 | out.abort = cancel; 380 | return out; 381 | }; 382 | 383 | }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 384 | },{"./jsonp":5,"lie":1}],4:[function(require,module,exports){ 385 | (function (global){ 386 | 'use strict'; 387 | var L = global.L || require('leaflet'); 388 | var Promise = require('lie'); 389 | var ajax = require('./ajax'); 390 | L.GeoJSON.AJAX = L.GeoJSON.extend({ 391 | defaultAJAXparams: { 392 | dataType: 'json', 393 | callbackParam: 'callback', 394 | local: false, 395 | middleware: function (f) { 396 | return f; 397 | } 398 | }, 399 | initialize: function (url, options) { 400 | this.urls = []; 401 | if (url) { 402 | if (typeof url === 'string') { 403 | this.urls.push(url); 404 | } else if (typeof url.pop === 'function') { 405 | this.urls = this.urls.concat(url); 406 | } else { 407 | options = url; 408 | url = undefined; 409 | } 410 | } 411 | var ajaxParams = L.Util.extend({}, this.defaultAJAXparams); 412 | 413 | for (var i in options) { 414 | if (this.defaultAJAXparams.hasOwnProperty(i)) { 415 | ajaxParams[i] = options[i]; 416 | } 417 | } 418 | this.ajaxParams = ajaxParams; 419 | this._layers = {}; 420 | L.Util.setOptions(this, options); 421 | this.on('data:loaded', function () { 422 | if (this.filter) { 423 | this.refilter(this.filter); 424 | } 425 | }, this); 426 | var self = this; 427 | if (this.urls.length > 0) { 428 | new Promise(function (resolve) { 429 | resolve(); 430 | }).then(function () { 431 | self.addUrl(); 432 | }); 433 | } 434 | }, 435 | clearLayers: function () { 436 | this.urls = []; 437 | L.GeoJSON.prototype.clearLayers.call(this); 438 | return this; 439 | }, 440 | addUrl: function (url) { 441 | var self = this; 442 | if (url) { 443 | if (typeof url === 'string') { 444 | self.urls.push(url); 445 | } else if (typeof url.pop === 'function') { 446 | self.urls = self.urls.concat(url); 447 | } 448 | } 449 | var loading = self.urls.length; 450 | var done = 0; 451 | self.fire('data:loading'); 452 | self.urls.forEach(function (url) { 453 | if (self.ajaxParams.dataType.toLowerCase() === 'json') { 454 | ajax(url, self.ajaxParams).then(function (d) { 455 | var data = self.ajaxParams.middleware(d); 456 | self.addData(data); 457 | self.fire('data:progress', data); 458 | }, function (err) { 459 | self.fire('data:progress', { 460 | error: err 461 | }); 462 | }); 463 | } else if (self.ajaxParams.dataType.toLowerCase() === 'jsonp') { 464 | L.Util.jsonp(url, self.ajaxParams).then(function (d) { 465 | var data = self.ajaxParams.middleware(d); 466 | self.addData(data); 467 | self.fire('data:progress', data); 468 | }, function (err) { 469 | self.fire('data:progress', { 470 | error: err 471 | }); 472 | }); 473 | } 474 | }); 475 | self.on('data:progress', function () { 476 | if (++done === loading) { 477 | self.fire('data:loaded'); 478 | } 479 | }); 480 | }, 481 | refresh: function (url) { 482 | url = url || this.urls; 483 | this.clearLayers(); 484 | this.addUrl(url); 485 | }, 486 | refilter: function (func) { 487 | if (typeof func !== 'function') { 488 | this.filter = false; 489 | this.eachLayer(function (a) { 490 | a.setStyle({ 491 | stroke: true, 492 | clickable: true 493 | }); 494 | }); 495 | } else { 496 | this.filter = func; 497 | this.eachLayer(function (a) { 498 | if (func(a.feature)) { 499 | a.setStyle({ 500 | stroke: true, 501 | clickable: true 502 | }); 503 | } else { 504 | a.setStyle({ 505 | stroke: false, 506 | clickable: false 507 | }); 508 | } 509 | }); 510 | } 511 | } 512 | }); 513 | L.Util.Promise = Promise; 514 | L.Util.ajax = ajax; 515 | L.Util.jsonp = require('./jsonp'); 516 | L.geoJson.ajax = function (geojson, options) { 517 | return new L.GeoJSON.AJAX(geojson, options); 518 | }; 519 | 520 | }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 521 | },{"./ajax":3,"./jsonp":5,"leaflet":undefined,"lie":1}],5:[function(require,module,exports){ 522 | (function (global){ 523 | 'use strict'; 524 | var L = global.L || require('leaflet'); 525 | var Promise = require('lie'); 526 | 527 | module.exports = function (url, options) { 528 | options = options || {}; 529 | var head = document.getElementsByTagName('head')[0]; 530 | var scriptNode = L.DomUtil.create('script', '', head); 531 | var cbName, ourl, cbSuffix, cancel; 532 | var out = new Promise(function (resolve, reject) { 533 | cancel = reject; 534 | var cbParam = options.cbParam || 'callback'; 535 | if (options.callbackName) { 536 | cbName = options.callbackName; 537 | } else { 538 | cbSuffix = '_' + ('' + Math.random()).slice(2); 539 | cbName = '_leafletJSONPcallbacks.' + cbSuffix; 540 | } 541 | scriptNode.type = 'text/javascript'; 542 | if (cbSuffix) { 543 | if (!global._leafletJSONPcallbacks) { 544 | global._leafletJSONPcallbacks = { 545 | length: 0 546 | }; 547 | } 548 | global._leafletJSONPcallbacks.length++; 549 | global._leafletJSONPcallbacks[cbSuffix] = function (data) { 550 | head.removeChild(scriptNode); 551 | delete global._leafletJSONPcallbacks[cbSuffix]; 552 | global._leafletJSONPcallbacks.length--; 553 | if (!global._leafletJSONPcallbacks.length) { 554 | delete global._leafletJSONPcallbacks; 555 | } 556 | resolve(data); 557 | }; 558 | } 559 | if (url.indexOf('?') === -1) { 560 | ourl = url + '?' + cbParam + '=' + cbName; 561 | } else { 562 | ourl = url + '&' + cbParam + '=' + cbName; 563 | } 564 | scriptNode.src = ourl; 565 | }).then(null, function (reason) { 566 | head.removeChild(scriptNode); 567 | delete L.Util.ajax.cb[cbSuffix]; 568 | return reason; 569 | }); 570 | out.abort = cancel; 571 | return out; 572 | }; 573 | 574 | }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 575 | },{"leaflet":undefined,"lie":1}]},{},[4]); 576 | -------------------------------------------------------------------------------- /dist/leaflet.ajax.min.js: -------------------------------------------------------------------------------- 1 | !function e(t,n,r){function a(i,s){if(!n[i]){if(!t[i]){var l="function"==typeof require&&require;if(!s&&l)return l(i,!0);if(o)return o(i,!0);var u=new Error("Cannot find module '"+i+"'");throw u.code="MODULE_NOT_FOUND",u}var c=n[i]={exports:{}};t[i][0].call(c.exports,function(e){var n=t[i][1][e];return a(n?n:e)},c,c.exports,e,t,n,r)}return n[i].exports}for(var o="function"==typeof require&&require,i=0;i0&&new r(function(e){e()}).then(function(){i.addUrl()})},clearLayers:function(){return this.urls=[],n.GeoJSON.prototype.clearLayers.call(this),this},addUrl:function(e){var t=this;e&&("string"==typeof e?t.urls.push(e):"function"==typeof e.pop&&(t.urls=t.urls.concat(e)));var r=t.urls.length,o=0;t.fire("data:loading"),t.urls.forEach(function(e){"json"===t.ajaxParams.dataType.toLowerCase()?a(e,t.ajaxParams).then(function(e){var n=t.ajaxParams.middleware(e);t.addData(n),t.fire("data:progress",n)},function(e){t.fire("data:progress",{error:e})}):"jsonp"===t.ajaxParams.dataType.toLowerCase()&&n.Util.jsonp(e,t.ajaxParams).then(function(e){var n=t.ajaxParams.middleware(e);t.addData(n),t.fire("data:progress",n)},function(e){t.fire("data:progress",{error:e})})}),t.on("data:progress",function(){++o===r&&t.fire("data:loaded")})},refresh:function(e){e=e||this.urls,this.clearLayers(),this.addUrl(e)},refilter:function(e){"function"!=typeof e?(this.filter=!1,this.eachLayer(function(e){e.setStyle({stroke:!0,clickable:!0})})):(this.filter=e,this.eachLayer(function(t){e(t.feature)?t.setStyle({stroke:!0,clickable:!0}):t.setStyle({stroke:!1,clickable:!1})}))}}),n.Util.Promise=r,n.Util.ajax=a,n.Util.jsonp=e("./jsonp"),n.geoJson.ajax=function(e,t){return new n.GeoJSON.AJAX(e,t)}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./ajax":3,"./jsonp":5,leaflet:void 0,lie:1}],5:[function(e,t,n){(function(n){"use strict";var r=n.L||e("leaflet"),a=e("lie");t.exports=function(e,t){t=t||{};var o,i,s,l,u=document.getElementsByTagName("head")[0],c=r.DomUtil.create("script","",u),f=new a(function(r,a){l=a;var f=t.cbParam||"callback";t.callbackName?o=t.callbackName:(s="_"+(""+Math.random()).slice(2),o="_leafletJSONPcallbacks."+s),c.type="text/javascript",s&&(n._leafletJSONPcallbacks||(n._leafletJSONPcallbacks={length:0}),n._leafletJSONPcallbacks.length++,n._leafletJSONPcallbacks[s]=function(e){u.removeChild(c),delete n._leafletJSONPcallbacks[s],n._leafletJSONPcallbacks.length--,n._leafletJSONPcallbacks.length||delete n._leafletJSONPcallbacks,r(e)}),i=-1===e.indexOf("?")?e+"?"+f+"="+o:e+"&"+f+"="+o,c.src=i}).then(null,function(e){return u.removeChild(c),delete r.Util.ajax.cb[s],e});return f.abort=l,f}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{leaflet:void 0,lie:1}]},{},[4]); 2 | -------------------------------------------------------------------------------- /example/images/layers-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvinmetcalf/leaflet-ajax/1f7c87600efe3fecf782ffdff9d44c8142441204/example/images/layers-2x.png -------------------------------------------------------------------------------- /example/images/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvinmetcalf/leaflet-ajax/1f7c87600efe3fecf782ffdff9d44c8142441204/example/images/layers.png -------------------------------------------------------------------------------- /example/images/marker-icon-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvinmetcalf/leaflet-ajax/1f7c87600efe3fecf782ffdff9d44c8142441204/example/images/marker-icon-2x.png -------------------------------------------------------------------------------- /example/images/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvinmetcalf/leaflet-ajax/1f7c87600efe3fecf782ffdff9d44c8142441204/example/images/marker-icon.png -------------------------------------------------------------------------------- /example/images/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvinmetcalf/leaflet-ajax/1f7c87600efe3fecf782ffdff9d44c8142441204/example/images/marker-shadow.png -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Leaflet AJAX 26 | 27 | 28 |
29 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /example/leaflet.css: -------------------------------------------------------------------------------- 1 | /* required styles */ 2 | 3 | .leaflet-pane, 4 | .leaflet-tile, 5 | .leaflet-marker-icon, 6 | .leaflet-marker-shadow, 7 | .leaflet-tile-container, 8 | .leaflet-map-pane svg, 9 | .leaflet-map-pane canvas, 10 | .leaflet-zoom-box, 11 | .leaflet-image-layer, 12 | .leaflet-layer { 13 | position: absolute; 14 | left: 0; 15 | top: 0; 16 | } 17 | .leaflet-container { 18 | overflow: hidden; 19 | -ms-touch-action: none; 20 | touch-action: none; 21 | } 22 | .leaflet-tile, 23 | .leaflet-marker-icon, 24 | .leaflet-marker-shadow { 25 | -webkit-user-select: none; 26 | -moz-user-select: none; 27 | user-select: none; 28 | -webkit-user-drag: none; 29 | } 30 | /* Safari renders non-retina tile on retina better with this, but Chrome is worse */ 31 | .leaflet-safari .leaflet-tile { 32 | image-rendering: -webkit-optimize-contrast; 33 | } 34 | /* hack that prevents hw layers "stretching" when loading new tiles */ 35 | .leaflet-safari .leaflet-tile-container { 36 | width: 1600px; 37 | height: 1600px; 38 | -webkit-transform-origin: 0 0; 39 | } 40 | .leaflet-marker-icon, 41 | .leaflet-marker-shadow { 42 | display: block; 43 | } 44 | /* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */ 45 | /* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */ 46 | .leaflet-container .leaflet-overlay-pane svg, 47 | .leaflet-container .leaflet-tile-pane img { 48 | max-width: none !important; 49 | } 50 | /* stupid Android 2 doesn't understand "max-width: none" properly */ 51 | .leaflet-container img.leaflet-image-layer { 52 | max-width: 15000px !important; 53 | } 54 | .leaflet-tile { 55 | filter: inherit; 56 | visibility: hidden; 57 | } 58 | .leaflet-tile-loaded { 59 | visibility: inherit; 60 | } 61 | .leaflet-zoom-box { 62 | width: 0; 63 | height: 0; 64 | -moz-box-sizing: border-box; 65 | box-sizing: border-box; 66 | z-index: 8; 67 | } 68 | /* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ 69 | .leaflet-overlay-pane svg { 70 | -moz-user-select: none; 71 | } 72 | 73 | .leaflet-pane { z-index: 400; } 74 | 75 | .leaflet-tile-pane { z-index: 200; } 76 | .leaflet-overlay-pane { z-index: 400; } 77 | .leaflet-shadow-pane { z-index: 500; } 78 | .leaflet-marker-pane { z-index: 600; } 79 | .leaflet-popup-pane { z-index: 700; } 80 | 81 | .leaflet-map-pane canvas { z-index: 100; } 82 | .leaflet-map-pane svg { z-index: 200; } 83 | 84 | .leaflet-vml-shape { 85 | width: 1px; 86 | height: 1px; 87 | } 88 | .lvml { 89 | behavior: url(#default#VML); 90 | display: inline-block; 91 | position: absolute; 92 | } 93 | 94 | 95 | /* control positioning */ 96 | 97 | .leaflet-control { 98 | position: relative; 99 | z-index: 7; 100 | pointer-events: auto; 101 | } 102 | .leaflet-top, 103 | .leaflet-bottom { 104 | position: absolute; 105 | z-index: 1000; 106 | pointer-events: none; 107 | } 108 | .leaflet-top { 109 | top: 0; 110 | } 111 | .leaflet-right { 112 | right: 0; 113 | } 114 | .leaflet-bottom { 115 | bottom: 0; 116 | } 117 | .leaflet-left { 118 | left: 0; 119 | } 120 | .leaflet-control { 121 | float: left; 122 | clear: both; 123 | } 124 | .leaflet-right .leaflet-control { 125 | float: right; 126 | } 127 | .leaflet-top .leaflet-control { 128 | margin-top: 10px; 129 | } 130 | .leaflet-bottom .leaflet-control { 131 | margin-bottom: 10px; 132 | } 133 | .leaflet-left .leaflet-control { 134 | margin-left: 10px; 135 | } 136 | .leaflet-right .leaflet-control { 137 | margin-right: 10px; 138 | } 139 | 140 | 141 | /* zoom and fade animations */ 142 | 143 | .leaflet-fade-anim .leaflet-tile { 144 | will-change: opacity; 145 | } 146 | .leaflet-fade-anim .leaflet-popup { 147 | opacity: 0; 148 | -webkit-transition: opacity 0.2s linear; 149 | -moz-transition: opacity 0.2s linear; 150 | -o-transition: opacity 0.2s linear; 151 | transition: opacity 0.2s linear; 152 | } 153 | .leaflet-fade-anim .leaflet-map-pane .leaflet-popup { 154 | opacity: 1; 155 | } 156 | .leaflet-zoom-animated { 157 | -webkit-transform-origin: 0 0; 158 | -ms-transform-origin: 0 0; 159 | transform-origin: 0 0; 160 | } 161 | .leaflet-zoom-anim .leaflet-zoom-animated { 162 | will-change: transform; 163 | } 164 | .leaflet-zoom-anim .leaflet-zoom-animated { 165 | -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); 166 | -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); 167 | -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1); 168 | transition: transform 0.25s cubic-bezier(0,0,0.25,1); 169 | } 170 | .leaflet-zoom-anim .leaflet-tile, 171 | .leaflet-pan-anim .leaflet-tile { 172 | -webkit-transition: none; 173 | -moz-transition: none; 174 | -o-transition: none; 175 | transition: none; 176 | } 177 | 178 | .leaflet-zoom-anim .leaflet-zoom-hide { 179 | visibility: hidden; 180 | } 181 | 182 | 183 | /* cursors */ 184 | 185 | .leaflet-interactive { 186 | cursor: pointer; 187 | } 188 | .leaflet-grab { 189 | cursor: -webkit-grab; 190 | cursor: -moz-grab; 191 | } 192 | .leaflet-crosshair, 193 | .leaflet-crosshair .leaflet-interactive { 194 | cursor: crosshair; 195 | } 196 | .leaflet-popup-pane, 197 | .leaflet-control { 198 | cursor: auto; 199 | } 200 | .leaflet-dragging .leaflet-grab, 201 | .leaflet-dragging .leaflet-grab .leaflet-interactive, 202 | .leaflet-dragging .leaflet-marker-draggable { 203 | cursor: move; 204 | cursor: -webkit-grabbing; 205 | cursor: -moz-grabbing; 206 | } 207 | 208 | 209 | /* visual tweaks */ 210 | 211 | .leaflet-container { 212 | background: #ddd; 213 | outline: 0; 214 | } 215 | .leaflet-container a { 216 | color: #0078A8; 217 | } 218 | .leaflet-container a.leaflet-active { 219 | outline: 2px solid orange; 220 | } 221 | .leaflet-zoom-box { 222 | border: 2px dotted #38f; 223 | background: rgba(255,255,255,0.5); 224 | } 225 | 226 | 227 | /* general typography */ 228 | .leaflet-container { 229 | font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; 230 | } 231 | 232 | 233 | /* general toolbar styles */ 234 | 235 | .leaflet-bar { 236 | box-shadow: 0 1px 5px rgba(0,0,0,0.65); 237 | border-radius: 4px; 238 | } 239 | .leaflet-bar a, 240 | .leaflet-bar a:hover { 241 | background-color: #fff; 242 | border-bottom: 1px solid #ccc; 243 | width: 26px; 244 | height: 26px; 245 | line-height: 26px; 246 | display: block; 247 | text-align: center; 248 | text-decoration: none; 249 | color: black; 250 | } 251 | .leaflet-bar a, 252 | .leaflet-control-layers-toggle { 253 | background-position: 50% 50%; 254 | background-repeat: no-repeat; 255 | display: block; 256 | } 257 | .leaflet-bar a:hover { 258 | background-color: #f4f4f4; 259 | } 260 | .leaflet-bar a:first-child { 261 | border-top-left-radius: 4px; 262 | border-top-right-radius: 4px; 263 | } 264 | .leaflet-bar a:last-child { 265 | border-bottom-left-radius: 4px; 266 | border-bottom-right-radius: 4px; 267 | border-bottom: none; 268 | } 269 | .leaflet-bar a.leaflet-disabled { 270 | cursor: default; 271 | background-color: #f4f4f4; 272 | color: #bbb; 273 | } 274 | 275 | .leaflet-touch .leaflet-bar a { 276 | width: 30px; 277 | height: 30px; 278 | line-height: 30px; 279 | } 280 | 281 | 282 | /* zoom control */ 283 | 284 | .leaflet-control-zoom-in, 285 | .leaflet-control-zoom-out { 286 | font: bold 18px 'Lucida Console', Monaco, monospace; 287 | text-indent: 1px; 288 | } 289 | .leaflet-control-zoom-out { 290 | font-size: 20px; 291 | } 292 | 293 | .leaflet-touch .leaflet-control-zoom-in { 294 | font-size: 22px; 295 | } 296 | .leaflet-touch .leaflet-control-zoom-out { 297 | font-size: 24px; 298 | } 299 | 300 | 301 | /* layers control */ 302 | 303 | .leaflet-control-layers { 304 | box-shadow: 0 1px 5px rgba(0,0,0,0.4); 305 | background: #fff; 306 | border-radius: 5px; 307 | } 308 | .leaflet-control-layers-toggle { 309 | background-image: url(images/layers.png); 310 | width: 36px; 311 | height: 36px; 312 | } 313 | .leaflet-retina .leaflet-control-layers-toggle { 314 | background-image: url(images/layers-2x.png); 315 | background-size: 26px 26px; 316 | } 317 | .leaflet-touch .leaflet-control-layers-toggle { 318 | width: 44px; 319 | height: 44px; 320 | } 321 | .leaflet-control-layers .leaflet-control-layers-list, 322 | .leaflet-control-layers-expanded .leaflet-control-layers-toggle { 323 | display: none; 324 | } 325 | .leaflet-control-layers-expanded .leaflet-control-layers-list { 326 | display: block; 327 | position: relative; 328 | } 329 | .leaflet-control-layers-expanded { 330 | padding: 6px 10px 6px 6px; 331 | color: #333; 332 | background: #fff; 333 | } 334 | .leaflet-control-layers-scrollbar { 335 | overflow-y: scroll; 336 | padding-right: 5px; 337 | } 338 | .leaflet-control-layers-selector { 339 | margin-top: 2px; 340 | position: relative; 341 | top: 1px; 342 | } 343 | .leaflet-control-layers label { 344 | display: block; 345 | } 346 | .leaflet-control-layers-separator { 347 | height: 0; 348 | border-top: 1px solid #ddd; 349 | margin: 5px -10px 5px -6px; 350 | } 351 | 352 | 353 | /* attribution and scale controls */ 354 | 355 | .leaflet-container .leaflet-control-attribution { 356 | background: #fff; 357 | background: rgba(255, 255, 255, 0.7); 358 | margin: 0; 359 | } 360 | .leaflet-control-attribution, 361 | .leaflet-control-scale-line { 362 | padding: 0 5px; 363 | color: #333; 364 | } 365 | .leaflet-control-attribution a { 366 | text-decoration: none; 367 | } 368 | .leaflet-control-attribution a:hover { 369 | text-decoration: underline; 370 | } 371 | .leaflet-container .leaflet-control-attribution, 372 | .leaflet-container .leaflet-control-scale { 373 | font-size: 11px; 374 | } 375 | .leaflet-left .leaflet-control-scale { 376 | margin-left: 5px; 377 | } 378 | .leaflet-bottom .leaflet-control-scale { 379 | margin-bottom: 5px; 380 | } 381 | .leaflet-control-scale-line { 382 | border: 2px solid #777; 383 | border-top: none; 384 | line-height: 1.1; 385 | padding: 2px 5px 1px; 386 | font-size: 11px; 387 | white-space: nowrap; 388 | overflow: hidden; 389 | -moz-box-sizing: content-box; 390 | box-sizing: content-box; 391 | 392 | background: #fff; 393 | background: rgba(255, 255, 255, 0.5); 394 | } 395 | .leaflet-control-scale-line:not(:first-child) { 396 | border-top: 2px solid #777; 397 | border-bottom: none; 398 | margin-top: -2px; 399 | } 400 | .leaflet-control-scale-line:not(:first-child):not(:last-child) { 401 | border-bottom: 2px solid #777; 402 | } 403 | 404 | .leaflet-touch .leaflet-control-attribution, 405 | .leaflet-touch .leaflet-control-layers, 406 | .leaflet-touch .leaflet-bar { 407 | box-shadow: none; 408 | } 409 | .leaflet-touch .leaflet-control-layers, 410 | .leaflet-touch .leaflet-bar { 411 | border: 2px solid rgba(0,0,0,0.2); 412 | background-clip: padding-box; 413 | } 414 | 415 | 416 | /* popup */ 417 | 418 | .leaflet-popup { 419 | position: absolute; 420 | text-align: center; 421 | } 422 | .leaflet-popup-content-wrapper { 423 | padding: 1px; 424 | text-align: left; 425 | border-radius: 12px; 426 | } 427 | .leaflet-popup-content { 428 | margin: 13px 19px; 429 | line-height: 1.4; 430 | } 431 | .leaflet-popup-content p { 432 | margin: 18px 0; 433 | } 434 | .leaflet-popup-tip-container { 435 | margin: 0 auto; 436 | width: 40px; 437 | height: 20px; 438 | position: relative; 439 | overflow: hidden; 440 | } 441 | .leaflet-popup-tip { 442 | width: 17px; 443 | height: 17px; 444 | padding: 1px; 445 | 446 | margin: -10px auto 0; 447 | 448 | -webkit-transform: rotate(45deg); 449 | -moz-transform: rotate(45deg); 450 | -ms-transform: rotate(45deg); 451 | -o-transform: rotate(45deg); 452 | transform: rotate(45deg); 453 | } 454 | .leaflet-popup-content-wrapper, 455 | .leaflet-popup-tip { 456 | background: white; 457 | color: #333; 458 | box-shadow: 0 3px 14px rgba(0,0,0,0.4); 459 | } 460 | .leaflet-container a.leaflet-popup-close-button { 461 | position: absolute; 462 | top: 0; 463 | right: 0; 464 | padding: 4px 4px 0 0; 465 | border: none; 466 | text-align: center; 467 | width: 18px; 468 | height: 14px; 469 | font: 16px/14px Tahoma, Verdana, sans-serif; 470 | color: #c3c3c3; 471 | text-decoration: none; 472 | font-weight: bold; 473 | background: transparent; 474 | } 475 | .leaflet-container a.leaflet-popup-close-button:hover { 476 | color: #999; 477 | } 478 | .leaflet-popup-scrolled { 479 | overflow: auto; 480 | border-bottom: 1px solid #ddd; 481 | border-top: 1px solid #ddd; 482 | } 483 | 484 | .leaflet-oldie .leaflet-popup-content-wrapper { 485 | zoom: 1; 486 | } 487 | .leaflet-oldie .leaflet-popup-tip { 488 | width: 24px; 489 | margin: 0 auto; 490 | 491 | -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; 492 | filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); 493 | } 494 | .leaflet-oldie .leaflet-popup-tip-container { 495 | margin-top: -1px; 496 | } 497 | 498 | .leaflet-oldie .leaflet-control-zoom, 499 | .leaflet-oldie .leaflet-control-layers, 500 | .leaflet-oldie .leaflet-popup-content-wrapper, 501 | .leaflet-oldie .leaflet-popup-tip { 502 | border: 1px solid #999; 503 | } 504 | 505 | 506 | /* div icon */ 507 | 508 | .leaflet-div-icon { 509 | background: #fff; 510 | border: 1px solid #666; 511 | } 512 | -------------------------------------------------------------------------------- /example/leaflet.spin.js: -------------------------------------------------------------------------------- 1 | L.SpinMapMixin = { 2 | spin: function (state, options) { 3 | if (!!state) { 4 | // start spinning ! 5 | if (!this._spinner) { 6 | this._spinner = new Spinner(options).spin(this._container); 7 | this._spinning = 0; 8 | } 9 | this._spinning++; 10 | } 11 | else { 12 | this._spinning--; 13 | if (this._spinning <= 0) { 14 | // end spinning ! 15 | if (this._spinner) { 16 | this._spinner.stop(); 17 | this._spinner = null; 18 | } 19 | } 20 | } 21 | } 22 | }; 23 | 24 | L.Map.include(L.SpinMapMixin); 25 | 26 | L.Map.addInitHook(function () { 27 | this.on('layeradd', function (e) { 28 | // If added layer is currently loading, spin ! 29 | if (e.layer.loading) this.spin(true); 30 | if (typeof e.layer.on != 'function') return; 31 | e.layer.on('data:loading', function () { this.spin(true); }, this); 32 | e.layer.on('data:loaded', function () { this.spin(false); }, this); 33 | }, this); 34 | this.on('layerremove', function (e) { 35 | // Clean-up 36 | if (e.layer.loading) this.spin(false); 37 | if (typeof e.layer.on != 'function') return; 38 | e.layer.off('data:loaded'); 39 | e.layer.off('data:loading'); 40 | }, this); 41 | }); 42 | -------------------------------------------------------------------------------- /example/local.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Leaflet AJAX 23 | 24 | 25 |
26 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /example/spin.js: -------------------------------------------------------------------------------- 1 | (function(t,e){if(typeof exports=="object")module.exports=e();else if(typeof define=="function"&&define.amd)define(e);else t.Spinner=e()})(this,function(){"use strict";var t=["webkit","Moz","ms","O"],e={},i;function o(t,e){var i=document.createElement(t||"div"),o;for(o in e)i[o]=e[o];return i}function n(t){for(var e=1,i=arguments.length;e>1):parseInt(n.left,10)+s)+"px",top:(n.top=="auto"?l.y-a.y+(t.offsetHeight>>1):parseInt(n.top,10)+s)+"px"})}r.setAttribute("role","progressbar");e.lines(r,e.opts);if(!i){var d=0,p=(n.lines-1)*(1-n.direction)/2,c,h=n.fps,m=h/n.speed,y=(1-n.opacity)/(m*n.trail/100),g=m/n.lines;(function v(){d++;for(var t=0;t>1)+"px"})}for(;r',e)}r.addRule(".spin-vml","behavior:url(#default#VML)");c.prototype.lines=function(e,i){var o=i.length+i.width,r=2*o;function s(){return f(t("group",{coordsize:r+" "+r,coordorigin:-o+" "+-o}),{width:r,height:r})}var a=-(i.width+i.length)*2+"px",l=f(s(),{position:"absolute",top:a,left:a}),u;function p(e,r,a){n(l,n(f(s(),{rotation:360/i.lines*e+"deg",left:~~r}),n(f(t("roundrect",{arcsize:i.corners}),{width:o,height:i.width,left:i.radius,top:-i.width>>1,filter:a}),t("fill",{color:d(i.color,e),opacity:i.opacity}),t("stroke",{opacity:0}))))}if(i.shadow)for(u=1;u<=i.lines;u++)p(u,-2,"progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)");for(u=1;u<=i.lines;u++)p(u);return n(e,l)};c.prototype.opacity=function(t,e,i,o){var n=t.firstChild;o=o.shadow&&o.lines||0;if(n&&e+o dist/leaflet.ajax.js && uglifyjs -mc < dist/leaflet.ajax.js > dist/leaflet.ajax.min.js" 12 | }, 13 | "devDependencies": { 14 | "browserify": "^11.0.1", 15 | "express": "^4.13.3", 16 | "semistandard": "^9.1.0", 17 | "uglify-js": "^2.4.24" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git://github.com/calvinmetcalf/leaflet-ajax.git" 22 | }, 23 | "keywords": [ 24 | "leaflet", 25 | "ajax", 26 | "geojson" 27 | ], 28 | "author": "Calvin Metcalf", 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/calvinmetcalf/leaflet-ajax/issues" 32 | }, 33 | "dependencies": { 34 | "lie": "^3.0.1" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var express = require('express'); 3 | var app = express(); 4 | var path = require('path'); 5 | var fs = require('fs'); 6 | var counties = JSON.parse(fs.readFileSync(path.join(__dirname, 'example', 'counties.geojson'), {encoding: 'utf8'})); 7 | 8 | app.get('/example/counties.jsonp', function (req, res){ 9 | res.jsonp(counties); 10 | }); 11 | app.use('/example', express.static(path.join(__dirname, 'example'))); 12 | app.use('/dist', express.static(path.join(__dirname, 'dist'))); 13 | app.listen(3000); 14 | -------------------------------------------------------------------------------- /src/ajax.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var jsonp = require('./jsonp'); 3 | var Promise = require('lie'); 4 | 5 | module.exports = function (url, options) { 6 | options = options || {}; 7 | if (options.jsonp) { 8 | return jsonp(url, options); 9 | } 10 | var request; 11 | var cancel; 12 | var out = new Promise(function (resolve, reject) { 13 | cancel = reject; 14 | if (global.XMLHttpRequest === undefined) { 15 | reject('XMLHttpRequest is not supported'); 16 | } 17 | var response; 18 | request = new global.XMLHttpRequest(); 19 | request.open('GET', url); 20 | if (options.headers) { 21 | Object.keys(options.headers).forEach(function (key) { 22 | request.setRequestHeader(key, options.headers[key]); 23 | }); 24 | } 25 | request.onreadystatechange = function () { 26 | if (request.readyState === 4) { 27 | if ((request.status < 400 && options.local) || request.status === 200) { 28 | if (global.JSON) { 29 | response = JSON.parse(request.responseText); 30 | } else { 31 | reject(new Error('JSON is not supported')); 32 | } 33 | resolve(response); 34 | } else { 35 | if (!request.status) { 36 | reject('Attempted cross origin request without CORS enabled'); 37 | } else { 38 | reject(request.statusText); 39 | } 40 | } 41 | } 42 | }; 43 | request.send(); 44 | }); 45 | out.catch(function (reason) { 46 | request.abort(); 47 | return reason; 48 | }); 49 | out.abort = cancel; 50 | return out; 51 | }; 52 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var L = global.L || require('leaflet'); 3 | var Promise = require('lie'); 4 | var ajax = require('./ajax'); 5 | L.GeoJSON.AJAX = L.GeoJSON.extend({ 6 | defaultAJAXparams: { 7 | dataType: 'json', 8 | callbackParam: 'callback', 9 | local: false, 10 | middleware: function (f) { 11 | return f; 12 | } 13 | }, 14 | initialize: function (url, options) { 15 | this.urls = []; 16 | if (url) { 17 | if (typeof url === 'string') { 18 | this.urls.push(url); 19 | } else if (typeof url.pop === 'function') { 20 | this.urls = this.urls.concat(url); 21 | } else { 22 | options = url; 23 | url = undefined; 24 | } 25 | } 26 | var ajaxParams = L.Util.extend({}, this.defaultAJAXparams); 27 | 28 | for (var i in options) { 29 | if (this.defaultAJAXparams.hasOwnProperty(i)) { 30 | ajaxParams[i] = options[i]; 31 | } 32 | } 33 | this.ajaxParams = ajaxParams; 34 | this._layers = {}; 35 | L.Util.setOptions(this, options); 36 | this.on('data:loaded', function () { 37 | if (this.filter) { 38 | this.refilter(this.filter); 39 | } 40 | }, this); 41 | var self = this; 42 | if (this.urls.length > 0) { 43 | new Promise(function (resolve) { 44 | resolve(); 45 | }).then(function () { 46 | self.addUrl(); 47 | }); 48 | } 49 | }, 50 | clearLayers: function () { 51 | this.urls = []; 52 | L.GeoJSON.prototype.clearLayers.call(this); 53 | return this; 54 | }, 55 | addUrl: function (url) { 56 | var self = this; 57 | if (url) { 58 | if (typeof url === 'string') { 59 | self.urls.push(url); 60 | } else if (typeof url.pop === 'function') { 61 | self.urls = self.urls.concat(url); 62 | } 63 | } 64 | var loading = self.urls.length; 65 | var done = 0; 66 | self.fire('data:loading'); 67 | self.urls.forEach(function (url) { 68 | if (self.ajaxParams.dataType.toLowerCase() === 'json') { 69 | ajax(url, self.ajaxParams).then(function (d) { 70 | var data = self.ajaxParams.middleware(d); 71 | self.addData(data); 72 | self.fire('data:progress', data); 73 | }, function (err) { 74 | self.fire('data:progress', { 75 | error: err 76 | }); 77 | }); 78 | } else if (self.ajaxParams.dataType.toLowerCase() === 'jsonp') { 79 | L.Util.jsonp(url, self.ajaxParams).then(function (d) { 80 | var data = self.ajaxParams.middleware(d); 81 | self.addData(data); 82 | self.fire('data:progress', data); 83 | }, function (err) { 84 | self.fire('data:progress', { 85 | error: err 86 | }); 87 | }); 88 | } 89 | }); 90 | self.on('data:progress', function () { 91 | if (++done === loading) { 92 | self.fire('data:loaded'); 93 | } 94 | }); 95 | }, 96 | refresh: function (url) { 97 | url = url || this.urls; 98 | this.clearLayers(); 99 | this.addUrl(url); 100 | }, 101 | refilter: function (func) { 102 | if (typeof func !== 'function') { 103 | this.filter = false; 104 | this.eachLayer(function (a) { 105 | a.setStyle({ 106 | stroke: true, 107 | clickable: true 108 | }); 109 | }); 110 | } else { 111 | this.filter = func; 112 | this.eachLayer(function (a) { 113 | if (func(a.feature)) { 114 | a.setStyle({ 115 | stroke: true, 116 | clickable: true 117 | }); 118 | } else { 119 | a.setStyle({ 120 | stroke: false, 121 | clickable: false 122 | }); 123 | } 124 | }); 125 | } 126 | } 127 | }); 128 | L.Util.Promise = Promise; 129 | L.Util.ajax = ajax; 130 | L.Util.jsonp = require('./jsonp'); 131 | L.geoJson.ajax = function (geojson, options) { 132 | return new L.GeoJSON.AJAX(geojson, options); 133 | }; 134 | -------------------------------------------------------------------------------- /src/jsonp.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var L = global.L || require('leaflet'); 3 | var Promise = require('lie'); 4 | 5 | module.exports = function (url, options) { 6 | options = options || {}; 7 | var head = document.getElementsByTagName('head')[0]; 8 | var scriptNode = L.DomUtil.create('script', '', head); 9 | var cbName, ourl, cbSuffix, cancel; 10 | var out = new Promise(function (resolve, reject) { 11 | cancel = reject; 12 | var cbParam = options.cbParam || 'callback'; 13 | if (options.callbackName) { 14 | cbName = options.callbackName; 15 | } else { 16 | cbSuffix = '_' + ('' + Math.random()).slice(2); 17 | cbName = '_leafletJSONPcallbacks.' + cbSuffix; 18 | } 19 | scriptNode.type = 'text/javascript'; 20 | if (cbSuffix) { 21 | if (!global._leafletJSONPcallbacks) { 22 | global._leafletJSONPcallbacks = { 23 | length: 0 24 | }; 25 | } 26 | global._leafletJSONPcallbacks.length++; 27 | global._leafletJSONPcallbacks[cbSuffix] = function (data) { 28 | head.removeChild(scriptNode); 29 | delete global._leafletJSONPcallbacks[cbSuffix]; 30 | global._leafletJSONPcallbacks.length--; 31 | if (!global._leafletJSONPcallbacks.length) { 32 | delete global._leafletJSONPcallbacks; 33 | } 34 | resolve(data); 35 | }; 36 | } 37 | if (url.indexOf('?') === -1) { 38 | ourl = url + '?' + cbParam + '=' + cbName; 39 | } else { 40 | ourl = url + '&' + cbParam + '=' + cbName; 41 | } 42 | scriptNode.src = ourl; 43 | }).then(null, function (reason) { 44 | head.removeChild(scriptNode); 45 | delete L.Util.ajax.cb[cbSuffix]; 46 | return reason; 47 | }); 48 | out.abort = cancel; 49 | return out; 50 | }; 51 | --------------------------------------------------------------------------------