diff --git a/assets/leaflet-extra-markers/.gitignore b/assets/leaflet-extra-markers/.gitignore new file mode 100644 index 0000000..7d5d602 --- /dev/null +++ b/assets/leaflet-extra-markers/.gitignore @@ -0,0 +1,60 @@ +### General Ignores ### + +.idea +*~ + + + + + +### Operating System Ignores ### + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + + +# OSX + +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + + +###Node### + +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Dependency directory +# Commenting this out is preferred by some people, see +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- +node_modules + +# Users Environment Variables +.lock-wscript \ No newline at end of file diff --git a/assets/leaflet-extra-markers/Gruntfile.js b/assets/leaflet-extra-markers/Gruntfile.js new file mode 100644 index 0000000..a94f23c --- /dev/null +++ b/assets/leaflet-extra-markers/Gruntfile.js @@ -0,0 +1,64 @@ +module.exports = function(grunt) { + + 'use strict'; + + // require it at the top and pass in the grunt instance + require('time-grunt')(grunt); + + /***************************************************** + Grunt Init Config: + load each task config into grunt via require + *****************************************************/ + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + + // Project Config + project: require('./build/grunt-config/project'), // Contains paths and banner + less: require('./build/grunt-config/less'), // Config to compile and autoprefix less files + uglify: require('./build/grunt-config/uglify'), + jshint: require('./build/grunt-config/jshint'), // Lint Javascript + + }); + + /***************************************************** + Dev Tasks - Compile and check files withing the /src/assets/ directory + *****************************************************/ + + // Default grunt task compiles & checks dev files + grunt.registerTask('default', [], function(){ + grunt.loadNpmTasks('grunt-contrib-less'); + grunt.task.run('less:dev','js-dev'); + }); + + // Javascript Dev Build - Checks for Errors in Javascript + grunt.registerTask('js-dev', [], function(){ + grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.task.run('jshint:all', 'uglify:dev'); + }); + + /***************************************************** + Dist Tasks + *****************************************************/ + + // 'grunt build' global build command for both less and js files + grunt.registerTask('build', 'Compiles all files for live environment', function() { + grunt.loadNpmTasks('grunt-contrib-less'); + grunt.task.run('less:build', 'js-build'); + }); + + // 'grunt js-build' compiles only javascript + grunt.registerTask('js-build', [], function(){ + grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.task.run('jshint:all', 'uglify:build'); + }); + + + // 'grunt less-build' compiles only less + grunt.registerTask('less-build', [], function(){ + grunt.loadNpmTasks('grunt-contrib-less'); + grunt.task.run('less:build'); + }); + +}; \ No newline at end of file diff --git a/assets/leaflet-extra-markers/LICENSE b/assets/leaflet-extra-markers/LICENSE new file mode 100644 index 0000000..c45fecd --- /dev/null +++ b/assets/leaflet-extra-markers/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 coryasilva + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/assets/leaflet-extra-markers/README.md b/assets/leaflet-extra-markers/README.md new file mode 100644 index 0000000..20ae4c4 --- /dev/null +++ b/assets/leaflet-extra-markers/README.md @@ -0,0 +1,69 @@ +# Leaflet.extra-markers plugin v1.0.4 +Big Thanks to lvoogdt of Leaflet.awesome-markers + +![ExtraMarkers screenshot](https://raw.github.com/coryasilva/Leaflet.ExtraMarkers/master/screenshot.png "Screenshot of ExtraMarkers") +### Demo + +## Icons +Version 1.0 of Leaflet.extra-markers is designed for: +- [Bootstrap 3 icons](http://twitter.github.com/bootstrap/) + - [Getting Started Guide](http://getbootstrap.com/getting-started/) +- [Font Awesome 4.0](http://fortawesome.github.com/Font-Awesome/) + - [Getting Started Guide](http://fortawesome.github.io/Font-Awesome/get-started/) +- [Semantic UI 0.9.8 icons](http://semantic-ui.com/) +- [Ion Icons 2.0.1](http://ionicons.com/) +- Leaflet 0.5-Latest + +## Using the plugin + +##### 1. Requirements ##### + +Follow the [getting started guide](#icons) for the desired font library and make sure its included in your project. + +##### 2. Installing Leaflet.extra-markers ##### + +Next, copy the `dist/img` directory, `/dist/css/leaflet.extra-markers.min.css`, and `/dist/js/leaflet.extra-markers.min.js` to your project and include them: + +````xml + +```` +or +````less +@import 'bower_components/src/assets/less/Leaflet.extra-markers.less +```` +and +````xml + +```` + +##### 3. Creating a Marker ##### + +Now use the plugin to create a marker like this: +````js + // Creates a red marker with the coffee icon + var redMarker = L.ExtraMarkers.icon({ + icon: 'fa-coffee', + markerColor: 'red', + shape: 'square', + prefix: 'fa' + }); + + L.marker([51.941196,4.512291], {icon: redMarker,}).addTo(map); +```` +--- + +### Properties + +| Property | Description | Default Value | Possible values | +| --------------- | ------------------------------------------- | ------------- | ---------------------------------------------------- | +| extraClasses | Additional classes in the created `` tag | `''` | `fa-rotate90 myclass`; space delimited classes to add | +| icon | Name of the icon **with** prefix | `''` | `fa-coffee` (see icon library's documentation) | +| iconColor | Color of the icon | `'white'` | `'white'`, `'black'` or css code (hex, rgba etc) | +| innerHTML | Custom HTML code | `''` | ``, images, or other HTML; a truthy assignment will override the default html icon creation behavior | +| markerColor | Color of the marker (css class) | `'blue'` | `'red'`, `'orange-dark'`, `'orange'`, `'yellow'`, `'blue-dark'`, `'cyan'`, `'purple'`, `'violet'`, `'pink'`, `'green-dark'`, `'green'`, `'green-light'`, `'black'`, or `'white'` | +| number | Instead of an icon, define a plain text | `''` | `'1'` or `'A'`, must set `icon: 'fa-number'` | +| prefix | The icon library's base class | `'glyphicon'` | `fa` (see icon library's documentation) | +| shape | Shape of the marker (css class) | `'circle'` | `'circle'`, `'square'`, `'star'`, or `'penta'` | + +## License +- Leaflet.ExtraMarkers and colored markers are licensed under the MIT License - http://opensource.org/licenses/mit-license.html. diff --git a/assets/leaflet-extra-markers/bower.json b/assets/leaflet-extra-markers/bower.json new file mode 100644 index 0000000..2ec9568 --- /dev/null +++ b/assets/leaflet-extra-markers/bower.json @@ -0,0 +1,30 @@ +{ + "name": "Leaflet.extra-markers", + "version": "1.0.6", + "homepage": "https://github.com/coryasilva/Leaflet.ExtraMarkers", + "authors": [ + "Cory Silva" + ], + "description": "Custom Markers for Leaflet JS based on Awesome Markers", + "main": [ + "src/assets/css/leaflet.extra-markers.css", + "src/assets/less/leaflet.extra-markers.less", + "src/assets/js/leaflet.extra-markers.js" + ], + "license": "MIT", + "ignore": [ + "gh-pages", + ".bowerrc", + ".gitignore", + ".jshintignore", + ".jshintrc", + "bower.json", + "gruntfile.js", + "package.json", + "README.md" + ], + "dependencies": { + }, + "devDependencies": { + } +} diff --git a/assets/leaflet-extra-markers/css/leaflet.extra-markers.min.css b/assets/leaflet-extra-markers/css/leaflet.extra-markers.min.css new file mode 100644 index 0000000..128c285 --- /dev/null +++ b/assets/leaflet-extra-markers/css/leaflet.extra-markers.min.css @@ -0,0 +1 @@ +.extra-marker{background:url("../img/markers_default.png") no-repeat 0 0;width:35px;height:46px;position:absolute;left:0;top:0;display:block;text-align:center}.extra-marker-shadow{background:url("../img/markers_shadow.png") no-repeat 0 0;width:36px;height:16px}@media (min--moz-device-pixel-ratio:1.5),(-webkit-min-device-pixel-ratio:1.5),(min-device-pixel-ratio:1.5),(min-resolution:1.5dppx){.extra-marker{background-image:url("../img/markers_default@2x.png");background-size:540px 184px}.extra-marker-shadow{background-image:url("../img/markers_shadow@2x.png");background-size:35px 16px}}.extra-marker i{color:#fff;margin-top:10px;display:inline-block;font-size:14px}.extra-marker i.icon{margin-right:0;opacity:1}.extra-marker-circle-red{background-position:0 0}.extra-marker-circle-orange-dark{background-position:-36px 0}.extra-marker-circle-orange{background-position:-72px 0}.extra-marker-circle-yellow{background-position:-108px 0}.extra-marker-circle-blue-dark{background-position:-144px 0}.extra-marker-circle-blue{background-position:-180px 0}.extra-marker-circle-cyan{background-position:-216px 0}.extra-marker-circle-purple{background-position:-252px 0}.extra-marker-circle-violet{background-position:-288px 0}.extra-marker-circle-pink{background-position:-324px 0}.extra-marker-circle-green-dark{background-position:-360px 0}.extra-marker-circle-green{background-position:-396px 0}.extra-marker-circle-green-light{background-position:-432px 0}.extra-marker-circle-black{background-position:-468px 0}.extra-marker-circle-white{background-position:-504px 0}.extra-marker-square-red{background-position:0 -46px}.extra-marker-square-orange-dark{background-position:-36px -46px}.extra-marker-square-orange{background-position:-72px -46px}.extra-marker-square-yellow{background-position:-108px -46px}.extra-marker-square-blue-dark{background-position:-144px -46px}.extra-marker-square-blue{background-position:-180px -46px}.extra-marker-square-cyan{background-position:-216px -46px}.extra-marker-square-purple{background-position:-252px -46px}.extra-marker-square-violet{background-position:-288px -46px}.extra-marker-square-pink{background-position:-324px -46px}.extra-marker-square-green-dark{background-position:-360px -46px}.extra-marker-square-green{background-position:-396px -46px}.extra-marker-square-green-light{background-position:-432px -46px}.extra-marker-square-black{background-position:-468px -46px}.extra-marker-square-white{background-position:-504px -46px}.extra-marker-star-red{background-position:0 -92px}.extra-marker-star-orange-dark{background-position:-36px -92px}.extra-marker-star-orange{background-position:-72px -92px}.extra-marker-star-yellow{background-position:-108px -92px}.extra-marker-star-blue-dark{background-position:-144px -92px}.extra-marker-star-blue{background-position:-180px -92px}.extra-marker-star-cyan{background-position:-216px -92px}.extra-marker-star-purple{background-position:-252px -92px}.extra-marker-star-violet{background-position:-288px -92px}.extra-marker-star-pink{background-position:-324px -92px}.extra-marker-star-green-dark{background-position:-360px -92px}.extra-marker-star-green{background-position:-396px -92px}.extra-marker-star-green-light{background-position:-432px -92px}.extra-marker-star-black{background-position:-468px -92px}.extra-marker-star-white{background-position:-504px -92px}.extra-marker-penta-red{background-position:0 -138px}.extra-marker-penta-orange-dark{background-position:-36px -138px}.extra-marker-penta-orange{background-position:-72px -138px}.extra-marker-penta-yellow{background-position:-108px -138px}.extra-marker-penta-blue-dark{background-position:-144px -138px}.extra-marker-penta-blue{background-position:-180px -138px}.extra-marker-penta-cyan{background-position:-216px -138px}.extra-marker-penta-purple{background-position:-252px -138px}.extra-marker-penta-violet{background-position:-288px -138px}.extra-marker-penta-pink{background-position:-324px -138px}.extra-marker-penta-green-dark{background-position:-360px -138px}.extra-marker-penta-green{background-position:-396px -138px}.extra-marker-penta-green-light{background-position:-432px -138px}.extra-marker-penta-black{background-position:-468px -138px}.extra-marker-penta-white{background-position:-504px -138px}.extra-marker .fa-number:before{content:attr(number)} \ No newline at end of file diff --git a/assets/leaflet-extra-markers/img/markers_default.png b/assets/leaflet-extra-markers/img/markers_default.png new file mode 100644 index 0000000..2c81d15 Binary files /dev/null and b/assets/leaflet-extra-markers/img/markers_default.png differ diff --git a/assets/leaflet-extra-markers/img/markers_default@2x.png b/assets/leaflet-extra-markers/img/markers_default@2x.png new file mode 100644 index 0000000..35ff61b Binary files /dev/null and b/assets/leaflet-extra-markers/img/markers_default@2x.png differ diff --git a/assets/leaflet-extra-markers/img/markers_shadow.png b/assets/leaflet-extra-markers/img/markers_shadow.png new file mode 100644 index 0000000..33cf955 Binary files /dev/null and b/assets/leaflet-extra-markers/img/markers_shadow.png differ diff --git a/assets/leaflet-extra-markers/img/markers_shadow@2x.png b/assets/leaflet-extra-markers/img/markers_shadow@2x.png new file mode 100644 index 0000000..1116503 Binary files /dev/null and b/assets/leaflet-extra-markers/img/markers_shadow@2x.png differ diff --git a/assets/leaflet-extra-markers/js/leaflet.extra-markers.min.js b/assets/leaflet-extra-markers/js/leaflet.extra-markers.min.js new file mode 100644 index 0000000..9ae8973 --- /dev/null +++ b/assets/leaflet-extra-markers/js/leaflet.extra-markers.min.js @@ -0,0 +1 @@ +!function(a,b){"use strict";L.ExtraMarkers={},L.ExtraMarkers.version="1.0.1",L.ExtraMarkers.Icon=L.Icon.extend({options:{iconSize:[35,45],iconAnchor:[17,42],popupAnchor:[1,-32],shadowAnchor:[10,12],shadowSize:[36,16],className:"extra-marker",prefix:"",extraClasses:"",shape:"circle",icon:"",innerHTML:"",markerColor:"red",iconColor:"#fff",number:""},initialize:function(a){a=L.Util.setOptions(this,a)},createIcon:function(){var a=b.createElement("div"),c=this.options;return c.icon&&(a.innerHTML=this._createInner()),c.innerHTML&&(a.innerHTML=c.innerHTML),c.bgPos&&(a.style.backgroundPosition=-c.bgPos.x+"px "+-c.bgPos.y+"px"),this._setIconStyles(a,c.shape+"-"+c.markerColor),a},_createInner:function(){var a="",b="",c=this.options;return c.iconColor&&(a="style='color: "+c.iconColor+"' "),c.number&&(b="number='"+c.number+"' "),""},_setIconStyles:function(a,b){var c,d,e=this.options,f=L.point(e["shadow"===b?"shadowSize":"iconSize"]);"shadow"===b?(c=L.point(e.shadowAnchor||e.iconAnchor),d="shadow"):(c=L.point(e.iconAnchor),d="icon"),!c&&f&&(c=f.divideBy(2,!0)),a.className="leaflet-marker-"+d+" extra-marker-"+b+" "+e.className,c&&(a.style.marginLeft=-c.x+"px",a.style.marginTop=-c.y+"px"),f&&(a.style.width=f.x+"px",a.style.height=f.y+"px")},createShadow:function(){var a=b.createElement("div");return this._setIconStyles(a,"shadow"),a}}),L.ExtraMarkers.icon=function(a){return new L.ExtraMarkers.Icon(a)}}(window,document); \ No newline at end of file diff --git a/assets/leaflet-extra-markers/package.json b/assets/leaflet-extra-markers/package.json new file mode 100644 index 0000000..e6a5330 --- /dev/null +++ b/assets/leaflet-extra-markers/package.json @@ -0,0 +1,21 @@ +{ + "name": "Leaflet.extra-markers", + "title": "Leaflet ExtraMarkers", + "url": "https://github.com/coryasilva/Leaflet.ExtraMarkers/", + "version": "1.0.6", + "description": "Custom Markers for Leaflet JS based on Awesome Markers", + "author": "coryasilva ", + "repository": { + "type": "git", + "url": "https://github.com/coryasilva/Leaflet.ExtraMarkers" + }, + "main": "src/assets/js/leaflet.extra-markers.js", + "devDependencies": { + "grunt": "^0.4.5", + "grunt-contrib-jshint": "^0.11.0", + "grunt-contrib-uglify": "^0.8.0", + "grunt-contrib-less": "^1.1.0", + "less-plugin-autoprefix": "^1.5.1", + "time-grunt": "^1.1.0" + } +} diff --git a/assets/leaflet-extra-markers/screenshot.png b/assets/leaflet-extra-markers/screenshot.png new file mode 100644 index 0000000..3686eae Binary files /dev/null and b/assets/leaflet-extra-markers/screenshot.png differ diff --git a/assets/osmtogeojson/.travis.yml b/assets/osmtogeojson/.travis.yml new file mode 100644 index 0000000..ab2b61f --- /dev/null +++ b/assets/osmtogeojson/.travis.yml @@ -0,0 +1,13 @@ +sudo: false +language: node_js +node_js: + - "iojs" + - "4.1" + - "4.0" + - "0.12" + - "0.10" +# whitelist +branches: + only: + - gh-pages + - /.*/ diff --git a/assets/osmtogeojson/CHANGELOG.md b/assets/osmtogeojson/CHANGELOG.md new file mode 100644 index 0000000..c4cddf4 --- /dev/null +++ b/assets/osmtogeojson/CHANGELOG.md @@ -0,0 +1,117 @@ +2.2.12 +------ +backported from 3.0.0-dev: de-namespace full geometry content in output (internal state leak) + +2.2.11 +------ +revert "use strict" because of some issues on older (0.…) nodejs + +2.2.10 +------ +* fix another undeclared variable breaking the module in strict mode +* enable "use strict"; + +2.2.9 +----- +split off polygon detection data: https://github.com/tyrasd/osm-polygon-features + +2.2.8 +----- +fix variable leaking into global scope + +2.2.7 +----- +fix a bug where loading certain complex `out geom` content resulted in invalid polygon geometries + +2.2.6 +----- +add bower support #45 + +2.2.5 +----- +add `-m` parameter that minifies output json + +2.2.4 +----- +fixed a bug where full geometry information caused additional Point features in the output + +2.2.3 +----- +* updates to polygon detection: natural=cliff is not automatically an area, golf=* is rendered as polygons +* speed optimizations for xml input in CLI mode #34 + +2.2.1 +----- +* fix bug with ref-less, clipped full geometry ways in JSON mode + +2.2.0 +----- +* support for Overpass API "full" geometry + +2.1.0 +----- +* implemented support for Overpass API geometry types "center" and "bounds" +* new command line option `-n` to make properties numeric +* added verbose option/mode that displays some debug info to the console/stderr + +2.0.5 +----- +* support input files larger than 256 MB. #17 + +2.0.4 +----- +* fix unresolved xml entities in command line mode + +2.0.2 +----- +* fix a dangling dependency (which led fresh installations to crash prematurely) + +2.0.0 +----- +* simpler API (module exports as a plain function), old `.toGeojson` still available as a fallback +* output (multi)polygons with consistent winding orders +* improve handling of simple multipolygons +* use browserify for browser library (comes bundeled with dependencies) +* some minor speed improvements + +1.4.0 +----- +* fix various speed bottlenecks (xml parsing, geojson construction, …) +* better performance when handling large xml files with the cli tool: ![](https://f.cloud.github.com/assets/1927298/1461813/4a1ce53e-44ce-11e3-9a96-d600eb3aba9b.png) + +1.3.1 +----- +* add --help and --version parameters to cli tool +* fix bug with not automatically detected file formats in pipe mode (cli tool) + +1.3.0 +----- +* more versatile cli tool (can read osm json, optionally enhanced output) +* many more unit tests +* fixed bugs with some partially incomplete data + +1.2.1 +----- +* fix wrong (inverse) logic in uninterestingTags callback evaluation (backported from master) + +1.2.0 +----- +* add [demo](http://tyrasd.github.io/osmtogeojson/) page +* support for pipes in cli tool: `echo '' | osmtogeojson` +* add flat-properties output mode (default for the cli tool) +* add options to override default `uninterestingTags` and `polygonFeatures` +* better documentation +* more test cases +* further improvements in polygon feature detection + +1.1.1 +----- +* bugfix: remove unneeded debugging code + +1.1.0 +----- +* command line tool added + +1.0.0 +----- +* initial release diff --git a/assets/osmtogeojson/LICENSE b/assets/osmtogeojson/LICENSE new file mode 100644 index 0000000..0cbe986 --- /dev/null +++ b/assets/osmtogeojson/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Martin Raifer + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/assets/osmtogeojson/Makefile b/assets/osmtogeojson/Makefile new file mode 100644 index 0000000..e41e2f1 --- /dev/null +++ b/assets/osmtogeojson/Makefile @@ -0,0 +1,6 @@ +osmtogeojson.js: index.js package.json lodash.custom.js node_modules + browserify -s osmtogeojson index.js | uglifyjs -c -m -o osmtogeojson.js +coverage: . + istanbul cover _mocha -x lodash.custom.js -- -R spec +lodash: . + lodash exports=node include=clone,merge,isEmpty,isArray,compact,each -d diff --git a/assets/osmtogeojson/README.md b/assets/osmtogeojson/README.md new file mode 100644 index 0000000..abe9407 --- /dev/null +++ b/assets/osmtogeojson/README.md @@ -0,0 +1,89 @@ +osmtogeojson +============ + +Converts [OSM](http://openstreetmap.org) [data](http://wiki.openstreetmap.org/wiki/OSM_XML) to [GeoJSON](http://www.geojson.org/). Try the [demo](http://tyrasd.github.io/osmtogeojson/)! + +* stable +* real OSM [polygon detection](https://wiki.openstreetmap.org/wiki/Overpass_turbo/Polygon_Features) +* proper OSM multipolygon support +* full support for extended Overpass API [geometry modes](http://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#Print_.28out.29) +* well [tested](https://github.com/tyrasd/osmtogeojson/tree/gh-pages/test/) and proven +* fast + +This code is used in and maintained by the [overpass turbo](http://github.com/tyrasd/overpass-ide) project. + +[![Build Status](https://secure.travis-ci.org/tyrasd/osmtogeojson.png)](https://travis-ci.org/tyrasd/osmtogeojson) + +Usage +----- + +### command line tool + +Installation: + + $ npm install -g osmtogeojson + +Usage: + + $ osmtogeojson file.osm > file.geojson + +Supported command line options are shown with: + + $ osmtogeojson --help + +When working with extra large data files (≳ 100 MB) it is recommended to run the programm with a little extra memory to avoid *process out of memory* errors. The easiest way to do this is by running the command as `node ` and setting the `--max_old_space_size=…` parameter to the available memory size in MB (osmtogeojson typically needs about 4-5 times the input data size): + + $ node --max_old_space_size=8192 `which osmtogeojson` large.osm > large.geojson + +### nodejs library + +Installation: + + $ npm install osmtogeojson + +Usage: + + var osmtogeojson = require('osmtogeojson'); + osmtogeojson(osm_data); + +### browser library + + + + osmtogeojson(osm_data); + +API +--- + +### `osmtogeojson( data, options )` + +Converts OSM data into GeoJSON. + +* `data`: the OSM data. Either as a XML DOM or in [OSM JSON](http://overpass-api.de/output_formats.html#json). +* `options`: optional. The following options can be used: + * `flatProperties`: If true, the resulting GeoJSON feature's properties will be a simple key-value list instead of a structured json object (with separate tags and metadata). default: false + * `uninterestingTags`: Either a [blacklist](https://github.com/tyrasd/osmtogeojson/blob/2.0.0/index.js#L14-L24) of tag keys or a callback function. Will be used to decide if a feature is *interesting* enough for its own GeoJSON feature. + * `polygonFeatures`: Either a [json object](https://github.com/tyrasd/osmtogeojson/blob/2.0.0/polygon_features.json) or callback function that is used to determine if a closed way should be treated as a Polygon or LineString. [read more](https://wiki.openstreetmap.org/wiki/Overpass_turbo/Polygon_Features) + +The result is a javascript object of GeoJSON data: + +GeoJSON +------- + +The GeoJSON produced by this library will include exactly one GeoJSON-feature for each of the following OSM objects (that is everything that is also visible in overpass turbo's map view): + +* all unconnected or [*interesting*](#api) tagged nodes (POIs) +* all ways (except [*uninteresting*](#api) multipolygon outlines) +* all multipolygons (simple multipolygons with exactly one closed outer way are present via their outer way) + +All data is given as a FeatureCollection. Each Feature in the collection has an `id` property that is formed from the type and id of the original OSM object (e.g. `node/123`) and has the member `properties` containing the following data: + +* `type`: the OSM data type +* `id`: the OSM id +* `tags`: a collection of all tags +* `meta`: metainformaton about the feature (e.g. version, timestamp, user, etc.) +* `relations`: an array of relations the feature is member of. Each relation is encoded as an object literal containing the following properties: `role` (membership role), `rel` (the relation's id) and `reltags` (contains all tags of the relation) +* `tainted`: this flag is set when the feature's geometry is incomplete (e.g. missing nodes of a way or missing ways of a multipolygon) + +If the [option](#api) `flatProperties` is set to true, the `properties` object will not contain any nested object literals, but directly provide a concise id, meta data and the tags of the respective OSM object. + diff --git a/assets/osmtogeojson/bower.json b/assets/osmtogeojson/bower.json new file mode 100644 index 0000000..2de94c3 --- /dev/null +++ b/assets/osmtogeojson/bower.json @@ -0,0 +1,26 @@ +{ + "name": "osmtogeojson", + "main": "osmtogeojson.js", + "version": "2.2.12", + "homepage": "http://tyrasd.github.io/osmtogeojson", + "authors": [ + "Martin Raifer" + ], + "description": "convert OSM to geojson", + "moduleType": [ + "node" + ], + "keywords": [ + "OSM", + "geoJSON", + "GIS" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/assets/osmtogeojson/index.html b/assets/osmtogeojson/index.html new file mode 100644 index 0000000..d7f943d --- /dev/null +++ b/assets/osmtogeojson/index.html @@ -0,0 +1,202 @@ + + + + osmtogeojson + + + + + + + + Fork me on GitHub +
+ osmtogeojson converts OSM data to GeoJSON. +
+
+
+

