├── .gitignore ├── .npmignore ├── Gruntfile.js ├── README.md ├── examples ├── ColorPicker.js ├── combined.html ├── control.gif ├── control.html ├── popup.gif └── popup.html ├── package.json ├── src ├── Toolbar.less ├── draw │ ├── DrawAction.js │ ├── DrawToolbar.js │ └── control │ │ ├── actions │ │ ├── DrawAction.Circle.js │ │ ├── DrawAction.Marker.js │ │ ├── DrawAction.Polygon.js │ │ ├── DrawAction.Polyline.js │ │ └── DrawAction.Rectangle.js │ │ └── subactions │ │ ├── DrawAction.Cancel.js │ │ └── DrawAction.RemoveLastPoint.js ├── edit │ ├── EditAction.js │ ├── EditToolbar.js │ ├── control │ │ ├── EditAction.Control.js │ │ ├── EditToolbar.Control.js │ │ ├── actions │ │ │ ├── EditAction.Control.Delete.js │ │ │ └── EditAction.Control.Edit.js │ │ └── subactions │ │ │ ├── EditAction.Control.Save.js │ │ │ └── EditAction.Control.Undo.js │ └── popup │ │ ├── EditAction.Popup.js │ │ ├── EditToolbar.Popup.js │ │ └── actions │ │ ├── EditAction.Popup.Delete.js │ │ └── EditAction.Popup.Edit.js └── images │ ├── spritesheet-2x.png │ ├── spritesheet.png │ └── spritesheet.svg └── test ├── SpecHelper.js ├── karma.conf.js └── src ├── DrawActionSpec.js ├── DrawToolbarSpec.js ├── EditActionPopupDeleteSpec.js ├── EditActionPopupEditSpec.js ├── EditActionSpec.js ├── EditToolbarControlSpec.js └── EditToolbarPopupSpec.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | coverage 2 | test 3 | Gruntfile.js 4 | .gitignore 5 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks); 4 | 5 | grunt.initConfig({ 6 | pkg: grunt.file.readJSON('package.json'), 7 | 8 | changelog: {}, 9 | 10 | bump: { 11 | options: { 12 | files: ['package.json'], 13 | updateConfigs: [], 14 | commit: true, 15 | commitMessage: 'Release v%VERSION%', 16 | commitFiles: ['package.json'], 17 | createTag: true, 18 | tagName: 'v%VERSION%', 19 | tagMessage: 'Version %VERSION%', 20 | push: true, 21 | pushTo: 'origin', 22 | gitDescribeOptions: '--tags --always --abbrev=1 --dirty=-d' 23 | } 24 | }, 25 | 26 | jshint: { 27 | options: { 28 | node: true, 29 | browser: true, 30 | esnext: true, 31 | bitwise: true, 32 | curly: true, 33 | eqeqeq: true, 34 | immed: true, 35 | indent: 4, 36 | latedef: true, 37 | newcap: true, 38 | noarg: true, 39 | regexp: true, 40 | undef: true, 41 | unused: true, 42 | trailing: true, 43 | smarttabs: true, 44 | globals: { 45 | L: false, 46 | LeafletToolbar: false, 47 | 48 | // Mocha 49 | 50 | describe: false, 51 | it: false, 52 | before: false, 53 | after: false, 54 | beforeEach: false, 55 | afterEach: false, 56 | chai: false, 57 | expect: false, 58 | sinon: false 59 | } 60 | }, 61 | source: { 62 | src: [ 'src/*.js', 'Gruntfile.js', 'package.json' ] 63 | }, 64 | test: { 65 | src: [ 'test/SpecHelper.js', 'test/src/**' ], 66 | }, 67 | grunt: { 68 | src: [ 'Gruntfile.js' ] 69 | } 70 | }, 71 | 72 | autoprefixer: { 73 | dist: { 74 | src: 'dist/leaflet.draw-toolbar.css', 75 | dest: 'dist/leaflet.draw-toolbar.css' 76 | } 77 | }, 78 | 79 | cssmin: { 80 | target: { 81 | files: { 82 | 'dist/leaflet.draw-toolbar.min.css': ['dist/leaflet.draw-toolbar.css'] 83 | } 84 | } 85 | }, 86 | 87 | uglify: { 88 | dist: { 89 | files: { 90 | 'dist/leaflet.draw-toolbar.min.js': ['dist/leaflet.draw-toolbar.js'] 91 | } 92 | } 93 | }, 94 | 95 | less: { 96 | source: { 97 | files: { 98 | 'dist/leaflet.draw-toolbar.css': 'src/Toolbar.less' 99 | } 100 | } 101 | }, 102 | 103 | copy: { 104 | dist: { 105 | files: [ 106 | { cwd: 'src/images', src: ['**'], dest: 'dist/images', expand: true } 107 | ] 108 | } 109 | }, 110 | 111 | karma: { 112 | travis: { 113 | configFile: 'test/karma.conf.js', 114 | background: false, 115 | singleRun: true, 116 | browsers: [ 'PhantomJS' ] 117 | }, 118 | development: { 119 | configFile: 'test/karma.conf.js', 120 | background: true 121 | }, 122 | unit: { 123 | configFile: 'test/karma.conf.js', 124 | background: false, 125 | singleRun: true 126 | } 127 | }, 128 | 129 | watch: { 130 | options : { 131 | livereload: true 132 | }, 133 | source: { 134 | files: [ 135 | 'src/**', 136 | 'test/**', 137 | 'Gruntfile.js' 138 | ], 139 | tasks: [ 'build:js' ] 140 | }, 141 | css: { 142 | files: [ 'src/*.less' ], 143 | tasks: [ 'build:css' ] 144 | } 145 | 146 | }, 147 | 148 | connect: { 149 | server: { 150 | options: { 151 | port: 8000, 152 | hostname: '*', 153 | keepalive: true 154 | } 155 | } 156 | }, 157 | 158 | concat: { 159 | dist: { 160 | options: { 161 | banner: '(function(window, document, undefined) {\n\n"use strict";\n\n', 162 | footer: '\n\n})(window, document);' 163 | }, 164 | src: [ 165 | 'src/draw/DrawAction.js', 166 | 'src/draw/control/subactions/DrawAction.Cancel.js', 167 | 'src/draw/control/subactions/DrawAction.RemoveLastPoint.js', 168 | 'src/draw/control/actions/DrawAction.Circle.js', 169 | 'src/draw/control/actions/DrawAction.Marker.js', 170 | 'src/draw/control/actions/DrawAction.Polygon.js', 171 | 'src/draw/control/actions/DrawAction.Polyline.js', 172 | 'src/draw/control/actions/DrawAction.Rectangle.js', 173 | 'src/draw/DrawToolbar.js', 174 | 'src/edit/EditToolbar.js', 175 | 'src/edit/EditAction.js', 176 | 'src/edit/control/EditAction.Control.js', 177 | 'src/edit/control/subactions/EditAction.Control.Save.js', 178 | 'src/edit/control/subactions/EditAction.Control.Undo.js', 179 | 'src/edit/control/actions/EditAction.Control.Edit.js', 180 | 'src/edit/control/actions/EditAction.Control.Delete.js', 181 | 'src/edit/control/EditToolbar.Control.js', 182 | 'src/edit/popup/EditAction.Popup.js', 183 | 'src/edit/popup/actions/EditAction.Popup.Edit.js', 184 | 'src/edit/popup/actions/EditAction.Popup.Delete.js', 185 | 'src/edit/popup/EditToolbar.Popup.js' 186 | ], 187 | dest: 'dist/leaflet.draw-toolbar.js', 188 | } 189 | }, 190 | 191 | 'gh-pages': { 192 | src: [ 193 | 'dist/**', 194 | 'examples/**', 195 | 'node_modules/leaflet/**', 196 | 'node_modules/leaflet-draw/**' 197 | ] 198 | } 199 | }); 200 | 201 | /* Run tests once. */ 202 | grunt.registerTask('test', [ 'jshint', 'karma:unit', 'coverage' ]); 203 | 204 | /* Default (development): Watch files and lint, test, and build on change. */ 205 | grunt.registerTask('default', ['karma:development:start', 'watch']); 206 | 207 | grunt.registerTask('travis', [ 'jshint', 'karma:travis' ]); 208 | 209 | grunt.registerTask('build:js', [ 210 | 'jshint', 211 | 'karma:development:run', 212 | 'coverage', 213 | 'concat:dist', 214 | 'uglify:dist' 215 | ]); 216 | 217 | grunt.registerTask('build:images', [ 'copy:dist' ]); 218 | 219 | grunt.registerTask('build:css', [ 'less', 'autoprefixer', 'cssmin' ]); 220 | 221 | grunt.registerTask('coverage', 'Custom commmand-line reporter for karma-coverage', function() { 222 | var coverageReports = grunt.file.expand('coverage/*/coverage.txt'), 223 | reports = {}, 224 | report, i, len; 225 | 226 | for (i = 0, len = coverageReports.length; i < len; i++) { 227 | report = grunt.file.read(coverageReports[i]); 228 | if (!reports[report]) { 229 | reports[report] = [coverageReports[i]]; 230 | } else { 231 | reports[report].push(coverageReports[i]); 232 | } 233 | } 234 | 235 | for (report in reports) { 236 | if (reports.hasOwnProperty(report)) { 237 | for (i = 0, len = reports[report].length; i < len; i++) { 238 | grunt.log.writeln(reports[report][i]); 239 | } 240 | grunt.log.writeln(report); 241 | } 242 | } 243 | }); 244 | }; 245 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | leaflet-draw-toolbar 2 | ==================== 3 | 4 | [Leaflet.toolbar]: https://github.com/Leaflet/Leaflet.toolbar 5 | [Leaflet.draw]: https://github.com/Leaflet/Leaflet.draw 6 | 7 | This Leaflet plugin provides [Leaflet.toolbar][]-based toolbars for 8 | Leaflet.draw. There are two different styles of toolbars: 9 | control-style (fixed relative to the window), and popup-style (attached to a 10 | latlng on the map). Here's what they look like: 11 | 12 | [GIF of control-style toolbar]: https://justinmanley.github.io/leaflet-draw-toolbar/examples/control.gif 13 | [GIF of popup-style toolbar]: https://justinmanley.github.io/leaflet-draw-toolbar/examples/popup.gif 14 | 15 | [interactive example (control-style)]: https://justinmanley.github.io/leaflet-draw-toolbar/examples/control.html 16 | [interactive example (popup-style)]: https://justinmanley.github.io/leaflet-draw-toolbar/examples/popup.html 17 | 18 | 19 | | Control-style toolbar | Popup-style toolbar | 20 | |---------------------------------------|-------------------------------------| 21 | | ![GIF of control-style toolbar][] | ![GIF of popup-style toolbar][] | 22 | | [interactive example (control-style)] | [interactive example (popup-style)] | 23 | 24 | Control-style toolbars are like the zoom controls that come with Leaflet. 25 | Popup-style toolbars provide a natural way for users to interact with features 26 | drawn on the map. 27 | 28 | The toolbars in this library are interoperable with the toolbars in 29 | Leaflet.draw, so both can be used on the same webpage (see 30 | [example](https://justinmanley.github.io/leaflet-draw-toolbar/examples/combined.html). 31 | 32 | For more information on Leaflet.toolbar, see the [API Reference](https://github.com/leaflet/Leaflet.Toolbar/wiki/API-Reference) and [Building custom toolbars](https://github.com/leaflet/Leaflet.Toolbar/wiki/Building-custom-toolbars) on the wiki. 33 | 34 | Usage 35 | ----- 36 | 37 | Include Leaflet.toolbar and this library: `npm install leaflet-toolbar leaflet-draw-toolbar`. 38 | 39 | You can then include Leaflet.Toolbar in your web application by adding the following HTML tags (paths below are relative to your project's root): 40 | 41 | ``` 42 | 43 | 44 | 45 | 46 | ``` 47 | 48 | To add a toolbar for drawing on the map with Leaflet.draw: 49 | ```javascript 50 | new L.Toolbar2.DrawToolbar({ 51 | position: 'topleft' 52 | }).addTo(map); 53 | 54 | ``` 55 | 56 | To add a control-style toolbar for editing features drawn on the map with Leaflet.draw: 57 | ```javascript 58 | new L.Toolbar2.EditToolbar.Control({ 59 | position: 'topleft' 60 | }).addTo(map, drawnItems); 61 | ``` 62 | Note that `drawnItems` is the `L.Layer` containing the items drawn on the map. 63 | 64 | To add a popup-style toolbar for editing features drawn on the map with Leaflet.draw: 65 | ```javascript 66 | new L.Toolbar2.EditToolbar.Popup({ 67 | position: 'topleft' 68 | }).addTo(map, drawnItems); 69 | ``` 70 | Note that `drawnItems` is the `L.Layer` containing the items drawn on the map. 71 | 72 | Development 73 | ----------- 74 | 75 | Run `grunt`. This will launch a PhantomJS headless browser and watch for 76 | changes. When you change a file, the JS and CSS will be linted, tested, 77 | and rebuilt. 78 | 79 | Note: This package is based on [Leaflet.draw#354](https://github.com/Leaflet/Leaflet.draw/pull/354). The icon spritesheet is taken from Leaflet.draw. 80 | -------------------------------------------------------------------------------- /examples/ColorPicker.js: -------------------------------------------------------------------------------- 1 | L.ColorPicker = L.Toolbar2.Action.extend({ 2 | options: { 3 | toolbarIcon: { className: 'leaflet-color-swatch' } 4 | }, 5 | 6 | initialize: function(map, shape, options) { 7 | this._map = map; 8 | this._shape = shape; 9 | 10 | L.setOptions(this, options); 11 | L.Toolbar2.Action.prototype.initialize.call(this, map, options); 12 | }, 13 | 14 | addHooks: function() { 15 | this._shape.setStyle({ color: this.options.color }); 16 | this.disable(); 17 | }, 18 | 19 | _createIcon: function(toolbar, container, args) { 20 | var colorSwatch = L.DomUtil.create('div'), 21 | width, height; 22 | 23 | L.Toolbar2.Action.prototype._createIcon.call(this, toolbar, container, args); 24 | 25 | L.extend(colorSwatch.style, { 26 | backgroundColor: this.options.color, 27 | width: L.DomUtil.getStyle(this._link, 'width'), 28 | height: L.DomUtil.getStyle(this._link, 'height'), 29 | border: '3px solid ' + L.DomUtil.getStyle(this._link, 'backgroundColor') 30 | }); 31 | 32 | this._link.appendChild(colorSwatch); 33 | 34 | L.DomEvent.on(this._link, 'click', function() { 35 | this._map.removeLayer(this.toolbar.parentToolbar); 36 | }, this); 37 | } 38 | }); 39 | -------------------------------------------------------------------------------- /examples/combined.html: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | 30 | 34 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /examples/control.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinmanley/leaflet-draw-toolbar/7c4644e3ca37301f3edd8b1e53c917e63f88c287/examples/control.gif -------------------------------------------------------------------------------- /examples/control.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | 22 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /examples/popup.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinmanley/leaflet-draw-toolbar/7c4644e3ca37301f3edd8b1e53c917e63f88c287/examples/popup.gif -------------------------------------------------------------------------------- /examples/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | 30 | 34 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leaflet-draw-toolbar", 3 | "version": "0.3.0-alpha.1", 4 | "description": "Leaflet.toolbar for Leaflet.draw.", 5 | "peerDependencies": { 6 | "leaflet": "^1.0.0", 7 | "leaflet-draw": "^0.4.0", 8 | "leaflet-toolbar": "^0.3.0" 9 | }, 10 | "devDependencies": { 11 | "autoprefixer": "~7.1.4", 12 | "chai": "~4.1.2", 13 | "grunt": "~1.0.1", 14 | "grunt-autoprefixer": "~3.0.4", 15 | "grunt-bump": "~0.8.0", 16 | "grunt-contrib-clean": "~1.1.0", 17 | "grunt-contrib-coffee": "~1.0.0", 18 | "grunt-contrib-concat": "~1.0.1", 19 | "grunt-contrib-cssmin": "~2.2.1", 20 | "grunt-contrib-jshint": "~1.1.0", 21 | "grunt-contrib-less": "~1.4.1", 22 | "grunt-contrib-nodeunit": "~1.0.0", 23 | "grunt-contrib-uglify": "~3.0.1", 24 | "grunt-contrib-watch": "~1.0.0", 25 | "grunt-contrib-connect": "~1.0.2", 26 | "grunt-contrib-copy": "~1.0.0", 27 | "grunt-gh-pages": "~2.0.0", 28 | "grunt-mocha": "~1.0.4", 29 | "grunt-karma": "~2.0.0", 30 | "jshint": "~2.9.5", 31 | "karma": "~1.7.1", 32 | "karma-chrome-launcher": "~2.2.0", 33 | "karma-coverage": "~1.1.1", 34 | "karma-firefox-launcher": "~1.0.1", 35 | "karma-mocha": "~1.3.0", 36 | "karma-mocha-reporter": "~2.2.4", 37 | "karma-phantomjs-launcher": "~1.0.4", 38 | "karma-safari-launcher": "~1.0.0", 39 | "leaflet": "~1.2.0", 40 | "leaflet-draw": "~0.4.12", 41 | "leaflet-toolbar": "~0.3.0", 42 | "matchdep": "~2.0.0", 43 | "mocha": "~3.5.2", 44 | "sinon": "~3.2.1", 45 | "uglify-js": "~3.1.0" 46 | }, 47 | "directories": { 48 | "distribution": "./dist", 49 | "source": "./src" 50 | }, 51 | "repository": { 52 | "type": "git", 53 | "url": "https://github.com/justinmmanley/leaflet-draw-toolbar" 54 | }, 55 | "keywords": [ 56 | "maps", 57 | "leaflet", 58 | "toolbar", 59 | "user-interface" 60 | ], 61 | "author": "Justin Manley", 62 | "license": "MIT", 63 | "readmeFilename": "README.md" 64 | } 65 | -------------------------------------------------------------------------------- /src/Toolbar.less: -------------------------------------------------------------------------------- 1 | .leaflet-draw-toolbar.leaflet-control-toolbar { 2 | margin-top: 12px; 3 | margin-top: 12px; 4 | } 5 | 6 | // NOTE: This is bad because it makes it impossible to use Leaflet.draw and Leaflet.toolbar on the same page. 7 | .leaflet-draw-toolbar a { 8 | background-image: none; 9 | background-repeat: no-repeat; 10 | } 11 | 12 | .leaflet-retina .leaflet-draw-toolbar a { 13 | background-image: none; 14 | background-size: 300px 30px; 15 | } 16 | 17 | .leaflet-draw-toolbar { 18 | .leaflet-draw-edit-edit, .leaflet-draw-edit-remove, .leaflet-draw-draw-polygon, 19 | .leaflet-draw-draw-polyline, .leaflet-draw-draw-circle, .leaflet-draw-draw-marker, 20 | .leaflet-draw-draw-rectangle { 21 | background-image: url('images/spritesheet.png'); 22 | background-repeat: no-repeat; 23 | } 24 | } 25 | 26 | .leaflet-retina { 27 | .leaflet-draw-toolbar { 28 | .leaflet-draw-edit-edit, .leaflet-draw-edit-remove, .leaflet-draw-draw-polygon, 29 | .leaflet-draw-draw-polyline, .leaflet-draw-draw-circle, .leaflet-draw-draw-marker, 30 | .leaflet-draw-draw-rectangle { 31 | background-image: url('images/spritesheet-2x.png'); 32 | background-size: 300px 30px; 33 | } 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/draw/DrawAction.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.DrawAction = { 2 | fromHandler: function(Handler, defaultToolbarIcon, defaultSubToolbar) { 3 | return L.Toolbar2.Action.extend({ 4 | options: { 5 | toolbarIcon: L.extend({}, L.Toolbar2.Action.prototype.options.toolbarIcon, defaultToolbarIcon), 6 | subToolbar: defaultSubToolbar ? defaultSubToolbar : L.Toolbar2.Action.prototype.options.subToolbar 7 | }, 8 | 9 | initialize: function(map, options) { 10 | var action = this; 11 | 12 | this._handler = new Handler(map, options); 13 | this._handler.on('disabled', function() { 14 | action.disable(); 15 | }); 16 | 17 | L.Toolbar2.Action.prototype.initialize.call(this, options); 18 | }, 19 | 20 | enable: function(e) { 21 | this._handler.enable(); 22 | L.Toolbar2.Action.prototype.enable.call(this, e); 23 | }, 24 | 25 | disable: function() { 26 | this._handler.disable(); 27 | L.Toolbar2.Action.prototype.disable.call(this); 28 | }, 29 | 30 | setOptions: function(options) { 31 | this._handler.setOptions(options); 32 | L.Toolbar2.Action.prototype.setOptions.call(this, options); 33 | }, 34 | }); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /src/draw/DrawToolbar.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.DrawToolbar = L.Toolbar2.Control.extend({ 2 | options: { 3 | actions: [ 4 | L.Toolbar2.DrawAction.Polygon, 5 | L.Toolbar2.DrawAction.Polyline, 6 | L.Toolbar2.DrawAction.Marker, 7 | L.Toolbar2.DrawAction.Rectangle, 8 | L.Toolbar2.DrawAction.Circle 9 | ], 10 | className: 'leaflet-draw-toolbar' 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /src/draw/control/actions/DrawAction.Circle.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.DrawAction.Circle = L.Toolbar2.DrawAction.fromHandler( 2 | L.Draw.Circle, 3 | { 4 | className: 'leaflet-draw-draw-circle', 5 | tooltip: L.drawLocal.draw.toolbar.buttons.circle 6 | }, 7 | new L.Toolbar2({ actions: [L.Toolbar2.DrawAction.Cancel] }) 8 | ); 9 | -------------------------------------------------------------------------------- /src/draw/control/actions/DrawAction.Marker.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.DrawAction.Marker = L.Toolbar2.DrawAction.fromHandler( 2 | L.Draw.Marker, 3 | { 4 | className: 'leaflet-draw-draw-marker', 5 | tooltip: L.drawLocal.draw.toolbar.buttons.marker 6 | }, 7 | new L.Toolbar2({ actions: [L.Toolbar2.DrawAction.Cancel] }) 8 | ); 9 | -------------------------------------------------------------------------------- /src/draw/control/actions/DrawAction.Polygon.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.DrawAction.Polygon = L.Toolbar2.DrawAction.fromHandler( 2 | L.Draw.Polygon, 3 | { 4 | className: 'leaflet-draw-draw-polygon', 5 | tooltip: L.drawLocal.draw.toolbar.buttons.polygon 6 | }, 7 | new L.Toolbar2({ actions: [L.Toolbar2.DrawAction.Cancel, L.Toolbar2.DrawAction.RemoveLastPoint] }) 8 | ); 9 | 10 | // Support for DrawAction.RemoveLastPoint. 11 | L.Toolbar2.DrawAction.Polygon.prototype.deleteLastVertex = function() { 12 | this._handler.deleteLastVertex(); 13 | } 14 | -------------------------------------------------------------------------------- /src/draw/control/actions/DrawAction.Polyline.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.DrawAction.Polyline = L.Toolbar2.DrawAction.fromHandler( 2 | L.Draw.Polyline, 3 | { 4 | className: 'leaflet-draw-draw-polyline', 5 | tooltip: L.drawLocal.draw.toolbar.buttons.polyline 6 | }, 7 | new L.Toolbar2({ actions: [L.Toolbar2.DrawAction.Cancel, L.Toolbar2.DrawAction.RemoveLastPoint] }) 8 | ); 9 | 10 | // Support for DrawAction.RemoveLastPoint. 11 | L.Toolbar2.DrawAction.Polyline.prototype.deleteLastVertex = function() { 12 | this._handler.deleteLastVertex(); 13 | } 14 | -------------------------------------------------------------------------------- /src/draw/control/actions/DrawAction.Rectangle.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.DrawAction.Rectangle = L.Toolbar2.DrawAction.fromHandler( 2 | L.Draw.Rectangle, 3 | { 4 | className: 'leaflet-draw-draw-rectangle', 5 | tooltip: L.drawLocal.draw.toolbar.buttons.rectangle 6 | }, 7 | new L.Toolbar2({ actions: [L.Toolbar2.DrawAction.Cancel] }) 8 | ); 9 | -------------------------------------------------------------------------------- /src/draw/control/subactions/DrawAction.Cancel.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.DrawAction.Cancel = L.Toolbar2.Action.extend({ 2 | options: { 3 | toolbarIcon: { html: 'Cancel' } 4 | }, 5 | 6 | initialize: function (map, drawing) { 7 | this.drawing = drawing; 8 | L.Toolbar2.Action.prototype.initialize.call(this); 9 | }, 10 | 11 | addHooks: function () { 12 | this.drawing.disable(); 13 | this.disable(); 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /src/draw/control/subactions/DrawAction.RemoveLastPoint.js: -------------------------------------------------------------------------------- 1 | // NOTE: This subaction is only appropriate for actions which have a deleteLastVertex method. 2 | L.Toolbar2.DrawAction.RemoveLastPoint = L.Toolbar2.Action.extend({ 3 | options: { 4 | toolbarIcon: { html: L.drawLocal.draw.toolbar.undo.text } 5 | }, 6 | 7 | initialize: function (map, drawing) { 8 | this.drawing = drawing; 9 | L.Toolbar2.Action.prototype.initialize.call(this); 10 | }, 11 | 12 | addHooks: function () { 13 | this.drawing.deleteLastVertex(); 14 | this.disable(); 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /src/edit/EditAction.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.EditAction = { 2 | fromHandler: function(Handler, defaultToolbarIcon, defaultSubToolbar) { 3 | return L.Toolbar2.Action.extend({ 4 | options: { 5 | toolbarIcon: L.extend({}, L.Toolbar2.Action.prototype.options.toolbarIcon, defaultToolbarIcon), 6 | subToolbar: defaultSubToolbar ? defaultSubToolbar : L.Toolbar2.Action.prototype.options.subToolbar 7 | }, 8 | 9 | initialize: function(map, featureGroup, options) { 10 | var action = this; 11 | 12 | options = options || {}; 13 | options.featureGroup = featureGroup; 14 | 15 | this._handler = new Handler(map, options); 16 | this._handler.on('disabled', function() { 17 | action.disable(); 18 | }); 19 | 20 | L.Toolbar2.Action.prototype.initialize.call(this, options); 21 | }, 22 | 23 | enable: function(e) { 24 | this._handler.enable(); 25 | L.Toolbar2.Action.prototype.enable.call(this, e); 26 | }, 27 | 28 | disable: function() { 29 | this._handler.disable(); 30 | L.Toolbar2.Action.prototype.disable.call(this); 31 | }, 32 | 33 | setOptions: function(options) { 34 | this._handler.setOptions(options); 35 | L.Toolbar2.Action.prototype.setOptions.call(this, options); 36 | }, 37 | 38 | // For the undo subaction. 39 | revertLayers: function() { 40 | this._handler.revertLayers(); 41 | }, 42 | 43 | // For the save subaction. 44 | save: function() { 45 | this._handler.save(); 46 | } 47 | }); 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /src/edit/EditToolbar.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.EditToolbar = {}; 2 | -------------------------------------------------------------------------------- /src/edit/control/EditAction.Control.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.EditAction.Control = {}; 2 | -------------------------------------------------------------------------------- /src/edit/control/EditToolbar.Control.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.EditToolbar.Control = L.Toolbar2.Control.extend({ 2 | options: { 3 | actions: [ 4 | L.Toolbar2.EditAction.Control.Edit, 5 | L.Toolbar2.EditAction.Control.Delete 6 | ], 7 | className: 'leaflet-draw-toolbar', 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /src/edit/control/actions/EditAction.Control.Delete.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.EditAction.Control.Delete = L.Toolbar2.EditAction.fromHandler( 2 | L.EditToolbar.Delete, 3 | { 4 | className: 'leaflet-draw-edit-remove', 5 | tooltip: 'Remove features' 6 | }, 7 | new L.Toolbar2({ 8 | actions: [ 9 | L.Toolbar2.EditAction.Control.Save, 10 | L.Toolbar2.EditAction.Control.Undo 11 | ] 12 | }) 13 | ); 14 | -------------------------------------------------------------------------------- /src/edit/control/actions/EditAction.Control.Edit.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.EditAction.Control.Edit = L.Toolbar2.EditAction.fromHandler( 2 | L.EditToolbar.Edit, 3 | { 4 | className: 'leaflet-draw-edit-edit', 5 | tooltip: 'Edit features' 6 | }, 7 | new L.Toolbar2({ 8 | actions: [ 9 | L.Toolbar2.EditAction.Control.Save, 10 | L.Toolbar2.EditAction.Control.Undo 11 | ] 12 | }) 13 | ); 14 | -------------------------------------------------------------------------------- /src/edit/control/subactions/EditAction.Control.Save.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.EditAction.Control.Save = L.Toolbar2.Action.extend({ 2 | options: { 3 | toolbarIcon: { html: 'Save' } 4 | }, 5 | initialize: function(map, featureGroup, editing) { 6 | this.editing = editing; 7 | L.Toolbar2.Action.prototype.initialize.call(this); 8 | }, 9 | addHooks: function() { 10 | this.editing.save(); 11 | this.editing.disable(); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /src/edit/control/subactions/EditAction.Control.Undo.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.EditAction.Control.Undo = L.Toolbar2.Action.extend({ 2 | options: { 3 | toolbarIcon: { html: 'Undo' } 4 | }, 5 | initialize: function(map, featureGroup, editing) { 6 | this.editing = editing; 7 | L.Toolbar2.Action.prototype.initialize.call(this); 8 | }, 9 | addHooks: function() { 10 | this.editing.revertLayers(); 11 | this.editing.disable(); 12 | } 13 | }); 14 | 15 | -------------------------------------------------------------------------------- /src/edit/popup/EditAction.Popup.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.EditAction.Popup = {}; 2 | -------------------------------------------------------------------------------- /src/edit/popup/EditToolbar.Popup.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.EditToolbar.Popup = L.Toolbar2.Popup.extend({ 2 | options: { 3 | actions: [ 4 | L.Toolbar2.EditAction.Popup.Edit, 5 | L.Toolbar2.EditAction.Popup.Delete 6 | ], 7 | className: 'leaflet-draw-toolbar' 8 | }, 9 | 10 | onAdd: function (map) { 11 | var shape = this._arguments[1]; 12 | 13 | if (shape instanceof L.Marker) { 14 | /* Adjust the toolbar position so that it doesn't cover the marker. */ 15 | this.options.anchor = L.point(shape.options.icon.options.popupAnchor); 16 | } 17 | 18 | L.Toolbar2.Popup.prototype.onAdd.call(this, map); 19 | } 20 | }); 21 | -------------------------------------------------------------------------------- /src/edit/popup/actions/EditAction.Popup.Delete.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.EditAction.Popup.Delete = L.Toolbar2.Action.extend({ 2 | options: { 3 | toolbarIcon: { className: 'leaflet-draw-edit-remove' } 4 | }, 5 | 6 | initialize: function (map, shape, options) { 7 | this._map = map; 8 | this._shape = shape; 9 | 10 | L.Toolbar2.Action.prototype.initialize.call(this, map, options); 11 | }, 12 | 13 | addHooks: function () { 14 | var map = this._map; 15 | 16 | map.removeLayer(this._shape); 17 | map.removeLayer(this.toolbar); 18 | 19 | console.log('firing draw:deleted'); 20 | map.fire(L.Draw.Event.DELETED, { layers: L.layerGroup([this._shape]) }); 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /src/edit/popup/actions/EditAction.Popup.Edit.js: -------------------------------------------------------------------------------- 1 | L.Toolbar2.EditAction.Popup.Edit = L.Toolbar2.Action.extend({ 2 | options: { 3 | toolbarIcon: { className: 'leaflet-draw-edit-edit' } 4 | }, 5 | 6 | initialize: function (map, shape, options) { 7 | this._map = map; 8 | 9 | this._shape = shape; 10 | this._shape.options.editing = this._shape.options.editing || {}; 11 | 12 | L.Toolbar2.Action.prototype.initialize.call(this, map, options); 13 | }, 14 | 15 | enable: function () { 16 | var map = this._map, 17 | shape = this._shape; 18 | 19 | shape.editing.enable(); 20 | map.removeLayer(this.toolbar); 21 | 22 | map.on('click', function () { 23 | this.save(); 24 | shape.editing.disable(); 25 | }, this); 26 | }, 27 | 28 | save: function() { 29 | var map = this._map, 30 | shape = this._shape; 31 | 32 | if (shape.edited) { 33 | map.fire(L.Draw.Event.EDITED, { layers: L.layerGroup([shape]) }); 34 | } 35 | shape.edited = false; 36 | } 37 | }); 38 | -------------------------------------------------------------------------------- /src/images/spritesheet-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinmanley/leaflet-draw-toolbar/7c4644e3ca37301f3edd8b1e53c917e63f88c287/src/images/spritesheet-2x.png -------------------------------------------------------------------------------- /src/images/spritesheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinmanley/leaflet-draw-toolbar/7c4644e3ca37301f3edd8b1e53c917e63f88c287/src/images/spritesheet.png -------------------------------------------------------------------------------- /src/images/spritesheet.svg: -------------------------------------------------------------------------------- 1 | 2 | 157 | -------------------------------------------------------------------------------- /test/SpecHelper.js: -------------------------------------------------------------------------------- 1 | beforeEach(function() { 2 | window.expect = chai.expect; 3 | }); 4 | -------------------------------------------------------------------------------- /test/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Tue Jul 08 2014 12:47:31 GMT-0500 (CDT) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path that will be used to resolve all patterns (eg. files, exclude) 8 | basePath: '', 9 | 10 | 11 | // frameworks to use 12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 13 | frameworks: ['mocha'], 14 | 15 | 16 | // list of files / patterns to load in the browser 17 | files: [ 18 | '../node_modules/leaflet/dist/leaflet-src.js', 19 | '../node_modules/leaflet/dist/leaflet.css', 20 | '../node_modules/leaflet-draw/dist/leaflet.draw-src.js', 21 | '../node_modules/leaflet-draw/dist/leaflet.draw.css', 22 | '../node_modules/leaflet-toolbar/dist/leaflet.toolbar-src.js', 23 | '../node_modules/leaflet-toolbar/dist/leaflet.toolbar.css', 24 | '../node_modules/chai/chai.js', 25 | '../node_modules/sinon/pkg/sinon-*.js', 26 | '../src/**/*.js', 27 | '../test/SpecHelper.js', 28 | '../test/src/*Spec.js', 29 | ], 30 | 31 | 32 | // list of files to exclude 33 | exclude: [ 34 | 35 | ], 36 | 37 | 38 | // preprocess matching files before serving them to the browser 39 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 40 | preprocessors: { 41 | '../src/*.js': 'coverage' 42 | }, 43 | 44 | 45 | // test results reporter to use 46 | // possible values: 'dots', 'progress' 47 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 48 | reporters: [ 'mocha', 'coverage' ], 49 | 50 | 51 | // web server port 52 | port: 9876, 53 | 54 | 55 | // enable / disable colors in the output (reporters and logs) 56 | colors: true, 57 | 58 | 59 | // level of logging 60 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 61 | logLevel: config.LOG_INFO, 62 | 63 | 64 | // enable / disable watching file and executing tests whenever any file changes 65 | autoWatch: true, 66 | 67 | // start these browsers 68 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 69 | browsers: [ 'PhantomJS' ], 70 | 71 | plugins: [ 72 | 'karma-mocha', 73 | 'karma-phantomjs-launcher', 74 | 'karma-chrome-launcher', 75 | 'karma-firefox-launcher', 76 | 'karma-safari-launcher', 77 | 'karma-mocha-reporter', 78 | 'karma-coverage' 79 | ], 80 | 81 | // Continuous Integration mode 82 | // if true, Karma captures browsers, runs the tests and exits 83 | singleRun: false, 84 | 85 | coverageReporter: { 86 | reporters: [ 87 | { type: 'text', dir: '../coverage/', file: 'coverage.txt' }, 88 | { type: 'lcovonly', dir: '../coverage/' }, 89 | { type: 'html', dir: '../coverage/' } 90 | ] 91 | } 92 | }); 93 | }; 94 | -------------------------------------------------------------------------------- /test/src/DrawActionSpec.js: -------------------------------------------------------------------------------- 1 | describe("L.Toolbar2.DrawAction", function() { 2 | describe(".fromHandler", function() { 3 | it("Should return a L.Toolbar2.Action", function() { 4 | // Mocks 5 | var map = { _panes: {} }, 6 | layer = {}; 7 | 8 | var Action = L.Toolbar2.DrawAction.fromHandler(L.Draw.Polyline, { 9 | className: 'leaflet-draw-draw-polyline', 10 | tooltip: 'Draw a polyline' 11 | }, new L.Toolbar2()), 12 | action = new Action(map, layer); 13 | 14 | expect(action).to.be.an.instanceof(L.Toolbar2.Action); 15 | expect(action.options.subToolbar).to.be.an.instanceof(L.Toolbar2); 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/src/DrawToolbarSpec.js: -------------------------------------------------------------------------------- 1 | // TODO: Write better tests. 2 | describe("L.Toolbar2.DrawToolbar", function() { 3 | beforeEach(function() { 4 | var container = document.createElement('div'); 5 | container.id = 'map'; 6 | document.body.appendChild(container); 7 | }); 8 | 9 | // Extremely simple test to ensure that the DrawToolbar and its actions can 10 | // be instantiated and added to the map without throwing any exceptions. 11 | it("Can be added to the map", function() { 12 | var map = L.map('map'), 13 | toolbar = new L.Toolbar2.DrawToolbar().addTo(map); 14 | expect(toolbar).to.be.an.instanceof(L.Toolbar2.Control); 15 | }); 16 | 17 | afterEach(function() { 18 | var container = document.getElementById('map'); 19 | container.parentNode.removeChild(container); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /test/src/EditActionPopupDeleteSpec.js: -------------------------------------------------------------------------------- 1 | describe("L.Toolbar2.EditAction.Popup.Delete", function() { 2 | beforeEach(function() { 3 | var container = document.createElement('div'); 4 | container.id = 'map'; 5 | document.body.appendChild(container); 6 | }); 7 | 8 | it("Should fire a draw:deleted event when completed.", function(done) { 9 | var map = L.map('map'), 10 | shape = L.circle([0, 0], { radius: 1 }), 11 | deleteAction = new L.Toolbar2.EditAction.Popup.Delete(map, shape); 12 | 13 | // Necessary so that the popup delete action can remove its toolbar. 14 | deleteAction.toolbar = new L.Toolbar2(); 15 | 16 | map.on('draw:deleted', function(evt) { 17 | expect(evt.layers.hasLayer(shape)).to.equal(true); 18 | done(); 19 | }); 20 | 21 | deleteAction.enable(); 22 | }); 23 | 24 | afterEach(function() { 25 | var container = document.getElementById('map'); 26 | container.parentNode.removeChild(container); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/src/EditActionPopupEditSpec.js: -------------------------------------------------------------------------------- 1 | describe("L.Toolbar2.EditAction.Popup.Edit", function() { 2 | beforeEach(function() { 3 | var container = document.createElement('div'); 4 | container.id = 'map'; 5 | document.body.appendChild(container); 6 | }); 7 | 8 | it("Should fire a draw:edited event when completed.", function(done) { 9 | var map = L.map('map'), 10 | shape = L.circle([0, 0], { radius: 1 }), 11 | editAction = new L.Toolbar2.EditAction.Popup.Edit(map, shape); 12 | 13 | // Necessary so that the popup delete action can remove its toolbar. 14 | editAction.toolbar = new L.Toolbar2(); 15 | 16 | map.on('draw:edited', function(evt) { 17 | expect(evt.layers.hasLayer(shape)).to.equal(true); 18 | done(); 19 | }); 20 | 21 | editAction.enable(); 22 | shape.edited = true; 23 | editAction.save(); 24 | }); 25 | 26 | afterEach(function() { 27 | var container = document.getElementById('map'); 28 | container.parentNode.removeChild(container); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/src/EditActionSpec.js: -------------------------------------------------------------------------------- 1 | describe("L.Toolbar2.EditAction", function() { 2 | describe(".fromHandler", function() { 3 | it("Should return a L.Toolbar2.Action", function() { 4 | // Mocks 5 | var map = { _panes: {} }, 6 | featureGroup = new L.FeatureGroup(), 7 | options = {}; 8 | 9 | var Action = L.Toolbar2.EditAction.fromHandler(L.EditToolbar.Edit, { 10 | className: 'leaflet-draw-edit-edit', 11 | tooltip: 'Edit features' 12 | }, new L.Toolbar2()), 13 | action = new Action(map, featureGroup, options); 14 | 15 | expect(action).to.be.an.instanceof(L.Toolbar2.Action); 16 | expect(action.options.subToolbar).to.be.an.instanceof(L.Toolbar2); 17 | }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/src/EditToolbarControlSpec.js: -------------------------------------------------------------------------------- 1 | // TODO: Write better tests. 2 | describe("L.Toolbar2.EditToolbar.Control", function() { 3 | beforeEach(function() { 4 | var container = document.createElement('div'); 5 | container.id = 'map'; 6 | document.body.appendChild(container); 7 | }); 8 | 9 | // Extremely simple test to ensure that the DrawToolbar and its actions can 10 | // be instantiated and added to the map without throwing any exceptions. 11 | it("Can be added to the map", function() { 12 | var map = L.map('map'), 13 | drawnItems = new L.FeatureGroup().addTo(map), 14 | toolbar = new L.Toolbar2.EditToolbar.Control().addTo(map, drawnItems); 15 | 16 | expect(toolbar).to.be.an.instanceof(L.Toolbar2.Control); 17 | }); 18 | 19 | afterEach(function() { 20 | var container = document.getElementById('map'); 21 | container.parentNode.removeChild(container); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/src/EditToolbarPopupSpec.js: -------------------------------------------------------------------------------- 1 | // TODO: Write better tests. 2 | describe("L.Toolbar2.EditToolbar.Popup", function() { 3 | beforeEach(function() { 4 | var container = document.createElement('div'); 5 | container.id = 'map'; 6 | document.body.appendChild(container); 7 | }); 8 | 9 | // Extremely simple test to ensure that the DrawToolbar and its actions can 10 | // be instantiated and added to the map without throwing any exceptions. 11 | it("Can be added to the map", function() { 12 | var map = L.map('map'), 13 | drawnItems = new L.FeatureGroup().addTo(map), 14 | toolbar = new L.Toolbar2.EditToolbar.Popup().addTo(map, drawnItems); 15 | 16 | expect(toolbar).to.be.an.instanceof(L.Toolbar2.Popup); 17 | }); 18 | 19 | afterEach(function() { 20 | var container = document.getElementById('map'); 21 | container.parentNode.removeChild(container); 22 | }); 23 | }); 24 | --------------------------------------------------------------------------------