├── .gitignore
├── LICENSE
├── README.md
├── USAGE.md
├── bower.json
├── package.json
└── src
├── easy-button.css
├── easy-button.d.ts
└── easy-button.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /index.html
2 | node_modules
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (C) 2014 Daniel Montague
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # L.EasyButton
2 |
3 | The easiest way to add buttons with Leaflet — so simple it fits in a gif:
4 |
5 | 
6 |
7 | ### More [running examples and docs](http://danielmontague.com/projects/easyButton.js/v2/examples/)
8 |
9 | -----------------------------------------------------------------------------------
10 |
11 | ## Boilerplate Examples
12 |
13 | These use `YOUR_LEAFLET_MAP` as a placeholder;
14 | remember to change it to the variable name of your map.
15 |
16 | ##### Hello World
17 |
18 | open a popup
19 |
20 | ```javascript
21 | var helloPopup = L.popup().setContent('Hello World!');
22 |
23 | L.easyButton('fa-globe', function(btn, map){
24 | helloPopup.setLatLng(map.getCenter()).openOn(map);
25 | }).addTo( YOUR_LEAFLET_MAP );
26 | ```
27 |
28 | ##### Map State
29 |
30 | set the map's center and use an `img` for the icon
31 |
32 | ```javascript
33 | L.easyButton('
', function(btn, map){
34 | var antarctica = [-77,70];
35 | map.setView(antarctica);
36 | }).addTo( YOUR_LEAFLET_MAP );
37 | ```
38 |
39 | ##### Button States
40 |
41 | change the button's function and appearance
42 |
43 | ```javascript
44 | var stateChangingButton = L.easyButton({
45 | states: [{
46 | stateName: 'zoom-to-forest', // name the state
47 | icon: 'fa-tree', // and define its properties
48 | title: 'zoom to a forest', // like its title
49 | onClick: function(btn, map) { // and its callback
50 | map.setView([46.25,-121.8],10);
51 | btn.state('zoom-to-school'); // change state on click!
52 | }
53 | }, {
54 | stateName: 'zoom-to-school',
55 | icon: 'fa-university',
56 | title: 'zoom to a school',
57 | onClick: function(btn, map) {
58 | map.setView([42.3748204,-71.1161913],16);
59 | btn.state('zoom-to-forest');
60 | }
61 | }]
62 | });
63 |
64 | stateChangingButton.addTo( YOUR_LEAFLET_MAP );
65 | ```
66 |
67 | -----------------------------------------------------------------------------------
68 |
69 | ## Download/Install
70 |
71 | EasyButton version `2.x.x` and up expect Leaflet `1.x.x` or higher;
72 | for Leaflet `0.7.x` use EasyButton `1.3.x`.
73 |
74 | ### jsDelivr
75 |
76 | ```
77 |
78 |
79 | ```
80 |
81 | ### NPM
82 |
83 | ```
84 | npm install --save leaflet-easybutton
85 | ```
86 |
87 | ### Bower
88 |
89 | ```
90 | bower install --save Leaflet.EasyButton
91 | ```
92 |
93 | ### Icon Dependencies
94 |
95 | If you haven't already, make sure to install/include the icon library of your
96 | choice (your lib should have its own instructions)
97 | — EasyButton should work with anything!
98 |
--------------------------------------------------------------------------------
/USAGE.md:
--------------------------------------------------------------------------------
1 | ### Usage
2 |
3 |
4 | L.easyButton(, );
5 |
6 | L.easyButton();
7 |
8 |
9 | #### ``
10 |
11 | it can take many shapes
12 |
13 | * `'fa-map-marker'` or another [font awesome](http://fortawesome.github.io/Font-Awesome/icons/) class
14 | * `'glyphicon-globe'` or another [glyphicon](http://getbootstrap.com/components/#glyphicons-glyphs) class
15 | * `'random assortment-of-classes'` for custom icons and other icon libraries
16 | * `'
'` old fashioned html
17 |
18 | #### ``
19 |
20 | gets a reference to the button and its map
21 |
22 | L.easyButton(, function( buttonArg, mapArg ){
23 | buttonArg.doStuff();
24 | mapArg.doStuff();
25 | });
26 |
27 | #### ``
28 |
29 | the options object looks like this:
30 |
31 | L.easyButton({
32 | id: 'id-for-the-button', // an id for the generated button
33 | position: 'topleft', // inherited from L.Control -- the corner it goes in
34 | type: 'replace', // set to animate when you're comfy with css
35 | leafletClasses: true, // use leaflet classes to style the button?
36 | states:[{ // specify different icons and responses for your button
37 | stateName: 'get-center',
38 | onClick: function(button, map){
39 | alert('Map is centered at: ' + map.getCenter().toString());
40 | },
41 | title: 'show me the middle',
42 | icon: 'fa-crosshairs'
43 | }]
44 | });
45 |
46 | ### More
47 |
48 | `L.easyButton` now has a partner, `L.easyBar`
49 |
50 | // start with an array of easy buttons
51 | var buttons = [ L.easyButton(options),
52 | L.easyButton(options2),
53 | L.easyButton(options3)];
54 |
55 | // build a toolbar with them
56 | L.easyBar(buttons).addTo(map);
57 |
58 | Buttons that preform related tasks (e.g. zooming in and out)
59 | can be lumped into logical groups.
60 |
61 | ### Alternatives
62 |
63 | * Follow Leaflet's [docs](http://leafletjs.com/reference.html)
64 | * Browse around for [something](leafletjs.com/plugins.html) that already does what you want
65 | * Copy one of [Leaflet's](https://github.com/Leaflet/Leaflet/tree/master/src/control) controls
66 |
67 | ### etc
68 |
69 | Changes from v0:
70 | * function signature
71 | * now:
72 | * friendly: `L.easyButton(,)`
73 | * fancy: `L.easyButton()`
74 | * was:
75 | * `L.easyButton(,,,,)`
76 | * The Constructor Name
77 | * now: `L.Control.EasyButton ` (no trailing 's')
78 | * was: `L.Control.EasyButtons` (trailing 's')
79 | * The factory, `L.easyButton`, is unchanged
80 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Leaflet.EasyButton",
3 | "homepage": "https://github.com/CliffCloud/Leaflet.EasyButtons",
4 | "authors": [
5 | "atstp "
6 | ],
7 | "description": "Easily add Font Awesome control buttons to Leaflet maps with attached callbacks",
8 | "main": [
9 | "src/easy-button.js",
10 | "src/easy-button.css"
11 | ],
12 | "dependencies": {
13 | "leaflet": "~1.0.1"
14 | },
15 | "keywords": [
16 | "Leaflet",
17 | "map",
18 | "button"
19 | ],
20 | "license": "MIT",
21 | "ignore": [
22 | "**/.*",
23 | "node_modules",
24 | "bower_components",
25 | "test",
26 | "tests"
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "leaflet-easybutton",
3 | "version": "2.4.0",
4 | "description": "easily add control buttons to your leaflet maps with icon support",
5 | "main": "src/easy-button.js",
6 | "style": "src/easy-button.css",
7 | "types": "src/easy-button.d.ts",
8 | "scripts": {
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/CliffCloud/Leaflet.EasyButton.git"
14 | },
15 | "keywords": [
16 | "leaflet",
17 | "font-awesome",
18 | "glyphicon",
19 | "button",
20 | "control",
21 | "map"
22 | ],
23 | "author": "rocks.in.the.cloud+easybutton@gmail.com",
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/CliffCloud/Leaflet.EasyButton/issues"
27 | },
28 | "homepage": "https://github.com/CliffCloud/Leaflet.EasyButton",
29 | "dependencies": {
30 | "leaflet": "^1.0.1"
31 | },
32 | "devDependencies": {
33 | "@types/leaflet": "^1.2.0"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/easy-button.css:
--------------------------------------------------------------------------------
1 | .leaflet-bar button,
2 | .leaflet-bar button:hover {
3 | background-color: #fff;
4 | border: none;
5 | border-bottom: 1px solid #ccc;
6 | width: 26px;
7 | height: 26px;
8 | line-height: 26px;
9 | display: block;
10 | text-align: center;
11 | text-decoration: none;
12 | color: black;
13 | }
14 |
15 | .leaflet-bar button {
16 | background-position: 50% 50%;
17 | background-repeat: no-repeat;
18 | overflow: hidden;
19 | display: block;
20 | }
21 |
22 | .leaflet-bar button:hover {
23 | background-color: #f4f4f4;
24 | }
25 |
26 | .leaflet-bar button:first-of-type {
27 | border-top-left-radius: 4px;
28 | border-top-right-radius: 4px;
29 | }
30 |
31 | .leaflet-bar button:last-of-type {
32 | border-bottom-left-radius: 4px;
33 | border-bottom-right-radius: 4px;
34 | border-bottom: none;
35 | }
36 |
37 | .leaflet-bar.disabled,
38 | .leaflet-bar button.disabled {
39 | cursor: default;
40 | pointer-events: none;
41 | opacity: .4;
42 | }
43 |
44 | .easy-button-button .button-state{
45 | display: block;
46 | width: 100%;
47 | height: 100%;
48 | position: relative;
49 | }
50 |
51 |
52 | .leaflet-touch .leaflet-bar button {
53 | width: 30px;
54 | height: 30px;
55 | line-height: 30px;
56 | }
57 |
--------------------------------------------------------------------------------
/src/easy-button.d.ts:
--------------------------------------------------------------------------------
1 | import * as L from 'leaflet'
2 | import {ControlPosition} from 'leaflet';
3 |
4 | declare module 'leaflet' {
5 |
6 | /**
7 | * Creates a bar that holds a group of EasyButtons
8 | * @param buttons array of EasyButtons that will be grouped together in the EasyBar
9 | * @param options
10 | */
11 | function easyBar(buttons: Control.EasyButton[], options?: EasyBarOptions): Control.EasyBar;
12 |
13 | /**
14 | * Creates a easyButton
15 | * @param icon e.g. fa-globe
16 | * @param onClick the button click handler
17 | * @param title title on the button
18 | * @param id an id to tag the button with
19 | * @example
20 | * var helloPopup = L.popup().setContent('Hello World!');
21 | *
22 | * L.easyButton('fa-globe', function(btn, map){
23 | * helloPopup.setLatLng(map.getCenter()).openOn(map);
24 | * }).addTo( YOUR_LEAFLET_MAP );
25 | */
26 | function easyButton(icon: string,
27 | onClick: (btn: Control.EasyButton, map: L.Map) => void,
28 | title?: string,
29 | id?: string): Control.EasyButton;
30 |
31 | /**
32 | * Creates a easyButton
33 | * @param options the options object
34 | * @example
35 | *
36 | *
37 | * L.easyButton({
38 | * position: 'topleft',
39 | * leafletClasses: true,
40 | * states: [
41 | * {
42 | * stateName: 'center',
43 | * onClick: function(btn, map){},
44 | * title: 'Get Center',
45 | * icon: 'fa-globe'
46 | * }
47 | * ]
48 | * }).addTo( YOUR_LEAFLET_MAP );
49 | */
50 | function easyButton(options: EasyButtonOptions): Control.EasyButton;
51 |
52 | interface EasyBarOptions {
53 | position?: ControlPosition
54 | id?: string
55 | leafletClasses?: boolean
56 | }
57 |
58 | interface EasyButtonOptions {
59 | position?: ControlPosition
60 | id?: string
61 | type?: 'replace' | 'animate'
62 | states?: EasyButtonState[]
63 | leafletClasses?: boolean
64 | tagName?: string
65 | }
66 |
67 | interface EasyButtonState {
68 | stateName: string
69 | onClick: (btn: L.Control.EasyButton, map: L.Map) => void
70 | title: string
71 | icon: string
72 | }
73 |
74 | namespace Control {
75 | class EasyButton extends L.Control {
76 | constructor(options?: EasyButtonOptions)
77 |
78 | state(stateName: string): EasyButton
79 | enable(): void
80 | disable(): void
81 | }
82 |
83 | class EasyBar extends L.Control {
84 | constructor(options?: EasyBarOptions)
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/easy-button.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | // This is for grouping buttons into a bar
4 | // takes an array of `L.easyButton`s and
5 | // then the usual `.addTo(map)`
6 | L.Control.EasyBar = L.Control.extend({
7 |
8 | options: {
9 | position: 'topleft', // part of leaflet's defaults
10 | id: null, // an id to tag the Bar with
11 | leafletClasses: true // use leaflet classes?
12 | },
13 |
14 |
15 | initialize: function(buttons, options){
16 |
17 | if(options){
18 | L.Util.setOptions( this, options );
19 | }
20 |
21 | this._buildContainer();
22 | this._buttons = [];
23 |
24 | for(var i = 0; i < buttons.length; i++){
25 | buttons[i]._bar = this;
26 | buttons[i]._container = buttons[i].button;
27 | this._buttons.push(buttons[i]);
28 | this.container.appendChild(buttons[i].button);
29 | }
30 |
31 | },
32 |
33 |
34 | _buildContainer: function(){
35 | this._container = this.container = L.DomUtil.create('div', '');
36 | this.options.leafletClasses && L.DomUtil.addClass(this.container, 'leaflet-bar easy-button-container leaflet-control');
37 | this.options.id && (this.container.id = this.options.id);
38 | },
39 |
40 |
41 | enable: function(){
42 | L.DomUtil.addClass(this.container, 'enabled');
43 | L.DomUtil.removeClass(this.container, 'disabled');
44 | this.container.setAttribute('aria-hidden', 'false');
45 | return this;
46 | },
47 |
48 |
49 | disable: function(){
50 | L.DomUtil.addClass(this.container, 'disabled');
51 | L.DomUtil.removeClass(this.container, 'enabled');
52 | this.container.setAttribute('aria-hidden', 'true');
53 | return this;
54 | },
55 |
56 |
57 | onAdd: function () {
58 | return this.container;
59 | },
60 |
61 | addTo: function (map) {
62 | this._map = map;
63 |
64 | for(var i = 0; i < this._buttons.length; i++){
65 | this._buttons[i]._map = map;
66 | }
67 |
68 | var container = this._container = this.onAdd(map),
69 | pos = this.getPosition(),
70 | corner = map._controlCorners[pos];
71 |
72 | L.DomUtil.addClass(container, 'leaflet-control');
73 |
74 | if (pos.indexOf('bottom') !== -1) {
75 | corner.insertBefore(container, corner.firstChild);
76 | } else {
77 | corner.appendChild(container);
78 | }
79 |
80 | return this;
81 | }
82 |
83 | });
84 |
85 | L.easyBar = function(){
86 | var args = [L.Control.EasyBar];
87 | for(var i = 0; i < arguments.length; i++){
88 | args.push( arguments[i] );
89 | }
90 | return new (Function.prototype.bind.apply(L.Control.EasyBar, args));
91 | };
92 |
93 | // L.EasyButton is the actual buttons
94 | // can be called without being grouped into a bar
95 | L.Control.EasyButton = L.Control.extend({
96 |
97 | options: {
98 | position: 'topleft', // part of leaflet's defaults
99 |
100 | id: null, // an id to tag the button with
101 |
102 | type: 'replace', // [(replace|animate)]
103 | // replace swaps out elements
104 | // animate changes classes with all elements inserted
105 |
106 | states: [], // state names look like this
107 | // {
108 | // stateName: 'untracked',
109 | // onClick: function(){ handle_nav_manually(); };
110 | // title: 'click to make inactive',
111 | // icon: 'fa-circle', // wrapped with
112 | // }
113 |
114 | leafletClasses: true, // use leaflet styles for the button
115 | tagName: 'button',
116 | },
117 |
118 |
119 |
120 | initialize: function(icon, onClick, title, id){
121 |
122 | // clear the states manually
123 | this.options.states = [];
124 |
125 | // add id to options
126 | if(id != null){
127 | this.options.id = id;
128 | }
129 |
130 | // storage between state functions
131 | this.storage = {};
132 |
133 | // is the last item an object?
134 | if( typeof arguments[arguments.length-1] === 'object' ){
135 |
136 | // if so, it should be the options
137 | L.Util.setOptions( this, arguments[arguments.length-1] );
138 | }
139 |
140 | // if there aren't any states in options
141 | // use the early params
142 | if( this.options.states.length === 0 &&
143 | typeof icon === 'string' &&
144 | typeof onClick === 'function'){
145 |
146 | // turn the options object into a state
147 | this.options.states.push({
148 | icon: icon,
149 | onClick: onClick,
150 | title: typeof title === 'string' ? title : ''
151 | });
152 | }
153 |
154 | // curate and move user's states into
155 | // the _states for internal use
156 | this._states = [];
157 |
158 | for(var i = 0; i < this.options.states.length; i++){
159 | this._states.push( new State(this.options.states[i], this) );
160 | }
161 |
162 | this._buildButton();
163 |
164 | this._activateState(this._states[0]);
165 |
166 | },
167 |
168 | _buildButton: function(){
169 |
170 | this.button = L.DomUtil.create(this.options.tagName, '');
171 |
172 | if (this.options.tagName === 'button') {
173 | this.button.setAttribute('type', 'button');
174 | }
175 |
176 | if (this.options.id ){
177 | this.button.id = this.options.id;
178 | }
179 |
180 | if (this.options.leafletClasses){
181 | L.DomUtil.addClass(this.button, 'easy-button-button leaflet-bar-part leaflet-interactive');
182 | }
183 |
184 | // don't let double clicks and mousedown get to the map
185 | L.DomEvent.addListener(this.button, 'dblclick', L.DomEvent.stop);
186 | L.DomEvent.addListener(this.button, 'mousedown', L.DomEvent.stop);
187 | L.DomEvent.addListener(this.button, 'mouseup', L.DomEvent.stop);
188 |
189 | // take care of normal clicks
190 | L.DomEvent.addListener(this.button,'click', function(e){
191 | L.DomEvent.stop(e);
192 | this._currentState.onClick(this, this._map ? this._map : null );
193 | this._map && this._map.getContainer().focus();
194 | }, this);
195 |
196 | // prep the contents of the control
197 | if(this.options.type == 'replace'){
198 | this.button.appendChild(this._currentState.icon);
199 | } else {
200 | for(var i=0;i"']/) ){
350 |
351 | // if so, the user should have put in html
352 | // so move forward as such
353 | tmpIcon = ambiguousIconString;
354 |
355 | // then it wasn't html, so
356 | // it's a class list, figure out what kind
357 | } else {
358 | ambiguousIconString = ambiguousIconString.replace(/(^\s*|\s*$)/g,'');
359 | tmpIcon = L.DomUtil.create('span', '');
360 |
361 | if( ambiguousIconString.indexOf('fa-') === 0 ){
362 | L.DomUtil.addClass(tmpIcon, 'fa ' + ambiguousIconString)
363 | } else if ( ambiguousIconString.indexOf('glyphicon-') === 0 ) {
364 | L.DomUtil.addClass(tmpIcon, 'glyphicon ' + ambiguousIconString)
365 | } else {
366 | L.DomUtil.addClass(tmpIcon, /*rollwithit*/ ambiguousIconString)
367 | }
368 |
369 | // make this a string so that it's easy to set innerHTML below
370 | tmpIcon = tmpIcon.outerHTML;
371 | }
372 |
373 | return tmpIcon;
374 | }
375 |
376 | })();
377 |
--------------------------------------------------------------------------------