OSM data

+ +
+ + convert to GeoJSON → +
+
+

osmtogeojson is a Javascript module for converting OSM data (OSM XML or Overpass JSON) to GeoJSON. It works in the browser, nodejs and can also be used as a command line tool. This OSM conversion code was written for and is maintained by the overpass-turbo project.

+ Usage: +
    +
  • In the browser: +
      +
    • + <script src='osmtogeojson.js'></script> +
    • +
    • + osmtogeojson(osm_data); +
    • +
    +
  • +
  • As a nodejs library: +
      +
    • + $ npm install osmtogeojson +
    • +
    • + var osmtogeojson = require('osmtogeojson');
      + osmtogeojson(xml_data); +
    • +
    +
  • +
  • As a command line tool: +
      +
    • + $ npm install -g osmtogeojson +
    • +
    • + $ osmtogeojson file.osm > file.geojson +
    • +
    +
  • +
+ Read more about the API in the official documentation. +
+

download osmtogeojson.js

+ github: tyrasd/osmtogeojson
+ npm: osmtogeojson
+ license: MIT
+

+
+
+ + + diff --git a/assets/osmtogeojson/index.js b/assets/osmtogeojson/index.js new file mode 100644 index 0000000..4f9cf2d --- /dev/null +++ b/assets/osmtogeojson/index.js @@ -0,0 +1,945 @@ +var _ = require("./lodash.custom.js"); +var rewind = require("geojson-rewind"); + +// see https://wiki.openstreetmap.org/wiki/Overpass_turbo/Polygon_Features +var polygonFeatures = {}; +require("osm-polygon-features").forEach(function(tags) { + if (tags.polygon === "all") + polygonFeatures[tags.key] = true; + else { + var list = (tags.polygon === "whitelist") ? "included_values" : "excluded_values", + tagValuesObj = {}; + tags.values.forEach(function(value) { tagValuesObj[value] = true; }); + polygonFeatures[tags.key] = {}; + polygonFeatures[tags.key][list] = tagValuesObj; + } +}); + +var osmtogeojson = {}; + +osmtogeojson = function( data, options ) { + + options = _.merge( + { + verbose: false, + flatProperties: false, + uninterestingTags: { + "source": true, + "source_ref": true, + "source:ref": true, + "history": true, + "attribution": true, + "created_by": true, + "tiger:county": true, + "tiger:tlid": true, + "tiger:upload_uuid": true + }, + polygonFeatures: polygonFeatures, + }, + options + ); + + var result; + if ( ((typeof XMLDocument !== "undefined") && data instanceof XMLDocument || + (typeof XMLDocument === "undefined") && data.childNodes) ) + result = _osmXML2geoJSON(data); + else + result = _overpassJSON2geoJSON(data); + return result; + + function _overpassJSON2geoJSON(json) { + // sort elements + var nodes = new Array(); + var ways = new Array(); + var rels = new Array(); + // helper functions + function centerGeometry(object) { + var pseudoNode = _.clone(object); + pseudoNode.lat = object.center.lat; + pseudoNode.lon = object.center.lon; + pseudoNode.__is_center_placeholder = true; + nodes.push(pseudoNode); + } + function boundsGeometry(object) { + var pseudoWay = _.clone(object); + pseudoWay.nodes = []; + function addPseudoNode(lat,lon,i) { + var pseudoNode = { + type:"node", + id: "_"+pseudoWay.type+"/"+pseudoWay.id+"bounds"+i, + lat: lat, + lon: lon + } + pseudoWay.nodes.push(pseudoNode.id); + nodes.push(pseudoNode); + } + addPseudoNode(pseudoWay.bounds.minlat,pseudoWay.bounds.minlon,1); + addPseudoNode(pseudoWay.bounds.maxlat,pseudoWay.bounds.minlon,2); + addPseudoNode(pseudoWay.bounds.maxlat,pseudoWay.bounds.maxlon,3); + addPseudoNode(pseudoWay.bounds.minlat,pseudoWay.bounds.maxlon,4); + pseudoWay.nodes.push(pseudoWay.nodes[0]); + pseudoWay.__is_bounds_placeholder = true; + ways.push(pseudoWay); + } + function fullGeometryWay(way) { + function addFullGeometryNode(lat,lon,id) { + var geometryNode = { + type:"node", + id: id, + lat: lat, + lon: lon, + __is_uninteresting: true + } + nodes.push(geometryNode); + } + if (!_.isArray(way.nodes)) { + way.nodes = way.geometry.map(function(nd) { + if (nd !== null) // have to skip ref-less nodes + return "_anonymous@"+nd.lat+"/"+nd.lon; + else + return "_anonymous@unknown_location"; + }); + } + way.geometry.forEach(function(nd, i) { + if (nd) { + addFullGeometryNode( + nd.lat, + nd.lon, + way.nodes[i] + ); + } + }); + } + function fullGeometryRelation(rel) { + function addFullGeometryNode(lat,lon,id) { + var geometryNode = { + type:"node", + id: id, + lat: lat, + lon: lon + } + nodes.push(geometryNode); + } + function addFullGeometryWay(geometry,id) { + // shared multipolygon ways cannot be defined multiple times with the same id. + if (ways.some(function (way) { // todo: this is slow :( + return way.type == "way" && way.id == id; + })) return; + var geometryWay = { + type: "way", + id: id, + nodes:[] + } + function addFullGeometryWayPseudoNode(lat,lon) { + // todo? do not save the same pseudo node multiple times + var geometryPseudoNode = { + type:"node", + id: "_anonymous@"+lat+"/"+lon, + lat: lat, + lon: lon, + __is_uninteresting: true + } + geometryWay.nodes.push(geometryPseudoNode.id); + nodes.push(geometryPseudoNode); + } + geometry.forEach(function(nd) { + if (nd) { + addFullGeometryWayPseudoNode( + nd.lat, + nd.lon + ); + } else { + geometryWay.nodes.push(undefined); + } + }); + ways.push(geometryWay); + } + rel.members.forEach(function(member, i) { + if (member.type == "node") { + if (member.lat) { + addFullGeometryNode( + member.lat, + member.lon, + member.ref + ); + } + } else if (member.type == "way") { + if (member.geometry) { + member.ref = "_fullGeom"+member.ref; + addFullGeometryWay( + member.geometry, + member.ref + ); + } + } + }); + } + // create copies of individual json objects to make sure the original data doesn't get altered + // todo: cloning is slow: see if this can be done differently! + for (var i=0;i 0 + }); + if (rel.center) + centerGeometry(rel); + if (has_full_geometry) + fullGeometryRelation(rel); + else if (rel.bounds) + boundsGeometry(rel); + break; + default: + // type=area (from coord-query) is an example for this case. + } + } + return _convert2geoJSON(nodes,ways,rels); + } + function _osmXML2geoJSON(xml) { + // sort elements + var nodes = new Array(); + var ways = new Array(); + var rels = new Array(); + // helper function + function copy_attribute( x, o, attr ) { + if (x.hasAttribute(attr)) + o[attr] = x.getAttribute(attr); + } + function centerGeometry(object, centroid) { + var pseudoNode = _.clone(object); + copy_attribute(centroid, pseudoNode, 'lat'); + copy_attribute(centroid, pseudoNode, 'lon'); + pseudoNode.__is_center_placeholder = true; + nodes.push(pseudoNode); + } + function boundsGeometry(object, bounds) { + var pseudoWay = _.clone(object); + pseudoWay.nodes = []; + function addPseudoNode(lat,lon,i) { + var pseudoNode = { + type:"node", + id: "_"+pseudoWay.type+"/"+pseudoWay.id+"bounds"+i, + lat: lat, + lon: lon + } + pseudoWay.nodes.push(pseudoNode.id); + nodes.push(pseudoNode); + } + addPseudoNode(bounds.getAttribute('minlat'),bounds.getAttribute('minlon'),1); + addPseudoNode(bounds.getAttribute('maxlat'),bounds.getAttribute('minlon'),2); + addPseudoNode(bounds.getAttribute('maxlat'),bounds.getAttribute('maxlon'),3); + addPseudoNode(bounds.getAttribute('minlat'),bounds.getAttribute('maxlon'),4); + pseudoWay.nodes.push(pseudoWay.nodes[0]); + pseudoWay.__is_bounds_placeholder = true; + ways.push(pseudoWay); + } + function fullGeometryWay(way, nds) { + function addFullGeometryNode(lat,lon,id) { + var geometryNode = { + type:"node", + id: id, + lat: lat, + lon: lon, + __is_uninteresting: true + } + nodes.push(geometryNode); + return geometryNode.id; + } + if (!_.isArray(way.nodes)) { + way.nodes = []; + _.each( nds, function( nd, i ) { + way.nodes.push("_anonymous@"+nd.getAttribute('lat')+"/"+nd.getAttribute('lon')); + }); + } + _.each( nds, function( nd, i ) { + if (nd.getAttribute('lat')) { + addFullGeometryNode( + nd.getAttribute('lat'), + nd.getAttribute('lon'), + way.nodes[i] + ); + } + }); + } + function fullGeometryRelation(rel, members) { + function addFullGeometryNode(lat,lon,id) { + var geometryNode = { + type:"node", + id: id, + lat: lat, + lon: lon + } + nodes.push(geometryNode); + } + function addFullGeometryWay(nds,id) { + // shared multipolygon ways cannot be defined multiple times with the same id. + if (ways.some(function (way) { // todo: this is slow :( + return way.type == "way" && way.id == id; + })) return; + var geometryWay = { + type: "way", + id: id, + nodes:[] + } + function addFullGeometryWayPseudoNode(lat,lon) { + // todo? do not save the same pseudo node multiple times + var geometryPseudoNode = { + type:"node", + id: "_anonymous@"+lat+"/"+lon, + lat: lat, + lon: lon, + __is_uninteresting: true + } + geometryWay.nodes.push(geometryPseudoNode.id); + nodes.push(geometryPseudoNode); + } + _.each(nds, function(nd) { + if (nd.getAttribute('lat')) { + addFullGeometryWayPseudoNode( + nd.getAttribute('lat'), + nd.getAttribute('lon') + ); + } else { + geometryWay.nodes.push(undefined); + } + }); + ways.push(geometryWay); + } + _.each( members, function( member, i ) { + if (rel.members[i].type == "node") { + if (member.getAttribute('lat')) { + addFullGeometryNode( + member.getAttribute('lat'), + member.getAttribute('lon'), + rel.members[i].ref + ); + } + } else if (rel.members[i].type == "way") { + if (member.getElementsByTagName('nd').length > 0) { + rel.members[i].ref = "_fullGeom"+rel.members[i].ref; + addFullGeometryWay( + member.getElementsByTagName('nd'), + rel.members[i].ref + ); + } + } + }); + } + // nodes + _.each( xml.getElementsByTagName('node'), function( node, i ) { + var tags = {}; + _.each( node.getElementsByTagName('tag'), function( tag ) { + tags[tag.getAttribute('k')] = tag.getAttribute('v'); + }); + var nodeObject = { + 'type': 'node' + }; + copy_attribute( node, nodeObject, 'id' ); + copy_attribute( node, nodeObject, 'lat' ); + copy_attribute( node, nodeObject, 'lon' ); + copy_attribute( node, nodeObject, 'version' ); + copy_attribute( node, nodeObject, 'timestamp' ); + copy_attribute( node, nodeObject, 'changeset' ); + copy_attribute( node, nodeObject, 'uid' ); + copy_attribute( node, nodeObject, 'user' ); + if (!_.isEmpty(tags)) + nodeObject.tags = tags; + nodes.push(nodeObject); + }); + // ways + var centroid,bounds; + _.each( xml.getElementsByTagName('way'), function( way, i ) { + var tags = {}; + var wnodes = []; + _.each( way.getElementsByTagName('tag'), function( tag ) { + tags[tag.getAttribute('k')] = tag.getAttribute('v'); + }); + var has_full_geometry = false; + _.each( way.getElementsByTagName('nd'), function( nd, i ) { + var id; + if (id = nd.getAttribute('ref')) + wnodes[i] = id; + if (!has_full_geometry && nd.getAttribute('lat')) + has_full_geometry = true; + }); + var wayObject = { + "type": "way" + }; + copy_attribute( way, wayObject, 'id' ); + copy_attribute( way, wayObject, 'version' ); + copy_attribute( way, wayObject, 'timestamp' ); + copy_attribute( way, wayObject, 'changeset' ); + copy_attribute( way, wayObject, 'uid' ); + copy_attribute( way, wayObject, 'user' ); + if (wnodes.length > 0) + wayObject.nodes = wnodes; + if (!_.isEmpty(tags)) + wayObject.tags = tags; + if (centroid = way.getElementsByTagName('center')[0]) + centerGeometry(wayObject,centroid); + if (has_full_geometry) + fullGeometryWay(wayObject, way.getElementsByTagName('nd')); + else if (bounds = way.getElementsByTagName('bounds')[0]) + boundsGeometry(wayObject,bounds); + ways.push(wayObject); + }); + // relations + _.each( xml.getElementsByTagName('relation'), function( relation, i ) { + var tags = {}; + var members = []; + _.each( relation.getElementsByTagName('tag'), function( tag ) { + tags[tag.getAttribute('k')] = tag.getAttribute('v'); + }); + var has_full_geometry = false; + _.each( relation.getElementsByTagName('member'), function( member, i ) { + members[i] = {}; + copy_attribute( member, members[i], 'ref' ); + copy_attribute( member, members[i], 'role' ); + copy_attribute( member, members[i], 'type' ); + if (!has_full_geometry && + (members[i].type == 'node' && member.getAttribute('lat')) || + (members[i].type == 'way' && member.getElementsByTagName('nd').length>0) ) + has_full_geometry = true; + }); + var relObject = { + "type": "relation" + } + copy_attribute( relation, relObject, 'id' ); + copy_attribute( relation, relObject, 'version' ); + copy_attribute( relation, relObject, 'timestamp' ); + copy_attribute( relation, relObject, 'changeset' ); + copy_attribute( relation, relObject, 'uid' ); + copy_attribute( relation, relObject, 'user' ); + if (members.length > 0) + relObject.members = members; + if (!_.isEmpty(tags)) + relObject.tags = tags; + if (centroid = relation.getElementsByTagName('center')[0]) + centerGeometry(relObject,centroid); + if (has_full_geometry) + fullGeometryRelation(relObject, relation.getElementsByTagName('member')); + else if (bounds = relation.getElementsByTagName('bounds')[0]) + boundsGeometry(relObject,bounds); + rels.push(relObject); + }); + return _convert2geoJSON(nodes,ways,rels); + } + function _convert2geoJSON(nodes,ways,rels) { + + // helper function that checks if there are any tags other than "created_by", "source", etc. or any tag provided in ignore_tags + function has_interesting_tags(t, ignore_tags) { + if (typeof ignore_tags !== "object") + ignore_tags={}; + if (typeof options.uninterestingTags === "function") + return !options.uninterestingTags(t, ignore_tags); + for (var k in t) + if (!(options.uninterestingTags[k]===true) && + !(ignore_tags[k]===true || ignore_tags[k]===t[k])) + return true; + return false; + }; + // helper function to extract meta information + function build_meta_information(object) { + var res = { + "timestamp": object.timestamp, + "version": object.version, + "changeset": object.changeset, + "user": object.user, + "uid": object.uid + }; + for (var k in res) + if (res[k] === undefined) + delete res[k]; + return res; + } + + // some data processing (e.g. filter nodes only used for ways) + var nodeids = new Object(); + for (var i=0;i y) != (yj > y)) && + (x < (xj - xi) * (y - yi) / (yj - yi) + xi); + if (intersect) inside = !inside; + } + return inside; + }; + // stolen from iD/relation.js + var o, outer; + // todo: all this coordinate mapping makes this unneccesarily slow. + // see the "todo: this is slow! :(" above. + inner = mapCoordinates(inner); + /*for (o = 0; o < outers.length; o++) { + outer = mapCoordinates(outers[o]); + if (polygonContainsPolygon(outer, inner)) + return o; + }*/ + for (o = 0; o < outers.length; o++) { + outer = mapCoordinates(outers[o]); + if (polygonIntersectsPolygon(outer, inner)) + return o; + } + } + mp = outers.map(function(o) {return [o];}); + for (var j=0; j0.1.0*/); + return geojson; + } + function _isPolygonFeature( tags ) { + var polygonFeatures = options.polygonFeatures; + if (typeof polygonFeatures === "function") + return polygonFeatures(tags); + // explicitely tagged non-areas + if ( tags['area'] === 'no' ) + return false; + // assuming that a typical OSM way has in average less tags than + // the polygonFeatures list, this way around should be faster + for ( var key in tags ) { + var val = tags[key]; + var pfk = polygonFeatures[key]; + // continue with next if tag is unknown or not "categorizing" + if ( typeof pfk === 'undefined' ) + continue; + // continue with next if tag is explicitely un-set ("building=no") + if ( val === 'no' ) + continue; + // check polygon features for: general acceptance, included or excluded values + if ( pfk === true ) + return true; + if ( pfk.included_values && pfk.included_values[val] === true ) + return true; + if ( pfk.excluded_values && pfk.excluded_values[val] !== true ) + return true; + } + // if no tags matched, this ain't no area. + return false; + } +}; + +// for backwards compatibility +osmtogeojson.toGeojson = osmtogeojson; + +module.exports = osmtogeojson; diff --git a/assets/osmtogeojson/lodash.custom.js b/assets/osmtogeojson/lodash.custom.js new file mode 100644 index 0000000..43d71b6 --- /dev/null +++ b/assets/osmtogeojson/lodash.custom.js @@ -0,0 +1,1794 @@ +/** + * @license + * Lo-Dash 2.4.1 (Custom Build) + * Build: `lodash exports="node" include="clone,merge,isEmpty,isArray,compact,each" -d` + * Copyright 2012-2013 The Dojo Foundation + * Based on Underscore.js 1.5.2 + * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + * Available under MIT license + */ +;(function() { + + /** Used to pool arrays and objects used internally */ + var arrayPool = []; + + /** Used internally to indicate various things */ + var indicatorObject = {}; + + /** Used as the max size of the `arrayPool` and `objectPool` */ + var maxPoolSize = 40; + + /** Used to match regexp flags from their coerced string values */ + var reFlags = /\w*$/; + + /** Used to detected named functions */ + var reFuncName = /^\s*function[ \n\r\t]+\w/; + + /** Used to detect functions containing a `this` reference */ + var reThis = /\bthis\b/; + + /** Used to fix the JScript [[DontEnum]] bug */ + var shadowedProps = [ + 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', + 'toLocaleString', 'toString', 'valueOf' + ]; + + /** `Object#toString` result shortcuts */ + var argsClass = '[object Arguments]', + arrayClass = '[object Array]', + boolClass = '[object Boolean]', + dateClass = '[object Date]', + errorClass = '[object Error]', + funcClass = '[object Function]', + numberClass = '[object Number]', + objectClass = '[object Object]', + regexpClass = '[object RegExp]', + stringClass = '[object String]'; + + /** Used to identify object classifications that `_.clone` supports */ + var cloneableClasses = {}; + cloneableClasses[funcClass] = false; + cloneableClasses[argsClass] = cloneableClasses[arrayClass] = + cloneableClasses[boolClass] = cloneableClasses[dateClass] = + cloneableClasses[numberClass] = cloneableClasses[objectClass] = + cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true; + + /** Used as the property descriptor for `__bindData__` */ + var descriptor = { + 'configurable': false, + 'enumerable': false, + 'value': null, + 'writable': false + }; + + /** Used as the data object for `iteratorTemplate` */ + var iteratorData = { + 'args': '', + 'array': null, + 'bottom': '', + 'firstArg': '', + 'init': '', + 'keys': null, + 'loop': '', + 'shadowedProps': null, + 'support': null, + 'top': '', + 'useHas': false + }; + + /** Used to determine if values are of the language type Object */ + var objectTypes = { + 'boolean': false, + 'function': true, + 'object': true, + 'number': false, + 'string': false, + 'undefined': false + }; + + /** Used as a reference to the global object */ + var root = (objectTypes[typeof window] && window) || this; + + /** Detect free variable `exports` */ + var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; + + /** Detect free variable `module` */ + var freeModule = objectTypes[typeof module] && module && !module.nodeType && module; + + /** Detect the popular CommonJS extension `module.exports` */ + var moduleExports = freeModule && freeModule.exports === freeExports && freeExports; + + /** Detect free variable `global` from Node.js or Browserified code and use it as `root` */ + var freeGlobal = objectTypes[typeof global] && global; + if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) { + root = freeGlobal; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Gets an array from the array pool or creates a new one if the pool is empty. + * + * @private + * @returns {Array} The array from the pool. + */ + function getArray() { + return arrayPool.pop() || []; + } + + /** + * Checks if `value` is a DOM node in IE < 9. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is a DOM node, else `false`. + */ + function isNode(value) { + // IE < 9 presents DOM nodes as `Object` objects except they have `toString` + // methods that are `typeof` "string" and still can coerce nodes to strings + return typeof value.toString != 'function' && typeof (value + '') == 'string'; + } + + /** + * Releases the given array back to the array pool. + * + * @private + * @param {Array} [array] The array to release. + */ + function releaseArray(array) { + array.length = 0; + if (arrayPool.length < maxPoolSize) { + arrayPool.push(array); + } + } + + /** + * Slices the `collection` from the `start` index up to, but not including, + * the `end` index. + * + * Note: This function is used instead of `Array#slice` to support node lists + * in IE < 9 and to ensure dense arrays are returned. + * + * @private + * @param {Array|Object|string} collection The collection to slice. + * @param {number} start The start index. + * @param {number} end The end index. + * @returns {Array} Returns the new array. + */ + function slice(array, start, end) { + start || (start = 0); + if (typeof end == 'undefined') { + end = array ? array.length : 0; + } + var index = -1, + length = end - start || 0, + result = Array(length < 0 ? 0 : length); + + while (++index < length) { + result[index] = array[start + index]; + } + return result; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Used for `Array` method references. + * + * Normally `Array.prototype` would suffice, however, using an array literal + * avoids issues in Narwhal. + */ + var arrayRef = []; + + /** Used for native method references */ + var errorProto = Error.prototype, + objectProto = Object.prototype, + stringProto = String.prototype; + + /** Used to resolve the internal [[Class]] of values */ + var toString = objectProto.toString; + + /** Used to detect if a method is native */ + var reNative = RegExp('^' + + String(toString) + .replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + .replace(/toString| for [^\]]+/g, '.*?') + '$' + ); + + /** Native method shortcuts */ + var fnToString = Function.prototype.toString, + getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf, + hasOwnProperty = objectProto.hasOwnProperty, + push = arrayRef.push, + propertyIsEnumerable = objectProto.propertyIsEnumerable, + unshift = arrayRef.unshift; + + /** Used to set meta data on functions */ + var defineProperty = (function() { + // IE 8 only accepts DOM elements + try { + var o = {}, + func = isNative(func = Object.defineProperty) && func, + result = func(o, o, o) && func; + } catch(e) { } + return result; + }()); + + /* Native method shortcuts for methods with the same name as other `lodash` methods */ + var nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate, + nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray, + nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys; + + /** Used to lookup a built-in constructor by [[Class]] */ + var ctorByClass = {}; + ctorByClass[arrayClass] = Array; + ctorByClass[boolClass] = Boolean; + ctorByClass[dateClass] = Date; + ctorByClass[funcClass] = Function; + ctorByClass[objectClass] = Object; + ctorByClass[numberClass] = Number; + ctorByClass[regexpClass] = RegExp; + ctorByClass[stringClass] = String; + + /** Used to avoid iterating non-enumerable properties in IE < 9 */ + var nonEnumProps = {}; + nonEnumProps[arrayClass] = nonEnumProps[dateClass] = nonEnumProps[numberClass] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true }; + nonEnumProps[boolClass] = nonEnumProps[stringClass] = { 'constructor': true, 'toString': true, 'valueOf': true }; + nonEnumProps[errorClass] = nonEnumProps[funcClass] = nonEnumProps[regexpClass] = { 'constructor': true, 'toString': true }; + nonEnumProps[objectClass] = { 'constructor': true }; + + (function() { + var length = shadowedProps.length; + while (length--) { + var key = shadowedProps[length]; + for (var className in nonEnumProps) { + if (hasOwnProperty.call(nonEnumProps, className) && !hasOwnProperty.call(nonEnumProps[className], key)) { + nonEnumProps[className][key] = false; + } + } + } + }()); + + /*--------------------------------------------------------------------------*/ + + /** + * Creates a `lodash` object which wraps the given value to enable intuitive + * method chaining. + * + * In addition to Lo-Dash methods, wrappers also have the following `Array` methods: + * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`, + * and `unshift` + * + * Chaining is supported in custom builds as long as the `value` method is + * implicitly or explicitly included in the build. + * + * The chainable wrapper functions are: + * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`, + * `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`, + * `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`, + * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, + * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, + * `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`, + * `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`, + * `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, + * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`, + * `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`, + * and `zip` + * + * The non-chainable wrapper functions are: + * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`, + * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`, `identity`, + * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, + * `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`, + * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`, + * `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, `pop`, `random`, `reduce`, + * `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `runInContext`, + * `template`, `unescape`, `uniqueId`, and `value` + * + * The wrapper functions `first` and `last` return wrapped values when `n` is + * provided, otherwise they return unwrapped values. + * + * Explicit chaining can be enabled by using the `_.chain` method. + * + * @name _ + * @constructor + * @category Chaining + * @param {*} value The value to wrap in a `lodash` instance. + * @returns {Object} Returns a `lodash` instance. + * @example + * + * var wrapped = _([1, 2, 3]); + * + * // returns an unwrapped value + * wrapped.reduce(function(sum, num) { + * return sum + num; + * }); + * // => 6 + * + * // returns a wrapped value + * var squares = wrapped.map(function(num) { + * return num * num; + * }); + * + * _.isArray(squares); + * // => false + * + * _.isArray(squares.value()); + * // => true + */ + function lodash() { + // no operation performed + } + + /** + * An object used to flag environments features. + * + * @static + * @memberOf _ + * @type Object + */ + var support = lodash.support = {}; + + (function() { + var ctor = function() { this.x = 1; }, + object = { '0': 1, 'length': 1 }, + props = []; + + ctor.prototype = { 'valueOf': 1, 'y': 1 }; + for (var key in new ctor) { props.push(key); } + for (key in arguments) { } + + /** + * Detect if an `arguments` object's [[Class]] is resolvable (all but Firefox < 4, IE < 9). + * + * @memberOf _.support + * @type boolean + */ + support.argsClass = toString.call(arguments) == argsClass; + + /** + * Detect if `arguments` objects are `Object` objects (all but Narwhal and Opera < 10.5). + * + * @memberOf _.support + * @type boolean + */ + support.argsObject = arguments.constructor == Object && !(arguments instanceof Array); + + /** + * Detect if `name` or `message` properties of `Error.prototype` are + * enumerable by default. (IE < 9, Safari < 5.1) + * + * @memberOf _.support + * @type boolean + */ + support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') || propertyIsEnumerable.call(errorProto, 'name'); + + /** + * Detect if `prototype` properties are enumerable by default. + * + * Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 + * (if the prototype or a property on the prototype has been set) + * incorrectly sets a function's `prototype` property [[Enumerable]] + * value to `true`. + * + * @memberOf _.support + * @type boolean + */ + support.enumPrototypes = propertyIsEnumerable.call(ctor, 'prototype'); + + /** + * Detect if functions can be decompiled by `Function#toString` + * (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps). + * + * @memberOf _.support + * @type boolean + */ + support.funcDecomp = !isNative(root.WinRTError) && reThis.test(function() { return this; }); + + /** + * Detect if `Function#name` is supported (all but IE). + * + * @memberOf _.support + * @type boolean + */ + support.funcNames = typeof Function.name == 'string'; + + /** + * Detect if `arguments` object indexes are non-enumerable + * (Firefox < 4, IE < 9, PhantomJS, Safari < 5.1). + * + * @memberOf _.support + * @type boolean + */ + support.nonEnumArgs = key != 0; + + /** + * Detect if properties shadowing those on `Object.prototype` are non-enumerable. + * + * In IE < 9 an objects own properties, shadowing non-enumerable ones, are + * made non-enumerable as well (a.k.a the JScript [[DontEnum]] bug). + * + * @memberOf _.support + * @type boolean + */ + support.nonEnumShadows = !/valueOf/.test(props); + + /** + * Detect if own properties are iterated after inherited properties (all but IE < 9). + * + * @memberOf _.support + * @type boolean + */ + support.ownLast = props[0] != 'x'; + + /** + * Detect if `Array#shift` and `Array#splice` augment array-like objects correctly. + * + * Firefox < 10, IE compatibility mode, and IE < 9 have buggy Array `shift()` + * and `splice()` functions that fail to remove the last element, `value[0]`, + * of array-like objects even though the `length` property is set to `0`. + * The `shift()` method is buggy in IE 8 compatibility mode, while `splice()` + * is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9. + * + * @memberOf _.support + * @type boolean + */ + support.spliceObjects = (arrayRef.splice.call(object, 0, 1), !object[0]); + + /** + * Detect lack of support for accessing string characters by index. + * + * IE < 8 can't access characters by index and IE 8 can only access + * characters by index on string literals. + * + * @memberOf _.support + * @type boolean + */ + support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx'; + + /** + * Detect if a DOM node's [[Class]] is resolvable (all but IE < 9) + * and that the JS engine errors when attempting to coerce an object to + * a string without a `toString` function. + * + * @memberOf _.support + * @type boolean + */ + try { + support.nodeClass = !(toString.call(document) == objectClass && !({ 'toString': 0 } + '')); + } catch(e) { + support.nodeClass = true; + } + }(1)); + + /*--------------------------------------------------------------------------*/ + + /** + * The template used to create iterator functions. + * + * @private + * @param {Object} data The data object used to populate the text. + * @returns {string} Returns the interpolated text. + */ + var iteratorTemplate = function(obj) { + + var __p = 'var index, iterable = ' + + (obj.firstArg) + + ', result = ' + + (obj.init) + + ';\nif (!iterable) return result;\n' + + (obj.top) + + ';'; + if (obj.array) { + __p += '\nvar length = iterable.length; index = -1;\nif (' + + (obj.array) + + ') { '; + if (support.unindexedChars) { + __p += '\n if (isString(iterable)) {\n iterable = iterable.split(\'\')\n } '; + } + __p += '\n while (++index < length) {\n ' + + (obj.loop) + + ';\n }\n}\nelse { '; + } else if (support.nonEnumArgs) { + __p += '\n var length = iterable.length; index = -1;\n if (length && isArguments(iterable)) {\n while (++index < length) {\n index += \'\';\n ' + + (obj.loop) + + ';\n }\n } else { '; + } + + if (support.enumPrototypes) { + __p += '\n var skipProto = typeof iterable == \'function\';\n '; + } + + if (support.enumErrorProps) { + __p += '\n var skipErrorProps = iterable === errorProto || iterable instanceof Error;\n '; + } + + var conditions = []; if (support.enumPrototypes) { conditions.push('!(skipProto && index == "prototype")'); } if (support.enumErrorProps) { conditions.push('!(skipErrorProps && (index == "message" || index == "name"))'); } + + if (obj.useHas && obj.keys) { + __p += '\n var ownIndex = -1,\n ownProps = objectTypes[typeof iterable] && keys(iterable),\n length = ownProps ? ownProps.length : 0;\n\n while (++ownIndex < length) {\n index = ownProps[ownIndex];\n'; + if (conditions.length) { + __p += ' if (' + + (conditions.join(' && ')) + + ') {\n '; + } + __p += + (obj.loop) + + '; '; + if (conditions.length) { + __p += '\n }'; + } + __p += '\n } '; + } else { + __p += '\n for (index in iterable) {\n'; + if (obj.useHas) { conditions.push("hasOwnProperty.call(iterable, index)"); } if (conditions.length) { + __p += ' if (' + + (conditions.join(' && ')) + + ') {\n '; + } + __p += + (obj.loop) + + '; '; + if (conditions.length) { + __p += '\n }'; + } + __p += '\n } '; + if (support.nonEnumShadows) { + __p += '\n\n if (iterable !== objectProto) {\n var ctor = iterable.constructor,\n isProto = iterable === (ctor && ctor.prototype),\n className = iterable === stringProto ? stringClass : iterable === errorProto ? errorClass : toString.call(iterable),\n nonEnum = nonEnumProps[className];\n '; + for (k = 0; k < 7; k++) { + __p += '\n index = \'' + + (obj.shadowedProps[k]) + + '\';\n if ((!(isProto && nonEnum[index]) && hasOwnProperty.call(iterable, index))'; + if (!obj.useHas) { + __p += ' || (!nonEnum[index] && iterable[index] !== objectProto[index])'; + } + __p += ') {\n ' + + (obj.loop) + + ';\n } '; + } + __p += '\n } '; + } + + } + + if (obj.array || support.nonEnumArgs) { + __p += '\n}'; + } + __p += + (obj.bottom) + + ';\nreturn result'; + + return __p + }; + + /*--------------------------------------------------------------------------*/ + + /** + * The base implementation of `_.bind` that creates the bound function and + * sets its meta data. + * + * @private + * @param {Array} bindData The bind data array. + * @returns {Function} Returns the new bound function. + */ + function baseBind(bindData) { + var func = bindData[0], + partialArgs = bindData[2], + thisArg = bindData[4]; + + function bound() { + // `Function#bind` spec + // http://es5.github.io/#x15.3.4.5 + if (partialArgs) { + // avoid `arguments` object deoptimizations by using `slice` instead + // of `Array.prototype.slice.call` and not assigning `arguments` to a + // variable as a ternary expression + var args = slice(partialArgs); + push.apply(args, arguments); + } + // mimic the constructor's `return` behavior + // http://es5.github.io/#x13.2.2 + if (this instanceof bound) { + // ensure `new bound` is an instance of `func` + var thisBinding = baseCreate(func.prototype), + result = func.apply(thisBinding, args || arguments); + return isObject(result) ? result : thisBinding; + } + return func.apply(thisArg, args || arguments); + } + setBindData(bound, bindData); + return bound; + } + + /** + * The base implementation of `_.clone` without argument juggling or support + * for `thisArg` binding. + * + * @private + * @param {*} value The value to clone. + * @param {boolean} [isDeep=false] Specify a deep clone. + * @param {Function} [callback] The function to customize cloning values. + * @param {Array} [stackA=[]] Tracks traversed source objects. + * @param {Array} [stackB=[]] Associates clones with source counterparts. + * @returns {*} Returns the cloned value. + */ + function baseClone(value, isDeep, callback, stackA, stackB) { + if (callback) { + var result = callback(value); + if (typeof result != 'undefined') { + return result; + } + } + // inspect [[Class]] + var isObj = isObject(value); + if (isObj) { + var className = toString.call(value); + if (!cloneableClasses[className] || (!support.nodeClass && isNode(value))) { + return value; + } + var ctor = ctorByClass[className]; + switch (className) { + case boolClass: + case dateClass: + return new ctor(+value); + + case numberClass: + case stringClass: + return new ctor(value); + + case regexpClass: + result = ctor(value.source, reFlags.exec(value)); + result.lastIndex = value.lastIndex; + return result; + } + } else { + return value; + } + var isArr = isArray(value); + if (isDeep) { + // check for circular references and return corresponding clone + var initedStack = !stackA; + stackA || (stackA = getArray()); + stackB || (stackB = getArray()); + + var length = stackA.length; + while (length--) { + if (stackA[length] == value) { + return stackB[length]; + } + } + result = isArr ? ctor(value.length) : {}; + } + else { + result = isArr ? slice(value) : assign({}, value); + } + // add array properties assigned by `RegExp#exec` + if (isArr) { + if (hasOwnProperty.call(value, 'index')) { + result.index = value.index; + } + if (hasOwnProperty.call(value, 'input')) { + result.input = value.input; + } + } + // exit for shallow clone + if (!isDeep) { + return result; + } + // add the source value to the stack of traversed objects + // and associate it with its clone + stackA.push(value); + stackB.push(result); + + // recursively populate clone (susceptible to call stack limits) + (isArr ? baseEach : forOwn)(value, function(objValue, key) { + result[key] = baseClone(objValue, isDeep, callback, stackA, stackB); + }); + + if (initedStack) { + releaseArray(stackA); + releaseArray(stackB); + } + return result; + } + + /** + * The base implementation of `_.create` without support for assigning + * properties to the created object. + * + * @private + * @param {Object} prototype The object to inherit from. + * @returns {Object} Returns the new object. + */ + function baseCreate(prototype, properties) { + return isObject(prototype) ? nativeCreate(prototype) : {}; + } + // fallback for browsers without `Object.create` + if (!nativeCreate) { + baseCreate = (function() { + function Object() {} + return function(prototype) { + if (isObject(prototype)) { + Object.prototype = prototype; + var result = new Object; + Object.prototype = null; + } + return result || root.Object(); + }; + }()); + } + + /** + * The base implementation of `_.createCallback` without support for creating + * "_.pluck" or "_.where" style callbacks. + * + * @private + * @param {*} [func=identity] The value to convert to a callback. + * @param {*} [thisArg] The `this` binding of the created callback. + * @param {number} [argCount] The number of arguments the callback accepts. + * @returns {Function} Returns a callback function. + */ + function baseCreateCallback(func, thisArg, argCount) { + if (typeof func != 'function') { + return identity; + } + // exit early for no `thisArg` or already bound by `Function#bind` + if (typeof thisArg == 'undefined' || !('prototype' in func)) { + return func; + } + var bindData = func.__bindData__; + if (typeof bindData == 'undefined') { + if (support.funcNames) { + bindData = !func.name; + } + bindData = bindData || !support.funcDecomp; + if (!bindData) { + var source = fnToString.call(func); + if (!support.funcNames) { + bindData = !reFuncName.test(source); + } + if (!bindData) { + // checks if `func` references the `this` keyword and stores the result + bindData = reThis.test(source); + setBindData(func, bindData); + } + } + } + // exit early if there are no `this` references or `func` is bound + if (bindData === false || (bindData !== true && bindData[1] & 1)) { + return func; + } + switch (argCount) { + case 1: return function(value) { + return func.call(thisArg, value); + }; + case 2: return function(a, b) { + return func.call(thisArg, a, b); + }; + case 3: return function(value, index, collection) { + return func.call(thisArg, value, index, collection); + }; + case 4: return function(accumulator, value, index, collection) { + return func.call(thisArg, accumulator, value, index, collection); + }; + } + return bind(func, thisArg); + } + + /** + * The base implementation of `createWrapper` that creates the wrapper and + * sets its meta data. + * + * @private + * @param {Array} bindData The bind data array. + * @returns {Function} Returns the new function. + */ + function baseCreateWrapper(bindData) { + var func = bindData[0], + bitmask = bindData[1], + partialArgs = bindData[2], + partialRightArgs = bindData[3], + thisArg = bindData[4], + arity = bindData[5]; + + var isBind = bitmask & 1, + isBindKey = bitmask & 2, + isCurry = bitmask & 4, + isCurryBound = bitmask & 8, + key = func; + + function bound() { + var thisBinding = isBind ? thisArg : this; + if (partialArgs) { + var args = slice(partialArgs); + push.apply(args, arguments); + } + if (partialRightArgs || isCurry) { + args || (args = slice(arguments)); + if (partialRightArgs) { + push.apply(args, partialRightArgs); + } + if (isCurry && args.length < arity) { + bitmask |= 16 & ~32; + return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]); + } + } + args || (args = arguments); + if (isBindKey) { + func = thisBinding[key]; + } + if (this instanceof bound) { + thisBinding = baseCreate(func.prototype); + var result = func.apply(thisBinding, args); + return isObject(result) ? result : thisBinding; + } + return func.apply(thisBinding, args); + } + setBindData(bound, bindData); + return bound; + } + + /** + * The base implementation of `_.merge` without argument juggling or support + * for `thisArg` binding. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {Function} [callback] The function to customize merging properties. + * @param {Array} [stackA=[]] Tracks traversed source objects. + * @param {Array} [stackB=[]] Associates values with source counterparts. + */ + function baseMerge(object, source, callback, stackA, stackB) { + (isArray(source) ? forEach : forOwn)(source, function(source, key) { + var found, + isArr, + result = source, + value = object[key]; + + if (source && ((isArr = isArray(source)) || isPlainObject(source))) { + // avoid merging previously merged cyclic sources + var stackLength = stackA.length; + while (stackLength--) { + if ((found = stackA[stackLength] == source)) { + value = stackB[stackLength]; + break; + } + } + if (!found) { + var isShallow; + if (callback) { + result = callback(value, source); + if ((isShallow = typeof result != 'undefined')) { + value = result; + } + } + if (!isShallow) { + value = isArr + ? (isArray(value) ? value : []) + : (isPlainObject(value) ? value : {}); + } + // add `source` and associated `value` to the stack of traversed objects + stackA.push(source); + stackB.push(value); + + // recursively merge objects and arrays (susceptible to call stack limits) + if (!isShallow) { + baseMerge(value, source, callback, stackA, stackB); + } + } + } + else { + if (callback) { + result = callback(value, source); + if (typeof result == 'undefined') { + result = source; + } + } + if (typeof result != 'undefined') { + value = result; + } + } + object[key] = value; + }); + } + + /** + * Creates a function that, when called, either curries or invokes `func` + * with an optional `this` binding and partially applied arguments. + * + * @private + * @param {Function|string} func The function or method name to reference. + * @param {number} bitmask The bitmask of method flags to compose. + * The bitmask may be composed of the following flags: + * 1 - `_.bind` + * 2 - `_.bindKey` + * 4 - `_.curry` + * 8 - `_.curry` (bound) + * 16 - `_.partial` + * 32 - `_.partialRight` + * @param {Array} [partialArgs] An array of arguments to prepend to those + * provided to the new function. + * @param {Array} [partialRightArgs] An array of arguments to append to those + * provided to the new function. + * @param {*} [thisArg] The `this` binding of `func`. + * @param {number} [arity] The arity of `func`. + * @returns {Function} Returns the new function. + */ + function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) { + var isBind = bitmask & 1, + isBindKey = bitmask & 2, + isCurry = bitmask & 4, + isCurryBound = bitmask & 8, + isPartial = bitmask & 16, + isPartialRight = bitmask & 32; + + if (!isBindKey && !isFunction(func)) { + throw new TypeError; + } + if (isPartial && !partialArgs.length) { + bitmask &= ~16; + isPartial = partialArgs = false; + } + if (isPartialRight && !partialRightArgs.length) { + bitmask &= ~32; + isPartialRight = partialRightArgs = false; + } + var bindData = func && func.__bindData__; + if (bindData && bindData !== true) { + // clone `bindData` + bindData = slice(bindData); + if (bindData[2]) { + bindData[2] = slice(bindData[2]); + } + if (bindData[3]) { + bindData[3] = slice(bindData[3]); + } + // set `thisBinding` is not previously bound + if (isBind && !(bindData[1] & 1)) { + bindData[4] = thisArg; + } + // set if previously bound but not currently (subsequent curried functions) + if (!isBind && bindData[1] & 1) { + bitmask |= 8; + } + // set curried arity if not yet set + if (isCurry && !(bindData[1] & 4)) { + bindData[5] = arity; + } + // append partial left arguments + if (isPartial) { + push.apply(bindData[2] || (bindData[2] = []), partialArgs); + } + // append partial right arguments + if (isPartialRight) { + unshift.apply(bindData[3] || (bindData[3] = []), partialRightArgs); + } + // merge flags + bindData[1] |= bitmask; + return createWrapper.apply(null, bindData); + } + // fast path for `_.bind` + var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper; + return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]); + } + + /** + * Creates compiled iteration functions. + * + * @private + * @param {...Object} [options] The compile options object(s). + * @param {string} [options.array] Code to determine if the iterable is an array or array-like. + * @param {boolean} [options.useHas] Specify using `hasOwnProperty` checks in the object loop. + * @param {Function} [options.keys] A reference to `_.keys` for use in own property iteration. + * @param {string} [options.args] A comma separated string of iteration function arguments. + * @param {string} [options.top] Code to execute before the iteration branches. + * @param {string} [options.loop] Code to execute in the object loop. + * @param {string} [options.bottom] Code to execute after the iteration branches. + * @returns {Function} Returns the compiled function. + */ + function createIterator() { + // data properties + iteratorData.shadowedProps = shadowedProps; + + // iterator options + iteratorData.array = iteratorData.bottom = iteratorData.loop = iteratorData.top = ''; + iteratorData.init = 'iterable'; + iteratorData.useHas = true; + + // merge options into a template data object + for (var object, index = 0; object = arguments[index]; index++) { + for (var key in object) { + iteratorData[key] = object[key]; + } + } + var args = iteratorData.args; + iteratorData.firstArg = /^[^,]+/.exec(args)[0]; + + // create the function factory + var factory = Function( + 'baseCreateCallback, errorClass, errorProto, hasOwnProperty, ' + + 'indicatorObject, isArguments, isArray, isString, keys, objectProto, ' + + 'objectTypes, nonEnumProps, stringClass, stringProto, toString', + 'return function(' + args + ') {\n' + iteratorTemplate(iteratorData) + '\n}' + ); + + // return the compiled function + return factory( + baseCreateCallback, errorClass, errorProto, hasOwnProperty, + indicatorObject, isArguments, isArray, isString, iteratorData.keys, objectProto, + objectTypes, nonEnumProps, stringClass, stringProto, toString + ); + } + + /** + * Checks if `value` is a native function. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is a native function, else `false`. + */ + function isNative(value) { + return typeof value == 'function' && reNative.test(value); + } + + /** + * Sets `this` binding data on a given function. + * + * @private + * @param {Function} func The function to set data on. + * @param {Array} value The data array to set. + */ + var setBindData = !defineProperty ? noop : function(func, value) { + descriptor.value = value; + defineProperty(func, '__bindData__', descriptor); + }; + + /** + * A fallback implementation of `isPlainObject` which checks if a given value + * is an object created by the `Object` constructor, assuming objects created + * by the `Object` constructor have no inherited enumerable properties and that + * there are no `Object.prototype` extensions. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + */ + function shimIsPlainObject(value) { + var ctor, + result; + + // avoid non Object objects, `arguments` objects, and DOM elements + if (!(value && toString.call(value) == objectClass) || + (ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor)) || + (!support.argsClass && isArguments(value)) || + (!support.nodeClass && isNode(value))) { + return false; + } + // IE < 9 iterates inherited properties before own properties. If the first + // iterated property is an object's own property then there are no inherited + // enumerable properties. + if (support.ownLast) { + forIn(value, function(value, key, object) { + result = hasOwnProperty.call(object, key); + return false; + }); + return result !== false; + } + // In most environments an object's own properties are iterated before + // its inherited properties. If the last iterated property is an object's + // own property then there are no inherited enumerable properties. + forIn(value, function(value, key) { + result = key; + }); + return typeof result == 'undefined' || hasOwnProperty.call(value, result); + } + + /*--------------------------------------------------------------------------*/ + + /** + * Checks if `value` is an `arguments` object. + * + * @static + * @memberOf _ + * @category Objects + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is an `arguments` object, else `false`. + * @example + * + * (function() { return _.isArguments(arguments); })(1, 2, 3); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + function isArguments(value) { + return value && typeof value == 'object' && typeof value.length == 'number' && + toString.call(value) == argsClass || false; + } + // fallback for browsers that can't detect `arguments` objects by [[Class]] + if (!support.argsClass) { + isArguments = function(value) { + return value && typeof value == 'object' && typeof value.length == 'number' && + hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee') || false; + }; + } + + /** + * Checks if `value` is an array. + * + * @static + * @memberOf _ + * @type Function + * @category Objects + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is an array, else `false`. + * @example + * + * (function() { return _.isArray(arguments); })(); + * // => false + * + * _.isArray([1, 2, 3]); + * // => true + */ + var isArray = nativeIsArray || function(value) { + return value && typeof value == 'object' && typeof value.length == 'number' && + toString.call(value) == arrayClass || false; + }; + + /** + * A fallback implementation of `Object.keys` which produces an array of the + * given object's own enumerable property names. + * + * @private + * @type Function + * @param {Object} object The object to inspect. + * @returns {Array} Returns an array of property names. + */ + var shimKeys = createIterator({ + 'args': 'object', + 'init': '[]', + 'top': 'if (!(objectTypes[typeof object])) return result', + 'loop': 'result.push(index)' + }); + + /** + * Creates an array composed of the own enumerable property names of an object. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns an array of property names. + * @example + * + * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); + * // => ['one', 'two', 'three'] (property order is not guaranteed across environments) + */ + var keys = !nativeKeys ? shimKeys : function(object) { + if (!isObject(object)) { + return []; + } + if ((support.enumPrototypes && typeof object == 'function') || + (support.nonEnumArgs && object.length && isArguments(object))) { + return shimKeys(object); + } + return nativeKeys(object); + }; + + /** Reusable iterator options shared by `each`, `forIn`, and `forOwn` */ + var eachIteratorOptions = { + 'args': 'collection, callback, thisArg', + 'top': "callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3)", + 'array': "typeof length == 'number'", + 'keys': keys, + 'loop': 'if (callback(iterable[index], index, collection) === false) return result' + }; + + /** Reusable iterator options for `assign` and `defaults` */ + var defaultsIteratorOptions = { + 'args': 'object, source, guard', + 'top': + 'var args = arguments,\n' + + ' argsIndex = 0,\n' + + " argsLength = typeof guard == 'number' ? 2 : args.length;\n" + + 'while (++argsIndex < argsLength) {\n' + + ' iterable = args[argsIndex];\n' + + ' if (iterable && objectTypes[typeof iterable]) {', + 'keys': keys, + 'loop': "if (typeof result[index] == 'undefined') result[index] = iterable[index]", + 'bottom': ' }\n}' + }; + + /** Reusable iterator options for `forIn` and `forOwn` */ + var forOwnIteratorOptions = { + 'top': 'if (!objectTypes[typeof iterable]) return result;\n' + eachIteratorOptions.top, + 'array': false + }; + + /** + * A function compiled to iterate `arguments` objects, arrays, objects, and + * strings consistenly across environments, executing the callback for each + * element in the collection. The callback is bound to `thisArg` and invoked + * with three arguments; (value, index|key, collection). Callbacks may exit + * iteration early by explicitly returning `false`. + * + * @private + * @type Function + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Array|Object|string} Returns `collection`. + */ + var baseEach = createIterator(eachIteratorOptions); + + /*--------------------------------------------------------------------------*/ + + /** + * Assigns own enumerable properties of source object(s) to the destination + * object. Subsequent sources will overwrite property assignments of previous + * sources. If a callback is provided it will be executed to produce the + * assigned values. The callback is bound to `thisArg` and invoked with two + * arguments; (objectValue, sourceValue). + * + * @static + * @memberOf _ + * @type Function + * @alias extend + * @category Objects + * @param {Object} object The destination object. + * @param {...Object} [source] The source objects. + * @param {Function} [callback] The function to customize assigning values. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns the destination object. + * @example + * + * _.assign({ 'name': 'fred' }, { 'employer': 'slate' }); + * // => { 'name': 'fred', 'employer': 'slate' } + * + * var defaults = _.partialRight(_.assign, function(a, b) { + * return typeof a == 'undefined' ? b : a; + * }); + * + * var object = { 'name': 'barney' }; + * defaults(object, { 'name': 'fred', 'employer': 'slate' }); + * // => { 'name': 'barney', 'employer': 'slate' } + */ + var assign = createIterator(defaultsIteratorOptions, { + 'top': + defaultsIteratorOptions.top.replace(';', + ';\n' + + "if (argsLength > 3 && typeof args[argsLength - 2] == 'function') {\n" + + ' var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);\n' + + "} else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {\n" + + ' callback = args[--argsLength];\n' + + '}' + ), + 'loop': 'result[index] = callback ? callback(result[index], iterable[index]) : iterable[index]' + }); + + /** + * Creates a clone of `value`. If `isDeep` is `true` nested objects will also + * be cloned, otherwise they will be assigned by reference. If a callback + * is provided it will be executed to produce the cloned values. If the + * callback returns `undefined` cloning will be handled by the method instead. + * The callback is bound to `thisArg` and invoked with one argument; (value). + * + * @static + * @memberOf _ + * @category Objects + * @param {*} value The value to clone. + * @param {boolean} [isDeep=false] Specify a deep clone. + * @param {Function} [callback] The function to customize cloning values. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {*} Returns the cloned value. + * @example + * + * var characters = [ + * { 'name': 'barney', 'age': 36 }, + * { 'name': 'fred', 'age': 40 } + * ]; + * + * var shallow = _.clone(characters); + * shallow[0] === characters[0]; + * // => true + * + * var deep = _.clone(characters, true); + * deep[0] === characters[0]; + * // => false + * + * _.mixin({ + * 'clone': _.partialRight(_.clone, function(value) { + * return _.isElement(value) ? value.cloneNode(false) : undefined; + * }) + * }); + * + * var clone = _.clone(document.body); + * clone.childNodes.length; + * // => 0 + */ + function clone(value, isDeep, callback, thisArg) { + // allows working with "Collections" methods without using their `index` + // and `collection` arguments for `isDeep` and `callback` + if (typeof isDeep != 'boolean' && isDeep != null) { + thisArg = callback; + callback = isDeep; + isDeep = false; + } + return baseClone(value, isDeep, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1)); + } + + /** + * Iterates over own and inherited enumerable properties of an object, + * executing the callback for each property. The callback is bound to `thisArg` + * and invoked with three arguments; (value, key, object). Callbacks may exit + * iteration early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @type Function + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns `object`. + * @example + * + * function Shape() { + * this.x = 0; + * this.y = 0; + * } + * + * Shape.prototype.move = function(x, y) { + * this.x += x; + * this.y += y; + * }; + * + * _.forIn(new Shape, function(value, key) { + * console.log(key); + * }); + * // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments) + */ + var forIn = createIterator(eachIteratorOptions, forOwnIteratorOptions, { + 'useHas': false + }); + + /** + * Iterates over own enumerable properties of an object, executing the callback + * for each property. The callback is bound to `thisArg` and invoked with three + * arguments; (value, key, object). Callbacks may exit iteration early by + * explicitly returning `false`. + * + * @static + * @memberOf _ + * @type Function + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns `object`. + * @example + * + * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { + * console.log(key); + * }); + * // => logs '0', '1', and 'length' (property order is not guaranteed across environments) + */ + var forOwn = createIterator(eachIteratorOptions, forOwnIteratorOptions); + + /** + * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a + * length of `0` and objects with no own enumerable properties are considered + * "empty". + * + * @static + * @memberOf _ + * @category Objects + * @param {Array|Object|string} value The value to inspect. + * @returns {boolean} Returns `true` if the `value` is empty, else `false`. + * @example + * + * _.isEmpty([1, 2, 3]); + * // => false + * + * _.isEmpty({}); + * // => true + * + * _.isEmpty(''); + * // => true + */ + function isEmpty(value) { + var result = true; + if (!value) { + return result; + } + var className = toString.call(value), + length = value.length; + + if ((className == arrayClass || className == stringClass || + (support.argsClass ? className == argsClass : isArguments(value))) || + (className == objectClass && typeof length == 'number' && isFunction(value.splice))) { + return !length; + } + forOwn(value, function() { + return (result = false); + }); + return result; + } + + /** + * Checks if `value` is a function. + * + * @static + * @memberOf _ + * @category Objects + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is a function, else `false`. + * @example + * + * _.isFunction(_); + * // => true + */ + function isFunction(value) { + return typeof value == 'function'; + } + // fallback for older versions of Chrome and Safari + if (isFunction(/x/)) { + isFunction = function(value) { + return typeof value == 'function' && toString.call(value) == funcClass; + }; + } + + /** + * Checks if `value` is the language type of Object. + * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @category Objects + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(1); + * // => false + */ + function isObject(value) { + // check if the value is the ECMAScript language type of Object + // http://es5.github.io/#x8 + // and avoid a V8 bug + // http://code.google.com/p/v8/issues/detail?id=2291 + return !!(value && objectTypes[typeof value]); + } + + /** + * Checks if `value` is an object created by the `Object` constructor. + * + * @static + * @memberOf _ + * @category Objects + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + * @example + * + * function Shape() { + * this.x = 0; + * this.y = 0; + * } + * + * _.isPlainObject(new Shape); + * // => false + * + * _.isPlainObject([1, 2, 3]); + * // => false + * + * _.isPlainObject({ 'x': 0, 'y': 0 }); + * // => true + */ + var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) { + if (!(value && toString.call(value) == objectClass) || (!support.argsClass && isArguments(value))) { + return false; + } + var valueOf = value.valueOf, + objProto = isNative(valueOf) && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto); + + return objProto + ? (value == objProto || getPrototypeOf(value) == objProto) + : shimIsPlainObject(value); + }; + + /** + * Checks if `value` is a string. + * + * @static + * @memberOf _ + * @category Objects + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is a string, else `false`. + * @example + * + * _.isString('fred'); + * // => true + */ + function isString(value) { + return typeof value == 'string' || + value && typeof value == 'object' && toString.call(value) == stringClass || false; + } + + /** + * Recursively merges own enumerable properties of the source object(s), that + * don't resolve to `undefined` into the destination object. Subsequent sources + * will overwrite property assignments of previous sources. If a callback is + * provided it will be executed to produce the merged values of the destination + * and source properties. If the callback returns `undefined` merging will + * be handled by the method instead. The callback is bound to `thisArg` and + * invoked with two arguments; (objectValue, sourceValue). + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The destination object. + * @param {...Object} [source] The source objects. + * @param {Function} [callback] The function to customize merging properties. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns the destination object. + * @example + * + * var names = { + * 'characters': [ + * { 'name': 'barney' }, + * { 'name': 'fred' } + * ] + * }; + * + * var ages = { + * 'characters': [ + * { 'age': 36 }, + * { 'age': 40 } + * ] + * }; + * + * _.merge(names, ages); + * // => { 'characters': [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] } + * + * var food = { + * 'fruits': ['apple'], + * 'vegetables': ['beet'] + * }; + * + * var otherFood = { + * 'fruits': ['banana'], + * 'vegetables': ['carrot'] + * }; + * + * _.merge(food, otherFood, function(a, b) { + * return _.isArray(a) ? a.concat(b) : undefined; + * }); + * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] } + */ + function merge(object) { + var args = arguments, + length = 2; + + if (!isObject(object)) { + return object; + } + // allows working with `_.reduce` and `_.reduceRight` without using + // their `index` and `collection` arguments + if (typeof args[2] != 'number') { + length = args.length; + } + if (length > 3 && typeof args[length - 2] == 'function') { + var callback = baseCreateCallback(args[--length - 1], args[length--], 2); + } else if (length > 2 && typeof args[length - 1] == 'function') { + callback = args[--length]; + } + var sources = slice(arguments, 1, length), + index = -1, + stackA = getArray(), + stackB = getArray(); + + while (++index < length) { + baseMerge(object, sources[index], callback, stackA, stackB); + } + releaseArray(stackA); + releaseArray(stackB); + return object; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Iterates over elements of a collection, executing the callback for each + * element. The callback is bound to `thisArg` and invoked with three arguments; + * (value, index|key, collection). Callbacks may exit iteration early by + * explicitly returning `false`. + * + * Note: As with other "Collections" methods, objects with a `length` property + * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn` + * may be used for object iteration. + * + * @static + * @memberOf _ + * @alias each + * @category Collections + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Array|Object|string} Returns `collection`. + * @example + * + * _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(','); + * // => logs each number and returns '1,2,3' + * + * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); }); + * // => logs each number and returns the object (property order is not guaranteed across environments) + */ + function forEach(collection, callback, thisArg) { + if (callback && typeof thisArg == 'undefined' && isArray(collection)) { + var index = -1, + length = collection.length; + + while (++index < length) { + if (callback(collection[index], index, collection) === false) { + break; + } + } + } else { + baseEach(collection, callback, thisArg); + } + return collection; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Creates an array with all falsey values removed. The values `false`, `null`, + * `0`, `""`, `undefined`, and `NaN` are all falsey. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to compact. + * @returns {Array} Returns a new array of filtered values. + * @example + * + * _.compact([0, 1, false, 2, '', 3]); + * // => [1, 2, 3] + */ + function compact(array) { + var index = -1, + length = array ? array.length : 0, + result = []; + + while (++index < length) { + var value = array[index]; + if (value) { + result.push(value); + } + } + return result; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Creates a function that, when called, invokes `func` with the `this` + * binding of `thisArg` and prepends any additional `bind` arguments to those + * provided to the bound function. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to bind. + * @param {*} [thisArg] The `this` binding of `func`. + * @param {...*} [arg] Arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * var func = function(greeting) { + * return greeting + ' ' + this.name; + * }; + * + * func = _.bind(func, { 'name': 'fred' }, 'hi'); + * func(); + * // => 'hi fred' + */ + function bind(func, thisArg) { + return arguments.length > 2 + ? createWrapper(func, 17, slice(arguments, 2), null, thisArg) + : createWrapper(func, 1, null, null, thisArg); + } + + /*--------------------------------------------------------------------------*/ + + /** + * This method returns the first argument provided to it. + * + * @static + * @memberOf _ + * @category Utilities + * @param {*} value Any value. + * @returns {*} Returns `value`. + * @example + * + * var object = { 'name': 'fred' }; + * _.identity(object) === object; + * // => true + */ + function identity(value) { + return value; + } + + /** + * A no-operation function. + * + * @static + * @memberOf _ + * @category Utilities + * @example + * + * var object = { 'name': 'fred' }; + * _.noop(object) === undefined; + * // => true + */ + function noop() { + // no operation performed + } + + /*--------------------------------------------------------------------------*/ + + lodash.assign = assign; + lodash.bind = bind; + lodash.compact = compact; + lodash.forEach = forEach; + lodash.forIn = forIn; + lodash.forOwn = forOwn; + lodash.keys = keys; + lodash.merge = merge; + + lodash.each = forEach; + lodash.extend = assign; + + /*--------------------------------------------------------------------------*/ + + // add functions that return unwrapped values when chaining + lodash.clone = clone; + lodash.identity = identity; + lodash.isArguments = isArguments; + lodash.isArray = isArray; + lodash.isEmpty = isEmpty; + lodash.isFunction = isFunction; + lodash.isObject = isObject; + lodash.isPlainObject = isPlainObject; + lodash.isString = isString; + lodash.noop = noop; + + /*--------------------------------------------------------------------------*/ + + /** + * The semantic version number. + * + * @static + * @memberOf _ + * @type string + */ + lodash.VERSION = '2.4.1'; + + /*--------------------------------------------------------------------------*/ + + if (freeExports && freeModule) { + // in Node.js or RingoJS + if (moduleExports) { + (freeModule.exports = lodash)._ = lodash; + } + + } + +}.call(this)); diff --git a/assets/osmtogeojson/osmtogeojson b/assets/osmtogeojson/osmtogeojson new file mode 100755 index 0000000..d149bec --- /dev/null +++ b/assets/osmtogeojson/osmtogeojson @@ -0,0 +1,147 @@ +#!/usr/bin/env node + +var osmtogeojson = require('./'), + opt = require('optimist') + .usage('Usage: $0 [-f format] [-e] [-v] FILE') + .string('f').describe('f', 'file format. if not given, will be detected from filename. supported values: osm, json') + .boolean('e').describe('e', 'enhanced properties. if set, the resulting GeoJSON feature\'s properties will contain more structured information') + .boolean('n').describe('n', 'numeric properties. if set, the resulting GeoJSON feature\'s properties will be numbers if possible') + .boolean('v').describe('v', 'verbose mode. output diagnostic information during processing') + .boolean('m').describe('m', 'minify output json (no identation and linebreaks)') + .boolean('version').describe('version','display software version') + .boolean('help').describe('help','print this help message'), + argv = opt.argv, + fs = require('fs'), + concat = require('concat-stream'), + xmldom = new (require('xmldom').DOMParser)(), + osmxmlParser = require('./parse_osmxml.js'), + JSONStream = require('JSONStream'), + geojsonNumeric = require('geojson-numeric'), + pack = require('./package.json'); + +if (argv.help) { + return opt.showHelp(); +} +if (argv.version) { + process.stdout.write(pack.version+'\n'); + return; +} + +var filename = argv._[0] || ''; + +var enhanced_geojson = argv.e; +var format = argv.f; + +if (format === 'xml') format = 'osm'; +// detect file format from filename +if (!format) { + if (filename.match(/\.osm$/i)) format = 'osm'; + if (filename.match(/\.xml$/i)) format = 'osm'; + if (filename.match(/\.json$/i)) format = 'json'; +} +// fall back to the native JSON parser if the file is small enough +// (unfortunately, the streaming JSON parser isn't very fast) +if (format === 'json' && filename) { + if (fs.statSync(filename).size < 268435577) + format = 'nativejson'; +} +// fall back to autodetection if still no format +if (!format) format = 'auto'; + +var datastream = (filename ? fs.createReadStream(filename) : process.stdin); + +// use streaming parsers if format is already known +switch(format) { +case 'json': +case 'streamjson': + datastream.pipe(JSONStream.parse()) + .on('root', function(data) { + // iron out some nasty floating point rounding errors + if (data.version) data.version = Math.round(data.version*1000)/1000; + data.elements.forEach(function(element) { + if (element.lat) element.lat = Math.round(element.lat*1E12)/1E12; + if (element.lon) element.lon = Math.round(element.lon*1E12)/1E12; + }); + // convert to geojson + convert(data); + }) + .on('error', function(err) { + process.stderr.write("ERROR: JSON input stream could not be parsed.\n"); + process.exit(1); + }); +break; +case 'osm': +case 'streamxml': + datastream + .on('data', function(chunk) { + osmxmlParser.write(chunk); + }) + .on('end', function() { + osmxmlParser.end(); + data = osmxmlParser.getJSON(); + convert(data); + }); + datastream.resume(); +break; +default: + // otherwise use leagacy non-streaming parsers + datastream.pipe(concat(legacyParsers)); +} + +function legacyParsers(data) { + if (!data) data = ''; else data = data.toString(); + if (format === 'auto') { + if (data.match(/^\s*0});y.center&&n(y),g?a(y):y.bounds&&t(y)}return s(i,l,u)}function i(e){function n(e,n,t){e.hasAttribute(t)&&(n[t]=e.getAttribute(t))}function t(e,t){var o=r.clone(e);n(t,o,"lat"),n(t,o,"lon"),o.__is_center_placeholder=!0,l.push(o)}function o(e,n){function t(e,n,t){var r={type:"node",id:"_"+o.type+"/"+o.id+"bounds"+t,lat:e,lon:n};o.nodes.push(r.id),l.push(r)}var o=r.clone(e);o.nodes=[],t(n.getAttribute("minlat"),n.getAttribute("minlon"),1),t(n.getAttribute("maxlat"),n.getAttribute("minlon"),2),t(n.getAttribute("maxlat"),n.getAttribute("maxlon"),3),t(n.getAttribute("minlat"),n.getAttribute("maxlon"),4),o.nodes.push(o.nodes[0]),o.__is_bounds_placeholder=!0,u.push(o)}function a(e,n){function t(e,n,t){var r={type:"node",id:t,lat:e,lon:n,__is_uninteresting:!0};return l.push(r),r.id}r.isArray(e.nodes)||(e.nodes=[],r.each(n,function(n,t){e.nodes.push("_anonymous@"+n.getAttribute("lat")+"/"+n.getAttribute("lon"))})),r.each(n,function(n,r){n.getAttribute("lat")&&t(n.getAttribute("lat"),n.getAttribute("lon"),e.nodes[r])})}function i(e,n){function t(e,n,t){var r={type:"node",id:t,lat:e,lon:n};l.push(r)}function o(e,n){function t(e,n){var t={type:"node",id:"_anonymous@"+e+"/"+n,lat:e,lon:n,__is_uninteresting:!0};o.nodes.push(t.id),l.push(t)}if(!u.some(function(e){return"way"==e.type&&e.id==n})){var o={type:"way",id:n,nodes:[]};r.each(e,function(e){e.getAttribute("lat")?t(e.getAttribute("lat"),e.getAttribute("lon")):o.nodes.push(void 0)}),u.push(o)}}r.each(n,function(n,r){"node"==e.members[r].type?n.getAttribute("lat")&&t(n.getAttribute("lat"),n.getAttribute("lon"),e.members[r].ref):"way"==e.members[r].type&&n.getElementsByTagName("nd").length>0&&(e.members[r].ref="_fullGeom"+e.members[r].ref,o(n.getElementsByTagName("nd"),e.members[r].ref))})}var l=new Array,u=new Array,c=new Array;r.each(e.getElementsByTagName("node"),function(e,t){var o={};r.each(e.getElementsByTagName("tag"),function(e){o[e.getAttribute("k")]=e.getAttribute("v")});var a={type:"node"};n(e,a,"id"),n(e,a,"lat"),n(e,a,"lon"),n(e,a,"version"),n(e,a,"timestamp"),n(e,a,"changeset"),n(e,a,"uid"),n(e,a,"user"),r.isEmpty(o)||(a.tags=o),l.push(a)});var f,p;return r.each(e.getElementsByTagName("way"),function(e,i){var s={},l=[];r.each(e.getElementsByTagName("tag"),function(e){s[e.getAttribute("k")]=e.getAttribute("v")});var c=!1;r.each(e.getElementsByTagName("nd"),function(e,n){var t;(t=e.getAttribute("ref"))&&(l[n]=t),!c&&e.getAttribute("lat")&&(c=!0)});var y={type:"way"};n(e,y,"id"),n(e,y,"version"),n(e,y,"timestamp"),n(e,y,"changeset"),n(e,y,"uid"),n(e,y,"user"),l.length>0&&(y.nodes=l),r.isEmpty(s)||(y.tags=s),(f=e.getElementsByTagName("center")[0])&&t(y,f),c?a(y,e.getElementsByTagName("nd")):(p=e.getElementsByTagName("bounds")[0])&&o(y,p),u.push(y)}),r.each(e.getElementsByTagName("relation"),function(e,a){var s={},l=[];r.each(e.getElementsByTagName("tag"),function(e){s[e.getAttribute("k")]=e.getAttribute("v")});var u=!1;r.each(e.getElementsByTagName("member"),function(e,t){l[t]={},n(e,l[t],"ref"),n(e,l[t],"role"),n(e,l[t],"type"),(!u&&"node"==l[t].type&&e.getAttribute("lat")||"way"==l[t].type&&e.getElementsByTagName("nd").length>0)&&(u=!0)});var y={type:"relation"};n(e,y,"id"),n(e,y,"version"),n(e,y,"timestamp"),n(e,y,"changeset"),n(e,y,"uid"),n(e,y,"user"),l.length>0&&(y.members=l),r.isEmpty(s)||(y.tags=s),(f=e.getElementsByTagName("center")[0])&&t(y,f),u?i(y,e.getElementsByTagName("member")):(p=e.getElementsByTagName("bounds")[0])&&o(y,p),c.push(y)}),s(l,u,c)}function s(e,t,a){function i(e,t){if("object"!=typeof t&&(t={}),"function"==typeof n.uninterestingTags)return!n.uninterestingTags(e,t);for(var r in e)if(n.uninterestingTags[r]!==!0&&t[r]!==!0&&t[r]!==e[r])return!0;return!1}function s(e){var n={timestamp:e.timestamp,version:e.version,changeset:e.changeset,user:e.user,uid:e.uid};for(var t in n)void 0===n[t]&&delete n[t];return n}function u(e,t){function o(e){for(var t,r,o,a,i,s,l=function(e){return e[0]},f=function(e){return e[e.length-1]},p=[];e.length;)for(t=e.pop().nodes.slice(),p.push(t);e.length&&l(t)!==f(t);){for(r=l(t),o=f(t),a=0;ar!=c>r&&t<(u-s)*(r-l)/(c-l)+s;f&&(o=!o)}return o};for(e=o(e),n=0;n3&&"function"==typeof n[t-2])var o=f(n[--t-1],n[t--],2);else t>2&&"function"==typeof n[t-1]&&(o=n[--t]);for(var s=i(arguments,1,t),l=-1,u=r(),c=r();++l2?g(e,17,i(arguments,2),null,n):g(e,1,null,null,n)}function T(e){return e}function N(){}var S=[],C={},M=40,L=/\w*$/,B=/^\s*function[ \n\r\t]+\w/,F=/\bthis\b/,I=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],D="[object Arguments]",R="[object Array]",G="[object Boolean]",H="[object Date]",U="[object Error]",W="[object Function]",q="[object Number]",$="[object Object]",X="[object RegExp]",V="[object String]",z={};z[W]=!1,z[D]=z[R]=z[G]=z[H]=z[q]=z[$]=z[X]=z[V]=!0;var J={configurable:!1,enumerable:!1,value:null,writable:!1},K={args:"",array:null,bottom:"",firstArg:"",init:"",keys:null,loop:"",shadowedProps:null,support:null,top:"",useHas:!1},Q={boolean:!1,function:!0,object:!0,number:!1,string:!1,undefined:!1},Y=Q[typeof window]&&window||this,Z=Q[typeof t]&&t&&!t.nodeType&&t,ee=Q[typeof n]&&n&&!n.nodeType&&n,ne=ee&&ee.exports===Z&&Z,te=Q[typeof e]&&e;!te||te.global!==te&&te.window!==te||(Y=te);var re=[],oe=Error.prototype,ae=Object.prototype,ie=String.prototype,se=ae.toString,le=RegExp("^"+String(se).replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/toString| for [^\]]+/g,".*?")+"$"),ue=Function.prototype.toString,ce=b(ce=Object.getPrototypeOf)&&ce,fe=ae.hasOwnProperty,pe=re.push,ye=ae.propertyIsEnumerable,ge=re.unshift,de=function(){try{var e={},n=b(n=Object.defineProperty)&&n,t=n(e,e,e)&&n}catch(e){}return t}(),be=b(be=Object.create)&&be,me=b(me=Array.isArray)&&me,he=b(he=Object.keys)&&he,ve={};ve[R]=Array,ve[G]=Boolean,ve[H]=Date,ve[W]=Function,ve[$]=Object,ve[q]=Number,ve[X]=RegExp,ve[V]=String;var we={};we[R]=we[H]=we[q]={constructor:!0,toLocaleString:!0,toString:!0,valueOf:!0},we[G]=we[V]={constructor:!0,toString:!0,valueOf:!0},we[U]=we[W]=we[X]={constructor:!0,toString:!0},we[$]={constructor:!0},function(){for(var e=I.length;e--;){var n=I[e];for(var t in we)fe.call(we,t)&&!fe.call(we[t],n)&&(we[t][n]=!1)}}();var _e=s.support={};!function(){var e=function(){this.x=1},n={0:1,length:1},t=[];e.prototype={valueOf:1,y:1};for(var r in new e)t.push(r);for(r in arguments);_e.argsClass=se.call(arguments)==D,_e.argsObject=arguments.constructor==Object&&!(arguments instanceof Array),_e.enumErrorProps=ye.call(oe,"message")||ye.call(oe,"name"),_e.enumPrototypes=ye.call(e,"prototype"),_e.funcDecomp=!b(Y.WinRTError)&&F.test(function(){return this}),_e.funcNames="string"==typeof Function.name,_e.nonEnumArgs=0!=r,_e.nonEnumShadows=!/valueOf/.test(t),_e.ownLast="x"!=t[0],_e.spliceObjects=(re.splice.call(n,0,1),!n[0]),_e.unindexedChars="x"[0]+Object("x")[0]!="xx";try{_e.nodeClass=!(se.call(document)==$&&!({toString:0}+""))}catch(e){_e.nodeClass=!0}}(1);var xe=function(e){var n="var index, iterable = "+e.firstArg+", result = "+e.init+";\nif (!iterable) return result;\n"+e.top+";";e.array?(n+="\nvar length = iterable.length; index = -1;\nif ("+e.array+") { ",_e.unindexedChars&&(n+="\n if (isString(iterable)) {\n iterable = iterable.split('')\n } "),n+="\n while (++index < length) {\n "+e.loop+";\n }\n}\nelse { "):_e.nonEnumArgs&&(n+="\n var length = iterable.length; index = -1;\n if (length && isArguments(iterable)) {\n while (++index < length) {\n index += '';\n "+e.loop+";\n }\n } else { "),_e.enumPrototypes&&(n+="\n var skipProto = typeof iterable == 'function';\n "),_e.enumErrorProps&&(n+="\n var skipErrorProps = iterable === errorProto || iterable instanceof Error;\n ");var t=[];if(_e.enumPrototypes&&t.push('!(skipProto && index == "prototype")'),_e.enumErrorProps&&t.push('!(skipErrorProps && (index == "message" || index == "name"))'),e.useHas&&e.keys)n+="\n var ownIndex = -1,\n ownProps = objectTypes[typeof iterable] && keys(iterable),\n length = ownProps ? ownProps.length : 0;\n\n while (++ownIndex < length) {\n index = ownProps[ownIndex];\n",t.length&&(n+=" if ("+t.join(" && ")+") {\n "),n+=e.loop+"; ",t.length&&(n+="\n }"),n+="\n } ";else if(n+="\n for (index in iterable) {\n",e.useHas&&t.push("hasOwnProperty.call(iterable, index)"),t.length&&(n+=" if ("+t.join(" && ")+") {\n "),n+=e.loop+"; ",t.length&&(n+="\n }"),n+="\n } ",_e.nonEnumShadows){for(n+="\n\n if (iterable !== objectProto) {\n var ctor = iterable.constructor,\n isProto = iterable === (ctor && ctor.prototype),\n className = iterable === stringProto ? stringClass : iterable === errorProto ? errorClass : toString.call(iterable),\n nonEnum = nonEnumProps[className];\n ",k=0;k<7;k++)n+="\n index = '"+e.shadowedProps[k]+"';\n if ((!(isProto && nonEnum[index]) && hasOwnProperty.call(iterable, index))",e.useHas||(n+=" || (!nonEnum[index] && iterable[index] !== objectProto[index])"),n+=") {\n "+e.loop+";\n } ";n+="\n } "}return(e.array||_e.nonEnumArgs)&&(n+="\n}"),n+=e.bottom+";\nreturn result"};be||(c=function(){function e(){}return function(n){if(x(n)){e.prototype=n;var t=new e;e.prototype=null}return t||Y.Object()}}());var ke=de?function(e,n){J.value=n,de(e,"__bindData__",J)}:N;_e.argsClass||(h=function(e){return e&&"object"==typeof e&&"number"==typeof e.length&&fe.call(e,"callee")&&!ye.call(e,"callee")||!1});var Ae=me||function(e){return e&&"object"==typeof e&&"number"==typeof e.length&&se.call(e)==R||!1},je=d({args:"object",init:"[]",top:"if (!(objectTypes[typeof object])) return result",loop:"result.push(index)"}),Ee=he?function(e){return x(e)?_e.enumPrototypes&&"function"==typeof e||_e.nonEnumArgs&&e.length&&h(e)?je(e):he(e):[]}:je,Pe={args:"collection, callback, thisArg",top:"callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3)",array:"typeof length == 'number'",keys:Ee,loop:"if (callback(iterable[index], index, collection) === false) return result"},Oe={args:"object, source, guard",top:"var args = arguments,\n argsIndex = 0,\n argsLength = typeof guard == 'number' ? 2 : args.length;\nwhile (++argsIndex < argsLength) {\n iterable = args[argsIndex];\n if (iterable && objectTypes[typeof iterable]) {",keys:Ee,loop:"if (typeof result[index] == 'undefined') result[index] = iterable[index]",bottom:" }\n}"},Te={top:"if (!objectTypes[typeof iterable]) return result;\n"+Pe.top,array:!1},Ne=d(Pe),Se=d(Oe,{top:Oe.top.replace(";",";\nif (argsLength > 3 && typeof args[argsLength - 2] == 'function') {\n var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);\n} else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {\n callback = args[--argsLength];\n}"),loop:"result[index] = callback ? callback(result[index], iterable[index]) : iterable[index]"}),Ce=d(Pe,Te,{useHas:!1}),Me=d(Pe,Te);_(/x/)&&(_=function(e){return"function"==typeof e&&se.call(e)==W});var Le=ce?function(e){if(!e||se.call(e)!=$||!_e.argsClass&&h(e))return!1;var n=e.valueOf,t=b(n)&&(t=ce(n))&&ce(t);return t?e==t||ce(e)==t:m(e)}:m;s.assign=Se,s.bind=O,s.compact=P,s.forEach=E,s.forIn=Ce,s.forOwn=Me,s.keys=Ee,s.merge=j,s.each=E,s.extend=Se,s.clone=v,s.identity=T,s.isArguments=h,s.isArray=Ae,s.isEmpty=w,s.isFunction=_,s.isObject=x,s.isPlainObject=Le,s.isString=A,s.noop=N,s.VERSION="2.4.1",Z&&ee&&ne&&((ee.exports=s)._=s)}).call(this)}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],3:[function(e,n,t){function r(e,n){switch(e&&e.type||null){case"FeatureCollection":return e.features=e.features.map(o(r,n)),e;case"Feature":return e.geometry=r(e.geometry,n),e;case"Polygon":case"MultiPolygon":return a(e,n);default:return e}}function o(e,n){return function(t){return e(t,n)}}function a(e,n){return"Polygon"===e.type?e.coordinates=i(e.coordinates,n):"MultiPolygon"===e.type&&(e.coordinates=e.coordinates.map(o(i,n))),e}function i(e,n){n=!!n,e[0]=s(e[0],!n);for(var t=1;t=0}var u=e("geojson-area");n.exports=r},{"geojson-area":4}],4:[function(e,n,t){function r(e){if("Polygon"===e.type)return o(e.coordinates);if("MultiPolygon"===e.type){for(var n=0,t=0;t0){n+=Math.abs(a(e[0]));for(var t=1;t2){for(var t,r,o=0;o /dev/null", + "test": "npm run test-lib && npm run test-cli", + "test-lib": "mocha -R spec", + "test-cli": "node test-cli/cli.test.js | faucet" + }, + "bin": { + "osmtogeojson": "osmtogeojson" + }, + "repository": { + "type": "git", + "url": "https://github.com/tyrasd/osmtogeojson.git" + }, + "keywords": [ + "openstreetmap", + "geojson" + ], + "author": "Martin Raifer", + "license": "MIT", + "bugs": { + "url": "https://github.com/tyrasd/osmtogeojson/issues" + }, + "dependencies": { + "JSONStream": "0.8.0", + "concat-stream": "~1.0.1", + "geojson-numeric": "0.2.0", + "geojson-rewind": "0.1.0", + "htmlparser2": "3.5.1", + "optimist": "~0.3.5", + "osm-polygon-features": "^0.9.1", + "xmldom": "~0.1.16" + }, + "devDependencies": { + "expect.js": "~0.2.0", + "mocha": "~1.12.0", + "tape": "~2.10.2", + "faucet": "~0.0.1" + }, + "engines": { + "node": ">=0.5" + } +} diff --git a/assets/osmtogeojson/parse_osmxml.js b/assets/osmtogeojson/parse_osmxml.js new file mode 100644 index 0000000..54e8cbf --- /dev/null +++ b/assets/osmtogeojson/parse_osmxml.js @@ -0,0 +1,91 @@ +/* converts OSM XML to OSM JSON using a fast streaming parser */ +var htmlparser = require('htmlparser2'); +var _ = require("./lodash.custom.js"); + +var json = { + "version": 0.6, + "elements": [] +}; +var buffer = {}; +var p = new htmlparser.Parser({ + onopentag: function(name, attr) { + switch (name) { + case "node": + case "way": + case "relation": + buffer = { + type: name, + tags: {} + } + _.merge(buffer, attr); + if (name === "way") { + buffer.nodes = []; + buffer.geometry = []; + } + if (name === "relation") { + buffer.members = []; + buffer.nodes = []; + buffer.geometry = []; + } + break; + case "tag": + buffer.tags[attr.k] = attr.v; + break; + case "nd": + buffer.nodes.push(attr.ref); + if (attr.lat) { + buffer.geometry.push({ + lat: attr.lat, + lon: attr.lon + }); + } else { + buffer.geometry.push(null); + } + break; + case "member": + buffer.members.push(attr); + break; + case "center": + buffer.center = { + lat: attr.lat, + lon: attr.lon + }; + break; + case "bounds": + buffer.bounds = { + minlat: attr.minlat, + minlon: attr.minlon, + maxlat: attr.maxlat, + maxlon: attr.maxlon + }; + } + }, + ontext: function(text) { + }, + onclosetag: function(name) { + if (name === "node" || name === "way" || name === "relation" || name === "area") { + // remove empty geometry or nodes arrays + if (buffer.geometry && buffer.geometry.every(function(g) {return g===null;})) + delete buffer.geometry; + if (name === "relation") + delete buffer.nodes; + json.elements.push(buffer); + } + if (name === "member") { + if (buffer.geometry) { + buffer.members[buffer.members.length-1].geometry = buffer.geometry; + buffer.geometry = []; + } + } + } +}, { + decodeEntities: true, + xmlMode: true +}); + +p.parseFromString = function(xml_str) { p.write(xml_str); p.end(); return json; } +p.getJSON = function() { + return json; +} + +module.exports = p; diff --git a/gulpfile.js b/gulpfile.js index 537a5cd..b5ad94d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -29,6 +29,10 @@ var paths = [ dest: 'assets/leaflet-fullscreen', css: 'Control.FullScreen.css', js: 'Control.FullScreen.js' + }, + { + dest: 'assets/leaflet-layer-overpass', + css: 'OverPassLayer.css' } ]; diff --git a/module/config/config.php b/module/config/config.php index 08d0ec9..9948d78 100644 --- a/module/config/config.php +++ b/module/config/config.php @@ -21,7 +21,7 @@ $GLOBALS['LEAFLET_LIBRARIES']['leaflet'] = array 'license' => 'BSD-2-Clause', 'homepage' => 'http://leafletjs.com', 'css' => 'assets/leaflet/libs/leaflet/leaflet.min.css', - 'javascript' => 'assets/leaflet/libs/leaflet/leaflet.js', + 'javascript' => 'assets/leaflet/libs/leaflet/leaflet-src.js', ); $GLOBALS['LEAFLET_LIBRARIES']['leaflet-providers'] = array @@ -82,6 +82,25 @@ $GLOBALS['LEAFLET_LIBRARIES']['leaflet-control-geocoder'] = array 'javascript' => 'assets/leaflet/libs/control-geocoder/Control.Geocoder.min.js' ); +$GLOBALS['LEAFLET_LIBRARIES']['leaflet-extra-markers'] = array +( + 'name' => 'Leaflet Extra Markers', + 'version' => '1.0.6', + 'license' => 'MIT', + 'homepage' => 'https://github.com/coryasilva/Leaflet.ExtraMarkers', + 'css' => 'assets/leaflet/libs/leaflet-extra-markers/css/leaflet.extra-markers.min.css', + 'javascript' => 'assets/leaflet/libs/leaflet-extra-markers/js/leaflet.extra-markers.min.js' +); + +$GLOBALS['LEAFLET_LIBRARIES']['osmtogeojson'] = array +( + 'name' => 'osmtogeojson', + 'version' => '2.2.12', + 'license' => 'MIT', + 'homepage' => 'https://github.com/tyrasd/osmtogeojson', + 'javascript' => 'assets/leaflet/libs/osmtogeojson/osmtogeojson.js' +); + $GLOBALS['LEAFLET_LIBRARIES']['spin.js'] = array ( 'name' => 'spin.js',