From 4cc5e998da87b4371191870d7310242c895da920 Mon Sep 17 00:00:00 2001
From: Stephan Meijer
Date: Sat, 17 Dec 2016 01:23:48 +0100
Subject: [PATCH] total rewrite, rewritten in es6, added tests, improved
decoupling
---
.babelrc | 22 +
.editorconfig | 13 +
.env | 2 +
.eslintrc.yml | 55 +
.gitattributes | 22 -
.gitignore | 3 +
.npmignore | 1 +
assets/css/geosearch.css | 0
.../l.geosearch.css => assets/css/leaflet.css | 47 +-
{src => assets}/img/alert.png | Bin
{src => assets}/img/geosearch.png | Bin
{src => assets}/img/spinner.gif | Bin
example/Layout.js | 54 +
example/Map.js | 55 +
example/Search.js | 103 +
example/SearchResults.js | 11 +
example/google.html | 2 +-
example/index.html | 18 +
example/main.js | 61 +
example/nokia.html | 6 +-
example/openstreetmap.html | 19 +-
example/style.css | 128 +
package.json | 97 +-
src/__tests__/leafletControl.spec.js | 97 +
src/__tests__/searchElement.spec.js | 29 +
src/constants.js | 2 +
src/domUtils.js | 39 +
src/index.js | 8 +
src/js/l.control.geosearch.js | 289 -
src/js/l.geosearch.provider.bing.js | 40 -
src/js/l.geosearch.provider.esri.js | 41 -
src/js/l.geosearch.provider.google.js | 106 -
src/js/l.geosearch.provider.nokia.js | 40 -
src/js/l.geosearch.provider.openstreetmap.js | 50 -
src/leafletControl.js | 139 +
src/providers/__tests__/bingProvider.spec.js | 33 +
src/providers/__tests__/esriProvider.spec.js | 17 +
.../__tests__/googleProvider.spec.js | 41 +
.../__tests__/openstreetmapProvider.spec.js | 28 +
src/providers/bingProvider.js | 42 +
src/providers/esriProvider.js | 27 +
src/providers/googleProvider.js | 28 +
src/providers/index.js | 5 +
src/providers/openStreetMapProvider.js | 27 +
src/providers/provider.js | 20 +
src/searchElement.js | 71 +
test/browserEnv.js | 11 +
test/index.js | 2 +
test/randomId.js | 3 +
test/round.js | 9 +
wallaby.js | 36 +
webpack.config.babel.js | 49 +
webpack.config.js | 34 +
yarn.lock | 4687 +++++++++++++++++
54 files changed, 6126 insertions(+), 643 deletions(-)
create mode 100644 .babelrc
create mode 100644 .editorconfig
create mode 100644 .env
create mode 100644 .eslintrc.yml
delete mode 100644 .gitattributes
create mode 100644 .npmignore
create mode 100644 assets/css/geosearch.css
rename src/css/l.geosearch.css => assets/css/leaflet.css (56%)
rename {src => assets}/img/alert.png (100%)
rename {src => assets}/img/geosearch.png (100%)
rename {src => assets}/img/spinner.gif (100%)
create mode 100644 example/Layout.js
create mode 100644 example/Map.js
create mode 100644 example/Search.js
create mode 100644 example/SearchResults.js
create mode 100644 example/index.html
create mode 100644 example/main.js
create mode 100644 example/style.css
create mode 100644 src/__tests__/leafletControl.spec.js
create mode 100644 src/__tests__/searchElement.spec.js
create mode 100644 src/constants.js
create mode 100644 src/domUtils.js
create mode 100644 src/index.js
delete mode 100644 src/js/l.control.geosearch.js
delete mode 100644 src/js/l.geosearch.provider.bing.js
delete mode 100644 src/js/l.geosearch.provider.esri.js
delete mode 100644 src/js/l.geosearch.provider.google.js
delete mode 100644 src/js/l.geosearch.provider.nokia.js
delete mode 100644 src/js/l.geosearch.provider.openstreetmap.js
create mode 100644 src/leafletControl.js
create mode 100644 src/providers/__tests__/bingProvider.spec.js
create mode 100644 src/providers/__tests__/esriProvider.spec.js
create mode 100644 src/providers/__tests__/googleProvider.spec.js
create mode 100644 src/providers/__tests__/openstreetmapProvider.spec.js
create mode 100644 src/providers/bingProvider.js
create mode 100644 src/providers/esriProvider.js
create mode 100644 src/providers/googleProvider.js
create mode 100644 src/providers/index.js
create mode 100644 src/providers/openStreetMapProvider.js
create mode 100644 src/providers/provider.js
create mode 100644 src/searchElement.js
create mode 100644 test/browserEnv.js
create mode 100644 test/index.js
create mode 100644 test/randomId.js
create mode 100644 test/round.js
create mode 100644 wallaby.js
create mode 100644 webpack.config.babel.js
create mode 100644 webpack.config.js
create mode 100644 yarn.lock
diff --git a/.babelrc b/.babelrc
new file mode 100644
index 00000000..4389eff6
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,22 @@
+{
+ "presets": ["es2015", "stage-0", "power-assert"],
+ "plugins": [
+ ["transform-react-jsx", { "pragma": "preact.h"}],
+ "transform-async-to-generator",
+ ["babel-plugin-espower", {
+ "embedAst": true,
+ "patterns": [
+ "t.truthy(value, [message])",
+ "t.falsy(value, [message])",
+ "t.true(value, [message])",
+ "t.false(value, [message])",
+ "t.is(value, expected, [message])",
+ "t.not(value, expected, [message])",
+ "t.deepEqual(value, expected, [message])",
+ "t.notDeepEqual(value, expected, [message])",
+ "t.regex(contents, regex, [message])",
+ "t.notRegex(contents, regex, [message])"
+ ]
+ }]
+ ]
+}
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..bf5ed693
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,13 @@
+# @see http://editorconfig.org/
+
+# This is the top-most .editorconfig file; do not search in parent directories.
+root = true
+
+# All files.
+[*]
+end_of_line = LF
+indent_style = space
+indent_size = 2
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/.env b/.env
new file mode 100644
index 00000000..90a17847
--- /dev/null
+++ b/.env
@@ -0,0 +1,2 @@
+BING_API_KEY=AtUDjSVEBxo8BwgYUPdfnzHpznaYwDdjjS27jyFDj18nhTUDUjrhc0NwMndZvrXs
+GOOGLE_API_KEY=AIzaSyDigZ5WMPoTj_gnkUn3p1waYPDa5oE8WOw
diff --git a/.eslintrc.yml b/.eslintrc.yml
new file mode 100644
index 00000000..a98291e1
--- /dev/null
+++ b/.eslintrc.yml
@@ -0,0 +1,55 @@
+extends: "airbnb"
+
+env:
+ browser: true
+
+parser: "babel-eslint"
+parserOptions:
+ ecmaVersion: 6
+ ecmaFeatures:
+ experimentalObjectRestSpread: true
+ jsx: true
+ sourceType: "module"
+
+rules:
+ # we need this to test leaflet private vars
+ no-underscore-dangle: "off"
+
+ # force else and catch to a new line
+ brace-style:
+ - "error"
+ - "stroustrup"
+ - allowSingleLine: true
+
+ quote-props:
+ - "error"
+ - "consistent-as-needed"
+ - keywords: true
+
+ class-methods-use-this: "off"
+
+ import/no-extraneous-dependencies:
+ - "error"
+ - devDependencies:
+ - "**/*.spec.js"
+ - "**/test_helpers/**/*.js"
+ - "example/**/*.js"
+
+ react/jsx-filename-extension:
+ - "error"
+ - extensions:
+ - ".js"
+
+ react/prop-types: "off"
+
+ jsx-a11y/anchor-has-content: "off"
+
+globals:
+ L: false
+ fetch: false
+ document: false
+ location: false
+
+settings:
+ react:
+ pragma: "preact"
diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index 412eeda7..00000000
--- a/.gitattributes
+++ /dev/null
@@ -1,22 +0,0 @@
-# Auto detect text files and perform LF normalization
-* text=auto
-
-# Custom for Visual Studio
-*.cs diff=csharp
-*.sln merge=union
-*.csproj merge=union
-*.vbproj merge=union
-*.fsproj merge=union
-*.dbproj merge=union
-
-# Standard to msysgit
-*.doc diff=astextplain
-*.DOC diff=astextplain
-*.docx diff=astextplain
-*.DOCX diff=astextplain
-*.dot diff=astextplain
-*.DOT diff=astextplain
-*.pdf diff=astextplain
-*.PDF diff=astextplain
-*.rtf diff=astextplain
-*.RTF diff=astextplain
diff --git a/.gitignore b/.gitignore
index 600b3a93..81d8122a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -165,3 +165,6 @@ pip-log.txt
*.sublime-project
*.sublime-workspace
.idea
+dist/
+lib/
+node_modules/
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 00000000..8eba6c8d
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1 @@
+src/
diff --git a/assets/css/geosearch.css b/assets/css/geosearch.css
new file mode 100644
index 00000000..e69de29b
diff --git a/src/css/l.geosearch.css b/assets/css/leaflet.css
similarity index 56%
rename from src/css/l.geosearch.css
rename to assets/css/leaflet.css
index f0152623..6d72222c 100644
--- a/src/css/l.geosearch.css
+++ b/assets/css/leaflet.css
@@ -1,34 +1,32 @@
-.displayNone {
- display: none;
+.geosearch.leaflet-bar form,
+.geosearch.leaflet-bar .message {
+ display: none;
}
-.leaflet-control-geosearch {
- position: relative;
+.geosearch.leaflet-bar.active form {
+ display: block;
}
-.leaflet-control-geosearch a {
- -webkit-border-radius: 4px;
- border-radius: 4px;
+.geosearch a.leaflet-bar-part {
+ border-radius: 4px;
border-bottom: none;
-}
-
-.leaflet-control-geosearch a.glass {
background-image: url(../img/geosearch.png);
+ background-position: center center;
background-size: 100% 100%;
}
-.leaflet-control-geosearch a.spinner {
+.geosearch.pending a.leaflet-bar-part {
background-image: url(../img/spinner.gif);
- background-position: 50% 50%;
+ background-size: 12px 12px;
}
-.leaflet-control-geosearch a.alert {
+.geosearch.error a.leaflet-bar-part {
background-image: url(../img/alert.png);
- background-size: 64% 64%;
+ background-size: 18px 18px;
}
-.leaflet-control-geosearch a:hover {
- border-bottom: none;
+.leaflet-control-geosearch {
+ position: relative;
}
.leaflet-control-geosearch form {
@@ -36,17 +34,12 @@
top: 0;
left: 22px;
box-shadow: 0 1px 7px rgba(0, 0, 0, 0.65);
- -webkit-border-radius: 4px;
- border-radius: 0px 4px 4px 0px;
+ border-radius: 0 4px 4px 0;
z-index: -1;
background: #FFF;
- height: 26px;
- padding: 0 6px 0 6px;
-}
-
-.leaflet-touch .leaflet-control-geosearch form {
- padding: 2px 6px 2px 8px;
- line-height: 28px;
+ height: auto;
+ margin: 0;
+ padding: 0;
}
.leaflet-control-geosearch form input {
@@ -56,7 +49,9 @@
margin: 0;
padding: 0;
font-size: 12px;
- margin-top: 5px;
+ height: 30px;
+ border-radius: 0 4px 4px 0;
+
}
.leaflet-control-geosearch .message {
diff --git a/src/img/alert.png b/assets/img/alert.png
similarity index 100%
rename from src/img/alert.png
rename to assets/img/alert.png
diff --git a/src/img/geosearch.png b/assets/img/geosearch.png
similarity index 100%
rename from src/img/geosearch.png
rename to assets/img/geosearch.png
diff --git a/src/img/spinner.gif b/assets/img/spinner.gif
similarity index 100%
rename from src/img/spinner.gif
rename to assets/img/spinner.gif
diff --git a/example/Layout.js b/example/Layout.js
new file mode 100644
index 00000000..abd90cf1
--- /dev/null
+++ b/example/Layout.js
@@ -0,0 +1,54 @@
+import preact, { Component } from 'preact';
+import Search from './Search';
+
+class Layout extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ hash: window.location.hash.slice(1),
+ };
+ }
+
+ componentDidMount() {
+ window.addEventListener('hashchange', this.changePage, false);
+ }
+
+ componentWillUnmount() {
+ window.removeEventListener('hashchange', this.changePage, false);
+ }
+
+ changePage = () => {
+ this.setState({
+ hash: window.location.hash.slice(1),
+ });
+ };
+
+ render() {
+ const { pages } = this.props;
+ const { hash } = this.state;
+ const page = pages.find(p => p.slug === (hash || 'search'));
+
+ return (
+
+
+
{`GeoSearch / ${page.title}`}
+
+
+ {pages.map((p, idx) => (
+ -
+ {p.title}
+
+ ))}
+
+
+
+
+ {page && page.view()}
+
+
+ );
+ }
+}
+
+export default Layout;
diff --git a/example/Map.js b/example/Map.js
new file mode 100644
index 00000000..77391bb0
--- /dev/null
+++ b/example/Map.js
@@ -0,0 +1,55 @@
+import preact, { Component } from 'preact';
+import merge from 'lodash.merge';
+import { GeoSearchControl, OpenStreetMapProvider, Provider as BaseProvider } from '../src';
+
+const L = window.L;
+
+// eslint-disable-next-line no-confusing-arrow
+const ensureInstance = Provider => Provider instanceof BaseProvider ? Provider : new Provider();
+
+const mapOptions = () => ({
+ layers: [
+ new L.TileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
+ maxZoom: 18,
+ }),
+ ],
+ center: new L.LatLng(53.2, 5.8),
+ zoom: 12,
+});
+
+class Map extends Component {
+ componentDidMount() {
+ const { options, Provider } = this.props;
+
+ this.map = new L.Map(this.container, merge(mapOptions(), options));
+
+ const provider = (Provider) ? ensureInstance(Provider) : new OpenStreetMapProvider();
+
+ this.searchControl = new GeoSearchControl({
+ provider,
+ }).addTo(this.map);
+ }
+
+ componentDidUpdate() {
+ const Provider = this.props.Provider || OpenStreetMapProvider;
+ this.searchControl.options.provider = ensureInstance(Provider);
+ }
+
+ componentWillUnmount() {
+ this.map.remove();
+ }
+
+ bindContainer = (container) => {
+ this.container = container;
+ };
+
+ render() {
+ const { style } = this.props;
+
+ return (
+
+ );
+ }
+}
+
+export default Map;
diff --git a/example/Search.js b/example/Search.js
new file mode 100644
index 00000000..d8cab27d
--- /dev/null
+++ b/example/Search.js
@@ -0,0 +1,103 @@
+import preact, { Component } from 'preact';
+import debounce from 'lodash.debounce';
+import * as providers from '../src/providers';
+import SearchResults from './SearchResults';
+
+const specialKeys = ['ArrowDown', 'ArrowUp', 'Escape'];
+
+class Search extends Component {
+ constructor(props) {
+ super(props);
+
+ const Provider = providers[`${props.provider}Provider`] ||
+ providers.OpenStreetMapProvider;
+
+ this.provider = new Provider();
+ }
+
+ onSubmit = async (event) => {
+ event.preventDefault();
+ const { query } = this.state;
+
+ const results = await this.provider.search({ query });
+ this.setState({
+ results,
+ });
+ };
+
+ onKeyUp = (event) => {
+ if (specialKeys.includes(event.code)) {
+ return;
+ }
+
+ const query = event.target.value;
+
+ this.setState({
+ query,
+ });
+
+ this.autoSearch(event);
+ };
+
+ onKeyDown = (event) => {
+ if (event.code === 'Escape') {
+ this.reset();
+ return;
+ }
+
+ if (event.code !== 'ArrowDown' && event.code !== 'ArrowUp') {
+ return;
+ }
+
+ event.preventDefault();
+
+ const { selected = -1, results } = this.state;
+ const max = results.length - 1;
+
+ // eslint-disable-next-line no-bitwise
+ const next = (event.code === 'ArrowDown') ? ~~selected + 1 : ~~selected - 1;
+ // eslint-disable-next-line no-nested-ternary
+ const idx = (next < 0) ? max : (next > max) ? 0 : next;
+
+ this.setState({
+ selected: idx,
+ query: results[idx].label,
+ });
+ };
+
+ autoSearch = debounce((event) => {
+ this.onSubmit(event);
+ }, 250);
+
+ reset() {
+ this.setState({
+ results: [],
+ selected: -1,
+ query: '',
+ });
+ }
+
+ render() {
+ const { results, selected, query } = this.state;
+
+ return (
+
+
+
+ {results &&
+
+ }
+
+ );
+ }
+}
+
+export default Search;
diff --git a/example/SearchResults.js b/example/SearchResults.js
new file mode 100644
index 00000000..aef751b1
--- /dev/null
+++ b/example/SearchResults.js
@@ -0,0 +1,11 @@
+import preact, { Component } from 'preact';
+
+const SearchResults = ({ results = [], selected }) => (
+
+ {results.map((result, idx) => (
+
{result.label}
+ ))}
+
+);
+
+export default SearchResults;
diff --git a/example/google.html b/example/google.html
index cb024dec..d9d522e2 100644
--- a/example/google.html
+++ b/example/google.html
@@ -24,7 +24,7 @@
});
new L.Control.GeoSearch({
- provider: new L.GeoSearch.Provider.Google()
+ provider: new L.GeoSearch.Provider.Google()
}).addTo(map);
diff --git a/example/index.html b/example/index.html
new file mode 100644
index 00000000..30f31acc
--- /dev/null
+++ b/example/index.html
@@ -0,0 +1,18 @@
+
+
+
+ Leaflet.GeoSearch / Google Provider
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/main.js b/example/main.js
new file mode 100644
index 00000000..31f78f45
--- /dev/null
+++ b/example/main.js
@@ -0,0 +1,61 @@
+import preact, { render } from 'preact';
+import Layout from './Layout';
+import Search from './Search';
+import Map from './Map';
+import * as providers from '../src/providers';
+
+// import css to enable hot reloading
+if (process.env.NODE_ENV !== 'production') {
+ /* eslint-disable global-require */
+ require('./index.html');
+ require('./style.css');
+ require('../assets/css/leaflet.css');
+}
+
+const pages = [
+ {
+ slug: 'search',
+ title: 'Search',
+ view: () => (),
+ },
+ {
+ slug: 'openstreetmap',
+ title: 'OpenStreetMap',
+ view: () => (),
+ },
+ {
+ slug: 'google',
+ title: 'Google',
+ view: () => {
+ const Provider = new providers.GoogleProvider({ params: {
+ key: 'AIzaSyDigZ5WMPoTj_gnkUn3p1waYPDa5oE8WOw',
+ } });
+
+ return ;
+ },
+ },
+ {
+ slug: 'bing',
+ title: 'Bing',
+ view: () => {
+ const Provider = new providers.BingProvider({ params: {
+ key: 'AtUDjSVEBxo8BwgYUPdfnzHpznaYwDdjjS27jyFDj18nhTUDUjrhc0NwMndZvrXs',
+ } });
+
+ return ;
+ },
+ },
+ {
+ slug: 'esri',
+ title: 'Esri',
+ view: () => (),
+ },
+];
+
+if (location.hash === '') {
+ location.hash = 'search';
+}
+
+render((
+
+), document.getElementById('app'));
diff --git a/example/nokia.html b/example/nokia.html
index e287d345..0862950a 100644
--- a/example/nokia.html
+++ b/example/nokia.html
@@ -23,9 +23,9 @@
center: new L.LatLng(53.2, 5.8), zoom: 12
});
- new L.Control.GeoSearch({
- provider: new L.GeoSearch.Provider.Nokia()
- }).addTo(map);
+ new L.Control.GeoSearch({
+ provider: new L.GeoSearch.Provider.Nokia()
+ }).addTo(map);