├── .eslintrc ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Makefile ├── README.md ├── biome.json ├── bower.json ├── doc ├── api.html ├── build.js ├── index.leafdoc ├── leafdoc │ ├── comments.hbs │ ├── constructor.hbs │ ├── destructor.hbs │ ├── event.hbs │ ├── example.hbs │ ├── factory.hbs │ ├── function.hbs │ ├── html.hbs │ ├── inherited.hbs │ ├── method.hbs │ ├── namespace.hbs │ ├── option.hbs │ ├── property.hbs │ ├── section.hbs │ └── supersection.hbs └── style.css ├── example ├── change-line-colour-on-editing.html ├── continue-line.html ├── create-hole-on-click.html ├── delete-shape.html ├── index.html ├── multipolygon.html ├── snapping.html ├── tooltip-when-drawing.html └── undo-redo.html ├── package-lock.json ├── package.json ├── src └── Leaflet.Editable.js └── test ├── .eslintrc ├── CircleEditor.js ├── CircleMarkerEditor.js ├── Editable.js ├── MarkerEditor.js ├── MiddleMarker.js ├── PolygonEditor.js ├── PolylineEditor.js ├── RectangleEditor.js ├── VertexMarker.js ├── _pre.js └── index.html /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true 4 | }, 5 | "rules": { 6 | "quotes": [2, "single"], 7 | "no-underscore-dangle": 0, 8 | "curly": 0, 9 | "consistent-return": 0, 10 | "new-cap": 0, 11 | "strict": [2, "global"], 12 | "semi-spacing": 0 13 | }, 14 | "globals": {L: true} 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | npm-debug.log 2 | node_modules/* 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | branches: 4 | only: 5 | - master 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | # 1.2.0 4 | 5 | - add `editable:vertex:mouseover` and `editable:vertex:mouseout` events (#159, 6 | thanks to [@Git-Lior](https://github.com/Git-Lior)) 7 | - restrict large vertex icons to mobile only (cf #171, thanks to 8 | [@tyrasd](https://github.com/Git-Lior)) 9 | - prevent to add a vertex on right click (cf #157) 10 | 11 | # 1.1.0 12 | 13 | - compatibility with Leaflet 1.2.0 14 | - add `editable:vertex:new` event 15 | 16 | 17 | # 1.0.0 18 | 19 | - BREAKING editorClass are now properly looked in editTools.options instead of map (cf #92) 20 | - removed Leaflet as peerDependency (cf #72) 21 | - fixed error in canvas due to guides being added too early (cf #80) 22 | - added path dragging (when [Path.Drag.js](https://github.com/Leaflet/Path.Drag.js) is loaded) 23 | - allow to draw a rectangle in any direction (cf #87) 24 | - fixed editable:drawing:commit being fired on mousedown instead of mouseup for circle and rectangle (cf #70) 25 | - hide middle markers if there is not enough space 26 | - make possible to add new vertex on top of other paths vertex 27 | - leaflet 1.0 support 28 | - make editable:drawing:click and editable:vertex:click cancellable 29 | - add editable:drawing:clicked and editable:vertex:clicked events 30 | - add L.Editable.commitDrawing, to commit any ongoing drawing 31 | - AMD/UMD compliancy 32 | - fix middleMarker still triggering some events while not being visible 33 | - add map option editToolsClass, that allow to override L.Editable class to be 34 | used 35 | - added deleteShapeAt method 36 | - added events editable:shape:delete and editable:shape:deleted 37 | - added event editable:vertex:rawclick 38 | - added splitShape method 39 | - added appendShape method 40 | - added Vertex.continue, as a shortcut to continueBackward / continueForward 41 | - removed newClickHandler, now relying only on mousedown/mouseup event (simpler 42 | touch support) 43 | - added editable:drawing:move 44 | - added drawingCursor option to L.Editable 45 | - added editable:vertex:metakeyclick event 46 | - reenable edit if the layer was active when removed from the map 47 | - only add created feature to the map at first user click (instead of adding it 48 | at startMarker/startPolygon call) 49 | - added editable:drawing:mousedown and editable:drawing:mouseup events 50 | - added support for L.Rectangle and L.Circle drawing and editing 51 | - do not try to extend Leaflet classes not exposed (cf #83) 52 | 53 | ## 0.5.0 54 | - added editable:vertex:drag event 55 | - added editable:vertex:dragstart event 56 | - added editable:vertex:dragend event 57 | 58 | ## 0.4.0 59 | - Marker/Polygon/Polyline.createEditor now pass this.options.editOptions to 60 | the created editor 61 | - fire editable events on the L.Editable instance also 62 | - added featuresLayer option to L.Editable, and by default add features to 63 | this layer instead of the map directly 64 | - added lineGuideOptions to L.Editable options 65 | - added skipMiddleMarkers to L.Editable options 66 | 67 | ## 0.3.0 68 | - added optional latlng param to L.Editable.startPolyline and startPolygon 69 | - move listening of feature remove to editor.enable/disable 70 | - fire editable:drawing:click after the click has been processed 71 | - renamed editable:drawing:finish in editable:drawing:commit 72 | - fixed that editable:drawing:click was fired after editable:drawing:end for MarkerEditor 73 | 74 | ## 0.2.0 75 | - removed multi primary/secondary management 76 | - added 'editable:middlemarker:onmousedown' event 77 | - forward events to multipolygon/multipolyline 78 | - refactored drawn latlngs validation 79 | - remove hole array when deleting last point 80 | - refactored internal events management 81 | - removed 'position' from event properties 82 | - renamed vertex.getPosition in vertex.getIndex 83 | - renamed vertex.remove in vertex.delete 84 | - added 'editable:drawing:cancel' event 85 | - removed 'editable:edited' in favor of 'editable:drawing:finish' 86 | 87 | ## 0.1.0 88 | - initial release 89 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | node_modules: 2 | npm install 3 | 4 | test: node_modules 5 | @./node_modules/phantomjs-prebuilt/bin/phantomjs node_modules/mocha-phantomjs-core/mocha-phantomjs-core.js test/index.html spec '{"viewportSize":{"width": 1024,"height": 768}}' 6 | 7 | test-fx: 8 | firefox test/index.html 9 | 10 | doc: 11 | @./doc/build.js 12 | 13 | .PHONY: test test-fx doc 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/Leaflet/Leaflet.Editable.svg?branch=master)](https://travis-ci.org/Leaflet/Leaflet.Editable) 2 | # Leaflet.Editable 3 | 4 | Make geometries editable in Leaflet. 5 | 6 | 7 | This is not a plug and play UI, and will not be. This is a minimal, lightweight, 8 | and fully extendable API to control editing of geometries. So you can easily 9 | build your own UI with your own needs and choices. 10 | 11 | See the [demo UI](http://Leaflet.github.io/Leaflet.Editable/example/index.html), an more [examples below](#examples). 12 | This is also the drawing engine behind [uMap](http://wiki.openstreetmap.org/wiki/UMap). 13 | 14 | 15 | Design keys: 16 | 17 | - only the core needs 18 | - no UI, instead hooks everywhere needed 19 | - everything programmatically controllable 20 | - MultiPolygon/MultiPolyline support 21 | - Polygons' holes support 22 | - touch support 23 | - tests 24 | 25 | ## Install 26 | 27 | You need Leaflet >= 1.0.0, and then include `src/Leaflet.Editable.js`. 28 | 29 | ### Path dragging 30 | 31 | If you want path dragging, you need to also include [Path.Drag.js](https://github.com/Leaflet/Path.Drag.js). 32 | 33 | 34 | ## Quick start 35 | 36 | Allow Leaflet.Editable in the map options: 37 | 38 | var map = L.map('map', {editable: true}); 39 | 40 | Then, to start editing an existing feature, call the `enableEdit` method on it: 41 | 42 | var polyline = L.polyline([[43.1, 1.2], [43.2, 1.3],[43.3, 1.2]]).addTo(map); 43 | polyline.enableEdit(); 44 | 45 | If you want to draw a new line: 46 | 47 | map.editTools.startPolyline(); // map.editTools has been created 48 | // by passing editable: true option to the map 49 | 50 | If you want to continue an existing line: 51 | 52 | polyline.editor.continueForward(); 53 | // or 54 | polyline.editor.continueBackward(); 55 | 56 | ## Examples 57 | 58 | - [Basic controls](http://Leaflet.github.io/Leaflet.Editable/example/index.html) 59 | - [Continue line by ctrl/command-clicking on first/last point](http://Leaflet.github.io/Leaflet.Editable/example/continue-line.html) 60 | - [Create hole in a polygon by ctrl-clicking on it](http://Leaflet.github.io/Leaflet.Editable/example/create-hole-on-click.html) 61 | - [Change line colour on editing](http://Leaflet.github.io/Leaflet.Editable/example/change-line-colour-on-editing.html) 62 | - [Display a tooltip near cursor while drawing](http://Leaflet.github.io/Leaflet.Editable/example/tooltip-when-drawing.html) 63 | - [Basic demo of undo/redo](http://Leaflet.github.io/Leaflet.Editable/example/undo-redo.html) (Use ctrl-Z to undo and ctrl-shift-Z to redo) 64 | - [Deleting shapes by ctrl/command clicking on it](http://Leaflet.github.io/Leaflet.Editable/example/delete-shape.html) 65 | - [Multipolygon example](http://Leaflet.github.io/Leaflet.Editable/example/multipolygon.html) 66 | - Example of [Leaflet.Snap](https://github.com/makinacorpus/Leaflet.Snap/) integration [to enable snapping](http://Leaflet.github.io/Leaflet.Editable/example/snapping.html) 67 | 68 | 69 | ## API 70 | 71 | [See the reference](http://Leaflet.github.io/Leaflet.Editable/doc/api.html) 72 | 73 | 74 | ## Licence 75 | 76 | `Leaflet.Editable` is released under the WTFPL licence. 77 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "linter": { 3 | "rules": { 4 | "style": { 5 | "noParameterAssign": "off" 6 | } 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leaflet.editable", 3 | "version": "0.6.0", 4 | "homepage": "https://github.com/Leaflet/Leaflet.Editable", 5 | "authors": [ 6 | "Yohan Boniface " 7 | ], 8 | "description": "Make geometries editable in Leaflet.", 9 | "main": "src/Leaflet.Editable.js", 10 | "keywords": [ 11 | "leaflet" 12 | ], 13 | "license": "WTFPL", 14 | "ignore": [ 15 | "**/.*", 16 | "node_modules", 17 | "bower_components", 18 | "test", 19 | "tests", 20 | "example" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /doc/build.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var Leafdoc = require('leafdoc'), 3 | fs = require('fs'), 4 | path = require('path'); 5 | 6 | var doc = new Leafdoc({ 7 | templateDir: './doc/leafdoc/', 8 | showInheritancesWhenEmpty: true 9 | }); 10 | doc.addFile('./doc/index.leafdoc', false); 11 | doc.addFile('./src/Leaflet.Editable.js', true); 12 | fs.writeFileSync('./doc/api.html', doc.outputStr()); 13 | -------------------------------------------------------------------------------- /doc/index.leafdoc: -------------------------------------------------------------------------------- 1 | 🍂class Map 2 | 🍂class EditableMixin 3 | 🍂class Editable 4 | 🍂class BaseEditor 5 | 🍂class MarkerEditor 6 | 🍂class PathEditor 7 | 🍂class PolylineEditor 8 | 🍂class PolygonEditor 9 | 🍂class RectangleEditor 10 | 🍂class CircleEditor 11 | 🍂class VertexMarker 12 | 🍂class Event objects 13 | -------------------------------------------------------------------------------- /doc/leafdoc/comments.hbs: -------------------------------------------------------------------------------- 1 | {{{rawmarkdown comments}}} -------------------------------------------------------------------------------- /doc/leafdoc/constructor.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{#each documentables}} 8 | 9 | 14 | 15 | 16 | {{/each}} 17 |
ConstructorDescription
new {{name}}( 10 | {{~#each params~}} 11 | {{#if type}}<{{{type type}}}> {{/if}}{{name}} 12 | {{~#unless @last}}, {{/unless}}{{/each~}} 13 | ){{{markdown comments}}}
-------------------------------------------------------------------------------- /doc/leafdoc/destructor.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{#each documentables}} 8 | 9 | 14 | 15 | 16 | {{/each}} 17 |
DestructorDescription
{{name}}( 10 | {{~#each params~}} 11 | {{#if type}}<{{{type type}}}> {{/if}}{{name}} 12 | {{~#unless @last}}, {{/unless}}{{/each~}} 13 | ){{{markdown comments}}}
-------------------------------------------------------------------------------- /doc/leafdoc/event.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{#each documentables}} 9 | 10 | 12 | 13 | 14 | {{/each}} 15 |
EventDataDescription
{{name}} 11 | {{{type type}}}{{{markdown comments}}}
-------------------------------------------------------------------------------- /doc/leafdoc/example.hbs: -------------------------------------------------------------------------------- 1 | 2 | {{#each documentables}} 3 | {{{rawmarkdown comments}}} 4 | {{/each}} -------------------------------------------------------------------------------- /doc/leafdoc/factory.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{#each documentables}} 8 | 9 | 14 | 15 | 16 | {{/each}} 17 |
FactoryDescription
{{name}}( 10 | {{~#each params~}} 11 | {{#if type}}<{{{type type}}}> {{/if}}{{name}} 12 | {{~#unless @last}}, {{/unless}}{{/each~}} 13 | ){{{markdown comments}}}
-------------------------------------------------------------------------------- /doc/leafdoc/function.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{#each documentables}} 9 | 10 | 15 | 16 | 17 | 18 | {{/each}} 19 |
FunctionReturnsDescription
{{name}}( 11 | {{~#each params~}} 12 | {{#if type}}<{{{type type}}}> {{/if}}{{name}} 13 | {{~#unless @last}}, {{/unless}}{{/each~}} 14 | ){{{type type}}}{{{markdown comments}}}
-------------------------------------------------------------------------------- /doc/leafdoc/html.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ title }} 5 | 6 | 7 | 8 | 9 | Fork me on GitHub 10 |
11 |

Leaflet.Editable API reference

12 | 13 | {{{ body }}} 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /doc/leafdoc/inherited.hbs: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 |
{{{inherited}}}
6 |
7 | -------------------------------------------------------------------------------- /doc/leafdoc/method.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{#each documentables}} 9 | 10 | 15 | 16 | 17 | 18 | {{/each}} 19 |
MethodReturnsDescription
{{name}}( 11 | {{~#each params~}} 12 | {{#if type}}<{{{type type}}}> {{/if}}{{name}} 13 | {{~#unless @last}}, {{/unless}}{{/each~}} 14 | ){{{type type}}}{{{markdown comments}}}
-------------------------------------------------------------------------------- /doc/leafdoc/namespace.hbs: -------------------------------------------------------------------------------- 1 |

{{name}}

2 | {{{rawmarkdown comments}}} 3 | {{{supersections}}} -------------------------------------------------------------------------------- /doc/leafdoc/option.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {{#each documentables}} 10 | 11 | 12 | 14 | 15 | 16 | {{/each}} 17 |
OptionTypeDefaultDescription
{{name}}{{{type type}}} 13 | {{defaultValue}}{{{markdown comments}}}
-------------------------------------------------------------------------------- /doc/leafdoc/property.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{#each documentables}} 9 | 10 | 12 | 13 | 14 | {{/each}} 15 |
PropertyTypeDescription
{{name}} 11 | {{{type type}}}{{{markdown comments}}}
-------------------------------------------------------------------------------- /doc/leafdoc/section.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{#if name}}

{{name}}

{{/if}} 3 | {{{markdown comments}}} 4 | {{{documentables}}} 5 |
-------------------------------------------------------------------------------- /doc/leafdoc/supersection.hbs: -------------------------------------------------------------------------------- 1 |

{{name}}

2 | {{markdown comments}} 3 | {{{sections}}} 4 | {{{inheritances}}} 5 | -------------------------------------------------------------------------------- /doc/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Sans; 3 | } 4 | .container { 5 | max-width: 800px; 6 | margin-left: auto; 7 | margin-right: auto; 8 | } 9 | table { 10 | margin-top: 10px; 11 | border-collapse: collapse; 12 | width: 100%; 13 | } 14 | td, th { 15 | border: 1px solid #eee; 16 | min-height: 1.5em; 17 | padding: 5px; 18 | } 19 | th { 20 | background-color: #e9eef8; 21 | } 22 | pre code, p code { 23 | background: #f8f3e9; 24 | padding: 2px; 25 | } 26 | pre code { 27 | display: block; 28 | padding: 10px; 29 | border: #f8f3e9 solid 1px; 30 | color: #333; 31 | } 32 | h1 { 33 | text-align: center; 34 | } 35 | h2 { 36 | margin-top: 40px; 37 | } 38 | h4, 39 | h3 { 40 | color: #333; 41 | text-transform: lowercase; 42 | font-variant: small-caps; 43 | } 44 | a { 45 | color: #7292d2; 46 | } 47 | -------------------------------------------------------------------------------- /example/change-line-colour-on-editing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Leaflet.Editable change line color demo 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 18 |
19 | 20 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /example/continue-line.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Leaflet.Editable continue line demo 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 18 |
19 | 20 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /example/create-hole-on-click.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Leaflet.Editable create hole demo 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 18 |
19 | 20 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /example/delete-shape.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Leaflet.Editable demo 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 18 |
19 | 20 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Leaflet.Editable demo 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 18 |
19 | 20 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /example/multipolygon.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Leaflet.Editable demo 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 18 |
19 | 20 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /example/snapping.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Example of Leaflet.Snap integration to enable snapping 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 22 | 23 | 24 |
25 | 26 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /example/tooltip-when-drawing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Leaflet.Editable demo 7 | 8 | 9 | 10 | 11 | 12 | 30 | 31 | 32 |
33 |
34 | 35 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /example/undo-redo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Leaflet.Editable undo/redo demo 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 18 |
19 | 20 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leaflet-editable", 3 | "version": "1.3.1", 4 | "description": "Make geometries editable in Leaflet", 5 | "main": "src/Leaflet.Editable.js", 6 | "scripts": { 7 | "test": "make test" 8 | }, 9 | "keywords": [ 10 | "leaflet", 11 | "map" 12 | ], 13 | "author": "Yohan Boniface", 14 | "license": "WTFPL", 15 | "devDependencies": { 16 | "chai": "4.1.2", 17 | "happen": "0.3.2", 18 | "leafdoc": "2.3.0", 19 | "leaflet": "1.9.4", 20 | "leaflet.path.drag": "0.0.6", 21 | "mocha": "10.7.3", 22 | "mocha-phantomjs-core": "2.1.2", 23 | "phantomjs-prebuilt": "2.1.16" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "git://github.com/Leaflet/Leaflet.Editable.git" 28 | }, 29 | "bugs": { 30 | "url": "https://github.com/Leaflet/Leaflet.Editable/issues" 31 | }, 32 | "homepage": "https://github.com/Leaflet/Leaflet.Editable/" 33 | } 34 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "describe": true, 4 | "happen": true, 5 | "assert": true, 6 | "before": true, 7 | "after": true, 8 | "it": true, 9 | "map": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/CircleEditor.js: -------------------------------------------------------------------------------- 1 | describe('L.CircleEditor', () => { 2 | let p2ll 3 | 4 | before(function () { 5 | this.map = map 6 | map.setZoom(16) // So we don't need to use enormous radius. 7 | p2ll = (x, y) => map.layerPointToLatLng([x, y]) 8 | }) 9 | 10 | describe('#startCircle()', () => { 11 | it('should create circle and editor', function () { 12 | const layer = this.map.editTools.startCircle() 13 | assert.ok(layer) 14 | assert.ok(layer.editor) 15 | assert.notOk(map.hasLayer(layer)) 16 | layer.editor.disable() 17 | }) 18 | 19 | it('should add layer to map at first click', function () { 20 | const layer = this.map.editTools.startCircle() 21 | assert.notOk(map.hasLayer(layer)) 22 | happen.drawingClick(300, 300) 23 | assert.ok(map.hasLayer(layer)) 24 | layer.remove() 25 | }) 26 | 27 | it('should draw circle on click-drag', function (done) { 28 | const layer = this.map.editTools.startCircle() 29 | happen.drag(200, 200, 220, 220, () => { 30 | expect(layer._radius).to.be.gt(0) 31 | layer.remove() 32 | done() 33 | }) 34 | }) 35 | }) 36 | 37 | describe('#enableEdit()', () => { 38 | it('should attach editor', function () { 39 | const layer = L.circle(p2ll(200, 200)).addTo(this.map) 40 | layer.enableEdit() 41 | assert.ok(layer.editor) 42 | layer.remove() 43 | }) 44 | 45 | it('should update radius on radius handler drag', function (done) { 46 | const layer = L.circle(p2ll(200, 200), { radius: 20 }).addTo(this.map) 47 | const before = layer._radius 48 | layer.enableEdit() 49 | const startPoint = this.map.latLngToLayerPoint(layer.editor._resizeLatLng) 50 | const x = startPoint.x 51 | const y = startPoint.y 52 | happen.drag(x, y, x + 20, y + 20, () => { 53 | expect(layer._radius).to.be.gt(before) 54 | layer.remove() 55 | done() 56 | }) 57 | }) 58 | }) 59 | 60 | describe('#disableEdit()', () => { 61 | it('should stop editing on disableEdit', function () { 62 | const layer = L.circle(p2ll(200, 200)).addTo(this.map) 63 | layer.enableEdit() 64 | assert.ok(layer.editor) 65 | layer.disableEdit() 66 | assert.notOk(layer.editor) 67 | layer.remove() 68 | }) 69 | }) 70 | 71 | describe('#enableDragging()', () => { 72 | it('should drag a circle', function (done) { 73 | const layer = L.circle(p2ll(200, 200), { radius: 50 }).addTo(this.map) 74 | const before = layer._latlng.lat 75 | layer.enableEdit() 76 | assert.equal(before, layer._latlng.lat) 77 | happen.drag(210, 210, 220, 220, () => { 78 | assert.notEqual(before, layer._latlng.lat) 79 | layer.remove() 80 | done() 81 | }) 82 | }) 83 | 84 | it('should send editable:dragstart event', function (done) { 85 | const layer = L.circle(p2ll(200, 200), { radius: 50 }).addTo(this.map) 86 | let called = 0 87 | const call = () => { 88 | called++ 89 | } 90 | layer.on('editable:dragstart', call) 91 | layer.enableEdit() 92 | assert.equal(called, 0) 93 | happen.drag(210, 210, 220, 220, () => { 94 | assert.equal(called, 1) 95 | layer.remove() 96 | done() 97 | }) 98 | }) 99 | 100 | it('should send editable:dragend event', function (done) { 101 | const layer = L.circle(p2ll(200, 200), { radius: 50 }).addTo(this.map) 102 | let called = 0 103 | const call = () => { 104 | called++ 105 | } 106 | layer.on('editable:dragend', call) 107 | layer.enableEdit() 108 | assert.equal(called, 0) 109 | happen.drag(210, 210, 220, 220, () => { 110 | assert.equal(called, 1) 111 | layer.remove() 112 | done() 113 | }) 114 | }) 115 | 116 | it('should send editable:drag event', function (done) { 117 | const layer = L.circle(p2ll(200, 200), { radius: 50 }).addTo(this.map) 118 | let called = 0 119 | const call = () => { 120 | called++ 121 | } 122 | layer.on('editable:drag', call) 123 | layer.enableEdit() 124 | assert.notOk(called) 125 | happen.drag(210, 210, 220, 220, () => { 126 | assert.ok(called) 127 | layer.remove() 128 | done() 129 | }) 130 | }) 131 | }) 132 | 133 | describe('#events', () => { 134 | it('should fire editable:drawing:start on startCircle call', function () { 135 | let called = 0 136 | const call = () => { 137 | called++ 138 | } 139 | this.map.on('editable:drawing:start', call) 140 | const layer = this.map.editTools.startCircle() 141 | assert.equal(called, 1) 142 | this.map.off('editable:drawing:start', call) 143 | layer.editor.disable() 144 | assert.notOk(this.map.editTools._drawingEditor) 145 | }) 146 | 147 | it('should fire editable:drawing:end on mouseup', function (done) { 148 | let called = 0 149 | const call = () => { 150 | called++ 151 | } 152 | this.map.on('editable:drawing:end', call) 153 | const layer = this.map.editTools.startCircle() 154 | assert.equal(called, 0) 155 | happen.drag(200, 200, 220, 220, () => { 156 | assert.equal(called, 1) 157 | map.off('editable:drawing:end', call) 158 | layer.remove() 159 | assert.equal(called, 1) 160 | done() 161 | }) 162 | }) 163 | 164 | it('should fire editable:drawing:commit on mouseup', function (done) { 165 | let called = 0 166 | const call = () => { 167 | called++ 168 | } 169 | this.map.on('editable:drawing:commit', call) 170 | const layer = this.map.editTools.startCircle() 171 | assert.equal(called, 0) 172 | happen.drag(200, 200, 220, 220, () => { 173 | assert.equal(called, 1) 174 | map.off('editable:drawing:commit', call) 175 | layer.remove() 176 | assert.equal(called, 1) 177 | done() 178 | }) 179 | }) 180 | 181 | it('should not fire editable:drawing:commit on mousedown', function () { 182 | let called = 0 183 | const call = () => { 184 | called++ 185 | } 186 | this.map.on('editable:drawing:commit', call) 187 | const layer = this.map.editTools.startCircle() 188 | assert.equal(called, 0) 189 | happen.at('mousedown', 200, 200) 190 | assert.equal(called, 0) 191 | happen.at('mouseup', 200, 200) 192 | assert.equal(called, 1) 193 | this.map.off('editable:drawing:commit', call) 194 | layer.remove() 195 | }) 196 | 197 | it('should fire editable:drawing:end on stopDrawing', function () { 198 | let called = 0 199 | const call = () => { 200 | called++ 201 | } 202 | this.map.on('editable:drawing:end', call) 203 | const layer = this.map.editTools.startCircle() 204 | this.map.editTools.stopDrawing() 205 | assert.equal(called, 1) 206 | this.map.off('editable:drawing:end', call) 207 | layer.remove() 208 | assert.equal(called, 1) 209 | }) 210 | 211 | it('should not fire editable:drawing:commit on stopDrawing', function () { 212 | let called = 0 213 | const call = () => { 214 | called++ 215 | } 216 | this.map.on('editable:drawing:commit', call) 217 | const layer = this.map.editTools.startCircle() 218 | this.map.editTools.stopDrawing() 219 | assert.equal(called, 0) 220 | this.map.off('editable:drawing:commit', call) 221 | layer.remove() 222 | assert.equal(called, 0) 223 | }) 224 | 225 | it('should fire editable:drawing:move on mousemove while drawing', function () { 226 | let called = 0 227 | const call = () => { 228 | called++ 229 | } 230 | this.map.on('editable:drawing:move', call) 231 | const layer = this.map.editTools.startCircle() 232 | assert.equal(called, 0) 233 | happen.at('mousemove', 450, 450) 234 | assert.equal(called, 1) 235 | happen.drawingClick(450, 450) 236 | this.map.off('editable:drawing:move', call) 237 | layer.remove() 238 | assert.equal(called, 1) 239 | }) 240 | 241 | it('should fire editable:drawing:move on mousemove while resizing', function (done) { 242 | let called = 0 243 | const call = () => { 244 | called++ 245 | } 246 | const layer = L.circle(p2ll(200, 200), { radius: 20 }).addTo(this.map) 247 | layer.enableEdit() 248 | assert.equal(called, 0) 249 | this.map.on('editable:drawing:move', call) 250 | const startPoint = this.map.latLngToLayerPoint(layer.editor._resizeLatLng) 251 | const x = startPoint.x 252 | const y = startPoint.y 253 | happen.drag(x, y, x + 20, y + 20, () => { 254 | assert.ok(called > 0) 255 | map.off('editable:drawing:move', call) 256 | layer.remove() 257 | done() 258 | }) 259 | }) 260 | }) 261 | }) 262 | -------------------------------------------------------------------------------- /test/CircleMarkerEditor.js: -------------------------------------------------------------------------------- 1 | describe('L.CircleMarkerEditor', () => { 2 | let p2ll 3 | 4 | before(function () { 5 | this.map = map 6 | map.setZoom(16) // So we don't need to use enormous radius. 7 | p2ll = (x, y) => map.layerPointToLatLng([x, y]) 8 | }) 9 | 10 | describe('#startCircleMarker()', () => { 11 | it('should create circleMarker and editor', function () { 12 | const layer = this.map.editTools.startCircleMarker() 13 | assert.ok(layer) 14 | assert.ok(layer.editor) 15 | assert.notOk(map.hasLayer(layer)) 16 | layer.editor.disable() 17 | }) 18 | 19 | it('should add layer to map at first click', function () { 20 | const layer = this.map.editTools.startCircleMarker() 21 | assert.notOk(map.hasLayer(layer)) 22 | happen.drawingClick(300, 300) 23 | assert.ok(map.hasLayer(layer)) 24 | layer.remove() 25 | }) 26 | }) 27 | 28 | describe('#enableEdit()', () => { 29 | it('should attach editor', function () { 30 | const layer = L.circleMarker(p2ll(200, 200)).addTo(this.map) 31 | layer.enableEdit() 32 | assert.ok(layer.editor) 33 | layer.remove() 34 | }) 35 | }) 36 | 37 | describe('#disableEdit()', () => { 38 | it('should stop editing on disableEdit', function () { 39 | const layer = L.circleMarker(p2ll(200, 200)).addTo(this.map) 40 | layer.enableEdit() 41 | assert.ok(layer.editor) 42 | layer.disableEdit() 43 | assert.notOk(layer.editor) 44 | layer.remove() 45 | }) 46 | }) 47 | 48 | describe('#enableDragging()', () => { 49 | it('should drag a circleMarker', function (done) { 50 | const layer = L.circleMarker(p2ll(200, 200), { radius: 20 }).addTo(this.map) 51 | const before = layer._latlng.lat 52 | layer.enableEdit() 53 | assert.equal(before, layer._latlng.lat) 54 | happen.drag(210, 210, 220, 220, () => { 55 | assert.notEqual(before, layer._latlng.lat) 56 | layer.remove() 57 | done() 58 | }) 59 | }) 60 | 61 | it('should send editable:dragstart event', function (done) { 62 | const layer = L.circleMarker(p2ll(200, 200), { radius: 20 }).addTo(this.map) 63 | let called = 0 64 | const call = () => { 65 | called++ 66 | } 67 | layer.on('editable:dragstart', call) 68 | layer.enableEdit() 69 | assert.equal(called, 0) 70 | happen.drag(210, 210, 220, 220, () => { 71 | assert.equal(called, 1) 72 | layer.remove() 73 | done() 74 | }) 75 | }) 76 | 77 | it('should send editable:dragend event', function (done) { 78 | const layer = L.circleMarker(p2ll(200, 200), { radius: 20 }).addTo(this.map) 79 | let called = 0 80 | const call = () => { 81 | called++ 82 | } 83 | layer.on('editable:dragend', call) 84 | layer.enableEdit() 85 | assert.equal(called, 0) 86 | happen.drag(210, 210, 220, 220, () => { 87 | assert.equal(called, 1) 88 | layer.remove() 89 | done() 90 | }) 91 | }) 92 | 93 | it('should send editable:drag event', function (done) { 94 | const layer = L.circleMarker(p2ll(200, 200), { radius: 20 }).addTo(this.map) 95 | let called = 0 96 | const call = () => { 97 | called++ 98 | } 99 | layer.on('editable:drag', call) 100 | layer.enableEdit() 101 | assert.notOk(called) 102 | happen.drag(210, 210, 220, 220, () => { 103 | assert.ok(called) 104 | layer.remove() 105 | done() 106 | }) 107 | }) 108 | }) 109 | 110 | describe('#events', () => { 111 | it('should fire editable:drawing:start on startMarker call', function () { 112 | let called = 0 113 | const call = () => { 114 | called++ 115 | } 116 | this.map.on('editable:drawing:start', call) 117 | const other = this.map.editTools.startMarker() 118 | assert.equal(called, 1) 119 | this.map.off('editable:drawing:start', call) 120 | other.editor.disable() 121 | assert.notOk(this.map.editTools._drawingEditor) 122 | }) 123 | 124 | it('should fire editable:drawing:end on click', function () { 125 | let called = 0 126 | const call = () => { 127 | called++ 128 | } 129 | this.map.on('editable:drawing:end', call) 130 | const other = this.map.editTools.startMarker() 131 | assert.equal(called, 0) 132 | happen.drawingClick(450, 450) 133 | assert.equal(called, 1) 134 | this.map.off('editable:drawing:end', call) 135 | other.remove() 136 | assert.equal(called, 1) 137 | }) 138 | 139 | it('should fire editable:drawing:commit on finish', function () { 140 | let called = 0 141 | const call = () => { 142 | called++ 143 | } 144 | this.map.on('editable:drawing:commit', call) 145 | const other = this.map.editTools.startMarker() 146 | assert.equal(called, 0) 147 | happen.drawingClick(450, 450) 148 | assert.equal(called, 1) 149 | this.map.off('editable:drawing:commit', call) 150 | other.remove() 151 | assert.equal(called, 1) 152 | }) 153 | 154 | it('should fire editable:drawing:end on stopDrawing', function () { 155 | let called = 0 156 | const call = () => { 157 | called++ 158 | } 159 | this.map.on('editable:drawing:end', call) 160 | const other = this.map.editTools.startMarker() 161 | this.map.editTools.stopDrawing() 162 | assert.equal(called, 1) 163 | this.map.off('editable:drawing:end', call) 164 | other.remove() 165 | assert.equal(called, 1) 166 | }) 167 | 168 | it('should fire editable:drawing:clicked before end/commit on click', function () { 169 | let first = null 170 | let last 171 | const setFirst = (e) => { 172 | if (first === null) first = e.type 173 | } 174 | const setLast = (e) => { 175 | last = e.type 176 | } 177 | this.map.on('editable:drawing:end', setFirst) 178 | this.map.on('editable:drawing:clicked', setFirst) 179 | this.map.on('editable:drawing:commit', setFirst) 180 | this.map.on('editable:drawing:end', setLast) 181 | this.map.on('editable:drawing:clicked', setLast) 182 | this.map.on('editable:drawing:commit', setLast) 183 | const other = this.map.editTools.startMarker() 184 | happen.drawingClick(450, 450) 185 | assert.equal(first, 'editable:drawing:clicked') 186 | assert.equal(last, 'editable:drawing:end') 187 | this.map.off('editable:drawing:end', setFirst) 188 | this.map.off('editable:drawing:clicked', setFirst) 189 | this.map.off('editable:drawing:commit', setFirst) 190 | this.map.off('editable:drawing:end', setLast) 191 | this.map.off('editable:drawing:clicked', setLast) 192 | this.map.off('editable:drawing:commit', setLast) 193 | other.remove() 194 | }) 195 | 196 | it('should not fire editable:drawing:commit on stopDrawing', function () { 197 | let called = 0 198 | const call = () => { 199 | called++ 200 | } 201 | this.map.on('editable:drawing:commit', call) 202 | const other = this.map.editTools.startMarker() 203 | this.map.editTools.stopDrawing() 204 | assert.equal(called, 0) 205 | this.map.off('editable:drawing:commit', call) 206 | other.remove() 207 | assert.equal(called, 0) 208 | }) 209 | 210 | it('should fire editable:drawing:move on mousemove while drawing', function () { 211 | let called = 0 212 | const call = () => { 213 | called++ 214 | } 215 | this.map.on('editable:drawing:move', call) 216 | const other = this.map.editTools.startMarker() 217 | assert.equal(called, 0) 218 | happen.at('mousemove', 450, 450) 219 | assert.equal(called, 1) 220 | happen.drawingClick(450, 450) 221 | this.map.off('editable:drawing:move', call) 222 | other.remove() 223 | assert.equal(called, 1) 224 | }) 225 | 226 | it('should fire editable:drawing:move on mousemove while moving marker', function (done) { 227 | let called = 0 228 | const call = () => { 229 | called++ 230 | } 231 | const layer = L.marker(p2ll(200, 200)).addTo(this.map) 232 | layer.enableEdit() 233 | assert.equal(called, 0) 234 | this.map.on('editable:drawing:move', call) 235 | happen.drag(200, 190, 210, 210, () => { 236 | assert.ok(called > 0) 237 | map.off('editable:drawing:move', call) 238 | layer.remove() 239 | done() 240 | }) 241 | }) 242 | }) 243 | }) 244 | -------------------------------------------------------------------------------- /test/Editable.js: -------------------------------------------------------------------------------- 1 | describe('L.Editable', () => { 2 | before(function () { 3 | this.map = map 4 | }) 5 | 6 | afterEach(function () { 7 | this.map.editTools.editLayer.eachLayer((layer) => { 8 | assert.fail(layer, null, 'no layer expected but one found') 9 | }) 10 | }) 11 | 12 | describe('#init', () => { 13 | it('should be initialized', function () { 14 | assert.ok(this.map.editTools) 15 | }) 16 | }) 17 | 18 | describe('#drawing on top of other elements', () => { 19 | xit('should be possible to create latlng on top of previously created vertex', function () { 20 | const line1 = this.map.editTools.startPolyline() 21 | happen.drawingClick(450, 450) 22 | happen.drawingClick(500, 500) 23 | happen.drawingClick(500, 500) 24 | assert.equal(line1._latlngs.length, 2) 25 | const line2 = this.map.editTools.startPolyline() 26 | happen.drawingClick(450, 450) 27 | assert.equal(line2._latlngs.length, 1) 28 | assert.equal(line1._latlngs.length, 2) 29 | happen.drawingClick(500, 500) 30 | happen.drawingClick(500, 500) 31 | assert.equal(line2._latlngs.length, 2) 32 | assert.equal(line1._latlngs.length, 2) 33 | line1.remove() 34 | line2.remove() 35 | }) 36 | 37 | it('should be possible to delete other vertex of currently drawn path', function () { 38 | const line = this.map.editTools.startPolyline() 39 | happen.drawingClick(450, 450) 40 | happen.drawingClick(500, 500) 41 | happen.drawingClick(500, 550) 42 | assert.equal(line._latlngs.length, 3) 43 | happen.at('click', 500, 500) 44 | assert.equal(line._latlngs.length, 2) 45 | happen.drawingClick(500, 550) 46 | assert.equal(line._latlngs.length, 2) 47 | this.map.removeLayer(line) 48 | }) 49 | }) 50 | describe('#commitDrawing', () => { 51 | it('should commit drawing if drawing is active', function () { 52 | const layer = this.map.editTools.startPolyline() 53 | happen.drawingClick(450, 450) 54 | happen.drawingClick(500, 500) 55 | assert.equal(layer._latlngs.length, 2) 56 | this.map.editTools.commitDrawing() 57 | assert.equal(layer._latlngs.length, 2) 58 | happen.drawingClick(550, 550) 59 | assert.equal(layer._latlngs.length, 2) 60 | layer.remove() 61 | }) 62 | 63 | it('should fail silently if no drawing is active', function () { 64 | try { 65 | this.map.editTools.commitDrawing() 66 | } catch (e) { 67 | fail('commitDrawing has raised') 68 | } 69 | }) 70 | }) 71 | 72 | describe('#map options', () => { 73 | it('should possible to override editTools class with editToolsClass', () => { 74 | const CustomEditable = L.Editable.extend({ 75 | options: { 76 | checkme: true, 77 | }, 78 | }) 79 | const container = document.createElement('DIV') 80 | document.body.appendChild(container) 81 | const someMap = L.map(container, { 82 | editable: true, 83 | editToolsClass: CustomEditable, 84 | }) 85 | someMap.setView([0, 0], 0) 86 | assert.ok(someMap.editTools) 87 | assert.ok(someMap.editTools.options.checkme) 88 | assert.ok(someMap.editTools instanceof CustomEditable) 89 | }) 90 | }) 91 | 92 | describe('#drawing', () => { 93 | it('should return false if nothing happen', function () { 94 | assert.notOk(this.map.editTools.drawing()) 95 | }) 96 | 97 | it('should return false if an editor is active but not drawing', function () { 98 | const layer = L.polyline([]).addTo(this.map) 99 | layer.enableEdit() 100 | assert.notOk(this.map.editTools.drawing()) 101 | layer.remove() 102 | }) 103 | 104 | it('should return true if an editor is active and drawing forward', function () { 105 | const layer = this.map.editTools.startPolyline() 106 | assert.ok(this.map.editTools.drawing()) 107 | layer.editor.disable() 108 | }) 109 | 110 | it('should return true if an editor is active and drawing backward', function () { 111 | const layer = L.polyline([ 112 | [1, 2], 113 | [3, 4], 114 | ]).addTo(this.map) 115 | layer.enableEdit() 116 | layer.editor.continueBackward() 117 | assert.ok(this.map.editTools.drawing()) 118 | layer.remove() 119 | }) 120 | }) 121 | }) 122 | -------------------------------------------------------------------------------- /test/MarkerEditor.js: -------------------------------------------------------------------------------- 1 | describe('L.MarkerEditor', () => { 2 | let p2ll 3 | 4 | before(function () { 5 | this.map = map 6 | p2ll = (x, y) => map.layerPointToLatLng([x, y]) 7 | }) 8 | 9 | describe('#startNewMarker()', () => { 10 | let marker 11 | 12 | after(() => { 13 | marker.remove() 14 | }) 15 | 16 | it('should create feature and editor', function () { 17 | marker = this.map.editTools.startMarker() 18 | assert.ok(marker) 19 | assert.ok(marker.editor) 20 | }) 21 | 22 | it('should update marker position on mousemove', () => { 23 | happen.at('mousemove', 200, 200) 24 | const before = marker._latlng 25 | happen.at('mousemove', 300, 300) 26 | assert.notEqual(before, marker._latlng) 27 | }) 28 | 29 | it('should set latlng on first click', () => { 30 | happen.drawingClick(300, 300) 31 | const before = marker._latlng 32 | happen.at('mousemove', 400, 400) 33 | assert.equal(before, marker._latlng) 34 | }) 35 | 36 | it('should apply passed options to the marker', function () { 37 | const title = 'My title' 38 | const other = this.map.editTools.startPolygon(null, { title: title }) 39 | assert.equal(other.options.title, title) 40 | other.remove() 41 | }) 42 | 43 | it('should update latlng on marker drag', (done) => { 44 | const before = marker._latlng.lat 45 | happen.drag(300, 299, 350, 350, () => { 46 | assert.notEqual(before, marker._latlng.lat) 47 | done() 48 | }) 49 | }) 50 | }) 51 | 52 | describe('#enable()', () => { 53 | it('should start editing on enable() call', function () { 54 | const marker = L.marker([0, 0]).addTo(this.map) 55 | marker.enableEdit() 56 | assert.ok(marker.editor) 57 | }) 58 | }) 59 | 60 | describe('#disable()', () => { 61 | it('should stop editing on disable() call', function () { 62 | const marker = L.marker([0, 0]).addTo(this.map) 63 | marker.enableEdit() 64 | assert.ok(marker.editEnabled()) 65 | marker.disableEdit() 66 | assert.notOk(marker.editor) 67 | }) 68 | 69 | it('should be reenabled after remove if active', function () { 70 | const marker = L.marker([0, 0]).addTo(this.map) 71 | marker.enableEdit() 72 | this.map.removeLayer(marker) 73 | assert.notOk(marker.editEnabled()) 74 | this.map.addLayer(marker) 75 | assert.ok(marker.editEnabled()) 76 | }) 77 | 78 | it('should not be reenabled after remove if not active', function () { 79 | const marker = L.marker([0, 0]).addTo(this.map) 80 | marker.enableEdit() 81 | marker.disableEdit() 82 | this.map.removeLayer(marker) 83 | assert.notOk(marker.editEnabled()) 84 | this.map.addLayer(marker) 85 | assert.notOk(marker.editEnabled()) 86 | }) 87 | }) 88 | 89 | describe('#events', () => { 90 | it('should fire editable:drawing:start on startMarker call', function () { 91 | let called = 0 92 | const call = () => { 93 | called++ 94 | } 95 | this.map.on('editable:drawing:start', call) 96 | const other = this.map.editTools.startMarker() 97 | assert.equal(called, 1) 98 | this.map.off('editable:drawing:start', call) 99 | other.editor.disable() 100 | assert.notOk(this.map.editTools._drawingEditor) 101 | }) 102 | 103 | it('should fire editable:drawing:end on click', function () { 104 | let called = 0 105 | const call = () => { 106 | called++ 107 | } 108 | this.map.on('editable:drawing:end', call) 109 | const other = this.map.editTools.startMarker() 110 | assert.equal(called, 0) 111 | happen.drawingClick(450, 450) 112 | assert.equal(called, 1) 113 | this.map.off('editable:drawing:end', call) 114 | other.remove() 115 | assert.equal(called, 1) 116 | }) 117 | 118 | it('should fire editable:drawing:commit on finish', function () { 119 | let called = 0 120 | const call = () => { 121 | called++ 122 | } 123 | this.map.on('editable:drawing:commit', call) 124 | const other = this.map.editTools.startMarker() 125 | assert.equal(called, 0) 126 | happen.drawingClick(450, 450) 127 | assert.equal(called, 1) 128 | this.map.off('editable:drawing:commit', call) 129 | other.remove() 130 | assert.equal(called, 1) 131 | }) 132 | 133 | it('should fire editable:edited on finish', function () { 134 | let called = 0 135 | const call = () => { 136 | called++ 137 | } 138 | this.map.on('editable:edited', call) 139 | const other = this.map.editTools.startMarker() 140 | assert.equal(called, 0) 141 | happen.drawingClick(450, 450) 142 | assert.equal(called, 1) 143 | this.map.off('editable:edited', call) 144 | other.remove() 145 | assert.equal(called, 1) 146 | }) 147 | 148 | it('should fire editable:drawing:end on stopDrawing', function () { 149 | let called = 0 150 | const call = () => { 151 | called++ 152 | } 153 | this.map.on('editable:drawing:end', call) 154 | const other = this.map.editTools.startMarker() 155 | this.map.editTools.stopDrawing() 156 | assert.equal(called, 1) 157 | this.map.off('editable:drawing:end', call) 158 | other.remove() 159 | assert.equal(called, 1) 160 | }) 161 | 162 | it('should fire editable:drawing:clicked before end/commit on click', function () { 163 | let first = null 164 | let last 165 | const setFirst = (e) => { 166 | if (first === null) first = e.type 167 | } 168 | const setLast = (e) => { 169 | last = e.type 170 | } 171 | this.map.on('editable:drawing:end', setFirst) 172 | this.map.on('editable:drawing:clicked', setFirst) 173 | this.map.on('editable:drawing:commit', setFirst) 174 | this.map.on('editable:drawing:end', setLast) 175 | this.map.on('editable:drawing:clicked', setLast) 176 | this.map.on('editable:drawing:commit', setLast) 177 | const other = this.map.editTools.startMarker() 178 | happen.drawingClick(450, 450) 179 | assert.equal(first, 'editable:drawing:clicked') 180 | assert.equal(last, 'editable:drawing:end') 181 | this.map.off('editable:drawing:end', setFirst) 182 | this.map.off('editable:drawing:clicked', setFirst) 183 | this.map.off('editable:drawing:commit', setFirst) 184 | this.map.off('editable:drawing:end', setLast) 185 | this.map.off('editable:drawing:clicked', setLast) 186 | this.map.off('editable:drawing:commit', setLast) 187 | other.remove() 188 | }) 189 | 190 | it('should not fire editable:drawing:commit on stopDrawing', function () { 191 | let called = 0 192 | const call = () => { 193 | called++ 194 | } 195 | this.map.on('editable:drawing:commit', call) 196 | const other = this.map.editTools.startMarker() 197 | this.map.editTools.stopDrawing() 198 | assert.equal(called, 0) 199 | this.map.off('editable:drawing:commit', call) 200 | other.remove() 201 | assert.equal(called, 0) 202 | }) 203 | 204 | it('should fire editable:drawing:move on mousemove while drawing', function () { 205 | let called = 0 206 | const call = () => { 207 | called++ 208 | } 209 | this.map.on('editable:drawing:move', call) 210 | const other = this.map.editTools.startMarker() 211 | assert.equal(called, 0) 212 | happen.at('mousemove', 450, 450) 213 | assert.equal(called, 1) 214 | happen.drawingClick(450, 450) 215 | this.map.off('editable:drawing:move', call) 216 | other.remove() 217 | assert.equal(called, 1) 218 | }) 219 | 220 | it('should fire editable:drawing:move on mousemove while moving marker', function (done) { 221 | let called = 0 222 | const call = () => { 223 | called++ 224 | } 225 | const layer = L.marker(p2ll(200, 200)).addTo(this.map) 226 | layer.enableEdit() 227 | assert.equal(called, 0) 228 | this.map.on('editable:drawing:move', call) 229 | happen.drag(200, 190, 210, 210, () => { 230 | assert.ok(called > 0) 231 | map.off('editable:drawing:move', call) 232 | layer.remove() 233 | done() 234 | }) 235 | }) 236 | 237 | it('should fire editable:edited after moving marker', function (done) { 238 | let called = 0 239 | const call = () => { 240 | called++ 241 | } 242 | const layer = L.marker(p2ll(200, 200)).addTo(this.map) 243 | layer.enableEdit() 244 | assert.equal(called, 0) 245 | this.map.on('editable:edited', call) 246 | happen.drag(200, 190, 210, 210, () => { 247 | assert.ok(called > 0) 248 | map.off('editable:edited', call) 249 | layer.remove() 250 | done() 251 | }) 252 | }) 253 | }) 254 | }) 255 | -------------------------------------------------------------------------------- /test/MiddleMarker.js: -------------------------------------------------------------------------------- 1 | describe('L.MiddleMarker', () => { 2 | before(function () { 3 | this.map = map 4 | }) 5 | 6 | afterEach(function () { 7 | this.map.editTools.editLayer.eachLayer((layer) => { 8 | assert.fail(layer, null, 'no layer expected but one found') 9 | }) 10 | }) 11 | 12 | describe('#setVisibility()', () => { 13 | let line 14 | 15 | afterEach(() => { 16 | line.remove() 17 | }) 18 | 19 | it('should be visible if space is enough', function () { 20 | line = this.map.editTools.startPolyline() 21 | happen.drawingClick(450, 450) 22 | happen.drawingClick(500, 500) 23 | happen.at('click', 500, 500) 24 | assert.equal(line._latlngs[1].__vertex.middleMarker._icon.style.opacity, 0.5) 25 | }) 26 | 27 | it('should not be visible if space is not enough', function () { 28 | line = this.map.editTools.startPolyline() 29 | happen.drawingClick(450, 450) 30 | happen.drawingClick(450, 460) 31 | happen.at('click', 450, 460) 32 | assert.equal(line._latlngs[1].__vertex.middleMarker._icon.style.opacity, 0) 33 | }) 34 | 35 | it('should toggle visibilty on zoom', function (done) { 36 | line = this.map.editTools.startPolyline() 37 | happen.drawingClick(450, 450) 38 | happen.drawingClick(500, 500) 39 | happen.at('click', 500, 500) 40 | assert.equal(line._latlngs[1].__vertex.middleMarker._icon.style.opacity, 0.5) 41 | const mustBe05 = () => { 42 | window.setTimeout(() => { 43 | assert.equal(line._latlngs[1].__vertex.middleMarker._icon.style.opacity, 0.5) 44 | done() 45 | }, 10) 46 | } 47 | const mustBe0 = () => { 48 | window.setTimeout(() => { 49 | assert.equal(line._latlngs[1].__vertex.middleMarker._icon.style.opacity, 0) 50 | map.once('zoomend', mustBe05) 51 | map.zoomIn(3) 52 | }, 10) 53 | } 54 | map.once('zoomend', mustBe0) 55 | map.zoomOut(3) 56 | }) 57 | 58 | it('should show on drag out', function (done) { 59 | line = this.map.editTools.startPolyline() 60 | happen.drawingClick(450, 450) 61 | happen.drawingClick(450, 460) 62 | happen.at('click', 450, 460) 63 | assert.equal(line._latlngs[1].__vertex.middleMarker._icon.style.opacity, 0) 64 | happen.drag(450, 460, 450, 480, () => { 65 | assert.equal(line._latlngs[1].__vertex.middleMarker._icon.style.opacity, 0.5) 66 | done() 67 | }) 68 | }) 69 | 70 | it('should hide on drag closer', function (done) { 71 | line = this.map.editTools.startPolyline() 72 | happen.drawingClick(450, 450) 73 | happen.drawingClick(450, 480) 74 | happen.at('click', 450, 480) 75 | assert.equal(line._latlngs[1].__vertex.middleMarker._icon.style.opacity, 0.5) 76 | happen.drag(450, 450, 450, 470, () => { 77 | assert.equal(line._latlngs[1].__vertex.middleMarker._icon.style.opacity, 0) 78 | done() 79 | }) 80 | }) 81 | }) 82 | 83 | describe('#computeLatLng', () => { 84 | it('compute middlemarker in the middle even in areas close to pole', function () { 85 | this.map.setView([62.5, 22.7], 5) // Move to Scandinavia 86 | const line = this.map.editTools.startPolyline() 87 | happen.drawingClick(100, 100) 88 | happen.drawingClick(500, 500) 89 | happen.at('click', 500, 500) 90 | assert.equal( 91 | this.map.latLngToContainerPoint(line._latlngs[1].__vertex.middleMarker._latlng) 92 | .x, 93 | 300 94 | ) 95 | assert.equal( 96 | this.map.latLngToContainerPoint(line._latlngs[1].__vertex.middleMarker._latlng) 97 | .y, 98 | 300 99 | ) 100 | this.map.removeLayer(line) 101 | this.map.setView(startPoint, 16) // Move back to avoid rounding issues when projecting. 102 | }) 103 | }) 104 | }) 105 | -------------------------------------------------------------------------------- /test/PolygonEditor.js: -------------------------------------------------------------------------------- 1 | describe('L.PolygonEditor', () => { 2 | let p2ll 3 | let polygon 4 | 5 | before(function () { 6 | this.map = map 7 | p2ll = (x, y) => map.layerPointToLatLng([x, y]) 8 | }) 9 | 10 | describe('#startPolygon()', () => { 11 | it('should create feature and editor', function () { 12 | polygon = this.map.editTools.startPolygon() 13 | assert.ok(polygon) 14 | assert.ok(polygon.editor) 15 | assert.notOk(polygon._latlngs[0].length) 16 | }) 17 | 18 | it('should create latlng on click', () => { 19 | happen.drawingClick(100, 150) 20 | assert.equal(polygon._latlngs[0].length, 1) 21 | happen.drawingClick(200, 350) 22 | assert.equal(polygon._latlngs[0].length, 2) 23 | }) 24 | 25 | it('should not finish shape if not enough vertices', () => { 26 | happen.drawingClick(200, 350) 27 | assert.equal(polygon._latlngs[0].length, 2) 28 | assert.ok(polygon.editor.drawing()) 29 | }) 30 | 31 | it('should finish shape on last point click', () => { 32 | happen.drawingClick(300, 250) 33 | assert.equal(polygon._latlngs[0].length, 3) 34 | happen.drawingClick(300, 150) 35 | assert.equal(polygon._latlngs[0].length, 4) 36 | happen.drawingClick(300, 150) 37 | assert.equal(polygon._latlngs[0].length, 4) 38 | }) 39 | 40 | it('should finish drawing also on first point', function () { 41 | const other = this.map.editTools.startPolygon() 42 | assert.notOk(other._latlngs[0].length) 43 | happen.drawingClick(400, 450) 44 | assert.equal(other._latlngs[0].length, 1) 45 | happen.drawingClick(450, 500) 46 | assert.equal(other._latlngs[0].length, 2) 47 | happen.drawingClick(300, 450) 48 | assert.equal(other._latlngs[0].length, 3) 49 | happen.drawingClick(400, 450) 50 | assert.equal(other._latlngs[0].length, 3) 51 | other.remove() 52 | }) 53 | 54 | it('should apply passed options to the polygon', function () { 55 | const className = 'my-class' 56 | const other = this.map.editTools.startPolygon(null, { 57 | className: className, 58 | }) 59 | assert.equal(other.options.className, className) 60 | other.editor.disable() 61 | }) 62 | }) 63 | 64 | describe('#disable()', () => { 65 | it('should stop editing on disable() call', () => { 66 | polygon.disableEdit() 67 | assert.notOk(polygon.editor) 68 | }) 69 | 70 | it('should be reenabled after remove if active', function () { 71 | polygon.enableEdit() 72 | this.map.removeLayer(polygon) 73 | assert.notOk(polygon.editEnabled()) 74 | this.map.addLayer(polygon) 75 | assert.ok(polygon.editEnabled()) 76 | }) 77 | 78 | it('should not be reenabled after remove if not active', function () { 79 | polygon.disableEdit() 80 | this.map.removeLayer(polygon) 81 | assert.notOk(polygon.editEnabled()) 82 | this.map.addLayer(polygon) 83 | assert.notOk(polygon.editEnabled()) 84 | }) 85 | }) 86 | 87 | describe('#enable()', () => { 88 | it('should start editing on enable() call', () => { 89 | polygon.enableEdit() 90 | assert.ok(polygon.editor) 91 | }) 92 | }) 93 | 94 | describe('#dragVertex()', () => { 95 | it('should update latlng on vertex drag', (done) => { 96 | const before = polygon._latlngs[0][2].lat 97 | happen.drag(300, 250, 310, 260, () => { 98 | assert.notEqual(before, polygon._latlngs[0][2].lat) 99 | done() 100 | }) 101 | }) 102 | }) 103 | 104 | describe('#deleteVertex()', () => { 105 | it('should delete latlng on vertex click', () => { 106 | happen.at('click', 200, 350) 107 | assert.equal(polygon._latlngs[0].length, 3) 108 | }) 109 | 110 | it('should not delete last latlng on vertex click if only three vertices', function () { 111 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 112 | const layer = L.polygon(latlngs).addTo(this.map) 113 | assert.equal(layer._latlngs[0].length, 3) 114 | layer.enableEdit() 115 | happen.at('click', 200, 100) 116 | assert.equal(layer._latlngs[0].length, 3) 117 | layer.remove() 118 | }) 119 | 120 | it('should delete multi polygon hole shape at last vertex delete', function () { 121 | const latlngs = [ 122 | [ 123 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 124 | [p2ll(120, 160), p2ll(150, 170), p2ll(180, 120)], 125 | ], 126 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 127 | ] 128 | const layer = L.polygon(latlngs).addTo(this.map) 129 | layer.enableEdit() 130 | assert.equal(layer._latlngs[0][1].length, 3) 131 | happen.at('click', 120, 160) 132 | happen.at('click', 150, 170) 133 | happen.at('click', 180, 120) 134 | assert.notOk(layer._latlngs[0][1]) 135 | assert.ok(layer._latlngs[0]) 136 | assert.ok(layer._latlngs[1]) 137 | assert.ok(this.map.hasLayer(layer)) 138 | layer.remove() 139 | }) 140 | }) 141 | 142 | describe('#dragMiddleMarker()', () => { 143 | it('should insert new latlng on middle marker click', (done) => { 144 | const first = polygon._latlngs[0][0] 145 | const second = polygon._latlngs[0][1] 146 | const fromX = (100 + 310) / 2 147 | const fromY = (150 + 260) / 2 148 | happen.drag(fromX, fromY, 150, 300, () => { 149 | assert.equal(polygon._latlngs[0].length, 4) 150 | // New should have been inserted between first and second latlng, 151 | // so second should equal third, and first should not have changed 152 | assert.equal(first, polygon._latlngs[0][0]) 153 | assert.equal(second, polygon._latlngs[0][2]) 154 | done() 155 | }) 156 | }) 157 | }) 158 | 159 | describe('#newHole', () => { 160 | it('should create new hole on click', function () { 161 | assert.equal(polygon._latlngs[0].length, 4) 162 | polygon.editor.newHole(this.map.layerPointToLatLng([150, 170])) 163 | assert.equal(polygon._latlngs.length, 2) 164 | assert.equal(polygon._latlngs[0].length, 4) 165 | assert.equal(polygon._latlngs[1].length, 1) 166 | happen.drawingClick(200, 250) 167 | assert.equal(polygon._latlngs[1].length, 2) 168 | happen.drawingClick(250, 250) 169 | assert.equal(polygon._latlngs[1].length, 3) 170 | happen.drawingClick(250, 200) 171 | assert.equal(polygon._latlngs[1].length, 4) 172 | }) 173 | 174 | xit('should not create new point when clicking outside', () => { 175 | happen.drawingClick(400, 400) 176 | assert.equal(polygon._latlngs[1].length, 4) 177 | }) 178 | 179 | it('should finish shape on last point click', () => { 180 | happen.drawingClick(250, 200) 181 | happen.at('click', 250, 200) 182 | happen.drawingClick(260, 210) 183 | assert.equal(polygon._latlngs[1].length, 4) 184 | }) 185 | 186 | it('should remove hole latlngs on click', () => { 187 | happen.at('click', 150, 170) 188 | assert.equal(polygon._latlngs[1].length, 3) 189 | happen.at('click', 200, 250) 190 | assert.equal(polygon._latlngs[1].length, 2) 191 | happen.at('click', 250, 250) 192 | assert.equal(polygon._latlngs[1].length, 1) 193 | }) 194 | 195 | it('should remove hole array on last click', function () { 196 | happen.at('click', 250, 200) 197 | assert.notOk(polygon._latlngs[1]) 198 | assert.ok(polygon._latlngs[0]) 199 | assert.ok(polygon._latlngs) 200 | assert.ok(this.map.hasLayer(polygon)) 201 | polygon.remove() 202 | }) 203 | }) 204 | 205 | describe('#drawing', () => { 206 | it('should return false if no drawing happen', function () { 207 | const layer = L.polygon([p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]).addTo( 208 | this.map 209 | ) 210 | layer.enableEdit() 211 | assert.notOk(layer.editor.drawing()) 212 | layer.remove() 213 | }) 214 | 215 | it('should return true if an editor is active and drawing forward', function () { 216 | const layer = this.map.editTools.startPolygon() 217 | assert.ok(layer.editor.drawing()) 218 | layer.editor.disable() 219 | }) 220 | }) 221 | 222 | describe('#pop', () => { 223 | it('should remove last latlng when drawing', function () { 224 | const layer = this.map.editTools.startPolygon() 225 | happen.drawingClick(450, 450) 226 | happen.drawingClick(500, 500) 227 | assert.equal(layer._latlngs[0].length, 2) 228 | const last = layer._latlngs[0][1] 229 | assert.include(layer._latlngs[0], last) 230 | const latlng = layer.editor.pop() 231 | assert.equal(latlng.lat, last.lat) 232 | assert.ok(latlng) 233 | assert.equal(layer._latlngs[0].length, 1) 234 | assert.notInclude(layer._latlngs[0], last) 235 | layer.remove() 236 | }) 237 | }) 238 | 239 | describe('#push', () => { 240 | it('should add a latlng at the end when drawing forward', function () { 241 | const layer = this.map.editTools.startPolygon() 242 | happen.drawingClick(450, 450) 243 | happen.drawingClick(500, 500) 244 | assert.equal(layer._latlngs[0].length, 2) 245 | const latlng = p2ll(100, 150) 246 | layer.editor.push(latlng) 247 | assert.include(layer._latlngs[0], latlng) 248 | const last = layer._latlngs[0][2] 249 | assert.equal(latlng.lat, last.lat) 250 | assert.equal(layer._latlngs[0].length, 3) 251 | layer.remove() 252 | }) 253 | }) 254 | 255 | describe('#endDrawing', () => { 256 | it('should remove shape if not enough latlngs', function () { 257 | const layer = this.map.editTools.startPolygon() 258 | happen.drawingClick(450, 450) 259 | happen.drawingClick(500, 500) 260 | assert.equal(layer._latlngs[0].length, 2) 261 | layer.editor.cancelDrawing() 262 | assert.equal(layer._latlngs[0].length, 0) 263 | layer.remove() 264 | }) 265 | 266 | it('should remove shape if not enough latlngs (multi)', function () { 267 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 268 | const layer = L.polygon(latlngs).addTo(this.map) 269 | layer.enableEdit() 270 | assert.equal(layer._latlngs.length, 1) 271 | layer.editor.newShape() 272 | happen.drawingClick(400, 400) 273 | happen.drawingClick(500, 500) 274 | assert.equal(layer._latlngs.length, 2) 275 | layer.editor.cancelDrawing() 276 | assert.equal(layer._latlngs.length, 1) 277 | layer.remove() 278 | }) 279 | 280 | it('should not remove shape if enough latlngs (multi)', function () { 281 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 282 | const layer = L.polygon(latlngs).addTo(this.map) 283 | layer.enableEdit() 284 | assert.equal(layer._latlngs.length, 1) 285 | layer.editor.newShape() 286 | happen.drawingClick(400, 400) 287 | happen.drawingClick(500, 400) 288 | happen.drawingClick(400, 500) 289 | assert.equal(layer._latlngs.length, 2) 290 | layer.editor.cancelDrawing() 291 | assert.equal(layer._latlngs.length, 2) 292 | layer.remove() 293 | }) 294 | }) 295 | 296 | describe('#parentShape()', () => { 297 | it('should find parent shape on simple polygon', function () { 298 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 299 | const layer = L.polygon(latlngs).addTo(this.map) 300 | assert.equal(layer.parentShape(layer._latlngs[0]), layer._latlngs) 301 | layer.remove() 302 | }) 303 | 304 | it('should find parent shape on multi polygon', function () { 305 | const latlngs = [ 306 | [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]], 307 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 308 | ] 309 | const layer = L.polygon(latlngs).addTo(this.map) 310 | assert.equal(layer.parentShape(layer._latlngs[0][0]), layer._latlngs[0]) 311 | assert.equal(layer.parentShape(layer._latlngs[1][0]), layer._latlngs[1]) 312 | layer.remove() 313 | }) 314 | 315 | it('should find parent shape on multi polygon with hole', function () { 316 | const latlngs = [ 317 | [ 318 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 319 | [p2ll(120, 160), p2ll(150, 170), p2ll(180, 120)], 320 | ], 321 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 322 | ] 323 | const layer = L.polygon(latlngs).addTo(this.map) 324 | assert.equal(layer.parentShape(layer._latlngs[0][0]), layer._latlngs[0]) 325 | assert.equal(layer.parentShape(layer._latlngs[0][1]), layer._latlngs[0]) 326 | assert.equal(layer.parentShape(layer._latlngs[1][0]), layer._latlngs[1]) 327 | layer.remove() 328 | }) 329 | }) 330 | 331 | describe('#enableDragging()', () => { 332 | it('should drag a polygon', function (done) { 333 | const latlngs = [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]] 334 | const layer = L.polygon(latlngs).addTo(this.map) 335 | const before = layer._latlngs[0][2].lat 336 | layer.enableEdit() 337 | assert.equal(before, layer._latlngs[0][2].lat) 338 | happen.drag(150, 150, 170, 170, () => { 339 | assert.notEqual(before, layer._latlngs[0][2].lat) 340 | layer.remove() 341 | done() 342 | }) 343 | }) 344 | 345 | it('should drag a multipolygon with hole', function (done) { 346 | const latlngs = [ 347 | [ 348 | [p2ll(100, 150), p2ll(150, 300), p2ll(300, 100)], 349 | [p2ll(220, 160), p2ll(150, 170), p2ll(180, 220)], 350 | ], 351 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 352 | ] 353 | const layer = L.polygon(latlngs).addTo(this.map) 354 | const before = layer._latlngs[1][0][2].lat 355 | layer.enableEdit() 356 | assert.equal(before, layer._latlngs[1][0][2].lat) 357 | happen.drag(150, 150, 170, 170, () => { 358 | assert.notEqual(before, layer._latlngs[1][0][2].lat) 359 | layer.remove() 360 | done() 361 | }) 362 | }) 363 | 364 | it('should send editable:dragstart event', function (done) { 365 | const latlngs = [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]] 366 | const layer = L.polygon(latlngs).addTo(this.map) 367 | let called = 0 368 | const call = () => { 369 | called++ 370 | } 371 | layer.on('editable:dragstart', call) 372 | layer.enableEdit() 373 | assert.equal(called, 0) 374 | happen.drag(150, 150, 170, 170, () => { 375 | assert.equal(called, 1) 376 | layer.remove() 377 | done() 378 | }) 379 | }) 380 | 381 | it('should send editable:dragend event', function (done) { 382 | const latlngs = [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]] 383 | const layer = L.polygon(latlngs).addTo(this.map) 384 | let called = 0 385 | const call = () => { 386 | called++ 387 | } 388 | layer.on('editable:dragend', call) 389 | layer.enableEdit() 390 | assert.equal(called, 0) 391 | happen.drag(150, 150, 170, 170, () => { 392 | assert.equal(called, 1) 393 | layer.remove() 394 | done() 395 | }) 396 | }) 397 | 398 | it('should send editable:drag event', function (done) { 399 | const latlngs = [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]] 400 | const layer = L.polygon(latlngs).addTo(this.map) 401 | let called = 0 402 | const call = () => { 403 | called++ 404 | } 405 | layer.on('editable:drag', call) 406 | layer.enableEdit() 407 | assert.notOk(called) 408 | happen.drag(150, 150, 170, 170, () => { 409 | assert.ok(called) 410 | layer.remove() 411 | done() 412 | }) 413 | }) 414 | }) 415 | 416 | describe('Events', () => { 417 | afterEach(function () { 418 | this.map.editTools.editLayer.eachLayer((layer) => { 419 | assert.fail(layer, null, 'no layer expected but one found') 420 | }) 421 | }) 422 | 423 | it('should fire editable:drawing:start on startPolygon call', function () { 424 | let called = 0 425 | const call = () => { 426 | called++ 427 | } 428 | this.map.on('editable:drawing:start', call) 429 | const layer = this.map.editTools.startPolygon() 430 | assert.equal(called, 1) 431 | this.map.off('editable:drawing:start', call) 432 | layer.editor.disable() 433 | assert.notOk(this.map.editTools._drawingEditor) 434 | }) 435 | 436 | it('should fire editable:drawing:end on last click', function () { 437 | let called = 0 438 | const call = () => { 439 | called++ 440 | } 441 | this.map.on('editable:drawing:end', call) 442 | const layer = this.map.editTools.startPolygon() 443 | assert.equal(called, 0) 444 | happen.drawingClick(100, 150) 445 | assert.equal(layer._latlngs[0].length, 1) 446 | assert.equal(called, 0) 447 | happen.drawingClick(200, 350) 448 | assert.equal(layer._latlngs[0].length, 2) 449 | assert.equal(called, 0) 450 | happen.drawingClick(300, 250) 451 | assert.equal(layer._latlngs[0].length, 3) 452 | assert.equal(called, 0) 453 | happen.at('click', 300, 250) 454 | assert.equal(called, 1) 455 | this.map.off('editable:drawing:end', call) 456 | layer.remove() 457 | assert.equal(called, 1) 458 | }) 459 | 460 | it('should fire editable:drawing:commit on last click', function () { 461 | let called = 0 462 | const call = () => { 463 | called++ 464 | } 465 | this.map.on('editable:drawing:commit', call) 466 | const layer = this.map.editTools.startPolygon() 467 | assert.equal(called, 0) 468 | happen.drawingClick(100, 150) 469 | assert.equal(layer._latlngs[0].length, 1) 470 | assert.equal(called, 0) 471 | happen.drawingClick(200, 350) 472 | assert.equal(layer._latlngs[0].length, 2) 473 | assert.equal(called, 0) 474 | happen.drawingClick(300, 250) 475 | assert.equal(layer._latlngs[0].length, 3) 476 | assert.equal(called, 0) 477 | happen.at('click', 300, 250) 478 | assert.equal(called, 1) 479 | this.map.off('editable:drawing:commit', call) 480 | layer.remove() 481 | assert.equal(called, 1) 482 | }) 483 | 484 | it('should fire editable:drawing:end on stopDrawing', function () { 485 | let called = 0 486 | const call = () => { 487 | called++ 488 | } 489 | this.map.on('editable:drawing:end', call) 490 | const layer = this.map.editTools.startPolygon() 491 | this.map.editTools.stopDrawing() 492 | assert.equal(called, 1) 493 | this.map.off('editable:drawing:end', call) 494 | layer.editor.disable() 495 | assert.equal(called, 1) 496 | }) 497 | 498 | it('should not fire editable:drawing:commit on stopDrawing', function () { 499 | let called = 0 500 | const call = () => { 501 | called++ 502 | } 503 | this.map.on('editable:drawing:commit', call) 504 | const layer = this.map.editTools.startPolygon() 505 | this.map.editTools.stopDrawing() 506 | assert.equal(called, 0) 507 | this.map.off('editable:drawing:commit', call) 508 | layer.editor.disable() 509 | assert.equal(called, 0) 510 | }) 511 | 512 | it('should fire editable:vertex:clicked before end/commit on last click', function () { 513 | let first = null 514 | let second = 0 515 | let last 516 | const setFirst = (e) => { 517 | if (first === null) first = e.type 518 | } 519 | const setSecond = () => { 520 | second++ 521 | } 522 | const setLast = (e) => { 523 | last = e.type 524 | } 525 | this.map.on('editable:drawing:end', setFirst) 526 | this.map.on('editable:drawing:commit', setFirst) 527 | this.map.on('editable:drawing:end', setLast) 528 | this.map.on('editable:drawing:commit', setLast) 529 | this.map.on('editable:drawing:commit', setSecond) 530 | const layer = this.map.editTools.startPolygon() 531 | happen.drawingClick(450, 450) 532 | happen.drawingClick(500, 500) 533 | happen.drawingClick(400, 400) 534 | assert.notOk(first) 535 | assert.notOk(last) 536 | this.map.on('editable:vertex:clicked', setFirst) 537 | this.map.on('editable:vertex:clicked', setLast) 538 | assert.notOk(first) 539 | assert.notOk(last) 540 | assert.notOk(second) 541 | happen.at('click', 400, 400) 542 | assert.equal(first, 'editable:vertex:clicked') 543 | assert.equal(last, 'editable:drawing:end') 544 | assert.equal(second, 1) // commit has been called 545 | this.map.off('editable:drawing:end', setFirst) 546 | this.map.off('editable:drawing:commit', setFirst) 547 | this.map.off('editable:drawing:end', setLast) 548 | this.map.off('editable:drawing:commit', setLast) 549 | this.map.off('editable:vertex:clicked', setFirst) 550 | this.map.off('editable:vertex:clicked', setLast) 551 | layer.remove() 552 | }) 553 | 554 | it('should fire editable:drawing:click before adding vertex', function () { 555 | let called = 0 556 | let calledWhenEmpty = 0 557 | const call = () => { 558 | called++ 559 | if (!layer._latlngs[0].length) calledWhenEmpty = 1 560 | } 561 | this.map.on('editable:drawing:click', call) 562 | const layer = this.map.editTools.startPolygon() 563 | assert.equal(called, 0) 564 | happen.drawingClick(250, 200) 565 | assert.equal(called, 1) 566 | assert.ok(calledWhenEmpty) 567 | assert.ok(layer._latlngs[0].length) 568 | this.map.off('editable:drawing:click', call) 569 | layer.remove() 570 | }) 571 | 572 | it('should fire editable:drawing:clicked after adding vertex', function () { 573 | let called = 0 574 | let calledAfterClick = 0 575 | const call = () => { 576 | called++ 577 | if (polygon._latlngs[0].length) calledAfterClick = 1 578 | } 579 | this.map.on('editable:drawing:clicked', call) 580 | const polygon = this.map.editTools.startPolygon() 581 | assert.equal(called, 0) 582 | happen.drawingClick(250, 200) 583 | assert.equal(called, 1) 584 | assert.ok(calledAfterClick) 585 | assert.ok(polygon._latlngs[0].length) 586 | this.map.off('editable:drawing:clicked', call) 587 | polygon.remove() 588 | }) 589 | 590 | it('should fire editable:vertex:new ', function () { 591 | let newCount = 0 592 | const gotNew = (e) => { 593 | newCount++ 594 | } 595 | this.map.on('editable:vertex:new', gotNew) 596 | const polygon = this.map.editTools.startPolygon() 597 | assert.equal(newCount, 0) 598 | happen.drawingClick(250, 200) 599 | happen.drawingClick(350, 300) 600 | assert.equal(newCount, 2) 601 | this.map.off('editable:vertex:new', gotNew) 602 | polygon.remove() 603 | }) 604 | 605 | it('should fire editable:vertex:new on middle marker click', function (done) { 606 | let newCount = 0 607 | const gotNew = (e) => { 608 | newCount++ 609 | } 610 | const polygon = this.map.editTools.startPolygon() 611 | happen.drawingClick(500, 500) 612 | happen.drawingClick(400, 400) 613 | assert.equal(newCount, 0) 614 | this.map.on('editable:vertex:new', gotNew) 615 | happen.drag(450, 450, 300, 400, () => { 616 | assert.equal(newCount, 1) 617 | map.off('editable:vertex:new', gotNew) 618 | polygon.remove() 619 | done() 620 | }) 621 | }) 622 | 623 | it('should not trigger editable:vertex:new when enabling edition', function () { 624 | let newCount = 0 625 | const gotNew = (e) => { 626 | newCount++ 627 | } 628 | this.map.on('editable:vertex:new', gotNew) 629 | const layer = L.polygon([p2ll(100, 150), p2ll(150, 200)]).addTo(this.map) 630 | layer.enableEdit() 631 | assert.equal(newCount, 0) 632 | map.off('editable:vertex:new', gotNew) 633 | layer.remove() 634 | }) 635 | 636 | it('should be possible to cancel editable:drawing:click actions', function () { 637 | let called = 0 638 | const call = (e) => { 639 | e.cancel() 640 | called++ 641 | } 642 | this.map.on('editable:drawing:click', call) 643 | const polygon = this.map.editTools.startPolygon() 644 | assert.equal(called, 0) 645 | happen.drawingClick(250, 200) 646 | assert.equal(called, 1) 647 | assert.notOk(polygon._latlngs[0].length) 648 | this.map.off('editable:drawing:click', call) 649 | polygon.editor.disable() 650 | }) 651 | 652 | it('should be possible to cancel editable:vertex:rawclick', function () { 653 | const layer = L.polygon([ 654 | p2ll(100, 150), 655 | p2ll(150, 200), 656 | p2ll(200, 100), 657 | p2ll(100, 100), 658 | ]).addTo(this.map) 659 | let called = 0 660 | const call = (e) => { 661 | e.cancel() 662 | called++ 663 | } 664 | assert.equal(layer._latlngs[0].length, 4) 665 | this.map.on('editable:vertex:rawclick', call) 666 | layer.enableEdit() 667 | assert.equal(called, 0) 668 | happen.at('click', 100, 100) 669 | assert.equal(called, 1) 670 | assert.equal(layer._latlngs[0].length, 4) 671 | this.map.off('editable:vertex:rawclick', call) 672 | layer.remove() 673 | }) 674 | 675 | it('should fire editable:drawing:mouseover after hovering over vertex', function () { 676 | const layer = L.polygon([ 677 | p2ll(100, 150), 678 | p2ll(150, 200), 679 | p2ll(200, 100), 680 | p2ll(100, 100), 681 | ]).addTo(this.map) 682 | let called = 0 683 | const call = () => { 684 | called++ 685 | } 686 | this.map.on('editable:vertex:mouseover', call) 687 | layer.enableEdit() 688 | assert.equal(called, 0) 689 | happen.at('mouseover', 100, 150) 690 | assert.ok(called) 691 | this.map.off('editable:vertex:mouseover', call) 692 | layer.remove() 693 | }) 694 | 695 | it('should fire editable:drawing:mouseout after hovering out of a vertex', function () { 696 | const layer = L.polygon([ 697 | p2ll(100, 150), 698 | p2ll(150, 200), 699 | p2ll(200, 100), 700 | p2ll(100, 100), 701 | ]).addTo(this.map) 702 | let called = 0 703 | const call = () => { 704 | called++ 705 | } 706 | this.map.on('editable:vertex:mouseout', call) 707 | layer.enableEdit() 708 | assert.equal(called, 0) 709 | happen.at('mouseout', 100, 150) 710 | assert.ok(called) 711 | this.map.off('editable:vertex:mouseout', call) 712 | layer.remove() 713 | }) 714 | }) 715 | 716 | describe('Multi', () => { 717 | describe('#enableEdit', () => { 718 | it('should create vertex and middle markers for each ring', function () { 719 | const multi = L.polygon([ 720 | [ 721 | [ 722 | [43.1239, 1.244], 723 | [43.123, 1.253], 724 | [43.1252, 1.255], 725 | [43.125, 1.251], 726 | [43.1239, 1.244], 727 | ], 728 | [ 729 | [43.124, 1.246], 730 | [43.1236, 1.248], 731 | [43.12475, 1.25], 732 | ], 733 | ], 734 | [ 735 | [ 736 | [43.1269, 1.246], 737 | [43.126, 1.252], 738 | [43.1282, 1.255], 739 | [43.128, 1.245], 740 | ], 741 | ], 742 | ]).addTo(this.map) 743 | multi.enableEdit() 744 | assert.ok(multi._latlngs[0][0][0].__vertex) 745 | assert.ok(multi._latlngs[0][0][0].__vertex.middleMarker) 746 | assert.ok(multi._latlngs[0][0][1].__vertex) 747 | assert.ok(multi._latlngs[0][0][1].__vertex.middleMarker) 748 | assert.ok(multi._latlngs[0][1][0].__vertex) 749 | assert.ok(multi._latlngs[0][1][0].__vertex.middleMarker) 750 | assert.ok(multi._latlngs[1][0][0].__vertex) 751 | assert.ok(multi._latlngs[1][0][0].__vertex.middleMarker) 752 | multi.remove() 753 | this.map.editTools.editLayer.eachLayer((layer) => { 754 | assert.fail(layer, null, 'no layer expected but one found') 755 | }) 756 | }) 757 | }) 758 | 759 | describe('#formatShape', () => { 760 | let layer 761 | 762 | before(function () { 763 | layer = L.polygon([]).addTo(this.map) 764 | layer.enableEdit() 765 | }) 766 | 767 | after(() => { 768 | layer.remove() 769 | }) 770 | 771 | it('should nest flat shape', () => { 772 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 773 | assert.deepEqual(layer.editor.formatShape(latlngs), [latlngs]) 774 | }) 775 | 776 | it('should nest empty shape', () => { 777 | assert.deepEqual(layer.editor.formatShape([]), [[]]) 778 | }) 779 | 780 | it('should not renest nested empty shape', () => { 781 | assert.deepEqual(layer.editor.formatShape([[]]), [[]]) 782 | }) 783 | 784 | it('should not renest nested shape', () => { 785 | const latlngs = [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]] 786 | assert.deepEqual(layer.editor.formatShape(latlngs), latlngs) 787 | }) 788 | }) 789 | 790 | describe('#insertShape', () => { 791 | it('should add flat shape on multi polygon', function () { 792 | const latlngs = [ 793 | [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]], 794 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 795 | ] 796 | const shape = [p2ll(400, 450), p2ll(450, 500), p2ll(500, 400)] 797 | const layer = L.polygon(latlngs).addTo(this.map) 798 | layer.enableEdit() 799 | layer.editor.insertShape(shape, 1) 800 | assert.equal(layer._latlngs.length, 3) 801 | assert.deepEqual(shape, layer._latlngs[1][0]) 802 | layer.remove() 803 | }) 804 | 805 | it('should add nested shape on multi polygon', function () { 806 | const latlngs = [ 807 | [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]], 808 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 809 | ] 810 | const shape = [[p2ll(400, 450), p2ll(450, 500), p2ll(500, 400)]] 811 | const layer = L.polygon(latlngs).addTo(this.map) 812 | layer.enableEdit() 813 | layer.editor.insertShape(shape, 1) 814 | assert.equal(layer._latlngs.length, 3) 815 | assert.deepEqual(shape, layer._latlngs[1]) 816 | layer.remove() 817 | }) 818 | }) 819 | 820 | describe('#appendShape', () => { 821 | it('should add flat shape on flat polygon', function () { 822 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 823 | const shape = [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)] 824 | const layer = L.polygon(latlngs).addTo(this.map) 825 | layer.enableEdit() 826 | layer.editor.appendShape(shape) 827 | assert.equal(layer._latlngs.length, 2) 828 | assert.deepEqual(shape, layer._latlngs[1][0]) 829 | layer.remove() 830 | }) 831 | 832 | it('should add nested shape on flat polygon', function () { 833 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 834 | const shape = [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]] 835 | const layer = L.polygon(latlngs).addTo(this.map) 836 | layer.enableEdit() 837 | layer.editor.appendShape(shape) 838 | assert.equal(layer._latlngs.length, 2) 839 | assert.deepEqual(shape, layer._latlngs[1]) 840 | layer.remove() 841 | }) 842 | 843 | it('should add flat shape on multi polygon', function () { 844 | const latlngs = [ 845 | [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]], 846 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 847 | ] 848 | const shape = [p2ll(400, 450), p2ll(450, 500), p2ll(500, 400)] 849 | const layer = L.polygon(latlngs).addTo(this.map) 850 | layer.enableEdit() 851 | layer.editor.appendShape(shape) 852 | assert.equal(layer._latlngs.length, 3) 853 | assert.deepEqual(shape, layer._latlngs[2][0]) 854 | layer.remove() 855 | }) 856 | 857 | it('should add nested shape on multi polygon', function () { 858 | const latlngs = [ 859 | [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]], 860 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 861 | ] 862 | const shape = [[p2ll(400, 450), p2ll(450, 500), p2ll(500, 400)]] 863 | const layer = L.polygon(latlngs).addTo(this.map) 864 | layer.enableEdit() 865 | layer.editor.appendShape(shape) 866 | assert.equal(layer._latlngs.length, 3) 867 | assert.deepEqual(shape, layer._latlngs[2]) 868 | layer.remove() 869 | }) 870 | }) 871 | 872 | describe('#prependShape', () => { 873 | it('should add flat shape on flat polygon', function () { 874 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 875 | const shape = [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)] 876 | const layer = L.polygon(latlngs).addTo(this.map) 877 | layer.enableEdit() 878 | layer.editor.prependShape(shape) 879 | assert.equal(layer._latlngs.length, 2) 880 | assert.deepEqual(shape, layer._latlngs[0][0]) 881 | layer.remove() 882 | }) 883 | 884 | it('should add nested shape on flat polygon', function () { 885 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 886 | const shape = [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]] 887 | const layer = L.polygon(latlngs).addTo(this.map) 888 | layer.enableEdit() 889 | layer.editor.prependShape(shape) 890 | assert.equal(layer._latlngs.length, 2) 891 | assert.deepEqual(shape, layer._latlngs[0]) 892 | layer.remove() 893 | }) 894 | 895 | it('should add flat shape on multi polygon', function () { 896 | const latlngs = [ 897 | [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]], 898 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 899 | ] 900 | const shape = [p2ll(400, 450), p2ll(450, 500), p2ll(500, 400)] 901 | const layer = L.polygon(latlngs).addTo(this.map) 902 | layer.enableEdit() 903 | layer.editor.prependShape(shape) 904 | assert.equal(layer._latlngs.length, 3) 905 | assert.deepEqual(shape, layer._latlngs[0][0]) 906 | layer.remove() 907 | }) 908 | 909 | it('should add nested shape on multi polygon', function () { 910 | const latlngs = [ 911 | [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]], 912 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 913 | ] 914 | const shape = [[p2ll(400, 450), p2ll(450, 500), p2ll(500, 400)]] 915 | const layer = L.polygon(latlngs).addTo(this.map) 916 | layer.enableEdit() 917 | layer.editor.prependShape(shape) 918 | assert.equal(layer._latlngs.length, 3) 919 | assert.deepEqual(shape, layer._latlngs[0]) 920 | layer.remove() 921 | }) 922 | }) 923 | 924 | describe('#newShape', () => { 925 | it('should add a new outline on empty polygon', function () { 926 | const polygon = L.polygon([]).addTo(this.map) 927 | polygon.enableEdit() 928 | polygon.editor.newShape() 929 | happen.drawingClick(100, 150) 930 | assert.equal(polygon._latlngs[0].length, 1) 931 | happen.drawingClick(200, 350) 932 | assert.equal(polygon._latlngs[0].length, 2) 933 | happen.drawingClick(300, 250) 934 | assert.equal(polygon._latlngs[0].length, 3) 935 | happen.drawingClick(300, 250) 936 | polygon.remove() 937 | }) 938 | 939 | it('should add a new outline to existing simple polygon', function () { 940 | const polygon = L.polygon([ 941 | p2ll(100, 150), 942 | p2ll(150, 200), 943 | p2ll(200, 100), 944 | ]).addTo(this.map) 945 | polygon.enableEdit() 946 | polygon.editor.newShape() 947 | assert(L.Util.isArray(polygon._latlngs[0])) 948 | assert.ok(polygon._latlngs[0].length) 949 | assert.ok(L.Util.isArray(polygon._latlngs[0][0])) 950 | assert.ok(polygon._latlngs[0][0].length) 951 | assert.ok(L.Util.isArray(polygon._latlngs[1])) 952 | assert.ok(polygon._latlngs[1].length) 953 | assert.ok(L.Util.isArray(polygon._latlngs[1][0])) 954 | assert.notOk(polygon._latlngs[1][0].length) 955 | happen.drawingClick(300, 300) 956 | assert.equal(polygon._latlngs[1][0].length, 1) 957 | happen.drawingClick(350, 350) 958 | assert.equal(polygon._latlngs[1][0].length, 2) 959 | happen.drawingClick(400, 250) 960 | assert.equal(polygon._latlngs[1][0].length, 3) 961 | happen.drawingClick(400, 250) 962 | polygon.remove() 963 | }) 964 | 965 | it('should emit editable:shape:new on newShape call', function () { 966 | let called = 0 967 | const call = () => { 968 | called++ 969 | } 970 | this.map.on('editable:shape:new', call) 971 | const polygon = L.polygon([ 972 | p2ll(100, 150), 973 | p2ll(150, 200), 974 | p2ll(200, 100), 975 | ]).addTo(this.map) 976 | assert.equal(called, 0) 977 | polygon.enableEdit() 978 | assert.equal(called, 0) 979 | polygon.editor.newShape() 980 | assert.equal(called, 1) 981 | polygon.remove() 982 | this.map.off('editable:shape:new', call) 983 | }) 984 | }) 985 | 986 | describe('#shapeAt', () => { 987 | it('should return latlngs in case of a flat polygon', function () { 988 | const latlngs = [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]] 989 | const layer = L.polygon(latlngs).addTo(this.map) 990 | const shape = layer.shapeAt(p2ll(150, 150)) 991 | assert.equal(shape.length, 1) 992 | assert.equal(shape[0].length, 3) 993 | assert.equal(shape[0][0], latlngs[0][0]) 994 | layer.remove() 995 | }) 996 | 997 | it('should return whole shape in case of a multi polygon', function () { 998 | const latlngs = [ 999 | [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]], 1000 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 1001 | ] 1002 | const layer = L.polygon(latlngs).addTo(this.map) 1003 | const shape = layer.shapeAt(p2ll(150, 150)) 1004 | assert.equal(shape.length, 1) 1005 | assert.equal(shape[0].length, 3) 1006 | assert.equal(shape[0][0], latlngs[0][0][0]) 1007 | layer.remove() 1008 | }) 1009 | 1010 | it('should return whole shape in case of a multi polygon with hole', function () { 1011 | const latlngs = [ 1012 | [ 1013 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 1014 | [p2ll(120, 160), p2ll(150, 170), p2ll(180, 120)], 1015 | ], 1016 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 1017 | ] 1018 | const layer = L.polygon(latlngs).addTo(this.map) 1019 | const shape = layer.shapeAt(p2ll(140, 140)) 1020 | assert.equal(shape.length, 2) 1021 | assert.equal(shape[0].length, 3) 1022 | assert.equal(shape[0][0], latlngs[0][0][0]) 1023 | layer.remove() 1024 | }) 1025 | }) 1026 | 1027 | describe('#deleteShape', () => { 1028 | it('should emit editable:shape:delete before deleting the shape on flat polygon', function () { 1029 | const layer = L.polygon([p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]).addTo( 1030 | this.map 1031 | ) 1032 | let called = 0 1033 | const call = (e) => { 1034 | called++ 1035 | assert.equal(layer._latlngs[0].length, 3) // Not yet deleted 1036 | assert.equal(e.shape.length, 3) 1037 | } 1038 | this.map.on('editable:shape:delete', call) 1039 | layer.enableEdit() 1040 | assert.equal(called, 0) 1041 | layer.editor.deleteShape(layer._latlngs[0]) 1042 | assert.equal(layer._latlngs[0].length, 0) 1043 | assert.equal(called, 1) 1044 | this.map.off('editable:shape:delete', call) 1045 | layer.remove() 1046 | }) 1047 | 1048 | it('should emit editable:shape:delete before deleting the shape on multi', function () { 1049 | const latlngs = [ 1050 | [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]], 1051 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 1052 | ] 1053 | const layer = L.polygon(latlngs).addTo(this.map) 1054 | let called = 0 1055 | const call = (e) => { 1056 | called++ 1057 | assert.equal(layer._latlngs.length, 2) // Not yet deleted 1058 | assert.equal(e.shape.length, 1) 1059 | assert.equal(e.shape[0].length, 3) 1060 | } 1061 | this.map.on('editable:shape:delete', call) 1062 | layer.enableEdit() 1063 | assert.equal(called, 0) 1064 | layer.editor.deleteShape(layer._latlngs[0]) 1065 | assert.equal(called, 1) 1066 | assert.equal(layer._latlngs.length, 1) 1067 | assert.equal(layer._latlngs[0][0][0], latlngs[1][0][0]) 1068 | this.map.off('editable:shape:delete', call) 1069 | layer.remove() 1070 | }) 1071 | 1072 | it('editable:shape:delete should be cancellable on flat polygon', function () { 1073 | const layer = L.polygon([p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]).addTo( 1074 | this.map 1075 | ) 1076 | let called = 0 1077 | const call = (e) => { 1078 | called++ 1079 | e.cancel() 1080 | } 1081 | this.map.on('editable:shape:delete', call) 1082 | layer.enableEdit() 1083 | assert.equal(called, 0) 1084 | layer.editor.deleteShape(layer._latlngs) 1085 | assert.equal(called, 1) 1086 | assert.equal(layer._latlngs[0].length, 3) 1087 | this.map.off('editable:shape:delete', call) 1088 | layer.remove() 1089 | }) 1090 | 1091 | it('editable:shape:delete should be cancellable on multi polygon', function () { 1092 | const latlngs = [ 1093 | [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]], 1094 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 1095 | ] 1096 | const layer = L.polygon(latlngs).addTo(this.map) 1097 | let called = 0 1098 | const call = (e) => { 1099 | called++ 1100 | e.cancel() 1101 | } 1102 | this.map.on('editable:shape:delete', call) 1103 | layer.enableEdit() 1104 | assert.equal(called, 0) 1105 | layer.editor.deleteShape(layer._latlngs[0]) 1106 | assert.equal(called, 1) 1107 | assert.equal(layer._latlngs.length, 2) 1108 | assert.equal(layer._latlngs[0][0][0], latlngs[0][0][0]) 1109 | this.map.off('editable:shape:delete', call) 1110 | layer.remove() 1111 | }) 1112 | 1113 | it('should emit editable:shape:deleted after deleting the shape on flat polygon', function () { 1114 | const layer = L.polygon([p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]).addTo( 1115 | this.map 1116 | ) 1117 | let called = 0 1118 | const call = (e) => { 1119 | called++ 1120 | assert.equal(layer._latlngs[0].length, 0) // Already deleted 1121 | assert.equal(e.shape.length, 3) // Deleted elements 1122 | } 1123 | this.map.on('editable:shape:deleted', call) 1124 | layer.enableEdit() 1125 | assert.equal(called, 0) 1126 | layer.editor.deleteShape(layer._latlngs[0]) 1127 | assert.equal(called, 1) 1128 | assert.equal(layer._latlngs[0].length, 0) 1129 | this.map.off('editable:shape:deleted', call) 1130 | layer.remove() 1131 | }) 1132 | 1133 | it('should emit editable:shape:deleted after deleting the shape on multi', function () { 1134 | const latlngs = [ 1135 | [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]], 1136 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 1137 | ] 1138 | const layer = L.polygon(latlngs).addTo(this.map) 1139 | let called = 0 1140 | const call = (e) => { 1141 | called++ 1142 | assert.equal(layer._latlngs.length, 1) // Already deleted 1143 | assert.equal(e.shape.length, 1) // Deleted shape 1144 | assert.equal(e.shape[0].length, 3) 1145 | } 1146 | this.map.on('editable:shape:deleted', call) 1147 | layer.enableEdit() 1148 | assert.equal(called, 0) 1149 | layer.editor.deleteShape(layer._latlngs[0]) 1150 | assert.equal(called, 1) 1151 | assert.equal(layer._latlngs.length, 1) 1152 | assert.equal(layer._latlngs[0][0][0], latlngs[1][0][0]) 1153 | this.map.off('editable:shape:deleted', call) 1154 | layer.remove() 1155 | }) 1156 | 1157 | it('should return the deleted shape on flat polygon', function () { 1158 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 1159 | const layer = L.polygon(latlngs).addTo(this.map) 1160 | layer.enableEdit() 1161 | const deleted = layer.editor.deleteShape(layer._latlngs[0]) 1162 | assert.equal(layer._latlngs[0].length, 0) 1163 | assert.deepEqual(deleted, latlngs) 1164 | layer.remove() 1165 | }) 1166 | 1167 | it('should return the deleted shape on multi', function () { 1168 | const latlngs = [ 1169 | [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]], 1170 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 1171 | ] 1172 | const layer = L.polygon(latlngs).addTo(this.map) 1173 | layer.enableEdit() 1174 | const deleted = layer.editor.deleteShape(layer._latlngs[0]) 1175 | assert.equal(layer._latlngs.length, 1) 1176 | assert.equal(layer._latlngs[0].length, 1) 1177 | assert.deepEqual(deleted, latlngs[0]) 1178 | layer.remove() 1179 | }) 1180 | 1181 | it('should return the deleted shape on multi with hole', function () { 1182 | const latlngs = [ 1183 | [ 1184 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 1185 | [p2ll(120, 150), p2ll(150, 180), p2ll(180, 120)], 1186 | ], 1187 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 1188 | ] 1189 | const layer = L.polygon(latlngs).addTo(this.map) 1190 | layer.enableEdit() 1191 | const deleted = layer.editor.deleteShape(layer._latlngs[0]) 1192 | assert.deepEqual(deleted, latlngs[0]) 1193 | layer.remove() 1194 | }) 1195 | 1196 | it('should return the deleted shape on multi with hole 2', function () { 1197 | const latlngs = [ 1198 | [ 1199 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 1200 | [p2ll(120, 150), p2ll(150, 180), p2ll(180, 120)], 1201 | ], 1202 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 1203 | ] 1204 | const layer = L.polygon(latlngs).addTo(this.map) 1205 | layer.enableEdit() 1206 | const deleted = layer.editor.deleteShape(layer._latlngs[1]) 1207 | assert.deepEqual(deleted, latlngs[1]) 1208 | layer.remove() 1209 | }) 1210 | }) 1211 | 1212 | describe('#deleteShapeAt', () => { 1213 | it('should delete the shape on flat polygon', function () { 1214 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 1215 | const layer = L.polygon(latlngs).addTo(this.map) 1216 | layer.enableEdit() 1217 | const deleted = layer.editor.deleteShapeAt(p2ll(150, 150)) 1218 | assert.equal(layer._latlngs[0].length, 0) 1219 | assert.deepEqual([latlngs], deleted) 1220 | layer.remove() 1221 | }) 1222 | 1223 | it('should delete the shape on multi', function () { 1224 | const latlngs = [ 1225 | [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]], 1226 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 1227 | ] 1228 | const layer = L.polygon(latlngs).addTo(this.map) 1229 | layer.enableEdit() 1230 | const deleted = layer.editor.deleteShapeAt(p2ll(150, 150)) 1231 | assert.equal(layer._latlngs.length, 1) 1232 | assert.equal(layer._latlngs[0][0][0], latlngs[1][0][0]) 1233 | assert.deepEqual(latlngs[0], deleted) 1234 | layer.remove() 1235 | }) 1236 | 1237 | it('should delete the shape two on multi', function () { 1238 | const latlngs = [ 1239 | [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]], 1240 | [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]], 1241 | ] 1242 | const layer = L.polygon(latlngs).addTo(this.map) 1243 | layer.enableEdit() 1244 | const deleted = layer.editor.deleteShapeAt(p2ll(350, 350)) 1245 | assert.equal(layer._latlngs.length, 1) 1246 | assert.equal(layer._latlngs[0][0][0], latlngs[0][0][0]) 1247 | assert.deepEqual(latlngs[1], deleted) 1248 | layer.remove() 1249 | }) 1250 | 1251 | it('should delete the shape on multi with nested simple polygon', function () { 1252 | const latlngs = [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]] 1253 | const layer = L.polygon(latlngs).addTo(this.map) 1254 | layer.enableEdit() 1255 | const deleted = layer.editor.deleteShapeAt(p2ll(150, 150)) 1256 | assert.equal(layer._latlngs[0].length, 0) 1257 | assert.deepEqual(latlngs, deleted) 1258 | layer.remove() 1259 | }) 1260 | }) 1261 | }) 1262 | }) 1263 | -------------------------------------------------------------------------------- /test/PolylineEditor.js: -------------------------------------------------------------------------------- 1 | describe('L.PolylineEditor', () => { 2 | let p2ll 3 | 4 | before(function () { 5 | this.map = map 6 | p2ll = (x, y) => map.layerPointToLatLng([x, y]) 7 | }) 8 | 9 | describe('#startNewLine()', () => { 10 | let polyline 11 | 12 | after(() => { 13 | polyline.remove() 14 | }) 15 | 16 | it('should create feature and editor', function () { 17 | polyline = this.map.editTools.startPolyline() 18 | assert.ok(polyline) 19 | assert.ok(polyline.editor) 20 | assert.notOk(polyline._latlngs.length) 21 | }) 22 | 23 | it('should create first latlng on first click', () => { 24 | happen.drawingClick(100, 150) 25 | assert.equal(polyline._latlngs.length, 1) 26 | }) 27 | 28 | it('should not finish line on first point click', () => { 29 | happen.drawingClick(100, 150) 30 | assert.equal(polyline._latlngs.length, 1) 31 | assert(polyline.editor.drawing) 32 | }) 33 | 34 | it('should create more latlngs on more click', () => { 35 | happen.drawingClick(200, 350) 36 | assert.equal(polyline._latlngs.length, 2) 37 | happen.drawingClick(300, 250) 38 | assert.equal(polyline._latlngs.length, 3) 39 | }) 40 | 41 | it('should finish shape on last point click', () => { 42 | happen.drawingClick(300, 250) 43 | happen.at('click', 300, 250) 44 | assert.equal(polyline._latlngs.length, 3) 45 | }) 46 | 47 | it('should apply passed options to the polyline', function () { 48 | const className = 'my-class' 49 | const other = this.map.editTools.startPolyline(null, { 50 | className: className, 51 | }) 52 | assert.equal(other.options.className, className) 53 | other.disableEdit() 54 | }) 55 | 56 | describe('#dragVertex()', () => { 57 | it('should update latlng on vertex drag', (done) => { 58 | let called = 0 59 | const call = () => { 60 | called++ 61 | } 62 | polyline.on('editable:edited', call) 63 | const before = polyline._latlngs[1].lat 64 | happen.drag(200, 350, 220, 360, () => { 65 | assert.notEqual(before, polyline._latlngs[1].lat) 66 | assert.equal(called, 1) 67 | polyline.off('editable:edited', call) 68 | done() 69 | }) 70 | }) 71 | }) 72 | 73 | describe('#deleteVertex()', () => { 74 | it('should delete latlng on vertex click', () => { 75 | happen.drawingClick(300, 250) 76 | happen.at('click', 300, 250) 77 | assert.equal(polyline._latlngs.length, 2) 78 | }) 79 | }) 80 | 81 | describe('#continueForward()', () => { 82 | it('should add new latlng on map click', () => { 83 | polyline.editor.continueForward() 84 | happen.drawingClick(400, 400) 85 | assert.equal(polyline._latlngs.length, 3) 86 | happen.at('click', 400, 400) // Finish shape 87 | happen.at('click', 450, 450) // Click elsewhere on the map 88 | assert.equal(polyline._latlngs.length, 3) 89 | }) 90 | }) 91 | 92 | describe('#continueBackward()', () => { 93 | it('should add new latlng on map click', () => { 94 | polyline.editor.continueBackward() 95 | happen.drawingClick(400, 100) 96 | assert.equal(polyline._latlngs.length, 4) 97 | happen.at('click', 400, 100) // Finish shape 98 | happen.at('click', 450, 450) // Click elsewhere on the map 99 | assert.equal(polyline._latlngs.length, 4) 100 | }) 101 | }) 102 | 103 | describe('#dragMiddleMarker()', () => { 104 | it('should insert new latlng on middle marker click', (done) => { 105 | let called = 0 106 | const call = () => { 107 | called++ 108 | } 109 | const last = polyline._latlngs[3] 110 | const third = polyline._latlngs[2] 111 | const fromX = (400 + 220) / 2 112 | const fromY = (400 + 360) / 2 113 | polyline.on('editable:edited', call) 114 | happen.drag(fromX, fromY, 300, 440, () => { 115 | assert.equal(polyline._latlngs.length, 5) 116 | // New should have been inserted between third and last latlng, 117 | // so third and last should not have changed 118 | assert.equal(last, polyline._latlngs[4]) 119 | assert.equal(third, polyline._latlngs[2]) 120 | assert.equal(called, 1) 121 | polyline.off('editable:edited', call) 122 | done() 123 | }) 124 | }) 125 | }) 126 | 127 | describe('#removeVertex', () => { 128 | it('should remove vertex on click', () => { 129 | happen.at('click', 400, 400) 130 | assert.equal(polyline._latlngs.length, 4) 131 | happen.at('click', 100, 150) 132 | assert.equal(polyline._latlngs.length, 3) 133 | happen.at('click', 400, 100) 134 | assert.equal(polyline._latlngs.length, 2) 135 | }) 136 | 137 | it('should not remove last two vertex', () => { 138 | happen.at('click', 220, 360) 139 | assert.equal(polyline._latlngs.length, 2) 140 | happen.at('click', 300, 440) 141 | assert.equal(polyline._latlngs.length, 2) 142 | }) 143 | }) 144 | }) 145 | 146 | describe('#disableEdit()', () => { 147 | afterEach(function () { 148 | this.map.editTools.editLayer.eachLayer((layer) => { 149 | assert.fail(layer, null, 'no layer expected but one found') 150 | }) 151 | }) 152 | 153 | it('should stop editing on disableEdit() call', function () { 154 | const layer = this.map.editTools.startPolyline() 155 | layer.disableEdit() 156 | assert.notOk(layer.editor) 157 | }) 158 | 159 | it('should be reenabled after remove if active', function () { 160 | const layer = L.polyline([]).addTo(this.map) 161 | layer.enableEdit() 162 | this.map.removeLayer(layer) 163 | assert.notOk(layer.editEnabled()) 164 | this.map.addLayer(layer) 165 | assert.ok(layer.editEnabled()) 166 | layer.remove() 167 | }) 168 | 169 | it('should not be reenabled after remove if not active', function () { 170 | const layer = L.polyline([]).addTo(this.map) 171 | layer.enableEdit() 172 | layer.disableEdit() 173 | this.map.removeLayer(layer) 174 | assert.notOk(layer.editEnabled()) 175 | this.map.addLayer(layer) 176 | assert.notOk(layer.editEnabled()) 177 | }) 178 | }) 179 | 180 | describe('#enable()', () => { 181 | afterEach(function () { 182 | this.map.editTools.editLayer.eachLayer((layer) => { 183 | assert.fail(layer, null, 'no layer expected but one found') 184 | }) 185 | }) 186 | 187 | it('should start editing on enableEdit() call', function () { 188 | const layer = L.polyline([]).addTo(this.map) 189 | layer.enableEdit() 190 | assert.ok(layer.editEnabled()) 191 | layer.remove() 192 | }) 193 | 194 | it('should not reset editor when calling enableEdit() twice', function () { 195 | const layer = L.polyline([]).addTo(this.map) 196 | layer.enableEdit() 197 | const editor = layer.editor 198 | layer.enableEdit() 199 | assert.equal(editor, layer.editor) 200 | layer.remove() 201 | }) 202 | }) 203 | 204 | describe('#onRemove', () => { 205 | it('should remove every edit related layer on remove', function () { 206 | this.map.editTools.editLayer.eachLayer((layer) => { 207 | assert.fail(layer, null, 'no layer expected but one found before') 208 | }) 209 | const layer = L.polyline([p2ll(100, 150), p2ll(150, 200)]).addTo(this.map) 210 | layer.enableEdit() 211 | layer.remove() 212 | this.map.editTools.editLayer.eachLayer((layer) => { 213 | assert.fail(layer, null, 'no layer expected but one found after') 214 | }) 215 | }) 216 | }) 217 | 218 | describe('#drawing', () => { 219 | it('should return false if no drawing happen', function () { 220 | const layer = L.polyline([p2ll(100, 150), p2ll(150, 200)]).addTo(this.map) 221 | layer.enableEdit() 222 | assert.notOk(layer.editor.drawing()) 223 | layer.remove() 224 | }) 225 | 226 | it('should return true if an editor is active and drawing forward', function () { 227 | const layer = L.polyline([p2ll(100, 150), p2ll(150, 200)]).addTo(this.map) 228 | layer.enableEdit() 229 | layer.editor.continueForward() 230 | assert.ok(layer.editor.drawing()) 231 | layer.remove() 232 | }) 233 | 234 | it('should return true if an editor is active and drawing backward', function () { 235 | const layer = L.polyline([p2ll(100, 150), p2ll(150, 200)]).addTo(this.map) 236 | layer.enableEdit() 237 | layer.editor.continueBackward() 238 | assert.ok(layer.editor.drawing()) 239 | layer.remove() 240 | }) 241 | }) 242 | 243 | describe('#continue forward', () => { 244 | it('should attach forward line guide if points were drawn', function () { 245 | const layer = L.polyline([p2ll(100, 150), p2ll(150, 200)]).addTo(this.map) 246 | layer.enableEdit() 247 | layer.editor.continueForward() 248 | assert.equal( 249 | this.map.editTools.editLayer.hasLayer(this.map.editTools.forwardLineGuide), 250 | true, 251 | 'forward line guide is attached' 252 | ) 253 | layer.remove() 254 | }) 255 | 256 | it('should not attach forward line guide if no points were drawn', function () { 257 | const layer = L.polyline([]).addTo(this.map) 258 | layer.enableEdit() 259 | layer.editor.continueForward() 260 | assert.equal( 261 | this.map.editTools.editLayer.hasLayer(this.map.editTools.forwardLineGuide), 262 | false, 263 | 'forward line guide is not attached' 264 | ) 265 | layer.remove() 266 | }) 267 | }) 268 | 269 | describe('#continue backward', () => { 270 | it('should attach backward line guide if points were drawn', function () { 271 | const layer = L.polyline([p2ll(100, 150), p2ll(150, 200)]).addTo(this.map) 272 | layer.enableEdit() 273 | layer.editor.continueBackward() 274 | assert.equal( 275 | this.map.editTools.editLayer.hasLayer(this.map.editTools.backwardLineGuide), 276 | true, 277 | 'backward line guide is attached' 278 | ) 279 | layer.remove() 280 | }) 281 | 282 | it('should not attach backward line guide if no points were drawn', function () { 283 | const layer = L.polyline([]).addTo(this.map) 284 | layer.enableEdit() 285 | layer.editor.continueBackward() 286 | assert.equal( 287 | this.map.editTools.editLayer.hasLayer(this.map.editTools.backwardLineGuide), 288 | false, 289 | 'backward line guide is not attached' 290 | ) 291 | layer.remove() 292 | }) 293 | }) 294 | 295 | describe('#pop', () => { 296 | it('should remove last latlng when drawing forward', function () { 297 | const layer = this.map.editTools.startPolyline() 298 | happen.drawingClick(450, 450) 299 | happen.drawingClick(500, 500) 300 | assert.equal(layer._latlngs.length, 2) 301 | const last = layer._latlngs[1] 302 | assert.include(layer._latlngs, last) 303 | const latlng = layer.editor.pop() 304 | assert.equal(latlng.lat, last.lat) 305 | assert.ok(latlng) 306 | assert.equal(layer._latlngs.length, 1) 307 | assert.notInclude(layer._latlngs, last) 308 | this.map.removeLayer(layer) 309 | }) 310 | 311 | it('should remove first latlng when drawing backward', function () { 312 | const layer = L.polyline([p2ll(100, 150), p2ll(150, 200)]).addTo(this.map) 313 | layer.enableEdit() 314 | layer.editor.continueBackward() 315 | happen.drawingClick(450, 450) 316 | assert.equal(layer._latlngs.length, 3) 317 | const first = layer._latlngs[0] 318 | assert.include(layer._latlngs, first) 319 | const latlng = layer.editor.pop() 320 | assert.equal(latlng.lat, first.lat) 321 | assert.ok(latlng) 322 | assert.equal(layer._latlngs.length, 2) 323 | assert.notInclude(layer._latlngs, first) 324 | this.map.removeLayer(layer) 325 | }) 326 | }) 327 | 328 | describe('#push', () => { 329 | it('should add a latlng at the end when drawing forward', function () { 330 | const layer = this.map.editTools.startPolyline() 331 | happen.drawingClick(450, 450) 332 | happen.drawingClick(500, 500) 333 | assert.equal(layer._latlngs.length, 2) 334 | const latlng = p2ll(100, 150) 335 | layer.editor.push(latlng) 336 | assert.include(layer._latlngs, latlng) 337 | const last = layer._latlngs[2] 338 | assert.equal(latlng.lat, last.lat) 339 | assert.equal(layer._latlngs.length, 3) 340 | this.map.removeLayer(layer) 341 | }) 342 | 343 | it('should add latlng on the beginning when drawing backward', function () { 344 | const layer = L.polyline([p2ll(100, 150), p2ll(150, 200)]).addTo(this.map) 345 | layer.enableEdit() 346 | layer.editor.continueBackward() 347 | const latlng = p2ll(150, 100) 348 | layer.editor.push(latlng) 349 | assert.equal(layer._latlngs.length, 3) 350 | const first = layer._latlngs[0] 351 | assert.include(layer._latlngs, latlng) 352 | assert.equal(latlng.lat, first.lat) 353 | this.map.removeLayer(layer) 354 | }) 355 | }) 356 | 357 | describe('#endDrawing', () => { 358 | afterEach(function () { 359 | this.map.editTools.editLayer.eachLayer((layer) => { 360 | assert.fail(layer, null, 'no layer expected but one found') 361 | }) 362 | }) 363 | 364 | it('should remove shape if not enough latlngs', function () { 365 | const layer = this.map.editTools.startPolyline() 366 | happen.drawingClick(450, 450) 367 | assert.equal(layer._latlngs.length, 1) 368 | layer.editor.cancelDrawing() 369 | assert.equal(layer._latlngs.length, 0) 370 | layer.remove() 371 | }) 372 | 373 | it('should remove shape if not enough latlngs (multi)', function () { 374 | const latlngs = [ 375 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 376 | [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)], 377 | ] 378 | const layer = L.polyline(latlngs).addTo(this.map) 379 | layer.enableEdit() 380 | assert.equal(layer._latlngs.length, 2) 381 | layer.editor.newShape() 382 | happen.drawingClick(400, 400) 383 | assert.equal(layer._latlngs.length, 3) 384 | layer.editor.cancelDrawing() 385 | assert.equal(layer._latlngs.length, 2) 386 | layer.remove() 387 | }) 388 | 389 | it('should not remove shape if enough latlngs (multi)', function () { 390 | const latlngs = [ 391 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 392 | [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)], 393 | ] 394 | const layer = L.polyline(latlngs).addTo(this.map) 395 | layer.enableEdit() 396 | layer._latlngs[1][2].__vertex.continue() 397 | happen.drawingClick(400, 400) 398 | layer.editor.cancelDrawing() 399 | assert.equal(layer._latlngs[1].length, 4) 400 | layer.remove() 401 | }) 402 | }) 403 | 404 | describe('#enableDragging()', () => { 405 | it('should drag a polyline', function (done) { 406 | const latlngs = [p2ll(100, 100), p2ll(100, 200)] 407 | const layer = L.polyline(latlngs).addTo(this.map) 408 | const before = layer._latlngs[1].lat 409 | layer.enableEdit() 410 | assert.equal(before, layer._latlngs[1].lat) 411 | happen.drag(100, 130, 120, 150, () => { 412 | assert.notEqual(before, layer._latlngs[1].lat) 413 | layer.remove() 414 | done() 415 | }) 416 | }) 417 | 418 | it('should drag a multipolyline', function (done) { 419 | const latlngs = [ 420 | [p2ll(100, 100), p2ll(100, 200)], 421 | [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)], 422 | ] 423 | const layer = L.polyline(latlngs).addTo(this.map) 424 | const before = layer._latlngs[1][2].lat 425 | layer.enableEdit() 426 | assert.equal(before, layer._latlngs[1][2].lat) 427 | happen.drag(100, 130, 120, 150, () => { 428 | assert.notEqual(before, layer._latlngs[1][2].lat) 429 | layer.remove() 430 | done() 431 | }) 432 | }) 433 | 434 | it('should send editable:dragstart event', function (done) { 435 | const latlngs = [p2ll(100, 100), p2ll(100, 200)] 436 | const layer = L.polyline(latlngs).addTo(this.map) 437 | let called = 0 438 | const call = () => { 439 | called++ 440 | } 441 | layer.on('editable:dragstart', call) 442 | layer.enableEdit() 443 | assert.equal(called, 0) 444 | happen.drag(100, 130, 120, 150, () => { 445 | assert.equal(called, 1) 446 | layer.remove() 447 | done() 448 | }) 449 | }) 450 | 451 | it('should send editable:dragend event', function (done) { 452 | const latlngs = [p2ll(100, 100), p2ll(100, 200)] 453 | const layer = L.polyline(latlngs).addTo(this.map) 454 | let called = 0 455 | const call = () => { 456 | called++ 457 | } 458 | layer.on('editable:dragend', call) 459 | layer.enableEdit() 460 | assert.equal(called, 0) 461 | happen.drag(100, 130, 120, 150, () => { 462 | assert.equal(called, 1) 463 | layer.remove() 464 | done() 465 | }) 466 | }) 467 | 468 | it('should send editable:edited event after drag', function (done) { 469 | const latlngs = [p2ll(100, 100), p2ll(100, 200)] 470 | const layer = L.polyline(latlngs).addTo(this.map) 471 | let called = 0 472 | const call = () => { 473 | called++ 474 | } 475 | layer.on('editable:edited', call) 476 | layer.enableEdit() 477 | assert.equal(called, 0) 478 | happen.drag(100, 130, 120, 150, () => { 479 | assert.equal(called, 1) 480 | layer.remove() 481 | done() 482 | }) 483 | }) 484 | 485 | it('should send editable:drag event', function (done) { 486 | const latlngs = [p2ll(100, 100), p2ll(100, 200)] 487 | const layer = L.polyline(latlngs).addTo(this.map) 488 | let called = 0 489 | const call = () => { 490 | called++ 491 | } 492 | layer.on('editable:drag', call) 493 | layer.enableEdit() 494 | assert.notOk(called) 495 | happen.drag(100, 130, 120, 150, () => { 496 | assert.ok(called) 497 | layer.remove() 498 | done() 499 | }) 500 | }) 501 | }) 502 | 503 | describe('#events', () => { 504 | afterEach(function () { 505 | this.map.editTools.editLayer.eachLayer((layer) => { 506 | assert.fail(layer, null, 'no layer expected but one found') 507 | }) 508 | }) 509 | 510 | it('should fire editable:drawing:start on startPolyline call', function () { 511 | let called = 0 512 | const call = () => { 513 | called++ 514 | } 515 | this.map.on('editable:drawing:start', call) 516 | const other = this.map.editTools.startPolyline() 517 | assert.equal(called, 1) 518 | this.map.off('editable:drawing:start', call) 519 | other.editor.disable() // Not added to the map, just disable ongoing drawing. 520 | assert.notOk(this.map.editTools._drawingEditor) 521 | }) 522 | 523 | it('should fire editable:drawing:click on click', function () { 524 | let called = 0 525 | const call = () => { 526 | called++ 527 | } 528 | this.map.on('editable:drawing:click', call) 529 | const layer = this.map.editTools.startPolyline() 530 | assert.equal(called, 0) 531 | happen.drawingClick(450, 450) 532 | assert.equal(called, 1) 533 | happen.drawingClick(500, 500) 534 | assert.equal(called, 2) 535 | this.map.off('editable:drawing:click', call) 536 | layer.remove() 537 | assert.equal(called, 2) 538 | }) 539 | 540 | it('should fire editable:vertex:clicked before end/commit on last click', function () { 541 | let first = null 542 | let second = 0 543 | let last 544 | const setFirst = (e) => { 545 | if (first === null) first = e.type 546 | } 547 | const setSecond = () => { 548 | second++ 549 | } 550 | const setLast = (e) => { 551 | last = e.type 552 | } 553 | this.map.on('editable:drawing:end', setFirst) 554 | this.map.on('editable:drawing:commit', setFirst) 555 | this.map.on('editable:drawing:end', setLast) 556 | this.map.on('editable:drawing:commit', setLast) 557 | this.map.on('editable:drawing:commit', setSecond) 558 | const layer = this.map.editTools.startPolyline() 559 | happen.drawingClick(450, 450) 560 | happen.drawingClick(400, 400) 561 | assert.notOk(first) 562 | assert.notOk(last) 563 | this.map.on('editable:vertex:clicked', setFirst) 564 | this.map.on('editable:vertex:clicked', setLast) 565 | assert.notOk(first) 566 | assert.notOk(last) 567 | assert.notOk(second) 568 | happen.at('click', 400, 400) 569 | assert.equal(first, 'editable:vertex:clicked') 570 | assert.equal(last, 'editable:drawing:end') 571 | assert.equal(second, 1) // commit has been called 572 | this.map.off('editable:drawing:end', setFirst) 573 | this.map.off('editable:drawing:commit', setFirst) 574 | this.map.off('editable:drawing:end', setLast) 575 | this.map.off('editable:drawing:commit', setLast) 576 | this.map.off('editable:vertex:clicked', setFirst) 577 | this.map.off('editable:vertex:clicked', setLast) 578 | layer.remove() 579 | }) 580 | 581 | it('should fire editable:vertex:new ', function () { 582 | let newCount = 0 583 | const gotNew = (e) => { 584 | newCount++ 585 | } 586 | this.map.on('editable:vertex:new', gotNew) 587 | const layer = this.map.editTools.startPolyline() 588 | happen.drawingClick(450, 450) 589 | happen.drawingClick(400, 400) 590 | assert.equal(newCount, 2) 591 | this.map.off('editable:vertex:new', gotNew) 592 | layer.remove() 593 | }) 594 | 595 | it('should fire editable:vertex:new on middle marker click', function (done) { 596 | let newCount = 0 597 | const gotNew = (e) => { 598 | newCount++ 599 | } 600 | const layer = this.map.editTools.startPolyline() 601 | happen.drawingClick(500, 500) 602 | happen.drawingClick(400, 400) 603 | assert.equal(newCount, 0) 604 | this.map.on('editable:vertex:new', gotNew) 605 | happen.drag(450, 450, 300, 400, () => { 606 | assert.equal(newCount, 1) 607 | map.off('editable:vertex:new', gotNew) 608 | layer.remove() 609 | done() 610 | }) 611 | }) 612 | 613 | it('should not trigger editable:vertex:new when enabling edition', function () { 614 | let newCount = 0 615 | const gotNew = (e) => { 616 | newCount++ 617 | } 618 | this.map.on('editable:vertex:new', gotNew) 619 | const layer = L.polyline([p2ll(100, 150), p2ll(150, 200)]).addTo(this.map) 620 | layer.enableEdit() 621 | layer.editor.continueForward() 622 | happen.drawingClick(400, 400) 623 | assert.equal(newCount, 1) 624 | map.off('editable:vertex:new', gotNew) 625 | layer.remove() 626 | }) 627 | 628 | it('should fire editable:drawing:mouseover after hovering over vertex', function () { 629 | const layer = L.polyline([p2ll(100, 100), p2ll(150, 150)]).addTo(this.map) 630 | let called = 0 631 | const call = () => { 632 | called++ 633 | } 634 | this.map.on('editable:vertex:mouseover', call) 635 | layer.enableEdit() 636 | assert.equal(called, 0) 637 | happen.at('mouseover', 100, 100) 638 | assert.ok(called) 639 | this.map.off('editable:vertex:mouseover', call) 640 | layer.remove() 641 | }) 642 | 643 | it('should fire editable:drawing:mouseout after hovering out of a vertex', function () { 644 | const layer = L.polyline([p2ll(100, 100), p2ll(150, 150)]).addTo(this.map) 645 | let called = 0 646 | const call = () => { 647 | called++ 648 | } 649 | this.map.on('editable:vertex:mouseout', call) 650 | layer.enableEdit() 651 | assert.equal(called, 0) 652 | happen.at('mouseout', 100, 100) 653 | assert.ok(called) 654 | this.map.off('editable:vertex:mouseout', call) 655 | layer.remove() 656 | }) 657 | 658 | it('should send editable:drawing:click before adding vertex', function () { 659 | let called = 0 660 | let calledWhenEmpty = 0 661 | const call = () => { 662 | called++ 663 | if (!line._latlngs.length) calledWhenEmpty = 1 664 | } 665 | this.map.on('editable:drawing:click', call) 666 | const line = this.map.editTools.startPolyline() 667 | assert.equal(called, 0) 668 | happen.drawingClick(250, 250) 669 | assert.equal(called, 1) 670 | assert.ok(calledWhenEmpty) 671 | assert.ok(line._latlngs.length) 672 | this.map.off('editable:drawing:click', call) 673 | line.remove() 674 | }) 675 | 676 | it('should send editable:drawing:clicked after adding vertex', function () { 677 | let called = 0 678 | let calledAfterClick = 0 679 | const call = () => { 680 | called++ 681 | if (line._latlngs.length) calledAfterClick = 1 682 | } 683 | this.map.on('editable:drawing:clicked', call) 684 | const line = this.map.editTools.startPolyline() 685 | assert.equal(called, 0) 686 | happen.drawingClick(250, 250) 687 | assert.equal(called, 1) 688 | assert.ok(calledAfterClick) 689 | assert.ok(line._latlngs.length) 690 | this.map.off('editable:drawing:clicked', call) 691 | line.remove() 692 | }) 693 | 694 | it('should be possible to cancel editable:drawing:click actions', function () { 695 | let called = 0 696 | const call = (e) => { 697 | e.cancel() 698 | called++ 699 | } 700 | this.map.on('editable:drawing:click', call) 701 | const layer = this.map.editTools.startPolyline() 702 | assert.equal(called, 0) 703 | happen.drawingClick(250, 250) 704 | assert.equal(called, 1) 705 | assert.notOk(layer._latlngs.length) 706 | this.map.off('editable:drawing:click', call) 707 | layer.editor.disable() 708 | }) 709 | 710 | it('should send editable:drawing:move while drawing', function () { 711 | let called = 0 712 | const call = () => { 713 | called++ 714 | } 715 | this.map.on('editable:drawing:move', call) 716 | const layer = this.map.editTools.startPolyline() 717 | assert.equal(called, 0) 718 | happen.at('mousemove', 250, 250) 719 | assert.equal(called, 1) 720 | this.map.off('editable:drawing:move', call) 721 | layer.editor.disable() 722 | }) 723 | 724 | it('should send editable:drawing:move when dragging vertex', function (done) { 725 | let called = 0 726 | const call = () => { 727 | called++ 728 | } 729 | this.map.on('editable:drawing:move', call) 730 | const layer = L.polyline([p2ll(100, 100), p2ll(150, 150)]).addTo(this.map) 731 | layer.enableEdit() 732 | assert.equal(called, 0) 733 | happen.drag(100, 100, 110, 110, () => { 734 | assert.ok(called > 0) 735 | map.off('editable:drawing:move', call) 736 | layer.remove() 737 | done() 738 | }) 739 | }) 740 | 741 | it('should send editable:editing after adding vertex', function () { 742 | let called = 0 743 | let calledAfterClick = 0 744 | const call = () => { 745 | called++ 746 | if (line._latlngs[0].__vertex) calledAfterClick = 1 747 | } 748 | this.map.on('editable:editing', call) 749 | const line = this.map.editTools.startPolyline() 750 | assert.equal(called, 0) 751 | happen.drawingClick(250, 250) 752 | assert.equal(called, 1) 753 | assert.ok(calledAfterClick) 754 | assert.ok(line._latlngs.length) 755 | assert.ok(this.map.hasLayer(line)) 756 | this.map.off('editable:editing', call) 757 | line.remove() 758 | }) 759 | }) 760 | 761 | describe('Multi', () => { 762 | describe('#enableEdit', () => { 763 | it('should create vertex and middle markers for each line', function () { 764 | const multi = L.polyline([ 765 | [ 766 | [43.1239, 1.244], 767 | [43.123, 1.253], 768 | ], 769 | [ 770 | [43.1269, 1.246], 771 | [43.126, 1.252], 772 | [43.1282, 1.255], 773 | ], 774 | ]).addTo(this.map) 775 | multi.enableEdit() 776 | assert.ok(multi._latlngs[0][0].__vertex) 777 | assert.ok(multi._latlngs[0][1].__vertex) 778 | assert.ok(multi._latlngs[0][1].__vertex.middleMarker) 779 | assert.ok(multi._latlngs[1][0].__vertex) 780 | assert.ok(multi._latlngs[1][1].__vertex) 781 | assert.ok(multi._latlngs[1][1].__vertex.middleMarker) 782 | multi.remove() 783 | this.map.editTools.editLayer.eachLayer((layer) => { 784 | assert.fail(layer, null, 'no layer expected but one found') 785 | }) 786 | }) 787 | }) 788 | 789 | describe('#formatShape', () => { 790 | let layer 791 | 792 | before(function () { 793 | layer = L.polyline([]).addTo(this.map) 794 | layer.enableEdit() 795 | }) 796 | 797 | after(() => { 798 | layer.remove() 799 | }) 800 | 801 | it('should not nest flat shape', () => { 802 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 803 | assert.deepEqual(layer.editor.formatShape(latlngs), latlngs) 804 | }) 805 | 806 | it('should not nest empty shape', () => { 807 | assert.deepEqual(layer.editor.formatShape([]), []) 808 | }) 809 | 810 | it('should unnest nested shape', () => { 811 | const latlngs = [[p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)]] 812 | assert.deepEqual(layer.editor.formatShape(latlngs), latlngs[0]) 813 | }) 814 | }) 815 | 816 | describe('#insertShape', () => { 817 | it('should insert flat shape on multi polyline', function () { 818 | const latlngs = [ 819 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 820 | [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)], 821 | ] 822 | const shape = [p2ll(400, 450), p2ll(450, 500), p2ll(500, 400)] 823 | const layer = L.polyline(latlngs).addTo(this.map) 824 | layer.enableEdit() 825 | layer.editor.insertShape(shape, 1) 826 | assert.equal(layer._latlngs.length, 3) 827 | assert.deepEqual(shape, layer._latlngs[1]) 828 | layer.remove() 829 | }) 830 | 831 | it('should add nested shape on multi polyline', function () { 832 | const latlngs = [ 833 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 834 | [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)], 835 | ] 836 | const shape = [[p2ll(400, 450), p2ll(450, 500), p2ll(500, 400)]] 837 | const layer = L.polyline(latlngs).addTo(this.map) 838 | layer.enableEdit() 839 | layer.editor.insertShape(shape, 1) 840 | assert.equal(layer._latlngs.length, 3) 841 | assert.deepEqual(shape[0], layer._latlngs[1]) 842 | layer.remove() 843 | }) 844 | }) 845 | 846 | describe('#appendShape', () => { 847 | it('should add flat shape on flat line', function () { 848 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 849 | const shape = [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)] 850 | const layer = L.polyline(latlngs).addTo(this.map) 851 | layer.enableEdit() 852 | layer.editor.appendShape(shape) 853 | layer.disableEdit() 854 | assert.equal(layer._latlngs.length, 2) 855 | assert.deepEqual(shape, layer._latlngs[1]) 856 | layer.remove() 857 | }) 858 | 859 | it('should add nested shape on flat line', function () { 860 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 861 | const shape = [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]] 862 | const layer = L.polyline(latlngs).addTo(this.map) 863 | layer.enableEdit() 864 | layer.editor.appendShape(shape) 865 | assert.equal(layer._latlngs.length, 2) 866 | assert.deepEqual(shape[0], layer._latlngs[1]) 867 | layer.remove() 868 | }) 869 | 870 | it('should add flat shape on multi polyline', function () { 871 | const latlngs = [ 872 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 873 | [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)], 874 | ] 875 | const shape = [p2ll(400, 450), p2ll(450, 500), p2ll(500, 400)] 876 | const layer = L.polyline(latlngs).addTo(this.map) 877 | layer.enableEdit() 878 | layer.editor.appendShape(shape) 879 | assert.equal(layer._latlngs.length, 3) 880 | assert.deepEqual(shape, layer._latlngs[2]) 881 | layer.remove() 882 | }) 883 | 884 | it('should add nested shape on multi polyline', function () { 885 | const latlngs = [ 886 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 887 | [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)], 888 | ] 889 | const shape = [[p2ll(400, 450), p2ll(450, 500), p2ll(500, 400)]] 890 | const layer = L.polyline(latlngs).addTo(this.map) 891 | layer.enableEdit() 892 | layer.editor.appendShape(shape) 893 | assert.equal(layer._latlngs.length, 3) 894 | assert.deepEqual(shape[0], layer._latlngs[2]) 895 | layer.remove() 896 | }) 897 | }) 898 | 899 | describe('#prependShape', () => { 900 | it('should add flat shape on flat line', function () { 901 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 902 | const shape = [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)] 903 | const layer = L.polyline(latlngs).addTo(this.map) 904 | layer.enableEdit() 905 | layer.editor.prependShape(shape) 906 | assert.equal(layer._latlngs.length, 2) 907 | assert.deepEqual(shape, layer._latlngs[0]) 908 | layer.remove() 909 | }) 910 | 911 | it('should add nested shape on flat line', function () { 912 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 913 | const shape = [[p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)]] 914 | const layer = L.polyline(latlngs).addTo(this.map) 915 | layer.enableEdit() 916 | layer.editor.prependShape(shape) 917 | assert.equal(layer._latlngs.length, 2) 918 | assert.deepEqual(shape[0], layer._latlngs[0]) 919 | layer.remove() 920 | }) 921 | 922 | it('should add flat shape on multi polyline', function () { 923 | const latlngs = [ 924 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 925 | [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)], 926 | ] 927 | const shape = [p2ll(400, 450), p2ll(450, 500), p2ll(500, 400)] 928 | const layer = L.polyline(latlngs).addTo(this.map) 929 | layer.enableEdit() 930 | layer.editor.prependShape(shape) 931 | assert.equal(layer._latlngs.length, 3) 932 | assert.deepEqual(shape, layer._latlngs[0]) 933 | layer.remove() 934 | }) 935 | 936 | it('should add nested shape on multi polyline', function () { 937 | const latlngs = [ 938 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 939 | [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)], 940 | ] 941 | const shape = [[p2ll(400, 450), p2ll(450, 500), p2ll(500, 400)]] 942 | const layer = L.polyline(latlngs).addTo(this.map) 943 | layer.enableEdit() 944 | layer.editor.prependShape(shape) 945 | assert.equal(layer._latlngs.length, 3) 946 | assert.deepEqual(shape[0], layer._latlngs[0]) 947 | layer.remove() 948 | }) 949 | }) 950 | 951 | describe('#newShape', () => { 952 | it('should add a new shape on empty polyline', function () { 953 | const multi = L.polyline([]).addTo(this.map) 954 | multi.enableEdit() 955 | multi.editor.newShape() 956 | happen.drawingClick(100, 150) 957 | assert.equal(multi._latlngs.length, 1) 958 | happen.drawingClick(200, 350) 959 | assert.equal(multi._latlngs.length, 2) 960 | happen.drawingClick(300, 250) 961 | assert.equal(multi._latlngs.length, 3) 962 | happen.drawingClick(300, 250) 963 | multi.remove() 964 | }) 965 | 966 | it('should add a new outline to existing simple polyline', function () { 967 | const multi = L.polyline([p2ll(100, 150), p2ll(150, 200)]).addTo(this.map) 968 | multi.enableEdit() 969 | multi.editor.newShape() 970 | assert(L.Util.isArray(multi._latlngs[0])) 971 | assert.ok(multi._latlngs[0].length) 972 | assert.ok(L.Util.isArray(multi._latlngs[1])) 973 | assert.notOk(multi._latlngs[1].length) 974 | happen.drawingClick(300, 300) 975 | assert.equal(multi._latlngs[1].length, 1) 976 | happen.drawingClick(350, 350) 977 | assert.equal(multi._latlngs[1].length, 2) 978 | happen.at('click', 350, 350) 979 | multi.remove() 980 | }) 981 | 982 | it('should emit editable:shape:new on newShape call', function () { 983 | let called = 0 984 | const call = () => { 985 | called++ 986 | } 987 | this.map.on('editable:shape:new', call) 988 | const line = L.polyline([p2ll(100, 150), p2ll(150, 200)]).addTo(this.map) 989 | assert.equal(called, 0) 990 | line.enableEdit() 991 | assert.equal(called, 0) 992 | line.editor.newShape() 993 | assert.equal(called, 1) 994 | line.remove() 995 | }) 996 | }) 997 | 998 | describe('#shapeAt', () => { 999 | it('should return latlngs in case of a flat polyline', function () { 1000 | const latlngs = [p2ll(100, 100), p2ll(100, 200)] 1001 | const layer = L.polyline(latlngs).addTo(this.map) 1002 | const shape = layer.shapeAt(p2ll(100, 150)) 1003 | assert.equal(shape.length, 2) 1004 | assert.equal(shape[0], latlngs[0]) 1005 | layer.remove() 1006 | }) 1007 | 1008 | it('should return whole shape in case of a multi polyline', function () { 1009 | const latlngs = [ 1010 | [p2ll(100, 100), p2ll(100, 200)], 1011 | [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)], 1012 | ] 1013 | const layer = L.polyline(latlngs).addTo(this.map) 1014 | const shape = layer.shapeAt(p2ll(100, 150)) 1015 | assert.equal(shape.length, 2) 1016 | assert.equal(shape[0], latlngs[0][0]) 1017 | layer.remove() 1018 | }) 1019 | }) 1020 | 1021 | describe('#deleteShape', () => { 1022 | it('should emit editable:shape:delete before deleting the shape on flat polyline', function () { 1023 | const layer = L.polyline([ 1024 | p2ll(100, 150), 1025 | p2ll(150, 200), 1026 | p2ll(200, 100), 1027 | ]).addTo(this.map) 1028 | let called = 0 1029 | const call = (e) => { 1030 | called++ 1031 | assert.equal(layer._latlngs.length, 3) // Not yet deleted 1032 | assert.equal(e.shape.length, 3) 1033 | } 1034 | this.map.on('editable:shape:delete', call) 1035 | layer.enableEdit() 1036 | assert.equal(called, 0) 1037 | layer.editor.deleteShape(layer._latlngs) 1038 | assert.equal(layer._latlngs.length, 0) 1039 | assert.equal(called, 1) 1040 | this.map.off('editable:shape:delete', call) 1041 | layer.remove() 1042 | }) 1043 | 1044 | it('should emit editable:shape:delete before deleting the shape on multi', function () { 1045 | const latlngs = [ 1046 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 1047 | [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)], 1048 | ] 1049 | const layer = L.polyline(latlngs).addTo(this.map) 1050 | let called = 0 1051 | const call = (e) => { 1052 | called++ 1053 | assert.equal(layer._latlngs.length, 2) // Not yet deleted 1054 | assert.equal(e.shape.length, 3) 1055 | } 1056 | this.map.on('editable:shape:delete', call) 1057 | layer.enableEdit() 1058 | assert.equal(called, 0) 1059 | layer.editor.deleteShape(layer._latlngs[0]) 1060 | assert.equal(called, 1) 1061 | assert.equal(layer._latlngs.length, 1) 1062 | assert.equal(layer._latlngs[0][0], latlngs[1][0]) 1063 | this.map.off('editable:shape:delete', call) 1064 | layer.remove() 1065 | }) 1066 | 1067 | it('editable:shape:delete should be cancellable on flat polyline', function () { 1068 | const layer = L.polyline([ 1069 | p2ll(100, 150), 1070 | p2ll(150, 200), 1071 | p2ll(200, 100), 1072 | ]).addTo(this.map) 1073 | let called = 0 1074 | const call = (e) => { 1075 | called++ 1076 | e.cancel() 1077 | } 1078 | this.map.on('editable:shape:delete', call) 1079 | layer.enableEdit() 1080 | assert.equal(called, 0) 1081 | layer.editor.deleteShape(layer._latlngs) 1082 | assert.equal(called, 1) 1083 | assert.equal(layer._latlngs.length, 3) 1084 | this.map.off('editable:shape:delete', call) 1085 | layer.remove() 1086 | }) 1087 | 1088 | it('editable:shape:delete should be cancellable on multi polyline', function () { 1089 | const latlngs = [ 1090 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 1091 | [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)], 1092 | ] 1093 | const layer = L.polyline(latlngs).addTo(this.map) 1094 | let called = 0 1095 | const call = (e) => { 1096 | called++ 1097 | e.cancel() 1098 | } 1099 | this.map.on('editable:shape:delete', call) 1100 | layer.enableEdit() 1101 | assert.equal(called, 0) 1102 | layer.editor.deleteShape(layer._latlngs[0]) 1103 | assert.equal(called, 1) 1104 | assert.equal(layer._latlngs.length, 2) 1105 | assert.equal(layer._latlngs[0][0], latlngs[0][0]) 1106 | this.map.off('editable:shape:delete', call) 1107 | layer.remove() 1108 | }) 1109 | 1110 | it('should emit editable:shape:deleted after deleting the shape on flat polyline', function () { 1111 | const layer = L.polyline([ 1112 | p2ll(100, 150), 1113 | p2ll(150, 200), 1114 | p2ll(200, 100), 1115 | ]).addTo(this.map) 1116 | let called = 0 1117 | const call = (e) => { 1118 | called++ 1119 | assert.equal(layer._latlngs.length, 0) // Already deleted 1120 | assert.equal(e.shape.length, 3) // Deleted elements 1121 | } 1122 | this.map.on('editable:shape:deleted', call) 1123 | layer.enableEdit() 1124 | assert.equal(called, 0) 1125 | layer.editor.deleteShape(layer._latlngs) 1126 | assert.equal(called, 1) 1127 | assert.equal(layer._latlngs.length, 0) 1128 | this.map.off('editable:shape:deleted', call) 1129 | layer.remove() 1130 | }) 1131 | 1132 | it('should emit editable:edited after deleting the shape on flat polyline', function () { 1133 | const layer = L.polyline([ 1134 | p2ll(100, 150), 1135 | p2ll(150, 200), 1136 | p2ll(200, 100), 1137 | ]).addTo(this.map) 1138 | let called = 0 1139 | const call = () => { 1140 | called++ 1141 | } 1142 | this.map.on('editable:edited', call) 1143 | layer.enableEdit() 1144 | assert.equal(called, 0) 1145 | layer.editor.deleteShape(layer._latlngs) 1146 | assert.equal(called, 1) 1147 | assert.equal(layer._latlngs.length, 0) 1148 | this.map.off('editable:edited', call) 1149 | layer.remove() 1150 | }) 1151 | 1152 | it('should emit editable:shape:deleted after deleting the shape on multi', function () { 1153 | const latlngs = [ 1154 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 1155 | [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)], 1156 | ] 1157 | const layer = L.polyline(latlngs).addTo(this.map) 1158 | let called = 0 1159 | const call = (e) => { 1160 | called++ 1161 | assert.equal(layer._latlngs.length, 1) // Already deleted 1162 | assert.equal(e.shape.length, 3) // Deleted shape 1163 | } 1164 | this.map.on('editable:shape:deleted', call) 1165 | layer.enableEdit() 1166 | assert.equal(called, 0) 1167 | layer.editor.deleteShape(layer._latlngs[0]) 1168 | assert.equal(called, 1) 1169 | this.map.off('editable:shape:deleted', call) 1170 | layer.remove() 1171 | }) 1172 | 1173 | it('should emit editable:edited after deleting the shape on multi', function () { 1174 | const latlngs = [ 1175 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 1176 | [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)], 1177 | ] 1178 | const layer = L.polyline(latlngs).addTo(this.map) 1179 | let called = 0 1180 | const call = () => { 1181 | called++ 1182 | } 1183 | this.map.on('editable:edited', call) 1184 | layer.enableEdit() 1185 | assert.equal(called, 0) 1186 | layer.editor.deleteShape(layer._latlngs[0]) 1187 | assert.equal(called, 1) 1188 | this.map.off('editable:edited', call) 1189 | layer.remove() 1190 | }) 1191 | }) 1192 | 1193 | describe('#deleteShapeAt', () => { 1194 | it('should delete the shape on flat polyline', function () { 1195 | const layer = L.polyline([p2ll(100, 100), p2ll(100, 200)]).addTo(this.map) 1196 | layer.enableEdit() 1197 | layer.editor.deleteShapeAt(p2ll(100, 150)) 1198 | assert.equal(layer._latlngs.length, 0) 1199 | layer.remove() 1200 | }) 1201 | 1202 | it('should delete the shape on multi', function () { 1203 | const latlngs = [ 1204 | [p2ll(100, 100), p2ll(100, 200)], 1205 | [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)], 1206 | ] 1207 | const layer = L.polyline(latlngs).addTo(this.map) 1208 | layer.enableEdit() 1209 | layer.editor.deleteShapeAt(p2ll(100, 150)) 1210 | assert.equal(layer._latlngs.length, 1) 1211 | assert.equal(layer._latlngs[0][0], latlngs[1][0]) 1212 | layer.remove() 1213 | }) 1214 | }) 1215 | 1216 | describe('#splitShape', () => { 1217 | it('should split flat line at given index', function () { 1218 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 1219 | const layer = L.polyline(latlngs).addTo(this.map) 1220 | layer.enableEdit().splitShape(layer._latlngs, 1) 1221 | layer.disableEdit() 1222 | assert.deepEqual(layer._latlngs, [ 1223 | [p2ll(100, 150), p2ll(150, 200)], 1224 | [p2ll(150, 200), p2ll(200, 100)], 1225 | ]) 1226 | assert.notStrictEqual(layer._latlngs[0][1], layer._latlngs[1][0]) // LatLng has been cloned. 1227 | layer.remove() 1228 | }) 1229 | 1230 | it('should split multi line shape at given index', function () { 1231 | const latlngs = [ 1232 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 1233 | [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)], 1234 | ] 1235 | const layer = L.polyline(latlngs).addTo(this.map) 1236 | layer.enableEdit().splitShape(layer._latlngs[0], 1) 1237 | layer.disableEdit() 1238 | assert.deepEqual(layer._latlngs, [ 1239 | [p2ll(100, 150), p2ll(150, 200)], 1240 | [p2ll(150, 200), p2ll(200, 100)], 1241 | [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)], 1242 | ]) 1243 | layer.remove() 1244 | }) 1245 | 1246 | it('should not split if index is first', function () { 1247 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 1248 | const layer = L.polyline(latlngs).addTo(this.map) 1249 | layer.enableEdit().splitShape(layer._latlngs, 0) 1250 | layer.disableEdit() 1251 | assert.deepEqual(layer._latlngs, latlngs) 1252 | layer.remove() 1253 | }) 1254 | 1255 | it('should not split if index is last', function () { 1256 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 1257 | const layer = L.polyline(latlngs).addTo(this.map) 1258 | layer.enableEdit().splitShape(layer._latlngs, 2) 1259 | layer.disableEdit() 1260 | assert.deepEqual(layer._latlngs, latlngs) 1261 | layer.remove() 1262 | }) 1263 | 1264 | it('should not split if index gt than last', function () { 1265 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 1266 | const layer = L.polyline(latlngs).addTo(this.map) 1267 | layer.enableEdit().splitShape(layer._latlngs, 3) 1268 | layer.disableEdit() 1269 | assert.deepEqual(layer._latlngs, latlngs) 1270 | layer.remove() 1271 | }) 1272 | 1273 | it('should fire editable:editing', function () { 1274 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 1275 | const layer = L.polyline(latlngs).addTo(this.map) 1276 | let called = 0 1277 | const call = () => { 1278 | called++ 1279 | } 1280 | this.map.on('editable:editing', call) 1281 | layer.enableEdit().splitShape(layer._latlngs, 1) 1282 | assert.equal(called, 1) 1283 | layer.disableEdit() 1284 | assert.deepEqual(layer._latlngs, [ 1285 | [p2ll(100, 150), p2ll(150, 200)], 1286 | [p2ll(150, 200), p2ll(200, 100)], 1287 | ]) 1288 | this.map.off('editable:editing', call) 1289 | layer.remove() 1290 | }) 1291 | 1292 | it('should fire editable:edited', function () { 1293 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 1294 | const layer = L.polyline(latlngs).addTo(this.map) 1295 | let called = 0 1296 | const call = () => { 1297 | called++ 1298 | } 1299 | this.map.on('editable:edited', call) 1300 | layer.enableEdit().splitShape(layer._latlngs, 1) 1301 | assert.equal(called, 1) 1302 | layer.disableEdit() 1303 | assert.deepEqual(layer._latlngs, [ 1304 | [p2ll(100, 150), p2ll(150, 200)], 1305 | [p2ll(150, 200), p2ll(200, 100)], 1306 | ]) 1307 | this.map.off('editable:edited', call) 1308 | layer.remove() 1309 | }) 1310 | }) 1311 | }) 1312 | }) 1313 | -------------------------------------------------------------------------------- /test/RectangleEditor.js: -------------------------------------------------------------------------------- 1 | describe('L.RectangleEditor', () => { 2 | let p2ll 3 | 4 | before(function () { 5 | this.map = map 6 | p2ll = (x, y) => map.containerPointToLatLng([x, y]) 7 | }) 8 | 9 | describe('#startRectangle()', () => { 10 | it('should create rectangle and editor', function () { 11 | const layer = this.map.editTools.startRectangle() 12 | assert.ok(layer) 13 | assert.ok(layer.editor) 14 | assert.notOk(map.hasLayer(layer)) 15 | layer.editor.disable() 16 | layer.remove() 17 | }) 18 | 19 | it('should add rectangle to map at first click', function () { 20 | const layer = this.map.editTools.startRectangle() 21 | assert.notOk(map.hasLayer(layer)) 22 | happen.drawingClick(300, 300) 23 | assert.ok(map.hasLayer(layer)) 24 | layer.remove() 25 | }) 26 | 27 | it('should draw rectangle on click-drag', function (done) { 28 | const layer = this.map.editTools.startRectangle() 29 | happen.drag(200, 200, 240, 240, () => { 30 | expect(layer._latlngs[0][3].lat).not.to.be.eql(layer._latlngs[0][1].lat) 31 | expect(layer._latlngs[0][1]).to.be.nearLatLng(p2ll(200, 200)) 32 | expect(layer._latlngs[0][3]).to.be.nearLatLng(p2ll(240, 240)) 33 | layer.remove() 34 | done() 35 | }) 36 | }) 37 | 38 | it('should draw rectangle on click-drag reverse', function (done) { 39 | const layer = this.map.editTools.startRectangle() 40 | happen.drag(220, 220, 200, 200, () => { 41 | expect(layer._latlngs[0][3].lat).not.to.be.eql(layer._latlngs[0][1].lat) 42 | expect(layer._latlngs[0][1]).to.be.nearLatLng(p2ll(220, 220)) 43 | expect(layer._latlngs[0][3]).to.be.nearLatLng(p2ll(200, 200)) 44 | // if (!window.callPhantom) { 45 | // } 46 | layer.remove() 47 | done() 48 | }) 49 | }) 50 | }) 51 | 52 | describe('#enableEdit()', () => { 53 | it('should attach editor', function () { 54 | const layer = L.rectangle([p2ll(200, 200), p2ll(220, 220)]).addTo(this.map) 55 | layer.enableEdit() 56 | assert.ok(layer.editor) 57 | layer.remove() 58 | }) 59 | 60 | it('should update rectangle on south east corner drag', function (done) { 61 | const layer = L.rectangle([p2ll(200, 200), p2ll(220, 220)]).addTo(this.map) 62 | const before = layer._latlngs[0][3].lat 63 | layer.enableEdit() 64 | happen.drag(220, 220, 240, 240, () => { 65 | expect(layer._latlngs[0][3].lat).not.to.eql(before) 66 | if (!window.callPhantom) { 67 | expect(layer._latlngs[0][1]).to.be.nearLatLng(p2ll(200, 200)) // Untouched 68 | expect(layer._latlngs[0][3]).to.be.nearLatLng(p2ll(240, 240)) 69 | } 70 | layer.remove() 71 | done() 72 | }) 73 | }) 74 | 75 | it('should allow reverting rectangle', function (done) { 76 | const layer = L.rectangle([p2ll(200, 200), p2ll(220, 220)]).addTo(this.map) 77 | const before = layer._latlngs[0][3].lat 78 | layer.enableEdit() 79 | happen.drag(220, 220, 180, 180, () => { 80 | expect(layer._latlngs[0][3].lat).not.to.eql(before) 81 | if (!window.callPhantom) { 82 | expect(layer._latlngs[0][1]).to.be.nearLatLng(p2ll(200, 200)) // Untouched 83 | expect(layer._latlngs[0][3]).to.be.nearLatLng(p2ll(180, 180)) 84 | } 85 | layer.remove() 86 | done() 87 | }) 88 | }) 89 | }) 90 | 91 | describe('#disableEdit()', () => { 92 | it('should stop editing on disableEdit', function () { 93 | const layer = L.rectangle([p2ll(200, 200), p2ll(220, 220)]).addTo(this.map) 94 | layer.enableEdit() 95 | assert.ok(layer.editor) 96 | layer.disableEdit() 97 | assert.notOk(layer.editor) 98 | layer.remove() 99 | }) 100 | }) 101 | 102 | describe('#enableDragging()', () => { 103 | it('should drag a rectangle', function (done) { 104 | const latlngs = [p2ll(100, 100), p2ll(200, 200)] 105 | const layer = L.rectangle(latlngs).addTo(this.map) 106 | const before = layer._latlngs[0][1].lat 107 | layer.enableEdit() 108 | assert.equal(before, layer._latlngs[0][1].lat) 109 | happen.drag(100, 130, 120, 150, () => { 110 | assert.notEqual(before, layer._latlngs[0][1].lat) 111 | layer.remove() 112 | done() 113 | }) 114 | }) 115 | 116 | it('should send editable:dragstart event', function (done) { 117 | const latlngs = [p2ll(100, 100), p2ll(200, 200)] 118 | const layer = L.rectangle(latlngs).addTo(this.map) 119 | let called = 0 120 | const call = () => { 121 | called++ 122 | } 123 | layer.on('editable:dragstart', call) 124 | layer.enableEdit() 125 | assert.equal(called, 0) 126 | happen.drag(100, 130, 120, 150, () => { 127 | assert.equal(called, 1) 128 | layer.remove() 129 | done() 130 | }) 131 | }) 132 | 133 | it('should send editable:dragend event', function (done) { 134 | const latlngs = [p2ll(100, 100), p2ll(200, 200)] 135 | const layer = L.rectangle(latlngs).addTo(this.map) 136 | let called = 0 137 | const call = () => { 138 | called++ 139 | } 140 | layer.on('editable:dragend', call) 141 | layer.enableEdit() 142 | assert.equal(called, 0) 143 | happen.drag(100, 130, 120, 150, () => { 144 | assert.equal(called, 1) 145 | layer.remove() 146 | done() 147 | }) 148 | }) 149 | 150 | it('should send editable:drag event', function (done) { 151 | const latlngs = [p2ll(100, 100), p2ll(200, 200)] 152 | const layer = L.rectangle(latlngs).addTo(this.map) 153 | let called = 0 154 | const call = () => { 155 | called++ 156 | } 157 | layer.on('editable:drag', call) 158 | layer.enableEdit() 159 | assert.notOk(called) 160 | happen.drag(100, 130, 120, 150, () => { 161 | assert.ok(called) 162 | layer.remove() 163 | done() 164 | }) 165 | }) 166 | }) 167 | 168 | describe('#events', () => { 169 | it('should fire editable:drawing:start on startRectangle call', function () { 170 | let called = 0 171 | const call = () => { 172 | called++ 173 | } 174 | this.map.on('editable:drawing:start', call) 175 | const layer = this.map.editTools.startRectangle() 176 | assert.equal(called, 1) 177 | this.map.off('editable:drawing:start', call) 178 | layer.editor.disable() 179 | assert.notOk(this.map.editTools._drawingEditor) 180 | }) 181 | 182 | it('should fire editable:drawing:end on mouseup', function (done) { 183 | let called = 0 184 | const call = () => { 185 | called++ 186 | } 187 | this.map.on('editable:drawing:end', call) 188 | const layer = this.map.editTools.startRectangle() 189 | assert.equal(called, 0) 190 | happen.drag(200, 200, 220, 220, () => { 191 | assert.equal(called, 1) 192 | map.off('editable:drawing:end', call) 193 | layer.remove() 194 | assert.equal(called, 1) 195 | done() 196 | }) 197 | }) 198 | 199 | it('should fire editable:drawing:commit on mouseup', function (done) { 200 | let called = 0 201 | const call = () => { 202 | called++ 203 | } 204 | this.map.on('editable:drawing:commit', call) 205 | const layer = this.map.editTools.startRectangle() 206 | assert.equal(called, 0) 207 | happen.drag(200, 200, 220, 220, () => { 208 | assert.equal(called, 1) 209 | map.off('editable:drawing:commit', call) 210 | layer.remove() 211 | assert.equal(called, 1) 212 | done() 213 | }) 214 | }) 215 | 216 | it('should not fire editable:drawing:commit on mousedown', function () { 217 | let called = 0 218 | const call = () => { 219 | called++ 220 | } 221 | this.map.on('editable:drawing:commit', call) 222 | const layer = this.map.editTools.startRectangle() 223 | assert.equal(called, 0) 224 | happen.at('mousedown', 200, 200) 225 | assert.equal(called, 0) 226 | happen.at('mouseup', 200, 200) 227 | assert.equal(called, 1) 228 | this.map.off('editable:drawing:commit', call) 229 | layer.remove() 230 | }) 231 | 232 | it('should fire editable:drawing:end on stopDrawing', function () { 233 | let called = 0 234 | const call = () => { 235 | called++ 236 | } 237 | this.map.on('editable:drawing:end', call) 238 | const layer = this.map.editTools.startRectangle() 239 | this.map.editTools.stopDrawing() 240 | assert.equal(called, 1) 241 | this.map.off('editable:drawing:end', call) 242 | layer.remove() 243 | assert.equal(called, 1) 244 | }) 245 | 246 | it('should not fire editable:drawing:commit on stopDrawing', function () { 247 | let called = 0 248 | const call = () => { 249 | called++ 250 | } 251 | this.map.on('editable:drawing:commit', call) 252 | const layer = this.map.editTools.startRectangle() 253 | this.map.editTools.stopDrawing() 254 | assert.equal(called, 0) 255 | this.map.off('editable:drawing:commit', call) 256 | layer.remove() 257 | assert.equal(called, 0) 258 | }) 259 | 260 | it('should fire editable:drawing:move on mousemove while drawing', function () { 261 | let called = 0 262 | const call = () => { 263 | called++ 264 | } 265 | this.map.on('editable:drawing:move', call) 266 | const layer = this.map.editTools.startRectangle() 267 | assert.equal(called, 0) 268 | happen.at('mousemove', 450, 450) 269 | assert.equal(called, 1) 270 | happen.drawingClick(450, 450) 271 | this.map.off('editable:drawing:move', call) 272 | layer.remove() 273 | assert.equal(called, 1) 274 | }) 275 | 276 | it('should fire editable:drawing:move on mousemove while moving corner', function (done) { 277 | let called = 0 278 | const call = () => { 279 | called++ 280 | } 281 | const layer = L.rectangle([p2ll(200, 200), p2ll(220, 220)]).addTo(this.map) 282 | layer.enableEdit() 283 | assert.equal(called, 0) 284 | this.map.on('editable:drawing:move', call) 285 | happen.drag(200, 200, 220, 220, () => { 286 | assert.ok(called > 0) 287 | map.off('editable:drawing:move', call) 288 | layer.remove() 289 | done() 290 | }) 291 | }) 292 | }) 293 | }) 294 | -------------------------------------------------------------------------------- /test/VertexMarker.js: -------------------------------------------------------------------------------- 1 | describe('L.Editable.VertexMarker', () => { 2 | let polyline 3 | let p2ll 4 | 5 | before(function () { 6 | this.map = map 7 | p2ll = (x, y) => map.layerPointToLatLng([x, y]) 8 | }) 9 | 10 | afterEach(function () { 11 | this.map.editTools.editLayer.eachLayer((layer) => { 12 | assert.fail(layer, null, 'no layer expected but one found') 13 | }) 14 | }) 15 | 16 | describe('#init', () => { 17 | it('should only show visible vertex', function () { 18 | const latlngs = [p2ll(-10, -15), p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 19 | const layer = L.polyline(latlngs).addTo(this.map) 20 | layer.enableEdit() 21 | assert.notOk(latlngs[0].__vertex) 22 | assert.equal(qsa('.leaflet-vertex-icon').length, 3) 23 | map.zoomOut(1, {animate: false}) 24 | assert.ok(latlngs[0].__vertex) 25 | assert.equal(qsa('.leaflet-vertex-icon').length, 4) 26 | layer.remove() 27 | }) 28 | }) 29 | 30 | describe('#split', () => { 31 | it('should split line at its index', function () { 32 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 33 | const layer = L.polyline(latlngs).addTo(this.map) 34 | layer.enableEdit() 35 | layer._latlngs[1].__vertex.split() 36 | layer.disableEdit() 37 | assert.deepEqual(layer._latlngs, [ 38 | [p2ll(100, 150), p2ll(150, 200)], 39 | [p2ll(150, 200), p2ll(200, 100)], 40 | ]) 41 | layer.remove() 42 | }) 43 | }) 44 | 45 | describe('#delete', () => { 46 | it('should delete vertex', function () { 47 | let called = 0 48 | const call = () => { 49 | called++ 50 | } 51 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 52 | const layer = L.polyline(latlngs).addTo(this.map) 53 | layer.on('editable:edited', call) 54 | layer.enableEdit() 55 | layer._latlngs[1].__vertex.delete() 56 | assert.equal(called, 1) 57 | layer.off('editable:edited', call) 58 | layer.disableEdit() 59 | assert.deepEqual(layer._latlngs, [p2ll(100, 150), p2ll(200, 100)]) 60 | layer.remove() 61 | }) 62 | }) 63 | 64 | describe('#continue', () => { 65 | it('should continue backward on first index', function () { 66 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 67 | const layer = L.polyline(latlngs).addTo(this.map) 68 | layer.enableEdit() 69 | layer._latlngs[0].__vertex.continue() 70 | happen.drawingClick(200, 200) 71 | assert.equal(layer._latlngs.length, 4) 72 | happen.at('click', 200, 200) // Finish shape 73 | happen.at('click', 250, 250) // Click elsewhere on the map 74 | assert.equal(layer._latlngs.length, 4) 75 | layer.disableEdit() 76 | layer.remove() 77 | }) 78 | 79 | it('should continue backward on first index of multi', function () { 80 | const latlngs = [ 81 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 82 | [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)], 83 | ] 84 | const layer = L.polyline(latlngs).addTo(this.map) 85 | layer.enableEdit() 86 | layer._latlngs[0][0].__vertex.continue() 87 | happen.drawingClick(200, 200) 88 | assert.equal(layer._latlngs[0].length, 4) 89 | happen.at('click', 200, 200) // Finish shape 90 | happen.drawingClick(250, 250) // Drawing click elsewhere on the map 91 | assert.equal(layer._latlngs[0].length, 4) 92 | layer._latlngs[1][0].__vertex.continue() 93 | happen.drawingClick(400, 400) 94 | assert.equal(layer._latlngs[1].length, 4) 95 | happen.at('click', 400, 400) // Finish shape 96 | happen.drawingClick(450, 450) // Drawing click elsewhere on the map 97 | assert.equal(layer._latlngs[1].length, 4) 98 | layer.remove() 99 | }) 100 | 101 | it('should continue forward on last index', function () { 102 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 103 | const layer = L.polyline(latlngs).addTo(this.map) 104 | layer.enableEdit() 105 | layer._latlngs[2].__vertex.continue() 106 | happen.drawingClick(200, 200) 107 | assert.equal(layer._latlngs.length, 4) 108 | happen.at('click', 200, 200) // Finish shape 109 | happen.at('click', 250, 250) // Click elsewhere on the map 110 | assert.equal(layer._latlngs.length, 4) 111 | layer.disableEdit() 112 | layer.remove() 113 | }) 114 | 115 | it('should continue forward on first index of multi', function () { 116 | const latlngs = [ 117 | [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)], 118 | [p2ll(300, 350), p2ll(350, 400), p2ll(400, 300)], 119 | ] 120 | const layer = L.polyline(latlngs).addTo(this.map) 121 | layer.enableEdit() 122 | layer._latlngs[0][2].__vertex.continue() 123 | happen.drawingClick(200, 200) 124 | assert.equal(layer._latlngs[0].length, 4) 125 | happen.at('click', 200, 200) // Finish shape 126 | happen.drawingClick(250, 250) // Drawing click elsewhere on the map 127 | assert.equal(layer._latlngs[0].length, 4) 128 | layer._latlngs[1][2].__vertex.continue() 129 | happen.drawingClick(400, 400) 130 | assert.equal(layer._latlngs[1].length, 4) 131 | happen.at('click', 400, 400) // Finish shape 132 | happen.drawingClick(450, 450) // Drawing click elsewhere on the map 133 | assert.equal(layer._latlngs[1].length, 4) 134 | layer.remove() 135 | }) 136 | 137 | it('should do nothing if not first and not last index', function () { 138 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 139 | const layer = L.polyline(latlngs).addTo(this.map) 140 | layer.enableEdit() 141 | layer._latlngs[1].__vertex.continue() 142 | happen.at('mousemove', 200, 200) 143 | happen.at('click', 200, 200) 144 | assert.equal(layer._latlngs.length, 3) 145 | layer.disableEdit() 146 | layer.remove() 147 | }) 148 | 149 | it('should allow committing on clicked vertex', function () { 150 | const latlngs = [p2ll(100, 150), p2ll(150, 200), p2ll(200, 100)] 151 | const layer = L.polyline(latlngs).addTo(this.map) 152 | layer.enableEdit() 153 | layer._latlngs[0].__vertex.continue() 154 | happen.at('click', 100, 150) 155 | assert.equal(layer._latlngs.length, 3) 156 | assert.equal(layer.editor.drawing(), false) 157 | layer.disableEdit() 158 | layer.remove() 159 | }) 160 | }) 161 | }) 162 | -------------------------------------------------------------------------------- /test/_pre.js: -------------------------------------------------------------------------------- 1 | const qs = (selector) => document.querySelector(selector) 2 | const qsa = (selector) => document.querySelectorAll(selector) 3 | happen.at = function (what, x, y, props) { 4 | this.once( 5 | document.elementFromPoint(x, y), 6 | L.Util.extend( 7 | { 8 | type: what, 9 | clientX: x, 10 | clientY: y, 11 | screenX: x, 12 | screenY: y, 13 | which: 1, 14 | button: 0, 15 | }, 16 | props || {} 17 | ) 18 | ) 19 | } 20 | happen.drag = (fromX, fromY, toX, toY, then, delay) => { 21 | happen.at('mousemove', fromX, fromY) 22 | happen.at('mousedown', fromX, fromY) 23 | const stepX = fromX < toX ? 1 : -1 24 | const stepY = fromY < toY ? 1 : -1 25 | const moveX = () => { 26 | if (fromX !== toX) { 27 | happen.at('mousemove', (fromX += stepX), fromY) 28 | window.setTimeout(moveX, 1) 29 | } 30 | } 31 | moveX() 32 | const moveY = () => { 33 | if (fromY != toY) { 34 | happen.at('mousemove', fromX, (fromY += stepY)) 35 | window.setTimeout(moveY, 1) 36 | } 37 | } 38 | moveY() 39 | window.setTimeout(() => { 40 | happen.at('mouseup', toX, toY) 41 | happen.at('click', toX, toY) 42 | if (then) { 43 | then() 44 | } 45 | }, delay || 600) 46 | } 47 | happen.drawingClick = (x, y) => { 48 | happen.at('mousedown', x, y) 49 | happen.at('mouseup', x, y) 50 | } 51 | chai.Assertion.addMethod('nearLatLng', function (expected, delta) { 52 | delta = delta || 1e-4 53 | expect(this._obj.lat).to.be.within(expected.lat - delta, expected.lat + delta) 54 | expect(this._obj.lng).to.be.within(expected.lng - delta, expected.lng + delta) 55 | return this 56 | }) 57 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Leaflet.Editable Tests 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 51 | 52 | 53 |
54 |
55 | 70 | 71 | 72 | --------------------------------------------------------------------------------