diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..dc28f0f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: node_js + +node_js: + - "4" + +script: "make ci" + +branches: + only: + - master diff --git a/Makefile b/Makefile index bf2bfda..1e0fb7e 100644 --- a/Makefile +++ b/Makefile @@ -8,27 +8,28 @@ CSS_COMBINED := $(BUILD_DIR)/ol3-geocoder.css CSS_FINAL := $(BUILD_DIR)/ol3-geocoder.min.css TMPFILE := $(BUILD_DIR)/tmp PACKAGE_JSON := $(ROOT_DIR)/package.json -LAST_VERSION := $(shell cat $(PACKAGE_JSON) | node -pe "JSON.parse(require('fs').readFileSync('/dev/stdin').toString()).version") +LAST_VERSION := $(shell node -p "require('./package.json').version") JS_FILES := $(SRC_DIR)/wrapper-head.js \ + $(SRC_DIR)/utils.js \ $(SRC_DIR)/base.js \ $(SRC_DIR)/nominatim.js \ - $(SRC_DIR)/utils.js \ $(SRC_DIR)/wrapper-tail.js CSS_FILES := $(SRC_DIR)/ol3-geocoder.css -CLEANCSS := ./node_modules/.bin/cleancss +NODE_MODULES := ./node_modules/.bin +CLEANCSS := $(NODE_MODULES)/cleancss CLEANCSSFLAGS := --skip-restructuring -POSTCSS := ./node_modules/.bin/postcss +POSTCSS := $(NODE_MODULES)/postcss POSTCSSFLAGS := --use autoprefixer -b "last 2 versions" -JSHINT := ./node_modules/.bin/jshint -UGLIFYJS := ./node_modules/.bin/uglifyjs -UGLIFYJSFLAGS := --mangle --mangle-regex --screw-ie8 --lint -c warnings=true -JS_BEAUTIFY := ./node_modules/.bin/js-beautify +ESLINT := $(NODE_MODULES)/eslint +UGLIFYJS := $(NODE_MODULES)/uglifyjs +UGLIFYJSFLAGS := --mangle --mangle-regex --screw-ie8 -c warnings=false +JS_BEAUTIFY := $(NODE_MODULES)/js-beautify BEAUTIFYFLAGS := -f - --indent-size 2 --preserve-newlines -NODEMON := ./node_modules/.bin/nodemon -PARALLELSHELL := ./node_modules/.bin/parallelshell +NODEMON := $(NODE_MODULES)/nodemon +PARALLELSHELL := $(NODE_MODULES)/parallelshell # just to create variables like NODEMON_JS_FLAGS when called define NodemonFlags @@ -46,6 +47,10 @@ endef export HEADER # targets + +.PHONY: ci +ci: build + build-watch: build watch watch: @@ -53,17 +58,17 @@ watch: build: build-js build-css -build-js: combine-js jshint uglifyjs addheader - @echo "Build JS ... OK" +build-js: combine-js lint uglifyjs addheader + @echo `date +'%H:%M:%S'` " - build JS ... OK" build-css: combine-css cleancss - @echo "Build CSS ... OK" + @echo `date +'%H:%M:%S'` " - build CSS ... OK" uglifyjs: $(JS_DEBUG) @$(UGLIFYJS) $^ $(UGLIFYJSFLAGS) > $(JS_FINAL) -jshint: $(JS_DEBUG) - @$(JSHINT) $^ +lint: $(JS_DEBUG) + @$(ESLINT) $^ addheader-debug: $(JS_DEBUG) @echo "$$HEADER" | cat - $^ > $(TMPFILE) && mv $(TMPFILE) $^ diff --git a/build/.jshintrc b/build/.jshintrc deleted file mode 100644 index fefa45c..0000000 --- a/build/.jshintrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "laxbreak": true, - "-W002": true -} diff --git a/build/ol3-geocoder-debug.js b/build/ol3-geocoder-debug.js index 8038860..6d0b702 100644 --- a/build/ol3-geocoder-debug.js +++ b/build/ol3-geocoder-debug.js @@ -1,967 +1,945 @@ // Geocoder Nominatim for OpenLayers 3. // https://github.com/jonataswalker/ol3-geocoder -// Version: v1.6.0 -// Built: 2016-03-28T09:56:51-0300 - -(function(window, document) { - 'use strict'; - - this.Geocoder = (function() { - - /** - * @constructor - * @extends {ol.control.Control} - * @fires {Geocoder.EventType} - * @param {string} control_type Nominatim|Reverse. - * @param {object|undefined} opt_options Options. - */ - var Geocoder = function(control_type, opt_options) { - utils.assert(typeof control_type === 'string', '@param `control_type`' + - ' should be string type!' +// Version: v1.7.0 +// Built: 2016-04-01T08:24:58-0300 + +'use strict'; + +(function(root, factory) { + if (typeof exports === 'object') { + module.exports = factory(); + } else if (typeof define === 'function' && define.amd) { + define([], factory); + } else { + root.Geocoder = factory(); + } +}(this, function() { + + var G = {}; + /** + * Helper + */ + var utils = { + whiteSpaceRegex: /\s+/, + toQueryString: function(obj) { + return Object.keys(obj).reduce(function(a, k) { + a.push((typeof obj[k] === 'object') ? + utils.toQueryString(obj[k]) : + encodeURIComponent(k) + '=' + encodeURIComponent(obj[k]) + ); + return a; + }, []).join('&'); + }, + encodeUrlXhr: function(url, data) { + if (data && typeof(data) === 'object') { + var str_data = utils.toQueryString(data); + url += (/\?/.test(url) ? '&' : '?') + str_data; + } + return url; + }, + json: function(url, data) { + var xhr = new XMLHttpRequest(), + when = {}, + onload = function() { + if (xhr.status === 200) { + when.ready.call(undefined, JSON.parse(xhr.response)); + } + }, + onerror = function() { + console.info('Cannot XHR ' + JSON.stringify(url)); + }; + url = utils.encodeUrlXhr(url, data); + xhr.open('GET', url, true); + xhr.setRequestHeader('Accept', 'application/json'); + xhr.onload = onload; + xhr.onerror = onerror; + xhr.send(null); + + return { + when: function(obj) { + when.ready = obj.ready; + } + }; + }, + randomId: function(prefix) { + var id = (new Date().getTime()).toString(36); + return (prefix) ? prefix + id : id; + }, + to3857: function(coord) { + return ol.proj.transform( + [parseFloat(coord[0]), parseFloat(coord[1])], + 'EPSG:4326', 'EPSG:3857' ); - utils.assert(typeof opt_options === 'object' || typeof opt_options === 'undefined', - '@param `opt_options` should be object|undefined type!' + }, + to4326: function(coord) { + return ol.proj.transform( + [parseFloat(coord[0]), parseFloat(coord[1])], + 'EPSG:3857', 'EPSG:4326' ); - - control_type = control_type || 'nominatim'; - - var nominatim = new Geocoder.Nominatim(this, opt_options); - this.layer = nominatim.layer; - - ol.control.Control.call(this, { - element: nominatim.els.container - }); - }; - ol.inherits(Geocoder, ol.control.Control); - + }, + isNumeric: function(str) { + return /^\d+$/.test(str); + }, + classRegex: function(classname) { + return new RegExp('(^|\\s+)' + classname + '(\\s+|$)'); + }, /** - * @return {ol.source.Vector} Returns the source created by this control + * @param {Element|Array} element DOM node or array of nodes. + * @param {String|Array} classname Class or array of classes. + * For example: 'class1 class2' or ['class1', 'class2'] + * @param {Number|undefined} timeout Timeout to remove a class. */ - Geocoder.prototype.getSource = function() { - return this.layer.getSource(); - }; - + addClass: function(element, classname, timeout) { + if (Array.isArray(element)) { + element.forEach(function(each) { + utils.addClass(each, classname); + }); + return; + } + + var + array = (Array.isArray(classname)) ? classname : classname.split(/\s+/), + i = array.length; + while (i--) { + if (!utils.hasClass(element, array[i])) { + utils._addClass(element, array[i], timeout); + } + } + }, + _addClass: function(el, c, timeout) { + // use native if available + if (el.classList) { + el.classList.add(c); + } else { + el.className = (el.className + ' ' + c).trim(); + } + + if (timeout && utils.isNumeric(timeout)) { + window.setTimeout(function() { + utils._removeClass(el, c); + }, timeout); + } + }, /** - * @return {ol.layer.Vector} Returns the layer created by this control + * @param {Element|Array} element DOM node or array of nodes. + * @param {String|Array} classname Class or array of classes. + * For example: 'class1 class2' or ['class1', 'class2'] + * @param {Number|undefined} timeout Timeout to add a class. */ - Geocoder.prototype.getLayer = function() { - return this.layer; - }; - (function(Geocoder) { - - Geocoder.Nominatim = function(geocoder, opt_options) { - this.geocoder = geocoder; - this.layer_name = utils.randomId('geocoder-layer-'); - this.layer = new ol.layer.Vector({ - name: this.layer_name, - source: new ol.source.Vector() + removeClass: function(element, classname, timeout) { + if (Array.isArray(element)) { + element.forEach(function(each) { + utils.removeClass(each, classname, timeout); }); - var defaults = { - provider: 'osm', - placeholder: 'Search for an address', - featureStyle: Geocoder.Nominatim.featureStyle, - lang: 'en-US', - limit: 5, - keepOpen: false, - debug: false - }; - - this.options = utils.mergeOptions(defaults, opt_options); - this.options.provider = this.options.provider.toLowerCase(); - this.constants = { - road: 'ol-geocoder-road', - city: 'ol-geocoder-city', - country: 'ol-geocoder-country', - class_container: 'ol-geocoder', - expanded_class: 'ol-geocoder-search-expanded' - }; + return; + } + + var + array = (Array.isArray(classname)) ? classname : classname.split(/\s+/), + i = array.length; + while (i--) { + if (utils.hasClass(element, array[i])) { + utils._removeClass(element, array[i], timeout); + } + } + }, + _removeClass: function(el, c, timeout) { + if (el.classList) { + el.classList.remove(c); + } else { + el.className = (el.className.replace(utils.classRegex(c), ' ')).trim(); + } + if (timeout && utils.isNumeric(timeout)) { + window.setTimeout(function() { + utils._addClass(el, c); + }, timeout); + } + }, + /** + * @param {Element} element DOM node. + * @param {String} classname Classname. + * @return {Boolean} + */ + hasClass: function(element, c) { + // use native if available + return (element.classList) ? + element.classList.contains(c) : utils.classRegex(c).test(element.className); + }, + /** + * @param {Element|Array} element DOM node or array of nodes. + * @param {String} classname Classe. + */ + toggleClass: function(element, classname) { + if (Array.isArray(element)) { + element.forEach(function(each) { + utils.toggleClass(each, classname); + }); + return; + } + + // use native if available + if (element.classList) { + element.classList.toggle(classname); + } else { + if (utils.hasClass(element, classname)) { + utils._removeClass(element, classname); + } else { + utils._addClass(element, classname); + } + } + }, + $: function(id) { + id = (id[0] === '#') ? id.substr(1, id.length) : id; + return document.getElementById(id); + }, + isElement: function(obj) { + // DOM, Level2 + if ('HTMLElement' in window) { + return (!!obj && obj instanceof HTMLElement); + } + // Older browsers + return (!!obj && typeof obj === 'object' && + obj.nodeType === 1 && !!obj.nodeName); + }, + getAllChildren: function(node, tag) { + return [].slice.call(node.getElementsByTagName(tag)); + }, + isEmpty: function(str) { + return (!str || 0 === str.length); + }, + emptyArray: function(array) { + while (array.length) array.pop(); + }, + anyMatchInArray: function(source, target) { + return source.some(function(each) { + return target.indexOf(each) >= 0; + }); + }, + everyMatchInArray: function(arr1, arr2) { + return arr2.every(function(each) { + return arr1.indexOf(each) >= 0; + }); + }, + anyItemHasValue: function(obj) { + var has = false; + for (var key in obj) { + if (!utils.isEmpty(obj[key])) { + has = true; + } + } + return has; + }, + removeAllChildren: function(node) { + while (node.firstChild) { + node.removeChild(node.firstChild); + } + }, + removeAll: function(collection) { + var node; + while ((node = collection[0])) { + node.parentNode.removeChild(node); + } + }, + getChildren: function(node, tag) { + return [].filter.call(node.childNodes, function(el) { + return (tag) ? + el.nodeType == 1 && el.tagName.toLowerCase() == tag : + el.nodeType == 1; + }); + }, + template: function(html, row) { + var this_ = this; - this.createControl(); - this.els = Geocoder.Nominatim.elements; - this.registered_listeners = { - map_click: false - }; - this.setListeners(); - return this; - }; + return html.replace(/\{ *([\w_-]+) *\}/g, function(html, key) { + var value = (row[key] === undefined) ? '' : row[key]; + return this_.htmlEscape(value); + }); + }, + htmlEscape: function(str) { + return String(str) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + }, + /** + * Overwrites obj1's values with obj2's and adds + * obj2's if non existent in obj1 + * @returns obj3 a new object based on obj1 and obj2 + */ + mergeOptions: function(obj1, obj2) { + var obj3 = {}; + for (var attr1 in obj1) { + obj3[attr1] = obj1[attr1]; + } + for (var attr2 in obj2) { + obj3[attr2] = obj2[attr2]; + } + return obj3; + }, + createElement: function(node, html) { + var elem; + if (Array.isArray(node)) { + elem = document.createElement(node[0]); + + if (node[1].id) { + elem.id = node[1].id; + } + if (node[1].classname) { + elem.className = node[1].classname; + } - Geocoder.Nominatim.prototype = { - createControl: function() { - var container = utils.createElement([ - 'div', { - classname: this.constants.class_container + if (node[1].attr) { + var attr = node[1].attr; + if (Array.isArray(attr)) { + var i = -1; + while (++i < attr.length) { + elem.setAttribute(attr[i].name, attr[i].value); } - ], Geocoder.Nominatim.html); - - Geocoder.Nominatim.elements = { - container: container, - control: container.querySelector('.ol-geocoder-search'), - btn_search: container.querySelector('.ol-geocoder-btn-search'), - input_search: container.querySelector('.ol-geocoder-input-search'), - result_container: container.querySelector('.ol-geocoder-result') - }; - //set placeholder from options - Geocoder.Nominatim.elements.input_search.placeholder = - this.options.placeholder; - - return container; - }, - setListeners: function() { - var - this_ = this, - openSearch = function() { - if (utils.hasClass(this_.els.control, this_.constants.expanded_class)) { - this_.collapse(); - } else { - this_.expand(); - } - }, - query = function(evt) { - if (evt.keyCode == 13) { //enter key - var q = utils.htmlEscape(this_.els.input_search.value); - this_.query(q); - } - }; - this_.els.input_search.addEventListener('keydown', query, false); - this_.els.btn_search.addEventListener('click', openSearch, false); - }, - listenMapClick: function() { - if (this.registered_listeners.map_click) { - // already registered - return; + } else { + elem.setAttribute(attr.name, attr.value); } + } + } else { + elem = document.createElement(node); + } + elem.innerHTML = html; + var frag = document.createDocumentFragment(); + + while (elem.childNodes[0]) { + frag.appendChild(elem.childNodes[0]); + } + elem.appendChild(frag); + return elem; + }, + assert: function(condition, message) { + if (!condition) { + message = message || 'Assertion failed'; + if (typeof Error !== 'undefined') { + throw new Error(message); + } + throw message; // Fallback + } + }, + assertEqual: function(a, b, message) { + if (a != b) { + throw new Error(message + ' mismatch: ' + a + ' != ' + b); + } + } + }; + /** + * @constructor + * @extends {ol.control.Control} + * @fires {G.EventType} + * @param {string} control_type Nominatim|Reverse. + * @param {object|undefined} opt_options Options. + */ + G.Base = function(control_type, opt_options) { + utils.assert(typeof control_type === 'string', '@param `control_type`' + + ' should be string type!' + ); + utils.assert(typeof opt_options === 'object' || typeof opt_options === 'undefined', + '@param `opt_options` should be object|undefined type!' + ); + + control_type = control_type || 'nominatim'; + + G.$base = this; + G.$nominatim = new G.Nominatim(opt_options); + + ol.control.Control.call(this, { + element: G.$nominatim.container + }); + }; + ol.inherits(G.Base, ol.control.Control); + + /** + * @return {ol.source.Vector} Returns the source created by this control + */ + G.Base.prototype.getSource = function() { + return this.getLayer().getSource(); + }; + + /** + * @return {ol.layer.Vector} Returns the layer created by this control + */ + G.Base.prototype.getLayer = function() { + return G.$nominatim.layer; + }; + /** + * @constructor + */ + G.Nominatim = function(opt_options) { + this.layer_name = utils.randomId('geocoder-layer-'); + this.layer = new ol.layer.Vector({ + name: this.layer_name, + source: new ol.source.Vector() + }); + var defaults = { + provider: 'osm', + placeholder: 'Search for an address', + featureStyle: G.Nominatim.featureStyle, + lang: 'en-US', + limit: 5, + keepOpen: false, + debug: false + }; - var this_ = this; - var map_element = this.geocoder.getMap().getTargetElement(); - this.registered_listeners.map_click = true; + this.options = utils.mergeOptions(defaults, opt_options); + this.options.provider = this.options.provider.toLowerCase(); + this.constants = { + road: 'ol-geocoder-road', + city: 'ol-geocoder-city', + country: 'ol-geocoder-country', + class_container: 'ol-geocoder', + expanded_class: 'ol-geocoder-search-expanded' + }; - //one-time fire click - map_element.addEventListener('click', { - handleEvent: function(evt) { - this_.clearResults(true); - map_element.removeEventListener(evt.type, this, false); - this_.registered_listeners.map_click = false; - } - }, false); - }, - expand: function() { - utils.removeClass(this.els.input_search, 'ol-geocoder-loading'); - utils.addClass(this.els.control, this.constants.expanded_class); - var input = this.els.input_search; - window.setTimeout(function() { - input.focus(); - }, 100); - this.listenMapClick(); - }, - collapse: function() { - this.els.input_search.value = ""; - this.els.input_search.blur(); - utils.removeClass(this.els.control, this.constants.expanded_class); - this.clearResults(); - }, - clearResults: function(collapse) { - if (collapse) { - this.collapse(); + this.container = this.createControl(); + this.els = G.Nominatim.elements; + this.registered_listeners = { + map_click: false + }; + this.setListeners(); + return this; + }; + + G.Nominatim.prototype = { + createControl: function() { + var container = utils.createElement([ + 'div', { + classname: this.constants.class_container + } + ], G.Nominatim.html); + + G.Nominatim.elements = { + container: container, + control: container.querySelector('.ol-geocoder-search'), + btn_search: container.querySelector('.ol-geocoder-btn-search'), + input_search: container.querySelector('.ol-geocoder-input-search'), + result_container: container.querySelector('.ol-geocoder-result') + }; + //set placeholder from options + G.Nominatim.elements.input_search.placeholder = this.options.placeholder; + + return container; + }, + setListeners: function() { + var + this_ = this, + openSearch = function() { + if (utils.hasClass(this_.els.control, this_.constants.expanded_class)) { + this_.collapse(); } else { - utils.removeAllChildren(this.els.result_container); + this_.expand(); } }, - query: function(query) { - var - this_ = this, - options = this.options, - input = this.els.input_search, - providers_names = Geocoder.Nominatim.providers.names, - provider = this.getProvider({ - provider: options.provider, - key: options.key, - query: query, - lang: options.lang, - countrycodes: options.countrycodes, - limit: options.limit - }); - - this.clearResults(); - utils.addClass(input, 'ol-geocoder-loading'); - - utils.json(provider.url, provider.params).when({ - ready: function(response) { - if (options.debug) { - console.info(response); - } - - utils.removeClass(input, 'ol-geocoder-loading'); - - //will be fullfiled according to provider - var response__; - - switch (this_.options.provider) { - case providers_names.OSM: - case providers_names.MAPQUEST: - response__ = response.length > 0 ? - this_.mapquestResponse(response) : undefined; - break; - case providers_names.PELIAS: - response__ = response.features.length > 0 ? - this_.peliasResponse(response.features) : undefined; - break; - case providers_names.PHOTON: - response__ = response.features.length > 0 ? - this_.photonResponse(response.features) : undefined; - break; - case providers_names.GOOGLE: - response__ = response.results.length > 0 ? - this_.googleResponse(response.results) : undefined; - break; - } - if (response__) { - this_.createList(response__); - this_.listenMapClick(); - } - }, - error: function() { - utils.removeClass(input, 'ol-geocoder-loading'); - var li = utils.createElement('li', - '
Error! No internet connection?
'); - this_.els.result_container.appendChild(li); - } - }); - }, - createList: function(response) { - var this_ = this; - var ul = this.els.result_container; - response.forEach(function(row) { - var - address_html = this_.addressTemplate(row), - html = '' + address_html + '', - li = utils.createElement('li', html); - li.addEventListener('click', function(evt) { - evt.preventDefault(); - this_.chosen(row, address_html, row.address, row.original); - }, false); - - ul.appendChild(li); - }); - }, - addressTemplate: function(r) { - var row = r.address, - html = []; - if (row.name) { - html.push( - '{name}' - ); - } - if (row.road || row.building || row.house_number) { - html.push( - '{building} {road} {house_number}' - ); - } - if (row.city || row.town || row.village) { - html.push( - '{postcode} {city} {town} {village}' - ); - } - if (row.state || row.country) { - html.push( - '{state} {country}' - ); + query = function(evt) { + if (evt.keyCode == 13) { //enter key + var q = utils.htmlEscape(this_.els.input_search.value); + this_.query(q); } - return utils.template(html.join('
'), row); - }, - chosen: function(place, address_html, address_obj, address_original) { - if (this.options.keepOpen === false) { - this.clearResults(true); + }; + this_.els.input_search.addEventListener('keydown', query, false); + this_.els.btn_search.addEventListener('click', openSearch, false); + }, + listenMapClick: function() { + if (this.registered_listeners.map_click) { + // already registered + return; + } + + var this_ = this; + var map_element = G.$base.getMap().getTargetElement(); + this.registered_listeners.map_click = true; + + //one-time fire click + map_element.addEventListener('click', { + handleEvent: function(evt) { + this_.clearResults(true); + map_element.removeEventListener(evt.type, this, false); + this_.registered_listeners.map_click = false; + } + }, false); + }, + expand: function() { + utils.removeClass(this.els.input_search, 'ol-geocoder-loading'); + utils.addClass(this.els.control, this.constants.expanded_class); + var input = this.els.input_search; + window.setTimeout(function() { + input.focus(); + }, 100); + this.listenMapClick(); + }, + collapse: function() { + this.els.input_search.value = ''; + this.els.input_search.blur(); + utils.removeClass(this.els.control, this.constants.expanded_class); + this.clearResults(); + }, + clearResults: function(collapse) { + if (collapse) { + this.collapse(); + } else { + utils.removeAllChildren(this.els.result_container); + } + }, + query: function(query) { + var this_ = this, + options = this.options, + input = this.els.input_search, + providers_names = G.Nominatim.providers.names, + provider = this.getProvider({ + provider: options.provider, + key: options.key, + query: query, + lang: options.lang, + countrycodes: options.countrycodes, + limit: options.limit + }); + + this.clearResults(); + utils.addClass(input, 'ol-geocoder-loading'); + + utils.json(provider.url, provider.params).when({ + ready: function(response) { + if (options.debug) { + console.info(response); } - var - map = this.geocoder.getMap(), - view = map.getView(), - projection = view.getProjection(), - coord = ol.proj.transform( - [parseFloat(place.lon), parseFloat(place.lat)], - 'EPSG:4326', projection - ), - resolution = 2.388657133911758, - duration = 500, - obj = { - coord: coord, - address_html: address_html, - address_obj: address_obj, - address_original: address_original - }, - pan = ol.animation.pan({ - duration: duration, - source: view.getCenter() - }), - zoom = ol.animation.zoom({ - duration: duration, - resolution: view.getResolution() - }); - - map.beforeRender(pan, zoom); - view.setCenter(coord); - view.setResolution(resolution); - this.createFeature(obj); - }, - createFeature: function(obj) { - var - feature = new ol.Feature({ - address_html: obj.address_html, - address_obj: obj.address_obj, - address_original: obj.address_original, - geometry: new ol.geom.Point(obj.coord) - }), - feature_id = utils.randomId('geocoder-ft-'), - feature_style = this.options.featureStyle || - Geocoder.Nominatim.featureStyle; - - this.addLayer(); - feature.setStyle(feature_style); - feature.setId(feature_id); - this.getSource().addFeature(feature); - this.geocoder.dispatchEvent({ - type: Geocoder.EventType.ADDRESSCHOSEN, - feature: feature, - coordinate: obj.coord, - }); - }, - mapquestResponse: function(results) { - var array = results.map(function(result) { - return { - lon: result.lon, - lat: result.lat, - address: { - name: result.address.neighbourhood || '', - road: result.address.road || '', - postcode: result.address.postcode, - city: result.address.city || result.address.town, - state: result.address.state, - country: result.address.country - }, - original: { - formatted: result.display_name, - details: result.address - } - }; - }); - return array; - }, - photonResponse: function(features) { - var array = features.map(function(feature) { - return { - lon: feature.geometry.coordinates[0], - lat: feature.geometry.coordinates[1], - address: { - name: feature.properties.name, - postcode: feature.properties.postcode, - city: feature.properties.city, - state: feature.properties.state, - country: feature.properties.country - }, - original: { - formatted: feature.properties.name, - details: feature.properties - } - }; - }); - return array; - }, - peliasResponse: function(features) { - var array = features.map(function(feature) { - return { - lon: feature.geometry.coordinates[0], - lat: feature.geometry.coordinates[1], - address: { - name: feature.properties.name, - house_number: feature.properties.housenumber, - postcode: feature.properties.postalcode, - road: feature.properties.street, - city: feature.properties.city, - state: feature.properties.region, - country: feature.properties.country - }, - original: { - formatted: feature.properties.label, - details: feature.properties - } - }; - }); - return array; - }, - googleResponse: function(results) { - var - name = [ - 'point_of_interest', - 'establishment', - 'natural_feature', - 'airport' - ], - road = [ - 'street_address', - 'route', - 'sublocality_level_5', - 'intersection' - ], - postcode = ['postal_code'], - city = ['locality'], - state = ['administrative_area_level_1'], - country = ['country']; - - /* - * @param {Array} details - address_components - */ - var getDetails = function(details) { - var parts = { - name: '', - road: '', - postcode: '', - city: '', - state: '', - country: '' - }; - details.forEach(function(detail) { - if (utils.anyMatchInArray(detail.types, name)) { - parts.name = detail.long_name; - } else if (utils.anyMatchInArray(detail.types, road)) { - parts.road = detail.long_name; - } else if (utils.anyMatchInArray(detail.types, postcode)) { - parts.postcode = detail.long_name; - } else if (utils.anyMatchInArray(detail.types, city)) { - parts.city = detail.long_name; - } else if (utils.anyMatchInArray(detail.types, state)) { - parts.state = detail.long_name; - } else if (utils.anyMatchInArray(detail.types, country)) { - parts.country = detail.long_name; - } - }); - return parts; - }; + utils.removeClass(input, 'ol-geocoder-loading'); - var array = []; - results.forEach(function(result) { - var details = getDetails(result.address_components); - if (utils.anyItemHasValue(details)) { - array.push({ - lon: result.geometry.location.lng, - lat: result.geometry.location.lat, - address: { - name: details.name, - postcode: details.postcode, - road: details.road, - city: details.city, - state: details.state, - country: details.country - }, - original: { - formatted: result.formatted_address, - details: result.address_components - } - }); - } - }); - return array; - }, - getSource: function() { - return this.layer.getSource(); - }, - addLayer: function() { - var this_ = this, - found = false; - var map = this.geocoder.getMap(); + //will be fullfiled according to provider + var response__; - map.getLayers().forEach(function(layer) { - if (layer === this_.layer) found = true; - }); - if (!found) { - map.addLayer(this.layer); - } - }, - getProvider: function(options) { - var - params, - provider = Geocoder.Nominatim.providers[options.provider], - providers_names = Geocoder.Nominatim.providers.names, - requires_key = [ - providers_names.MAPQUEST, - providers_names.PELIAS, - providers_names.GOOGLE - ], - langs_photon = ['de', 'it', 'fr', 'en']; - switch (options.provider) { + switch (this_.options.provider) { case providers_names.OSM: case providers_names.MAPQUEST: - params = { - q: options.query, - limit: options.limit, - countrycodes: options.countrycodes, - 'accept-language': options.lang - }; - provider.params = utils.mergeOptions(provider.params, params); + response__ = response.length > 0 ? + this_.mapquestResponse(response) : undefined; + break; + case providers_names.PELIAS: + response__ = response.features.length > 0 ? + this_.peliasResponse(response.features) : undefined; break; case providers_names.PHOTON: - options.lang = options.lang.toLowerCase(); - params = { - q: options.query, - limit: options.limit || provider.params.limit, - lang: (langs_photon.indexOf(options.lang) > -1) ? - options.lang : provider.params.lang - }; - provider.params = utils.mergeOptions(provider.params, params); + response__ = response.features.length > 0 ? + this_.photonResponse(response.features) : undefined; break; case providers_names.GOOGLE: - params = { - address: options.query, - language: options.lang - }; - provider.params = utils.mergeOptions(provider.params, params); - break; - case providers_names.PELIAS: - params = { - text: options.query, - size: options.limit - }; - provider.params = utils.mergeOptions(provider.params, params); + response__ = response.results.length > 0 ? + this_.googleResponse(response.results) : undefined; break; } - if (requires_key.indexOf(options.provider) > -1) { - provider.params.key = options.key; + if (response__) { + this_.createList(response__); + this_.listenMapClick(); } - return provider; - } - }; - Geocoder.EventType = { - /** - * Triggered when an address is chosen. - */ - ADDRESSCHOSEN: 'addresschosen' - }; - - Geocoder.Nominatim.elements = {}; - Geocoder.Nominatim.providers = { - names: { - OSM: 'osm', - MAPQUEST: 'mapquest', - GOOGLE: 'google', - PHOTON: 'photon', - PELIAS: 'pelias' }, - osm: { - url: 'http://nominatim.openstreetmap.org/search/', - params: { - format: 'json', - q: '', - addressdetails: 1, - limit: 10, - countrycodes: '', - 'accept-language': 'en-US' - } - }, - mapquest: { - url: 'http://open.mapquestapi.com/nominatim/v1/search.php', - params: { - key: '', - format: 'json', - q: '', - addressdetails: 1, - limit: 10, - countrycodes: '', - 'accept-language': 'en-US' - } - }, - google: { - url: 'https://maps.googleapis.com/maps/api/geocode/json', - params: { - key: '', - address: '', - language: 'en-US' - } - }, - pelias: { - url: 'https://search.mapzen.com/v1/search', - params: { - key: '', - text: '', - size: 10 - } - }, - photon: { - url: 'http://photon.komoot.de/api/', - params: { - q: '', - limit: 10, - lang: 'en' - } + error: function() { + utils.removeClass(input, 'ol-geocoder-loading'); + var li = utils.createElement('li', + '
Error! No internet connection?
'); + this_.els.result_container.appendChild(li); } - }; - Geocoder.Nominatim.featureStyle = [ - new ol.style.Style({ - image: new ol.style.Icon({ - scale: 0.7, - anchor: [0.5, 1], - src: '//cdn.rawgit.com/jonataswalker/' + 'map-utils/master/images/marker.png' - }), - zIndex: 5 + }); + }, + createList: function(response) { + var this_ = this; + var ul = this.els.result_container; + response.forEach(function(row) { + var + address_html = this_.addressTemplate(row), + html = '' + address_html + '', + li = utils.createElement('li', html); + li.addEventListener('click', function(evt) { + evt.preventDefault(); + this_.chosen(row, address_html, row.address, row.original); + }, false); + + ul.appendChild(li); + }); + }, + addressTemplate: function(r) { + var row = r.address, + html = []; + if (row.name) { + html.push( + '{name}' + ); + } + if (row.road || row.building || row.house_number) { + html.push( + '{building} {road} {house_number}' + ); + } + if (row.city || row.town || row.village) { + html.push( + '{postcode} {city} {town} {village}' + ); + } + if (row.state || row.country) { + html.push( + '{state} {country}' + ); + } + return utils.template(html.join('
'), row); + }, + chosen: function(place, address_html, address_obj, address_original) { + if (this.options.keepOpen === false) { + this.clearResults(true); + } + + var map = G.$base.getMap(), + view = map.getView(), + projection = view.getProjection(), + coord = ol.proj.transform( + [parseFloat(place.lon), parseFloat(place.lat)], + 'EPSG:4326', projection + ), + resolution = 2.388657133911758, + duration = 500, + obj = { + coord: coord, + address_html: address_html, + address_obj: address_obj, + address_original: address_original + }, + pan = ol.animation.pan({ + duration: duration, + source: view.getCenter() }), - new ol.style.Style({ - image: new ol.style.Circle({ - fill: new ol.style.Fill({ - color: [235, 235, 235, 1] - }), - stroke: new ol.style.Stroke({ - color: [0, 0, 0, 1] - }), - radius: 5 - }), - zIndex: 4 - }) - ]; - Geocoder.Nominatim.html = [ - '', - '
    ' - ].join(''); - })(Geocoder); - - (function(window, document) { - - var getXhr = function() { - var xhr = false; - if (window.XMLHttpRequest) { - xhr = new XMLHttpRequest(); - } else if (window.ActiveXObject) { - try { - xhr = new ActiveXObject("Msxml2.XMLHTTP"); - } catch (e) { - try { - xhr = new ActiveXObject("Microsoft.XMLHTTP"); - } catch (e) { - xhr = false; - } - } - } - return xhr; - }; - var encodeUrlXhr = function(url, data) { - if (data && typeof(data) === 'object') { - var str_data = utils.toQueryString(data); - url += (/\?/.test(url) ? '&' : '?') + str_data; - } - return url; - }; - - Geocoder.Utils = { - whiteSpaceRegex: /\s+/, - toQueryString: function(obj) { - return Object.keys(obj).reduce(function(a, k) { - a.push((typeof obj[k] === 'object') ? - utils.toQueryString(obj[k]) : - encodeURIComponent(k) + '=' + encodeURIComponent(obj[k]) - ); - return a; - }, []).join('&'); - }, - json: function(url, data) { - var - xhr = getXhr(), - when = {}, - onload = function() { - if (xhr.status === 200) { - when.ready.call(undefined, JSON.parse(xhr.response)); - } - }, - onerror = function() { - console.info('Cannot XHR ' + JSON.stringify(url)); - }; - url = encodeUrlXhr(url, data); - xhr.open('GET', url, true); - xhr.setRequestHeader('Accept', 'application/json'); - xhr.onload = onload; - xhr.onerror = onerror; - xhr.onprogress = onprogress; - xhr.send(null); - - return { - when: function(obj) { - when.ready = obj.ready; - } - }; - }, - randomId: function(prefix) { - var id = (new Date().getTime()).toString(36); - return (prefix) ? prefix + id : id; - }, - to3857: function(coord) { - return ol.proj.transform( - [parseFloat(coord[0]), parseFloat(coord[1])], - 'EPSG:4326', 'EPSG:3857' - ); - }, - to4326: function(coord) { - return ol.proj.transform( - [parseFloat(coord[0]), parseFloat(coord[1])], - 'EPSG:3857', 'EPSG:4326' - ); - }, - isNumeric: function(str) { - return /^\d+$/.test(str); - }, - classRegex: function(classname) { - return new RegExp('(^|\\s+)' + classname + '(\\s+|$)'); - }, - /** - * @param {Element|Array} element DOM node or array of nodes. - * @param {String|Array} classname Class or array of classes. - * For example: 'class1 class2' or ['class1', 'class2'] - * @param {Number|undefined} timeout Timeout to remove a class. - */ - addClass: function(element, classname, timeout) { - if (Array.isArray(element)) { - element.forEach(function(each) { - utils.addClass(each, classname); - }); - return; - } - - var - array = (Array.isArray(classname)) ? classname : classname.split(/\s+/), - i = array.length; - while (i--) { - if (!utils.hasClass(element, array[i])) { - utils._addClass(element, array[i], timeout); - } - } - }, - _addClass: function(el, c, timeout) { - // use native if available - if (el.classList) { - el.classList.add(c); - } else { - el.className = (el.className + ' ' + c).trim(); - } - - if (timeout && utils.isNumeric(timeout)) { - window.setTimeout(function() { - utils._removeClass(el, c); - }, timeout); - } - }, - /** - * @param {Element|Array} element DOM node or array of nodes. - * @param {String|Array} classname Class or array of classes. - * For example: 'class1 class2' or ['class1', 'class2'] - * @param {Number|undefined} timeout Timeout to add a class. - */ - removeClass: function(element, classname, timeout) { - if (Array.isArray(element)) { - element.forEach(function(each) { - utils.removeClass(each, classname, timeout); - }); - return; - } + zoom = ol.animation.zoom({ + duration: duration, + resolution: view.getResolution() + }); - var - array = (Array.isArray(classname)) ? classname : classname.split(/\s+/), - i = array.length; - while (i--) { - if (utils.hasClass(element, array[i])) { - utils._removeClass(element, array[i], timeout); - } + map.beforeRender(pan, zoom); + view.setCenter(coord); + view.setResolution(resolution); + this.createFeature(obj); + }, + createFeature: function(obj) { + var feature = new ol.Feature({ + address_html: obj.address_html, + address_obj: obj.address_obj, + address_original: obj.address_original, + geometry: new ol.geom.Point(obj.coord) + }), + feature_id = utils.randomId('geocoder-ft-'), + feature_style = this.options.featureStyle || G.Nominatim.featureStyle; + + this.addLayer(); + feature.setStyle(feature_style); + feature.setId(feature_id); + this.getSource().addFeature(feature); + G.$base.dispatchEvent({ + type: G.EventType.ADDRESSCHOSEN, + feature: feature, + coordinate: obj.coord, + }); + }, + mapquestResponse: function(results) { + var array = results.map(function(result) { + return { + lon: result.lon, + lat: result.lat, + address: { + name: result.address.neighbourhood || '', + road: result.address.road || '', + postcode: result.address.postcode, + city: result.address.city || result.address.town, + state: result.address.state, + country: result.address.country + }, + original: { + formatted: result.display_name, + details: result.address } - }, - _removeClass: function(el, c, timeout) { - if (el.classList) { - el.classList.remove(c); - } else { - el.className = (el.className.replace(utils.classRegex(c), ' ')).trim(); + }; + }); + return array; + }, + photonResponse: function(features) { + var array = features.map(function(feature) { + return { + lon: feature.geometry.coordinates[0], + lat: feature.geometry.coordinates[1], + address: { + name: feature.properties.name, + postcode: feature.properties.postcode, + city: feature.properties.city, + state: feature.properties.state, + country: feature.properties.country + }, + original: { + formatted: feature.properties.name, + details: feature.properties } - if (timeout && utils.isNumeric(timeout)) { - window.setTimeout(function() { - utils._addClass(el, c); - }, timeout); + }; + }); + return array; + }, + peliasResponse: function(features) { + var array = features.map(function(feature) { + return { + lon: feature.geometry.coordinates[0], + lat: feature.geometry.coordinates[1], + address: { + name: feature.properties.name, + house_number: feature.properties.housenumber, + postcode: feature.properties.postalcode, + road: feature.properties.street, + city: feature.properties.city, + state: feature.properties.region, + country: feature.properties.country + }, + original: { + formatted: feature.properties.label, + details: feature.properties } - }, - /** - * @param {Element} element DOM node. - * @param {String} classname Classname. - * @return {Boolean} - */ - hasClass: function(element, c) { - // use native if available - return (element.classList) ? - element.classList.contains(c) : utils.classRegex(c).test(element.className); - }, - /** - * @param {Element|Array} element DOM node or array of nodes. - * @param {String} classname Classe. - */ - toggleClass: function(element, classname) { - if (Array.isArray(element)) { - element.forEach(function(each) { - utils.toggleClass(each, classname); - }); - return; + }; + }); + return array; + }, + googleResponse: function(results) { + var + name = [ + 'point_of_interest', + 'establishment', + 'natural_feature', + 'airport' + ], + road = [ + 'street_address', + 'route', + 'sublocality_level_5', + 'intersection' + ], + postcode = ['postal_code'], + city = ['locality'], + state = ['administrative_area_level_1'], + country = ['country']; + + /* + * @param {Array} details - address_components + */ + var getDetails = function(details) { + var parts = { + name: '', + road: '', + postcode: '', + city: '', + state: '', + country: '' + }; + details.forEach(function(detail) { + if (utils.anyMatchInArray(detail.types, name)) { + parts.name = detail.long_name; + } else if (utils.anyMatchInArray(detail.types, road)) { + parts.road = detail.long_name; + } else if (utils.anyMatchInArray(detail.types, postcode)) { + parts.postcode = detail.long_name; + } else if (utils.anyMatchInArray(detail.types, city)) { + parts.city = detail.long_name; + } else if (utils.anyMatchInArray(detail.types, state)) { + parts.state = detail.long_name; + } else if (utils.anyMatchInArray(detail.types, country)) { + parts.country = detail.long_name; } + }); + return parts; + }; - // use native if available - if (element.classList) { - element.classList.toggle(classname); - } else { - if (utils.hasClass(element, classname)) { - utils._removeClass(element, classname); - } else { - utils._addClass(element, classname); - } - } - }, - $: function(id) { - id = (id[0] === '#') ? id.substr(1, id.length) : id; - return document.getElementById(id); - }, - isElement: function(obj) { - // DOM, Level2 - if ("HTMLElement" in window) { - return (!!obj && obj instanceof HTMLElement); - } - // Older browsers - return (!!obj && typeof obj === "object" && - obj.nodeType === 1 && !!obj.nodeName); - }, - getAllChildren: function(node, tag) { - return [].slice.call(node.getElementsByTagName(tag)); - }, - isEmpty: function(str) { - return (!str || 0 === str.length); - }, - emptyArray: function(array) { - while (array.length) array.pop(); - }, - anyMatchInArray: function(source, target) { - return source.some(function(each) { - return target.indexOf(each) >= 0; - }); - }, - everyMatchInArray: function(arr1, arr2) { - return arr2.every(function(each) { - return arr1.indexOf(each) >= 0; - }); - }, - anyItemHasValue: function(obj) { - var has = false; - for (var key in obj) { - if (!utils.isEmpty(obj[key])) { - has = true; + var array = []; + results.forEach(function(result) { + var details = getDetails(result.address_components); + if (utils.anyItemHasValue(details)) { + array.push({ + lon: result.geometry.location.lng, + lat: result.geometry.location.lat, + address: { + name: details.name, + postcode: details.postcode, + road: details.road, + city: details.city, + state: details.state, + country: details.country + }, + original: { + formatted: result.formatted_address, + details: result.address_components } - } - return has; - }, - removeAllChildren: function(node) { - while (node.firstChild) { - node.removeChild(node.firstChild); - } - }, - removeAll: function(collection) { - var node; - while ((node = collection[0])) { - node.parentNode.removeChild(node); - } - }, - getChildren: function(node, tag) { - return [].filter.call(node.childNodes, function(el) { - return (tag) ? - el.nodeType == 1 && el.tagName.toLowerCase() == tag : - el.nodeType == 1; }); - }, - template: function(html, row) { - var this_ = this; - - return html.replace(/\{ *([\w_-]+) *\}/g, function(html, key) { - var value = (row[key] === undefined) ? '' : row[key]; - return this_.htmlEscape(value); - }); - }, - htmlEscape: function(str) { - return String(str) - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, "'"); - }, - /** - * Overwrites obj1's values with obj2's and adds - * obj2's if non existent in obj1 - * @returns obj3 a new object based on obj1 and obj2 - */ - mergeOptions: function(obj1, obj2) { - var obj3 = {}; - for (var attr1 in obj1) { - obj3[attr1] = obj1[attr1]; - } - for (var attr2 in obj2) { - obj3[attr2] = obj2[attr2]; - } - return obj3; - }, - createElement: function(node, html) { - var elem; - if (Array.isArray(node)) { - elem = document.createElement(node[0]); - - if (node[1].id) { - elem.id = node[1].id; - } - if (node[1].classname) { - elem.className = node[1].classname; - } - - if (node[1].attr) { - var attr = node[1].attr; - if (Array.isArray(attr)) { - var i = -1; - while (++i < attr.length) { - elem.setAttribute(attr[i].name, attr[i].value); - } - } else { - elem.setAttribute(attr.name, attr.value); - } - } - } else { - elem = document.createElement(node); - } - elem.innerHTML = html; - var frag = document.createDocumentFragment(); - - while (elem.childNodes[0]) { - frag.appendChild(elem.childNodes[0]); - } - elem.appendChild(frag); - return elem; - }, - assert: function(condition, message) { - if (!condition) { - message = message || "Assertion failed"; - if (typeof Error !== "undefined") { - throw new Error(message); - } - throw message; // Fallback - } - }, - assertEqual: function(a, b, message) { - if (a != b) { - throw new Error(message + " mismatch: " + a + " != " + b); - } } - }; - })(window, document); - return Geocoder; - })(); - var utils = Geocoder.Utils; -}).call(this, window, document); \ No newline at end of file + }); + return array; + }, + getSource: function() { + return this.layer.getSource(); + }, + addLayer: function() { + var this_ = this, + found = false; + var map = G.$base.getMap(); + + map.getLayers().forEach(function(layer) { + if (layer === this_.layer) found = true; + }); + if (!found) { + map.addLayer(this.layer); + } + }, + getProvider: function(options) { + var params, + provider = G.Nominatim.providers[options.provider], + providers_names = G.Nominatim.providers.names, + requires_key = [ + providers_names.MAPQUEST, + providers_names.PELIAS, + providers_names.GOOGLE + ], + langs_photon = ['de', 'it', 'fr', 'en']; + switch (options.provider) { + case providers_names.OSM: + case providers_names.MAPQUEST: + params = { + q: options.query, + limit: options.limit, + countrycodes: options.countrycodes, + 'accept-language': options.lang + }; + provider.params = utils.mergeOptions(provider.params, params); + break; + case providers_names.PHOTON: + options.lang = options.lang.toLowerCase(); + params = { + q: options.query, + limit: options.limit || provider.params.limit, + lang: (langs_photon.indexOf(options.lang) > -1) ? + options.lang : provider.params.lang + }; + provider.params = utils.mergeOptions(provider.params, params); + break; + case providers_names.GOOGLE: + params = { + address: options.query, + language: options.lang + }; + provider.params = utils.mergeOptions(provider.params, params); + break; + case providers_names.PELIAS: + params = { + text: options.query, + size: options.limit + }; + provider.params = utils.mergeOptions(provider.params, params); + break; + } + if (requires_key.indexOf(options.provider) > -1) { + provider.params.key = options.key; + } + return provider; + } + }; + G.EventType = { + /** + * Triggered when an address is chosen. + */ + ADDRESSCHOSEN: 'addresschosen' + }; + + G.Nominatim.elements = {}; + G.Nominatim.providers = { + names: { + OSM: 'osm', + MAPQUEST: 'mapquest', + GOOGLE: 'google', + PHOTON: 'photon', + PELIAS: 'pelias' + }, + osm: { + url: 'http://nominatim.openstreetmap.org/search/', + params: { + format: 'json', + q: '', + addressdetails: 1, + limit: 10, + countrycodes: '', + 'accept-language': 'en-US' + } + }, + mapquest: { + url: 'http://open.mapquestapi.com/nominatim/v1/search.php', + params: { + key: '', + format: 'json', + q: '', + addressdetails: 1, + limit: 10, + countrycodes: '', + 'accept-language': 'en-US' + } + }, + google: { + url: 'https://maps.googleapis.com/maps/api/geocode/json', + params: { + key: '', + address: '', + language: 'en-US' + } + }, + pelias: { + url: 'https://search.mapzen.com/v1/search', + params: { + key: '', + text: '', + size: 10 + } + }, + photon: { + url: 'http://photon.komoot.de/api/', + params: { + q: '', + limit: 10, + lang: 'en' + } + } + }; + G.Nominatim.featureStyle = [ + new ol.style.Style({ + image: new ol.style.Icon({ + scale: 0.7, + anchor: [0.5, 1], + src: '//cdn.rawgit.com/jonataswalker/' + 'map-utils/master/images/marker.png' + }), + zIndex: 5 + }), + new ol.style.Style({ + image: new ol.style.Circle({ + fill: new ol.style.Fill({ + color: [235, 235, 235, 1] + }), + stroke: new ol.style.Stroke({ + color: [0, 0, 0, 1] + }), + radius: 5 + }), + zIndex: 4 + }) + ]; + G.Nominatim.html = [ + '', + '
      ' + ].join(''); + return G.Base; +})); \ No newline at end of file diff --git a/build/ol3-geocoder.js b/build/ol3-geocoder.js index 943ac76..2a37de1 100644 --- a/build/ol3-geocoder.js +++ b/build/ol3-geocoder.js @@ -1,6 +1,6 @@ // Geocoder Nominatim for OpenLayers 3. // https://github.com/jonataswalker/ol3-geocoder -// Version: v1.6.0 -// Built: 2016-03-28T09:56:51-0300 +// Version: v1.7.0 +// Built: 2016-04-01T08:24:58-0300 -(function(e,t){"use strict";this.Geocoder=function(){var o=function(e,t){r.assert("string"==typeof e,"@param `control_type` should be string type!"),r.assert("object"==typeof t||"undefined"==typeof t,"@param `opt_options` should be object|undefined type!"),e=e||"nominatim";var s=new o.Nominatim(this,t);this.layer=s.layer,ol.control.Control.call(this,{element:s.els.container})};return ol.inherits(o,ol.control.Control),o.prototype.getSource=function(){return this.layer.getSource()},o.prototype.getLayer=function(){return this.layer},function(t){t.Nominatim=function(e,o){this.geocoder=e,this.layer_name=r.randomId("geocoder-layer-"),this.layer=new ol.layer.Vector({name:this.layer_name,source:new ol.source.Vector});var s={provider:"osm",placeholder:"Search for an address",featureStyle:t.Nominatim.featureStyle,lang:"en-US",limit:5,keepOpen:!1,debug:!1};return this.options=r.mergeOptions(s,o),this.options.provider=this.options.provider.toLowerCase(),this.constants={road:"ol-geocoder-road",city:"ol-geocoder-city",country:"ol-geocoder-country",class_container:"ol-geocoder",expanded_class:"ol-geocoder-search-expanded"},this.createControl(),this.els=t.Nominatim.elements,this.registered_listeners={map_click:!1},this.setListeners(),this},t.Nominatim.prototype={createControl:function(){var e=r.createElement(["div",{classname:this.constants.class_container}],t.Nominatim.html);return t.Nominatim.elements={container:e,control:e.querySelector(".ol-geocoder-search"),btn_search:e.querySelector(".ol-geocoder-btn-search"),input_search:e.querySelector(".ol-geocoder-input-search"),result_container:e.querySelector(".ol-geocoder-result")},t.Nominatim.elements.input_search.placeholder=this.options.placeholder,e},setListeners:function(){var e=this,t=function(){r.hasClass(e.els.control,e.constants.expanded_class)?e.collapse():e.expand()},o=function(t){if(13==t.keyCode){var o=r.htmlEscape(e.els.input_search.value);e.query(o)}};e.els.input_search.addEventListener("keydown",o,!1),e.els.btn_search.addEventListener("click",t,!1)},listenMapClick:function(){if(!this.registered_listeners.map_click){var e=this,t=this.geocoder.getMap().getTargetElement();this.registered_listeners.map_click=!0,t.addEventListener("click",{handleEvent:function(r){e.clearResults(!0),t.removeEventListener(r.type,this,!1),e.registered_listeners.map_click=!1}},!1)}},expand:function(){r.removeClass(this.els.input_search,"ol-geocoder-loading"),r.addClass(this.els.control,this.constants.expanded_class);var t=this.els.input_search;e.setTimeout(function(){t.focus()},100),this.listenMapClick()},collapse:function(){this.els.input_search.value="",this.els.input_search.blur(),r.removeClass(this.els.control,this.constants.expanded_class),this.clearResults()},clearResults:function(e){e?this.collapse():r.removeAllChildren(this.els.result_container)},query:function(e){var o=this,s=this.options,n=this.els.input_search,a=t.Nominatim.providers.names,i=this.getProvider({provider:s.provider,key:s.key,query:e,lang:s.lang,countrycodes:s.countrycodes,limit:s.limit});this.clearResults(),r.addClass(n,"ol-geocoder-loading"),r.json(i.url,i.params).when({ready:function(e){s.debug&&console.info(e),r.removeClass(n,"ol-geocoder-loading");var t;switch(o.options.provider){case a.OSM:case a.MAPQUEST:t=e.length>0?o.mapquestResponse(e):void 0;break;case a.PELIAS:t=e.features.length>0?o.peliasResponse(e.features):void 0;break;case a.PHOTON:t=e.features.length>0?o.photonResponse(e.features):void 0;break;case a.GOOGLE:t=e.results.length>0?o.googleResponse(e.results):void 0}t&&(o.createList(t),o.listenMapClick())},error:function(){r.removeClass(n,"ol-geocoder-loading");var e=r.createElement("li","
      Error! No internet connection?
      ");o.els.result_container.appendChild(e)}})},createList:function(e){var t=this,o=this.els.result_container;e.forEach(function(e){var s=t.addressTemplate(e),n=''+s+"",a=r.createElement("li",n);a.addEventListener("click",function(r){r.preventDefault(),t.chosen(e,s,e.address,e.original)},!1),o.appendChild(a)})},addressTemplate:function(e){var t=e.address,o=[];return t.name&&o.push('{name}'),(t.road||t.building||t.house_number)&&o.push('{building} {road} {house_number}'),(t.city||t.town||t.village)&&o.push('{postcode} {city} {town} {village}'),(t.state||t.country)&&o.push('{state} {country}'),r.template(o.join("
      "),t)},chosen:function(e,t,r,o){this.options.keepOpen===!1&&this.clearResults(!0);var s=this.geocoder.getMap(),n=s.getView(),a=n.getProjection(),i=ol.proj.transform([parseFloat(e.lon),parseFloat(e.lat)],"EPSG:4326",a),l=2.388657133911758,c=500,d={coord:i,address_html:t,address_obj:r,address_original:o},u=ol.animation.pan({duration:c,source:n.getCenter()}),p=ol.animation.zoom({duration:c,resolution:n.getResolution()});s.beforeRender(u,p),n.setCenter(i),n.setResolution(l),this.createFeature(d)},createFeature:function(e){var o=new ol.Feature({address_html:e.address_html,address_obj:e.address_obj,address_original:e.address_original,geometry:new ol.geom.Point(e.coord)}),s=r.randomId("geocoder-ft-"),n=this.options.featureStyle||t.Nominatim.featureStyle;this.addLayer(),o.setStyle(n),o.setId(s),this.getSource().addFeature(o),this.geocoder.dispatchEvent({type:t.EventType.ADDRESSCHOSEN,feature:o,coordinate:e.coord})},mapquestResponse:function(e){var t=e.map(function(e){return{lon:e.lon,lat:e.lat,address:{name:e.address.neighbourhood||"",road:e.address.road||"",postcode:e.address.postcode,city:e.address.city||e.address.town,state:e.address.state,country:e.address.country},original:{formatted:e.display_name,details:e.address}}});return t},photonResponse:function(e){var t=e.map(function(e){return{lon:e.geometry.coordinates[0],lat:e.geometry.coordinates[1],address:{name:e.properties.name,postcode:e.properties.postcode,city:e.properties.city,state:e.properties.state,country:e.properties.country},original:{formatted:e.properties.name,details:e.properties}}});return t},peliasResponse:function(e){var t=e.map(function(e){return{lon:e.geometry.coordinates[0],lat:e.geometry.coordinates[1],address:{name:e.properties.name,house_number:e.properties.housenumber,postcode:e.properties.postalcode,road:e.properties.street,city:e.properties.city,state:e.properties.region,country:e.properties.country},original:{formatted:e.properties.label,details:e.properties}}});return t},googleResponse:function(e){var t=["point_of_interest","establishment","natural_feature","airport"],o=["street_address","route","sublocality_level_5","intersection"],s=["postal_code"],n=["locality"],a=["administrative_area_level_1"],i=["country"],l=function(e){var l={name:"",road:"",postcode:"",city:"",state:"",country:""};return e.forEach(function(e){r.anyMatchInArray(e.types,t)?l.name=e.long_name:r.anyMatchInArray(e.types,o)?l.road=e.long_name:r.anyMatchInArray(e.types,s)?l.postcode=e.long_name:r.anyMatchInArray(e.types,n)?l.city=e.long_name:r.anyMatchInArray(e.types,a)?l.state=e.long_name:r.anyMatchInArray(e.types,i)&&(l.country=e.long_name)}),l},c=[];return e.forEach(function(e){var t=l(e.address_components);r.anyItemHasValue(t)&&c.push({lon:e.geometry.location.lng,lat:e.geometry.location.lat,address:{name:t.name,postcode:t.postcode,road:t.road,city:t.city,state:t.state,country:t.country},original:{formatted:e.formatted_address,details:e.address_components}})}),c},getSource:function(){return this.layer.getSource()},addLayer:function(){var e=this,t=!1,r=this.geocoder.getMap();r.getLayers().forEach(function(r){r===e.layer&&(t=!0)}),t||r.addLayer(this.layer)},getProvider:function(e){var o,s=t.Nominatim.providers[e.provider],n=t.Nominatim.providers.names,a=[n.MAPQUEST,n.PELIAS,n.GOOGLE],i=["de","it","fr","en"];switch(e.provider){case n.OSM:case n.MAPQUEST:o={q:e.query,limit:e.limit,countrycodes:e.countrycodes,"accept-language":e.lang},s.params=r.mergeOptions(s.params,o);break;case n.PHOTON:e.lang=e.lang.toLowerCase(),o={q:e.query,limit:e.limit||s.params.limit,lang:i.indexOf(e.lang)>-1?e.lang:s.params.lang},s.params=r.mergeOptions(s.params,o);break;case n.GOOGLE:o={address:e.query,language:e.lang},s.params=r.mergeOptions(s.params,o);break;case n.PELIAS:o={text:e.query,size:e.limit},s.params=r.mergeOptions(s.params,o)}return a.indexOf(e.provider)>-1&&(s.params.key=e.key),s}},t.EventType={ADDRESSCHOSEN:"addresschosen"},t.Nominatim.elements={},t.Nominatim.providers={names:{OSM:"osm",MAPQUEST:"mapquest",GOOGLE:"google",PHOTON:"photon",PELIAS:"pelias"},osm:{url:"http://nominatim.openstreetmap.org/search/",params:{format:"json",q:"",addressdetails:1,limit:10,countrycodes:"","accept-language":"en-US"}},mapquest:{url:"http://open.mapquestapi.com/nominatim/v1/search.php",params:{key:"",format:"json",q:"",addressdetails:1,limit:10,countrycodes:"","accept-language":"en-US"}},google:{url:"https://maps.googleapis.com/maps/api/geocode/json",params:{key:"",address:"",language:"en-US"}},pelias:{url:"https://search.mapzen.com/v1/search",params:{key:"",text:"",size:10}},photon:{url:"http://photon.komoot.de/api/",params:{q:"",limit:10,lang:"en"}}},t.Nominatim.featureStyle=[new ol.style.Style({image:new ol.style.Icon({scale:.7,anchor:[.5,1],src:"//cdn.rawgit.com/jonataswalker/map-utils/master/images/marker.png"}),zIndex:5}),new ol.style.Style({image:new ol.style.Circle({fill:new ol.style.Fill({color:[235,235,235,1]}),stroke:new ol.style.Stroke({color:[0,0,0,1]}),radius:5}),zIndex:4})],t.Nominatim.html=['",'
        '].join("")}(o),function(e,t){var s=function(){var t=!1;if(e.XMLHttpRequest)t=new XMLHttpRequest;else if(e.ActiveXObject)try{t=new ActiveXObject("Msxml2.XMLHTTP")}catch(e){try{t=new ActiveXObject("Microsoft.XMLHTTP")}catch(e){t=!1}}return t},n=function(e,t){if(t&&"object"==typeof t){var o=r.toQueryString(t);e+=(/\?/.test(e)?"&":"?")+o}return e};o.Utils={whiteSpaceRegex:/\s+/,toQueryString:function(e){return Object.keys(e).reduce(function(t,o){return t.push("object"==typeof e[o]?r.toQueryString(e[o]):encodeURIComponent(o)+"="+encodeURIComponent(e[o])),t},[]).join("&")},json:function(e,t){var r=s(),o={},a=function(){200===r.status&&o.ready.call(void 0,JSON.parse(r.response))},i=function(){console.info("Cannot XHR "+JSON.stringify(e))};return e=n(e,t),r.open("GET",e,!0),r.setRequestHeader("Accept","application/json"),r.onload=a,r.onerror=i,r.onprogress=onprogress,r.send(null),{when:function(e){o.ready=e.ready}}},randomId:function(e){var t=(new Date).getTime().toString(36);return e?e+t:t},to3857:function(e){return ol.proj.transform([parseFloat(e[0]),parseFloat(e[1])],"EPSG:4326","EPSG:3857")},to4326:function(e){return ol.proj.transform([parseFloat(e[0]),parseFloat(e[1])],"EPSG:3857","EPSG:4326")},isNumeric:function(e){return/^\d+$/.test(e)},classRegex:function(e){return new RegExp("(^|\\s+)"+e+"(\\s+|$)")},addClass:function(e,t,o){if(Array.isArray(e))return void e.forEach(function(e){r.addClass(e,t)});for(var s=Array.isArray(t)?t:t.split(/\s+/),n=s.length;n--;)r.hasClass(e,s[n])||r._addClass(e,s[n],o)},_addClass:function(t,o,s){t.classList?t.classList.add(o):t.className=(t.className+" "+o).trim(),s&&r.isNumeric(s)&&e.setTimeout(function(){r._removeClass(t,o)},s)},removeClass:function(e,t,o){if(Array.isArray(e))return void e.forEach(function(e){r.removeClass(e,t,o)});for(var s=Array.isArray(t)?t:t.split(/\s+/),n=s.length;n--;)r.hasClass(e,s[n])&&r._removeClass(e,s[n],o)},_removeClass:function(t,o,s){t.classList?t.classList.remove(o):t.className=t.className.replace(r.classRegex(o)," ").trim(),s&&r.isNumeric(s)&&e.setTimeout(function(){r._addClass(t,o)},s)},hasClass:function(e,t){return e.classList?e.classList.contains(t):r.classRegex(t).test(e.className)},toggleClass:function(e,t){return Array.isArray(e)?void e.forEach(function(e){r.toggleClass(e,t)}):void(e.classList?e.classList.toggle(t):r.hasClass(e,t)?r._removeClass(e,t):r._addClass(e,t))},$:function(e){return e="#"===e[0]?e.substr(1,e.length):e,t.getElementById(e)},isElement:function(t){return"HTMLElement"in e?!!t&&t instanceof HTMLElement:!!t&&"object"==typeof t&&1===t.nodeType&&!!t.nodeName},getAllChildren:function(e,t){return[].slice.call(e.getElementsByTagName(t))},isEmpty:function(e){return!e||0===e.length},emptyArray:function(e){for(;e.length;)e.pop()},anyMatchInArray:function(e,t){return e.some(function(e){return t.indexOf(e)>=0})},everyMatchInArray:function(e,t){return t.every(function(t){return e.indexOf(t)>=0})},anyItemHasValue:function(e){var t=!1;for(var o in e)r.isEmpty(e[o])||(t=!0);return t},removeAllChildren:function(e){for(;e.firstChild;)e.removeChild(e.firstChild)},removeAll:function(e){for(var t;t=e[0];)t.parentNode.removeChild(t)},getChildren:function(e,t){return[].filter.call(e.childNodes,function(e){return t?1==e.nodeType&&e.tagName.toLowerCase()==t:1==e.nodeType})},template:function(e,t){var r=this;return e.replace(/\{ *([\w_-]+) *\}/g,function(e,o){var s=void 0===t[o]?"":t[o];return r.htmlEscape(s)})},htmlEscape:function(e){return String(e).replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")},mergeOptions:function(e,t){var r={};for(var o in e)r[o]=e[o];for(var s in t)r[s]=t[s];return r},createElement:function(e,r){var o;if(Array.isArray(e)){if(o=t.createElement(e[0]),e[1].id&&(o.id=e[1].id),e[1].classname&&(o.className=e[1].classname),e[1].attr){var s=e[1].attr;if(Array.isArray(s))for(var n=-1;++n=0})},everyMatchInArray:function(e,t){return t.every(function(t){return e.indexOf(t)>=0})},anyItemHasValue:function(e){var r=!1;for(var n in e)t.isEmpty(e[n])||(r=!0);return r},removeAllChildren:function(e){for(;e.firstChild;)e.removeChild(e.firstChild)},removeAll:function(e){for(var t;t=e[0];)t.parentNode.removeChild(t)},getChildren:function(e,t){return[].filter.call(e.childNodes,function(e){return t?1==e.nodeType&&e.tagName.toLowerCase()==t:1==e.nodeType})},template:function(e,t){var r=this;return e.replace(/\{ *([\w_-]+) *\}/g,function(e,n){var o=void 0===t[n]?"":t[n];return r.htmlEscape(o)})},htmlEscape:function(e){return String(e).replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")},mergeOptions:function(e,t){var r={};for(var n in e)r[n]=e[n];for(var o in t)r[o]=t[o];return r},createElement:function(e,t){var r;if(Array.isArray(e)){if(r=document.createElement(e[0]),e[1].id&&(r.id=e[1].id),e[1].classname&&(r.className=e[1].classname),e[1].attr){var n=e[1].attr;if(Array.isArray(n))for(var o=-1;++o0?n.mapquestResponse(e):void 0;break;case a.PELIAS:r=e.features.length>0?n.peliasResponse(e.features):void 0;break;case a.PHOTON:r=e.features.length>0?n.photonResponse(e.features):void 0;break;case a.GOOGLE:r=e.results.length>0?n.googleResponse(e.results):void 0}r&&(n.createList(r),n.listenMapClick())},error:function(){t.removeClass(s,"ol-geocoder-loading");var e=t.createElement("li","
        Error! No internet connection?
        ");n.els.result_container.appendChild(e)}})},createList:function(e){var r=this,n=this.els.result_container;e.forEach(function(e){var o=r.addressTemplate(e),s=''+o+"",a=t.createElement("li",s);a.addEventListener("click",function(t){t.preventDefault(),r.chosen(e,o,e.address,e.original)},!1),n.appendChild(a)})},addressTemplate:function(e){var r=e.address,n=[];return r.name&&n.push('{name}'),(r.road||r.building||r.house_number)&&n.push('{building} {road} {house_number}'),(r.city||r.town||r.village)&&n.push('{postcode} {city} {town} {village}'),(r.state||r.country)&&n.push('{state} {country}'),t.template(n.join("
        "),r)},chosen:function(t,r,n,o){this.options.keepOpen===!1&&this.clearResults(!0);var s=e.$base.getMap(),a=s.getView(),i=a.getProjection(),l=ol.proj.transform([parseFloat(t.lon),parseFloat(t.lat)],"EPSG:4326",i),c=2.388657133911758,d=500,u={coord:l,address_html:r,address_obj:n,address_original:o},p=ol.animation.pan({duration:d,source:a.getCenter()}),m=ol.animation.zoom({duration:d,resolution:a.getResolution()});s.beforeRender(p,m),a.setCenter(l),a.setResolution(c),this.createFeature(u)},createFeature:function(r){var n=new ol.Feature({address_html:r.address_html,address_obj:r.address_obj,address_original:r.address_original,geometry:new ol.geom.Point(r.coord)}),o=t.randomId("geocoder-ft-"),s=this.options.featureStyle||e.Nominatim.featureStyle;this.addLayer(),n.setStyle(s),n.setId(o),this.getSource().addFeature(n),e.$base.dispatchEvent({type:e.EventType.ADDRESSCHOSEN,feature:n,coordinate:r.coord})},mapquestResponse:function(e){var t=e.map(function(e){return{lon:e.lon,lat:e.lat,address:{name:e.address.neighbourhood||"",road:e.address.road||"",postcode:e.address.postcode,city:e.address.city||e.address.town,state:e.address.state,country:e.address.country},original:{formatted:e.display_name,details:e.address}}});return t},photonResponse:function(e){var t=e.map(function(e){return{lon:e.geometry.coordinates[0],lat:e.geometry.coordinates[1],address:{name:e.properties.name,postcode:e.properties.postcode,city:e.properties.city,state:e.properties.state,country:e.properties.country},original:{formatted:e.properties.name,details:e.properties}}});return t},peliasResponse:function(e){var t=e.map(function(e){return{lon:e.geometry.coordinates[0],lat:e.geometry.coordinates[1],address:{name:e.properties.name,house_number:e.properties.housenumber,postcode:e.properties.postalcode,road:e.properties.street,city:e.properties.city,state:e.properties.region,country:e.properties.country},original:{formatted:e.properties.label,details:e.properties}}});return t},googleResponse:function(e){var r=["point_of_interest","establishment","natural_feature","airport"],n=["street_address","route","sublocality_level_5","intersection"],o=["postal_code"],s=["locality"],a=["administrative_area_level_1"],i=["country"],l=function(e){var l={name:"",road:"",postcode:"",city:"",state:"",country:""};return e.forEach(function(e){t.anyMatchInArray(e.types,r)?l.name=e.long_name:t.anyMatchInArray(e.types,n)?l.road=e.long_name:t.anyMatchInArray(e.types,o)?l.postcode=e.long_name:t.anyMatchInArray(e.types,s)?l.city=e.long_name:t.anyMatchInArray(e.types,a)?l.state=e.long_name:t.anyMatchInArray(e.types,i)&&(l.country=e.long_name)}),l},c=[];return e.forEach(function(e){var r=l(e.address_components);t.anyItemHasValue(r)&&c.push({lon:e.geometry.location.lng,lat:e.geometry.location.lat,address:{name:r.name,postcode:r.postcode,road:r.road,city:r.city,state:r.state,country:r.country},original:{formatted:e.formatted_address,details:e.address_components}})}),c},getSource:function(){return this.layer.getSource()},addLayer:function(){var t=this,r=!1,n=e.$base.getMap();n.getLayers().forEach(function(e){e===t.layer&&(r=!0)}),r||n.addLayer(this.layer)},getProvider:function(r){var n,o=e.Nominatim.providers[r.provider],s=e.Nominatim.providers.names,a=[s.MAPQUEST,s.PELIAS,s.GOOGLE],i=["de","it","fr","en"];switch(r.provider){case s.OSM:case s.MAPQUEST:n={q:r.query,limit:r.limit,countrycodes:r.countrycodes,"accept-language":r.lang},o.params=t.mergeOptions(o.params,n);break;case s.PHOTON:r.lang=r.lang.toLowerCase(),n={q:r.query,limit:r.limit||o.params.limit,lang:i.indexOf(r.lang)>-1?r.lang:o.params.lang},o.params=t.mergeOptions(o.params,n);break;case s.GOOGLE:n={address:r.query,language:r.lang},o.params=t.mergeOptions(o.params,n);break;case s.PELIAS:n={text:r.query,size:r.limit},o.params=t.mergeOptions(o.params,n)}return a.indexOf(r.provider)>-1&&(o.params.key=r.key),o}},e.EventType={ADDRESSCHOSEN:"addresschosen"},e.Nominatim.elements={},e.Nominatim.providers={names:{OSM:"osm",MAPQUEST:"mapquest",GOOGLE:"google",PHOTON:"photon",PELIAS:"pelias"},osm:{url:"http://nominatim.openstreetmap.org/search/",params:{format:"json",q:"",addressdetails:1,limit:10,countrycodes:"","accept-language":"en-US"}},mapquest:{url:"http://open.mapquestapi.com/nominatim/v1/search.php",params:{key:"",format:"json",q:"",addressdetails:1,limit:10,countrycodes:"","accept-language":"en-US"}},google:{url:"https://maps.googleapis.com/maps/api/geocode/json",params:{key:"",address:"",language:"en-US"}},pelias:{url:"https://search.mapzen.com/v1/search",params:{key:"",text:"",size:10}},photon:{url:"http://photon.komoot.de/api/",params:{q:"",limit:10,lang:"en"}}},e.Nominatim.featureStyle=[new ol.style.Style({image:new ol.style.Icon({scale:.7,anchor:[.5,1],src:"//cdn.rawgit.com/jonataswalker/map-utils/master/images/marker.png"}),zIndex:5}),new ol.style.Style({image:new ol.style.Circle({fill:new ol.style.Fill({color:[235,235,235,1]}),stroke:new ol.style.Stroke({color:[0,0,0,1]}),radius:5}),zIndex:4})],e.Nominatim.html=['",'
          '].join(""),e.Base}); diff --git a/package.json b/package.json index 011b05e..41344aa 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "ol3-geocoder", - "version": "1.6.0", + "version": "1.7.0", "description": "A geocoder extension for OpenLayers 3.", "main": "ol3-geocoder.js", "author": "Jonatas Walker", "license": "MIT", "devDependencies": { - "jshint": "latest", + "eslint": "latest", "js-beautify": "latest", "uglify-js": "latest", "clean-css": "latest", @@ -14,5 +14,40 @@ "postcss-cli": "latest", "nodemon": "latest", "parallelshell": "latest" + }, + "eslintConfig": { + "env": { + "browser": true, + "amd": true, + "commonjs": true, + "node": false + }, + "globals": { + "ol": true + }, + "rules": { + "block-scoped-var": 1, + "dot-notation": 1, + "no-alert": 2, + "no-caller": 2, + "no-eval": 2, + "no-proto": 2, + "no-redeclare": 2, + "valid-typeof": 2, + "no-unreachable": 2, + "no-irregular-whitespace": 1, + "no-invalid-regexp": 1, + "no-func-assign": 2, + "no-extra-semi": 2, + "no-dupe-keys": 2, + "no-undef": 2, + "no-use-before-define": 2, + "no-unused-vars": 1, + "no-unreachable": 2, + "strict": [1, "global"], + "key-spacing": 1, + "new-cap": 1, + "quotes": [2, "single"] + } } } diff --git a/src/base.js b/src/base.js index f9225d0..f9b8a31 100644 --- a/src/base.js +++ b/src/base.js @@ -1,11 +1,11 @@ /** * @constructor * @extends {ol.control.Control} - * @fires {Geocoder.EventType} + * @fires {G.EventType} * @param {string} control_type Nominatim|Reverse. * @param {object|undefined} opt_options Options. */ -var Geocoder = function(control_type, opt_options){ +G.Base = function(control_type, opt_options){ utils.assert(typeof control_type === 'string', '@param `control_type`' + ' should be string type!' ); @@ -15,25 +15,25 @@ var Geocoder = function(control_type, opt_options){ control_type = control_type || 'nominatim'; - var nominatim = new Geocoder.Nominatim(this, opt_options); - this.layer = nominatim.layer; + G.$base = this; + G.$nominatim = new G.Nominatim(opt_options); ol.control.Control.call(this, { - element: nominatim.els.container + element: G.$nominatim.container }); }; -ol.inherits(Geocoder, ol.control.Control); +ol.inherits(G.Base, ol.control.Control); /** * @return {ol.source.Vector} Returns the source created by this control */ -Geocoder.prototype.getSource = function(){ - return this.layer.getSource(); +G.Base.prototype.getSource = function(){ + return this.getLayer().getSource(); }; /** * @return {ol.layer.Vector} Returns the layer created by this control */ -Geocoder.prototype.getLayer = function(){ - return this.layer; +G.Base.prototype.getLayer = function(){ + return G.$nominatim.layer; }; diff --git a/src/nominatim.js b/src/nominatim.js index 1613a3f..7ae1a96 100644 --- a/src/nominatim.js +++ b/src/nominatim.js @@ -1,127 +1,125 @@ -(function(Geocoder){ +/** + * @constructor + */ +G.Nominatim = function(opt_options){ + this.layer_name = utils.randomId('geocoder-layer-'); + this.layer = new ol.layer.Vector({ + name: this.layer_name, + source: new ol.source.Vector() + }); + var defaults = { + provider: 'osm', + placeholder: 'Search for an address', + featureStyle: G.Nominatim.featureStyle, + lang: 'en-US', + limit: 5, + keepOpen: false, + debug: false + }; - Geocoder.Nominatim = function(geocoder, opt_options){ - this.geocoder = geocoder; - this.layer_name = utils.randomId('geocoder-layer-'); - this.layer = new ol.layer.Vector({ - name: this.layer_name, - source: new ol.source.Vector() - }); - var defaults = { - provider: 'osm', - placeholder: 'Search for an address', - featureStyle: Geocoder.Nominatim.featureStyle, - lang: 'en-US', - limit: 5, - keepOpen: false, - debug: false - }; - - this.options = utils.mergeOptions(defaults, opt_options); - this.options.provider = this.options.provider.toLowerCase(); - this.constants = { - road: 'ol-geocoder-road', - city: 'ol-geocoder-city', - country: 'ol-geocoder-country', - class_container: 'ol-geocoder', - expanded_class: 'ol-geocoder-search-expanded' - }; + this.options = utils.mergeOptions(defaults, opt_options); + this.options.provider = this.options.provider.toLowerCase(); + this.constants = { + road: 'ol-geocoder-road', + city: 'ol-geocoder-city', + country: 'ol-geocoder-country', + class_container: 'ol-geocoder', + expanded_class: 'ol-geocoder-search-expanded' + }; - this.createControl(); - this.els = Geocoder.Nominatim.elements; - this.registered_listeners = { - map_click: false - }; - this.setListeners(); - return this; + this.container = this.createControl(); + this.els = G.Nominatim.elements; + this.registered_listeners = { + map_click: false }; - - Geocoder.Nominatim.prototype = { - createControl: function(){ - var container = utils.createElement([ - 'div', { classname: this.constants.class_container } - ], Geocoder.Nominatim.html); - - Geocoder.Nominatim.elements = { - container: container, - control: container.querySelector('.ol-geocoder-search'), - btn_search: container.querySelector('.ol-geocoder-btn-search'), - input_search: container.querySelector('.ol-geocoder-input-search'), - result_container: container.querySelector('.ol-geocoder-result') - }; - //set placeholder from options - Geocoder.Nominatim.elements.input_search.placeholder = - this.options.placeholder; + this.setListeners(); + return this; +}; - return container; - }, - setListeners: function(){ - var - this_ = this, - openSearch = function() { - if(utils.hasClass(this_.els.control, this_.constants.expanded_class)){ - this_.collapse(); - } else { - this_.expand(); - } - }, - query = function(evt){ - if (evt.keyCode == 13){ //enter key - var q = utils.htmlEscape(this_.els.input_search.value); - this_.query(q); - } +G.Nominatim.prototype = { + createControl: function(){ + var container = utils.createElement([ + 'div', { classname: this.constants.class_container } + ], G.Nominatim.html); + + G.Nominatim.elements = { + container: container, + control: container.querySelector('.ol-geocoder-search'), + btn_search: container.querySelector('.ol-geocoder-btn-search'), + input_search: container.querySelector('.ol-geocoder-input-search'), + result_container: container.querySelector('.ol-geocoder-result') + }; + //set placeholder from options + G.Nominatim.elements.input_search.placeholder = this.options.placeholder; + + return container; + }, + setListeners: function(){ + var + this_ = this, + openSearch = function() { + if(utils.hasClass(this_.els.control, this_.constants.expanded_class)){ + this_.collapse(); + } else { + this_.expand(); } - ; - this_.els.input_search.addEventListener('keydown', query, false); - this_.els.btn_search.addEventListener('click', openSearch, false); - }, - listenMapClick: function() { - if(this.registered_listeners.map_click) { - // already registered - return; - } - - var this_ = this; - var map_element = this.geocoder.getMap().getTargetElement(); - this.registered_listeners.map_click = true; - - //one-time fire click - map_element.addEventListener('click', { - handleEvent: function (evt) { - this_.clearResults(true); - map_element.removeEventListener(evt.type, this, false); - this_.registered_listeners.map_click = false; + }, + query = function(evt){ + if (evt.keyCode == 13){ //enter key + var q = utils.htmlEscape(this_.els.input_search.value); + this_.query(q); } - }, false); - }, - expand: function(){ - utils.removeClass(this.els.input_search, 'ol-geocoder-loading'); - utils.addClass(this.els.control, this.constants.expanded_class); - var input = this.els.input_search; - window.setTimeout(function(){ - input.focus(); - }, 100); - this.listenMapClick(); - }, - collapse: function(){ - this.els.input_search.value = ""; - this.els.input_search.blur(); - utils.removeClass(this.els.control, this.constants.expanded_class); - this.clearResults(); - }, - clearResults: function(collapse){ - if(collapse) { - this.collapse(); - } else { - utils.removeAllChildren(this.els.result_container); } - }, - query: function(query){ - var - this_ = this, + ; + this_.els.input_search.addEventListener('keydown', query, false); + this_.els.btn_search.addEventListener('click', openSearch, false); + }, + listenMapClick: function() { + if(this.registered_listeners.map_click) { + // already registered + return; + } + + var this_ = this; + var map_element = G.$base.getMap().getTargetElement(); + this.registered_listeners.map_click = true; + + //one-time fire click + map_element.addEventListener('click', { + handleEvent: function (evt) { + this_.clearResults(true); + map_element.removeEventListener(evt.type, this, false); + this_.registered_listeners.map_click = false; + } + }, false); + }, + expand: function(){ + utils.removeClass(this.els.input_search, 'ol-geocoder-loading'); + utils.addClass(this.els.control, this.constants.expanded_class); + var input = this.els.input_search; + window.setTimeout(function(){ + input.focus(); + }, 100); + this.listenMapClick(); + }, + collapse: function(){ + this.els.input_search.value = ''; + this.els.input_search.blur(); + utils.removeClass(this.els.control, this.constants.expanded_class); + this.clearResults(); + }, + clearResults: function(collapse){ + if(collapse) { + this.collapse(); + } else { + utils.removeAllChildren(this.els.result_container); + } + }, + query: function(query){ + var this_ = this, options = this.options, input = this.els.input_search, - providers_names = Geocoder.Nominatim.providers.names, + providers_names = G.Nominatim.providers.names, provider = this.getProvider({ provider: options.provider, key: options.key, @@ -129,106 +127,104 @@ lang: options.lang, countrycodes: options.countrycodes, limit: options.limit - }) - ; + }); - this.clearResults(); - utils.addClass(input, 'ol-geocoder-loading'); + this.clearResults(); + utils.addClass(input, 'ol-geocoder-loading'); - utils.json(provider.url, provider.params).when({ - ready: function(response){ - if(options.debug){ - console.info(response); - } - - utils.removeClass(input, 'ol-geocoder-loading'); - - //will be fullfiled according to provider - var response__; - - switch (this_.options.provider) { - case providers_names.OSM: - case providers_names.MAPQUEST: - response__ = response.length > 0 ? - this_.mapquestResponse(response) : undefined; - break; - case providers_names.PELIAS: - response__ = response.features.length > 0 ? - this_.peliasResponse(response.features) : undefined; - break; - case providers_names.PHOTON: - response__ = response.features.length > 0 ? - this_.photonResponse(response.features) : undefined; - break; - case providers_names.GOOGLE: - response__ = response.results.length > 0 ? - this_.googleResponse(response.results) : undefined; - break; - } - if(response__){ - this_.createList(response__); - this_.listenMapClick(); - } - }, - error: function(){ - utils.removeClass(input, 'ol-geocoder-loading'); - var li = utils.createElement('li', - '
          Error! No internet connection?
          '); - this_.els.result_container.appendChild(li); + utils.json(provider.url, provider.params).when({ + ready: function(response){ + if(options.debug){ + console.info(response); } - }); - }, - createList: function(response){ - var this_ = this; - var ul = this.els.result_container; - response.forEach(function(row) { - var - address_html = this_.addressTemplate(row), - html = '' + address_html + '', - li = utils.createElement('li', html) - ; - li.addEventListener('click', function(evt){ - evt.preventDefault(); - this_.chosen(row, address_html, row.address, row.original); - }, false); - ul.appendChild(li); - }); - }, - addressTemplate: function(r){ - var row = r.address, html = []; - if (row.name) { - html.push( - '{name}' - ); - } - if (row.road || row.building || row.house_number) { - html.push( - '{building} {road} {house_number}' - ); - } - if (row.city || row.town || row.village) { - html.push( - '{postcode} {city} {town} {village}' - ); - } - if (row.state || row.country) { - html.push( - '{state} {country}' - ); - } - return utils.template(html.join('
          '), row); - }, - chosen: function(place, address_html, address_obj, address_original){ - if(this.options.keepOpen === false){ - this.clearResults(true); + utils.removeClass(input, 'ol-geocoder-loading'); + + //will be fullfiled according to provider + var response__; + + switch (this_.options.provider) { + case providers_names.OSM: + case providers_names.MAPQUEST: + response__ = response.length > 0 ? + this_.mapquestResponse(response) : undefined; + break; + case providers_names.PELIAS: + response__ = response.features.length > 0 ? + this_.peliasResponse(response.features) : undefined; + break; + case providers_names.PHOTON: + response__ = response.features.length > 0 ? + this_.photonResponse(response.features) : undefined; + break; + case providers_names.GOOGLE: + response__ = response.results.length > 0 ? + this_.googleResponse(response.results) : undefined; + break; + } + if(response__){ + this_.createList(response__); + this_.listenMapClick(); + } + }, + error: function(){ + utils.removeClass(input, 'ol-geocoder-loading'); + var li = utils.createElement('li', + '
          Error! No internet connection?
          '); + this_.els.result_container.appendChild(li); } - + }); + }, + createList: function(response){ + var this_ = this; + var ul = this.els.result_container; + response.forEach(function(row) { var - map = this.geocoder.getMap(), + address_html = this_.addressTemplate(row), + html = '' + address_html + '', + li = utils.createElement('li', html) + ; + li.addEventListener('click', function(evt){ + evt.preventDefault(); + this_.chosen(row, address_html, row.address, row.original); + }, false); + + ul.appendChild(li); + }); + }, + addressTemplate: function(r){ + var row = r.address, html = []; + if (row.name) { + html.push( + '{name}' + ); + } + if (row.road || row.building || row.house_number) { + html.push( + '{building} {road} {house_number}' + ); + } + if (row.city || row.town || row.village) { + html.push( + '{postcode} {city} {town} {village}' + ); + } + if (row.state || row.country) { + html.push( + '{state} {country}' + ); + } + return utils.template(html.join('
          '), row); + }, + chosen: function(place, address_html, address_obj, address_original){ + if(this.options.keepOpen === false){ + this.clearResults(true); + } + + var map = G.$base.getMap(), view = map.getView(), projection = view.getProjection(), coord = ol.proj.transform( @@ -249,333 +245,325 @@ zoom = ol.animation.zoom({ duration: duration, resolution: view.getResolution() - }) - ; - - map.beforeRender(pan, zoom); - view.setCenter(coord); - view.setResolution(resolution); - this.createFeature(obj); - }, - createFeature: function(obj){ - var - feature = new ol.Feature({ + }); + + map.beforeRender(pan, zoom); + view.setCenter(coord); + view.setResolution(resolution); + this.createFeature(obj); + }, + createFeature: function(obj){ + var feature = new ol.Feature({ address_html: obj.address_html, address_obj: obj.address_obj, address_original: obj.address_original, geometry: new ol.geom.Point(obj.coord) }), feature_id = utils.randomId('geocoder-ft-'), - feature_style = this.options.featureStyle || - Geocoder.Nominatim.featureStyle - ; - - this.addLayer(); - feature.setStyle(feature_style); - feature.setId(feature_id); - this.getSource().addFeature(feature); - this.geocoder.dispatchEvent({ - type: Geocoder.EventType.ADDRESSCHOSEN, - feature: feature, - coordinate: obj.coord, - }); - }, - mapquestResponse: function(results){ - var array = results.map(function(result){ - return { - lon: result.lon, - lat: result.lat, - address: { - name: result.address.neighbourhood || '', - road: result.address.road || '', - postcode: result.address.postcode, - city: result.address.city || result.address.town, - state: result.address.state, - country: result.address.country - }, - original: { - formatted: result.display_name, - details: result.address - } - }; - }); - return array; - }, - photonResponse: function(features){ - var array = features.map(function(feature){ - return { - lon: feature.geometry.coordinates[0], - lat: feature.geometry.coordinates[1], - address: { - name: feature.properties.name, - postcode: feature.properties.postcode, - city: feature.properties.city, - state: feature.properties.state, - country: feature.properties.country - }, - original: { - formatted: feature.properties.name, - details: feature.properties - } - }; + feature_style = this.options.featureStyle || G.Nominatim.featureStyle; + + this.addLayer(); + feature.setStyle(feature_style); + feature.setId(feature_id); + this.getSource().addFeature(feature); + G.$base.dispatchEvent({ + type: G.EventType.ADDRESSCHOSEN, + feature: feature, + coordinate: obj.coord, + }); + }, + mapquestResponse: function(results){ + var array = results.map(function(result){ + return { + lon: result.lon, + lat: result.lat, + address: { + name: result.address.neighbourhood || '', + road: result.address.road || '', + postcode: result.address.postcode, + city: result.address.city || result.address.town, + state: result.address.state, + country: result.address.country + }, + original: { + formatted: result.display_name, + details: result.address + } + }; + }); + return array; + }, + photonResponse: function(features){ + var array = features.map(function(feature){ + return { + lon: feature.geometry.coordinates[0], + lat: feature.geometry.coordinates[1], + address: { + name: feature.properties.name, + postcode: feature.properties.postcode, + city: feature.properties.city, + state: feature.properties.state, + country: feature.properties.country + }, + original: { + formatted: feature.properties.name, + details: feature.properties + } + }; + }); + return array; + }, + peliasResponse: function(features){ + var array = features.map(function(feature){ + return { + lon: feature.geometry.coordinates[0], + lat: feature.geometry.coordinates[1], + address: { + name: feature.properties.name, + house_number: feature.properties.housenumber, + postcode: feature.properties.postalcode, + road: feature.properties.street, + city: feature.properties.city, + state: feature.properties.region, + country: feature.properties.country + }, + original: { + formatted: feature.properties.label, + details: feature.properties + } + }; + }); + return array; + }, + googleResponse: function(results){ + var + name = [ + 'point_of_interest', + 'establishment', + 'natural_feature', + 'airport' + ], + road = [ + 'street_address', + 'route', + 'sublocality_level_5', + 'intersection' + ], + postcode = [ 'postal_code' ], + city = [ 'locality' ], + state = [ 'administrative_area_level_1' ], + country = [ 'country' ] + ; + + /* + * @param {Array} details - address_components + */ + var getDetails = function(details){ + var parts = { + name: '', + road: '', + postcode: '', + city: '', + state: '', + country: '' + }; + details.forEach(function(detail){ + if(utils.anyMatchInArray(detail.types, name)){ + parts.name = detail.long_name; + } else if(utils.anyMatchInArray(detail.types, road)){ + parts.road = detail.long_name; + } else if(utils.anyMatchInArray(detail.types, postcode)){ + parts.postcode = detail.long_name; + } else if(utils.anyMatchInArray(detail.types, city)){ + parts.city = detail.long_name; + } else if(utils.anyMatchInArray(detail.types, state)){ + parts.state = detail.long_name; + } else if(utils.anyMatchInArray(detail.types, country)){ + parts.country = detail.long_name; + } }); - return array; - }, - peliasResponse: function(features){ - var array = features.map(function(feature){ - return { - lon: feature.geometry.coordinates[0], - lat: feature.geometry.coordinates[1], + return parts; + }; + + var array = []; + results.forEach(function(result){ + var details = getDetails(result.address_components); + if(utils.anyItemHasValue(details)){ + array.push({ + lon: result.geometry.location.lng, + lat: result.geometry.location.lat, address: { - name: feature.properties.name, - house_number: feature.properties.housenumber, - postcode: feature.properties.postalcode, - road: feature.properties.street, - city: feature.properties.city, - state: feature.properties.region, - country: feature.properties.country + name: details.name, + postcode: details.postcode, + road: details.road, + city: details.city, + state: details.state, + country: details.country }, original: { - formatted: feature.properties.label, - details: feature.properties - } - }; - }); - return array; - }, - googleResponse: function(results){ - var - name = [ - 'point_of_interest', - 'establishment', - 'natural_feature', - 'airport' - ], - road = [ - 'street_address', - 'route', - 'sublocality_level_5', - 'intersection' - ], - postcode = [ 'postal_code' ], - city = [ 'locality' ], - state = [ 'administrative_area_level_1' ], - country = [ 'country' ] - ; - - /* - * @param {Array} details - address_components - */ - var getDetails = function(details){ - var parts = { - name: '', - road: '', - postcode: '', - city: '', - state: '', - country: '' - }; - details.forEach(function(detail){ - if(utils.anyMatchInArray(detail.types, name)){ - parts.name = detail.long_name; - } else if(utils.anyMatchInArray(detail.types, road)){ - parts.road = detail.long_name; - } else if(utils.anyMatchInArray(detail.types, postcode)){ - parts.postcode = detail.long_name; - } else if(utils.anyMatchInArray(detail.types, city)){ - parts.city = detail.long_name; - } else if(utils.anyMatchInArray(detail.types, state)){ - parts.state = detail.long_name; - } else if(utils.anyMatchInArray(detail.types, country)){ - parts.country = detail.long_name; + formatted: result.formatted_address, + details: result.address_components } }); - return parts; - }; - - var array = []; - results.forEach(function(result){ - var details = getDetails(result.address_components); - if(utils.anyItemHasValue(details)){ - array.push({ - lon: result.geometry.location.lng, - lat: result.geometry.location.lat, - address: { - name: details.name, - postcode: details.postcode, - road: details.road, - city: details.city, - state: details.state, - country: details.country - }, - original: { - formatted: result.formatted_address, - details: result.address_components - } - }); - } - }); - return array; - }, - getSource: function() { - return this.layer.getSource(); - }, - addLayer: function() { - var this_ = this, found = false; - var map = this.geocoder.getMap(); - - map.getLayers().forEach(function(layer){ - if (layer === this_.layer) found = true; - }); - if (!found) { - map.addLayer(this.layer); } - }, - getProvider: function(options) { - var - params, - provider = Geocoder.Nominatim.providers[options.provider], - providers_names = Geocoder.Nominatim.providers.names, + }); + return array; + }, + getSource: function() { + return this.layer.getSource(); + }, + addLayer: function() { + var this_ = this, found = false; + var map = G.$base.getMap(); + + map.getLayers().forEach(function(layer){ + if (layer === this_.layer) found = true; + }); + if (!found) { + map.addLayer(this.layer); + } + }, + getProvider: function(options) { + var params, + provider = G.Nominatim.providers[options.provider], + providers_names = G.Nominatim.providers.names, requires_key = [ providers_names.MAPQUEST, providers_names.PELIAS, providers_names.GOOGLE ], - langs_photon = ['de', 'it', 'fr', 'en'] - ; - switch(options.provider) { - case providers_names.OSM: - case providers_names.MAPQUEST: - params = { - q: options.query, - limit: options.limit, - countrycodes: options.countrycodes, - 'accept-language': options.lang - }; - provider.params = utils.mergeOptions(provider.params, params); - break; - case providers_names.PHOTON: - options.lang = options.lang.toLowerCase(); - params = { - q: options.query, - limit: options.limit || provider.params.limit, - lang: (langs_photon.indexOf(options.lang) > -1) ? - options.lang : provider.params.lang - }; - provider.params = utils.mergeOptions(provider.params, params); - break; - case providers_names.GOOGLE: - params = { - address: options.query, - language: options.lang - }; - provider.params = utils.mergeOptions(provider.params, params); - break; - case providers_names.PELIAS: - params = { - text: options.query, - size: options.limit - }; - provider.params = utils.mergeOptions(provider.params, params); - break; - } - if (requires_key.indexOf(options.provider) > -1) { - provider.params.key = options.key; - } - return provider; + langs_photon = ['de', 'it', 'fr', 'en']; + switch(options.provider) { + case providers_names.OSM: + case providers_names.MAPQUEST: + params = { + q: options.query, + limit: options.limit, + countrycodes: options.countrycodes, + 'accept-language': options.lang + }; + provider.params = utils.mergeOptions(provider.params, params); + break; + case providers_names.PHOTON: + options.lang = options.lang.toLowerCase(); + params = { + q: options.query, + limit: options.limit || provider.params.limit, + lang: (langs_photon.indexOf(options.lang) > -1) ? + options.lang : provider.params.lang + }; + provider.params = utils.mergeOptions(provider.params, params); + break; + case providers_names.GOOGLE: + params = { + address: options.query, + language: options.lang + }; + provider.params = utils.mergeOptions(provider.params, params); + break; + case providers_names.PELIAS: + params = { + text: options.query, + size: options.limit + }; + provider.params = utils.mergeOptions(provider.params, params); + break; } - }; - Geocoder.EventType = { - /** - * Triggered when an address is chosen. - */ - ADDRESSCHOSEN: 'addresschosen' - }; - - Geocoder.Nominatim.elements = {}; - Geocoder.Nominatim.providers = { - names: { - OSM: 'osm', - MAPQUEST: 'mapquest', - GOOGLE: 'google', - PHOTON: 'photon', - PELIAS: 'pelias' - }, - osm: { - url: 'http://nominatim.openstreetmap.org/search/', - params: { - format: 'json', - q: '', - addressdetails: 1, - limit: 10, - countrycodes: '', - 'accept-language': 'en-US' - } - }, - mapquest: { - url: 'http://open.mapquestapi.com/nominatim/v1/search.php', - params: { - key: '', - format: 'json', - q: '', - addressdetails: 1, - limit: 10, - countrycodes: '', - 'accept-language': 'en-US' - } - }, - google: { - url: 'https://maps.googleapis.com/maps/api/geocode/json', - params: { - key: '', - address: '', - language: 'en-US' - } - }, - pelias: { - url: 'https://search.mapzen.com/v1/search', - params: { - key: '', - text: '', - size: 10 - } - }, - photon: { - url: 'http://photon.komoot.de/api/', - params: { - q: '', - limit: 10, - lang: 'en' - } + if (requires_key.indexOf(options.provider) > -1) { + provider.params.key = options.key; } - }; - Geocoder.Nominatim.featureStyle = [ - new ol.style.Style({ - image: new ol.style.Icon({ - scale: 0.7, - anchor: [0.5, 1], - src: '//cdn.rawgit.com/jonataswalker/' - + 'map-utils/master/images/marker.png' - }), - zIndex: 5 - }), - new ol.style.Style({ - image: new ol.style.Circle({ - fill: new ol.style.Fill({ color: [235, 235, 235, 1]}), - stroke: new ol.style.Stroke({ color: [0, 0, 0, 1]}), - radius: 5 - }), - zIndex: 4 - }) - ]; - Geocoder.Nominatim.html = [ - '', - '
            ' - ].join(''); -})(Geocoder); + return provider; + } +}; +G.EventType = { + /** + * Triggered when an address is chosen. + */ + ADDRESSCHOSEN: 'addresschosen' +}; +G.Nominatim.elements = {}; +G.Nominatim.providers = { + names: { + OSM: 'osm', + MAPQUEST: 'mapquest', + GOOGLE: 'google', + PHOTON: 'photon', + PELIAS: 'pelias' + }, + osm: { + url: 'http://nominatim.openstreetmap.org/search/', + params: { + format: 'json', + q: '', + addressdetails: 1, + limit: 10, + countrycodes: '', + 'accept-language': 'en-US' + } + }, + mapquest: { + url: 'http://open.mapquestapi.com/nominatim/v1/search.php', + params: { + key: '', + format: 'json', + q: '', + addressdetails: 1, + limit: 10, + countrycodes: '', + 'accept-language': 'en-US' + } + }, + google: { + url: 'https://maps.googleapis.com/maps/api/geocode/json', + params: { + key: '', + address: '', + language: 'en-US' + } + }, + pelias: { + url: 'https://search.mapzen.com/v1/search', + params: { + key: '', + text: '', + size: 10 + } + }, + photon: { + url: 'http://photon.komoot.de/api/', + params: { + q: '', + limit: 10, + lang: 'en' + } + } +}; +G.Nominatim.featureStyle = [ + new ol.style.Style({ + image: new ol.style.Icon({ + scale: 0.7, + anchor: [0.5, 1], + src: '//cdn.rawgit.com/jonataswalker/' + + 'map-utils/master/images/marker.png' + }), + zIndex: 5 + }), + new ol.style.Style({ + image: new ol.style.Circle({ + fill: new ol.style.Fill({ color: [235, 235, 235, 1]}), + stroke: new ol.style.Stroke({ color: [0, 0, 0, 1]}), + radius: 5 + }), + zIndex: 4 + }) +]; +G.Nominatim.html = [ + '', + '
              ' +].join(''); diff --git a/src/utils.js b/src/utils.js index 8e3e7a0..ed26da6 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,44 +1,26 @@ -(function(window, document){ - - var getXhr = function() { - var xhr = false; - if (window.XMLHttpRequest) { - xhr = new XMLHttpRequest(); - } else if (window.ActiveXObject) { - try { - xhr = new ActiveXObject("Msxml2.XMLHTTP"); - } catch(e) { - try { - xhr = new ActiveXObject("Microsoft.XMLHTTP"); - } catch(e) { - xhr = false; - } - } - } - return xhr; - }; - var encodeUrlXhr = function(url, data) { +/** + * Helper + */ +var utils = { + whiteSpaceRegex: /\s+/, + toQueryString: function(obj){ + return Object.keys(obj).reduce(function(a, k) { + a.push((typeof obj[k] === 'object') ? + utils.toQueryString(obj[k]) : + encodeURIComponent(k) + '=' + encodeURIComponent(obj[k]) + ); + return a; + }, []).join('&'); + }, + encodeUrlXhr: function(url, data) { if(data && typeof(data) === 'object') { var str_data = utils.toQueryString(data); url += (/\?/.test(url) ? '&' : '?') + str_data; } return url; - }; - - Geocoder.Utils = { - whiteSpaceRegex: /\s+/, - toQueryString: function(obj){ - return Object.keys(obj).reduce(function(a, k) { - a.push((typeof obj[k] === 'object') ? - utils.toQueryString(obj[k]) : - encodeURIComponent(k) + '=' + encodeURIComponent(obj[k]) - ); - return a; - }, []).join('&'); - }, - json: function(url, data) { - var - xhr = getXhr(), + }, + json: function(url, data) { + var xhr = new XMLHttpRequest(), when = {}, onload = function() { if (xhr.status === 200) { @@ -47,284 +29,281 @@ }, onerror = function() { console.info('Cannot XHR ' + JSON.stringify(url)); - } - ; - url = encodeUrlXhr(url, data); - xhr.open('GET', url, true); - xhr.setRequestHeader('Accept','application/json'); - xhr.onload = onload; - xhr.onerror = onerror; - xhr.onprogress = onprogress; - xhr.send(null); - - return { - when: function(obj) { when.ready = obj.ready; } - }; - }, - randomId: function(prefix){ - var id = (new Date().getTime()).toString(36); - return (prefix) ? prefix + id : id; - }, - to3857: function(coord){ - return ol.proj.transform( - [parseFloat(coord[0]), parseFloat(coord[1])], - 'EPSG:4326', 'EPSG:3857' - ); - }, - to4326: function(coord){ - return ol.proj.transform( - [parseFloat(coord[0]), parseFloat(coord[1])], - 'EPSG:3857', 'EPSG:4326' - ); - }, - isNumeric: function(str){ - return /^\d+$/.test(str); - }, - classRegex: function(classname) { - return new RegExp('(^|\\s+)' + classname + '(\\s+|$)'); - }, - /** - * @param {Element|Array} element DOM node or array of nodes. - * @param {String|Array} classname Class or array of classes. - * For example: 'class1 class2' or ['class1', 'class2'] - * @param {Number|undefined} timeout Timeout to remove a class. - */ - addClass: function(element, classname, timeout){ - if(Array.isArray(element)){ - element.forEach(function(each){ - utils.addClass(each, classname); - }); - return; - } - - var - array = (Array.isArray(classname)) ? classname : classname.split(/\s+/), - i = array.length - ; - while(i--){ - if(!utils.hasClass(element, array[i])) { - utils._addClass(element, array[i], timeout); - } - } - }, - _addClass: function(el, c, timeout){ - // use native if available - if (el.classList) { - el.classList.add(c); - } else { - el.className = (el.className + ' ' + c).trim(); - } - - if(timeout && utils.isNumeric(timeout)){ - window.setTimeout(function(){ - utils._removeClass(el, c); - }, timeout); - } - }, - /** - * @param {Element|Array} element DOM node or array of nodes. - * @param {String|Array} classname Class or array of classes. - * For example: 'class1 class2' or ['class1', 'class2'] - * @param {Number|undefined} timeout Timeout to add a class. - */ - removeClass: function(element, classname, timeout){ - if(Array.isArray(element)){ - element.forEach(function(each){ - utils.removeClass(each, classname, timeout); - }); - return; + }; + url = utils.encodeUrlXhr(url, data); + xhr.open('GET', url, true); + xhr.setRequestHeader('Accept','application/json'); + xhr.onload = onload; + xhr.onerror = onerror; + xhr.send(null); + + return { + when: function(obj) { when.ready = obj.ready; } + }; + }, + randomId: function(prefix){ + var id = (new Date().getTime()).toString(36); + return (prefix) ? prefix + id : id; + }, + to3857: function(coord){ + return ol.proj.transform( + [parseFloat(coord[0]), parseFloat(coord[1])], + 'EPSG:4326', 'EPSG:3857' + ); + }, + to4326: function(coord){ + return ol.proj.transform( + [parseFloat(coord[0]), parseFloat(coord[1])], + 'EPSG:3857', 'EPSG:4326' + ); + }, + isNumeric: function(str){ + return /^\d+$/.test(str); + }, + classRegex: function(classname) { + return new RegExp('(^|\\s+)' + classname + '(\\s+|$)'); + }, + /** + * @param {Element|Array} element DOM node or array of nodes. + * @param {String|Array} classname Class or array of classes. + * For example: 'class1 class2' or ['class1', 'class2'] + * @param {Number|undefined} timeout Timeout to remove a class. + */ + addClass: function(element, classname, timeout){ + if(Array.isArray(element)){ + element.forEach(function(each){ + utils.addClass(each, classname); + }); + return; + } + + var + array = (Array.isArray(classname)) ? classname : classname.split(/\s+/), + i = array.length + ; + while(i--){ + if(!utils.hasClass(element, array[i])) { + utils._addClass(element, array[i], timeout); } - - var - array = (Array.isArray(classname)) ? classname : classname.split(/\s+/), - i = array.length - ; - while(i--){ - if(utils.hasClass(element, array[i])) { - utils._removeClass(element, array[i], timeout); - } + } + }, + _addClass: function(el, c, timeout){ + // use native if available + if (el.classList) { + el.classList.add(c); + } else { + el.className = (el.className + ' ' + c).trim(); + } + + if(timeout && utils.isNumeric(timeout)){ + window.setTimeout(function(){ + utils._removeClass(el, c); + }, timeout); + } + }, + /** + * @param {Element|Array} element DOM node or array of nodes. + * @param {String|Array} classname Class or array of classes. + * For example: 'class1 class2' or ['class1', 'class2'] + * @param {Number|undefined} timeout Timeout to add a class. + */ + removeClass: function(element, classname, timeout){ + if(Array.isArray(element)){ + element.forEach(function(each){ + utils.removeClass(each, classname, timeout); + }); + return; + } + + var + array = (Array.isArray(classname)) ? classname : classname.split(/\s+/), + i = array.length + ; + while(i--){ + if(utils.hasClass(element, array[i])) { + utils._removeClass(element, array[i], timeout); } - }, - _removeClass: function(el, c, timeout){ - if (el.classList){ - el.classList.remove(c); + } + }, + _removeClass: function(el, c, timeout){ + if (el.classList){ + el.classList.remove(c); + } else { + el.className = (el.className.replace(utils.classRegex(c), ' ')).trim(); + } + if(timeout && utils.isNumeric(timeout)){ + window.setTimeout(function() { + utils._addClass(el, c); + }, timeout); + } + }, + /** + * @param {Element} element DOM node. + * @param {String} classname Classname. + * @return {Boolean} + */ + hasClass: function(element, c) { + // use native if available + return (element.classList) ? + element.classList.contains(c) : utils.classRegex(c).test(element.className); + }, + /** + * @param {Element|Array} element DOM node or array of nodes. + * @param {String} classname Classe. + */ + toggleClass: function(element, classname){ + if(Array.isArray(element)) { + element.forEach(function(each) { + utils.toggleClass(each, classname); + }); + return; + } + + // use native if available + if(element.classList) { + element.classList.toggle(classname); + } else { + if(utils.hasClass(element, classname)){ + utils._removeClass(element, classname); } else { - el.className = (el.className.replace(utils.classRegex(c), ' ')).trim(); - } - if(timeout && utils.isNumeric(timeout)){ - window.setTimeout(function() { - utils._addClass(el, c); - }, timeout); + utils._addClass(element, classname); } - }, - /** - * @param {Element} element DOM node. - * @param {String} classname Classname. - * @return {Boolean} - */ - hasClass: function(element, c) { - // use native if available - return (element.classList) ? - element.classList.contains(c) : utils.classRegex(c).test(element.className); - }, - /** - * @param {Element|Array} element DOM node or array of nodes. - * @param {String} classname Classe. - */ - toggleClass: function(element, classname){ - if(Array.isArray(element)) { - element.forEach(function(each) { - utils.toggleClass(each, classname); - }); - return; + } + }, + $: function(id){ + id = (id[0] === '#') ? id.substr(1, id.length) : id; + return document.getElementById(id); + }, + isElement: function(obj){ + // DOM, Level2 + if ('HTMLElement' in window) { + return (!!obj && obj instanceof HTMLElement); + } + // Older browsers + return (!!obj && typeof obj === 'object' && + obj.nodeType === 1 && !!obj.nodeName); + }, + getAllChildren: function(node, tag){ + return [].slice.call(node.getElementsByTagName(tag)); + }, + isEmpty: function(str){ + return (!str || 0 === str.length); + }, + emptyArray: function(array){ + while(array.length) array.pop(); + }, + anyMatchInArray: function(source, target) { + return source.some(function(each){ + return target.indexOf(each) >= 0; + }); + }, + everyMatchInArray: function(arr1, arr2) { + return arr2.every(function(each){ + return arr1.indexOf(each) >= 0; + }); + }, + anyItemHasValue: function(obj){ + var has = false; + for(var key in obj){ + if(!utils.isEmpty(obj[key])){ + has = true; } + } + return has; + }, + removeAllChildren: function(node) { + while (node.firstChild) { + node.removeChild(node.firstChild); + } + }, + removeAll: function(collection) { + var node; + while ((node = collection[0])) { + node.parentNode.removeChild(node); + } + }, + getChildren: function(node, tag){ + return [].filter.call(node.childNodes, function(el) { + return (tag) ? + el.nodeType == 1 && el.tagName.toLowerCase() == tag + : + el.nodeType == 1; + }); + }, + template: function(html, row){ + var this_ = this; + + return html.replace(/\{ *([\w_-]+) *\}/g, function (html, key) { + var value = (row[key] === undefined) ? '' : row[key]; + return this_.htmlEscape(value); + }); + }, + htmlEscape: function(str){ + return String(str) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + }, + /** + * Overwrites obj1's values with obj2's and adds + * obj2's if non existent in obj1 + * @returns obj3 a new object based on obj1 and obj2 + */ + mergeOptions: function(obj1, obj2){ + var obj3 = {}; + for (var attr1 in obj1) { obj3[attr1] = obj1[attr1]; } + for (var attr2 in obj2) { obj3[attr2] = obj2[attr2]; } + return obj3; + }, + createElement: function(node, html){ + var elem; + if(Array.isArray(node)){ + elem = document.createElement(node[0]); - // use native if available - if(element.classList) { - element.classList.toggle(classname); - } else { - if(utils.hasClass(element, classname)){ - utils._removeClass(element, classname); - } else { - utils._addClass(element, classname); - } - } - }, - $: function(id){ - id = (id[0] === '#') ? id.substr(1, id.length) : id; - return document.getElementById(id); - }, - isElement: function(obj){ - // DOM, Level2 - if ("HTMLElement" in window) { - return (!!obj && obj instanceof HTMLElement); - } - // Older browsers - return (!!obj && typeof obj === "object" && - obj.nodeType === 1 && !!obj.nodeName); - }, - getAllChildren: function(node, tag){ - return [].slice.call(node.getElementsByTagName(tag)); - }, - isEmpty: function(str){ - return (!str || 0 === str.length); - }, - emptyArray: function(array){ - while(array.length) array.pop(); - }, - anyMatchInArray: function(source, target) { - return source.some(function(each){ - return target.indexOf(each) >= 0; - }); - }, - everyMatchInArray: function(arr1, arr2) { - return arr2.every(function(each){ - return arr1.indexOf(each) >= 0; - }); - }, - anyItemHasValue: function(obj){ - var has = false; - for(var key in obj){ - if(!utils.isEmpty(obj[key])){ - has = true; - } - } - return has; - }, - removeAllChildren: function(node) { - while (node.firstChild) { - node.removeChild(node.firstChild); + if(node[1].id) { + elem.id = node[1].id; } - }, - removeAll: function(collection) { - var node; - while ((node = collection[0])) { - node.parentNode.removeChild(node); + if(node[1].classname) { + elem.className = node[1].classname; } - }, - getChildren: function(node, tag){ - return [].filter.call(node.childNodes, function(el) { - return (tag) ? - el.nodeType == 1 && el.tagName.toLowerCase() == tag - : - el.nodeType == 1; - }); - }, - template: function(html, row){ - var this_ = this; - return html.replace(/\{ *([\w_-]+) *\}/g, function (html, key) { - var value = (row[key] === undefined) ? '' : row[key]; - return this_.htmlEscape(value); - }); - }, - htmlEscape: function(str){ - return String(str) - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, "'"); - }, - /** - * Overwrites obj1's values with obj2's and adds - * obj2's if non existent in obj1 - * @returns obj3 a new object based on obj1 and obj2 - */ - mergeOptions: function(obj1, obj2){ - var obj3 = {}; - for (var attr1 in obj1) { obj3[attr1] = obj1[attr1]; } - for (var attr2 in obj2) { obj3[attr2] = obj2[attr2]; } - return obj3; - }, - createElement: function(node, html){ - var elem; - if(Array.isArray(node)){ - elem = document.createElement(node[0]); - - if(node[1].id) { - elem.id = node[1].id; - } - if(node[1].classname) { - elem.className = node[1].classname; - } - - if(node[1].attr){ - var attr = node[1].attr; - if(Array.isArray(attr)){ - var i = -1; - while(++i < attr.length){ - elem.setAttribute(attr[i].name, attr[i].value); - } - } else { - elem.setAttribute(attr.name, attr.value); + if(node[1].attr){ + var attr = node[1].attr; + if(Array.isArray(attr)){ + var i = -1; + while(++i < attr.length){ + elem.setAttribute(attr[i].name, attr[i].value); } + } else { + elem.setAttribute(attr.name, attr.value); } - } else{ - elem = document.createElement(node); - } - elem.innerHTML = html; - var frag = document.createDocumentFragment(); - - while (elem.childNodes[0]) { - frag.appendChild(elem.childNodes[0]); - } - elem.appendChild(frag); - return elem; - }, - assert: function(condition, message) { - if (!condition) { - message = message || "Assertion failed"; - if (typeof Error !== "undefined") { - throw new Error(message); - } - throw message; // Fallback } - }, - assertEqual: function(a, b, message) { - if (a != b) { - throw new Error(message + " mismatch: " + a + " != " + b); + } else{ + elem = document.createElement(node); + } + elem.innerHTML = html; + var frag = document.createDocumentFragment(); + + while (elem.childNodes[0]) { + frag.appendChild(elem.childNodes[0]); + } + elem.appendChild(frag); + return elem; + }, + assert: function(condition, message) { + if (!condition) { + message = message || 'Assertion failed'; + if (typeof Error !== 'undefined') { + throw new Error(message); } + throw message; // Fallback + } + }, + assertEqual: function(a, b, message) { + if (a != b) { + throw new Error(message + ' mismatch: ' + a + ' != ' + b); } - }; -})(window, document); + } +}; diff --git a/src/wrapper-head.js b/src/wrapper-head.js index c4f7605..a8abae8 100644 --- a/src/wrapper-head.js +++ b/src/wrapper-head.js @@ -1,5 +1,13 @@ -(function(window, document){ - 'use strict'; - - this.Geocoder = (function() { +'use strict'; +(function (root, factory) { + if (typeof exports === 'object') { + module.exports = factory(); + } else if (typeof define === 'function' && define.amd) { + define([], factory); + } else { + root.Geocoder = factory(); + } +}(this, function () { + + var G = {}; \ No newline at end of file diff --git a/src/wrapper-tail.js b/src/wrapper-tail.js index 760443f..66f34cf 100644 --- a/src/wrapper-tail.js +++ b/src/wrapper-tail.js @@ -1,4 +1,2 @@ - return Geocoder; - })(); - var utils = Geocoder.Utils; -}).call(this, window, document); \ No newline at end of file + return G.Base; +}));