diff --git a/Gruntfile.js b/Gruntfile.js
index ce5e68a92..c2aded3d5 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -3,99 +3,145 @@ module.exports = function(grunt) {
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
grunt.initConfig({
- pkg: grunt.file.readJSON('package.json'),
-
- jshint: {
- options: {
- node: true,
- browser: true,
- esnext: true,
- bitwise: true,
- curly: true,
- eqeqeq: true,
- immed: true,
- indent: 4,
- latedef: true,
- newcap: true,
- noarg: true,
- regexp: true,
- undef: true,
- unused: 'strict',
- trailing: true,
- smarttabs: true,
- globals: {
- L: false,
- $: false,
- LeafletToolbar: false,
- warpWebGl: false,
- EXIF: false,
- alert: false,
-
- // Mocha
-
- describe: false,
- it: false,
- before: false,
- after: false,
- beforeEach: false,
- afterEach: false,
- chai: false,
- expect: false,
- sinon: false
- }
- },
- source: {
- src: [ 'src/**/*.js', 'package.json' ]
- },
- grunt: {
- src: [ 'Gruntfile.js' ]
- }
+ pkg: grunt.file.readJSON("package.json"),
+
+ jshint: {
+ options: {
+ node: true,
+ browser: true,
+ esnext: true,
+ bitwise: true,
+ curly: true,
+ eqeqeq: true,
+ immed: true,
+ indent: 4,
+ latedef: true,
+ newcap: true,
+ noarg: true,
+ regexp: true,
+ undef: true,
+ unused: "strict",
+ trailing: true,
+ smarttabs: true,
+ globals: {
+ L: false,
+ $: false,
+ LeafletToolbar: false,
+ warpWebGl: false,
+ EXIF: false,
+ alert: false,
+
+ // Mocha
+
+ describe: false,
+ it: false,
+ before: false,
+ after: false,
+ beforeEach: false,
+ afterEach: false,
+ chai: false,
+ expect: false,
+ sinon: false
+ }
+ },
+ source: {
+ src: ["src/**/*.js", "package.json"]
},
+ grunt: {
+ src: ["Gruntfile.js"]
+ }
+ },
- karma: {
- development: {
- configFile: 'test/karma.conf.js',
- },
- test: {
- configFile: 'test/karma.conf.js',
+ karma: {
+ development: {
+ configFile: "test/karma.conf.js"
+ },
+ test: {
+ configFile: "test/karma.conf.js"
+ }
+ },
+
+ // Minify SVGs from svg directory, output to svg-min
+ svgmin: {
+ dist: {
+ files: [
+ {
+ attrs: "fill",
+ expand: true,
+ cwd: "assets/icons/svg",
+ src: ["*.svg"],
+ dest: "assets/icons/svg-min/",
+ ext: ".svg"
}
+ ]
},
+ options: {
+ plugins: [
+ { removeViewBox: false },
+ { removeEmptyAttrs: false },
+ { removeTitle: true } // addtitle will add it back in later
+ ]
+ }
+ },
- watch: {
- options : {
- livereload: true
+ svg_sprite: {
+ options: {
+ // Task-specific options go here.
+ },
+ dist: {
+ expand: true,
+ cwd: "assets/icons/svg-min/",
+ src: ["*.svg"],
+ dest: "assets/icons/",
+ options: {
+ log: "info",
+ shape: {
+ dimension: {
+ maxWidth: 18,
+ maxHeight: 18
+ }
},
- source: {
- files: [
- 'src/**/*.js',
- 'test/**/*.js',
- 'Gruntfile.js'
- ],
- tasks: [ 'build:js' ]
+ mode: {
+ symbol: {
+ sprite: "sprite.symbol.svg",
+ example: true
+ }
}
+ }
+ }
+ },
+
+ watch: {
+ options: {
+ livereload: true
},
+ source: {
+ files: ["src/**/*.js", "test/**/*.js", "Gruntfile.js"],
+ tasks: ["build:js"]
+ }
+ },
- concat: {
- dist: {
- src: [
- 'src/util/*.js',
- 'src/DistortableImageOverlay.js',
- 'src/DistortableCollection.js',
- 'src/edit/getEXIFdata.js',
- 'src/edit/EditHandle.js',
- 'src/edit/LockHandle.js',
- 'src/edit/DistortHandle.js',
- 'src/edit/RotateScaleHandle.js',
- 'src/edit/RotateHandle.js',
- 'src/edit/ScaleHandle.js',
- 'src/edit/DistortableImage.EditToolbar.js',
- 'src/edit/DistortableImage.Edit.js',
- 'src/edit/tools/DistortableImage.Keymapper.js',
- 'src/edit/BoxSelectHandle.js'
- ],
- dest: 'dist/leaflet.distortableimage.js',
- }
+ concat: {
+ dist: {
+ src: [
+ "src/util/*.js",
+ "src/DistortableImageOverlay.js",
+ "src/DistortableCollection.js",
+ "src/edit/getEXIFdata.js",
+ "src/edit/EditHandle.js",
+ "src/edit/LockHandle.js",
+ "src/edit/DistortHandle.js",
+ "src/edit/RotateScaleHandle.js",
+ "src/edit/RotateHandle.js",
+ "src/edit/ScaleHandle.js",
+ "src/edit/DistortableImage.EditToolbar.js",
+ "src/edit/DistortableImage.Edit.js",
+ "src/edit/tools/DistortableImage.Keymapper.js",
+ "src/edit/BoxSelectHandle.js"
+ ],
+ dest: "dist/leaflet.distortableimage.js"
}
+ }
});
/* Run tests once. */
@@ -107,6 +153,8 @@ module.exports = function(grunt) {
grunt.registerTask('build', [
'jshint',
'karma:development:start',
+ 'svgmin',
+ 'svg_sprite',
'coverage',
'concat:dist'
]);
diff --git a/README.md b/README.md
index 9dafde5f4..62d671fbd 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,8 @@ Here's a screenshot:
## Setup
1. From the root directory, run `npm install` or `sudo npm install`
-2. Open examples/index.html in a browser
+
+2. Open `examples/index.html` in a browser
## Demo
@@ -64,17 +65,16 @@ img = L.distortableImageOverlay(
L.latLng(51.50,-0.14),
L.latLng(51.50,-0.10)
],
- }
-).addTo(map);
+ }).addTo(map);
// enable editing
L.DomEvent.on(img._image, 'load', img.editing.enable, img.editing);
```
Options available to pass during `L.DistortableImageOverlay` initialization:
-- corners
+- [corners](#corners)
-- [selected](#selection)
+- [selected](#selected)
- [mode](#mode)
@@ -84,6 +84,33 @@ Options available to pass during `L.DistortableImageOverlay` initialization:
- [suppressToolbar](#Suppress-Toolbar)
+## Corners
+
+The corners are stored as `L.latLng` objects
+on the image, and can be accessed using our `getCorners()` method after the image is instantiated and added to the map.
+
+Useful usage example:
+
+```js
+// instantiate, add to map and enable
+img = L.distortableImageOverlay(...);
+img.addTo(map);
+L.DomEvent.on(img._image, 'load', img.editing.enable, img.editing);
+
+// grab the initial corner positions
+JSON.stringify(img.getCorners())
+=> "[{"lat":51.52,"lng":-0.14},{"lat":51.52,"lng":-0.1},{"lat":51.5,"lng":-0.14},{"lat":51.5,"lng":-0.1}]"
+
+// ...move the image around...
+
+// check the new corner positions.
+JSON.stringify(img.getCorners())
+=> "[{"lat":51.50685099607552,"lng":-0.06058305501937867},{"lat":51.50685099607552,"lng":-0.02058595418930054},{"lat":51.486652692081925,"lng":-0.06058305501937867},{"lat":51.486652692081925,"lng":-0.02058595418930054}]"
+
+// note there is an added level of precision after dragging the image for debugging purposes
+
+```
+We further added a `getCorner(idx)` method used the same way as its plural counterpart but with an index passed to it.
## Selected
@@ -97,16 +124,23 @@ Some developers prefer that an image initially appears as "selected" instead of
`mode` (*optional*, default: "distort", value: *string*)
-Each primary editing mode corresponds to a separate editing tool.
+Each primary editing mode corresponds to a separate editing handle.
-This option sets the image's initial editing mode, meaning the corresponding editing tool will always appear first when you interact with the image.
+This option sets the image's initial editing mode, meaning the corresponding editing handle will always appear first when you interact with the image.
Values available to pass to `mode` are:
-- "distort" (the default)
-- "lock"
-- "rotate"
-- "scale"
-- "rotateScale"
+
+- #### distort (_default_)
+
+- #### rotate
+
+- #### scale
+
+- #### rotateScale:
+
+- #### lock:
+
+ - mode which prevents any image actions (including those triggered from the toolbar, user gestures, and hotkeys) until the toolbar action [ToggleLock](#ToggleLock-(l )) is explicitly triggered (or its hotkey l )
In the below example, the image will be initialiazed with "rotateScale" handles:
@@ -158,8 +192,7 @@ img = L.distortableImageOverlay(
L.latLng(51.50,-0.10)
],
fullResolutionSrc: 'large.jpg'
- }
-).addTo(map);
+ }).addTo(map);
L.DomEvent.on(img._image, 'load', img.editing.enable, img.editing);
@@ -198,76 +231,67 @@ imageFeatureGroup.addLayer(img3);
```
-## Default Toolbar Actions
-
-- "ToggleLock"
+## Default Toolbar Actions (& Keybindings)
-- "ToggleOrder"
+#### ToggleLock (l )
-- "ToggleOutline"
+- Toggles between [lock mode](#lock) and [distort mode](#distort-(_default_))
-- "ToggleTransparency"
+#### ToggleRotateScale (r )
-- "ToggleRotateScale"
-- "EnableEXIF"
+#### ToggleOrder (j , k )
-- "Export"
+- For multiple images, switches overlapping images back and forth into view by employing [`bringToFront()`](https://leafletjs.com/reference-1.4.0.html#popup-bringtofront) and [`bringToBack()`](https://leafletjs.com/reference-1.4.0.html#popup-bringtoback) from the Leaflet API.
-- "Delete"
+#### ToggleOutline (o )
-## Addons
-
-- "ToggleRotate"
+#### ToggleTransparency (t )
-- "ToggleScale"
+#### EnableEXIF (WIP)
-### Image-ordering
+#### Restore
-For multiple images, the `ToggleOrder` action switches overlapping images back and forth into view by employing [`bringToFront()`](https://leafletjs.com/reference-1.4.0.html#popup-bringtofront) and [`bringToBack()`](https://leafletjs.com/reference-1.4.0.html#popup-bringtoback) from the Leaflet API.
+- Restores the image to its original proportions and scale, but keeps its current rotation angle and location intact.
+#### Export
-## Quick API Reference
+#### Delete (delete , backscpace )
-- [`getCorners()`](corners) and [`getCorner(idx)`](corners)
+- Permanently deletes the image from the map.
-- `getCenter()` - Calculates the centroid of the image
+## Addons
-## Corners
+- **ToggleRotate** (caps lock ):
-The corners are stored as `L.latLng` objects
-on the image, and can be accessed using our `getCorners()` method after the image is instantiated and added to the map.
+- **ToggleScale** (s ):
-Useful usage example:
+## Quick API Reference
-```js
-// instantiate, add to map and enable
-img = L.distortableImageOverlay(...);
-img.addTo(map);
-L.DomEvent.on(img._image, 'load', img.editing.enable, img.editing);
+- [`getCorners()`](#corners) and [`getCorner(idx)`](#corners)
-// grab the initial corner positions
-JSON.stringify(img.getCorners())
-=> "[{"lat":51.52,"lng":-0.14},{"lat":51.52,"lng":-0.1},{"lat":51.5,"lng":-0.14},{"lat":51.5,"lng":-0.1}]"
+- `getCenter()` - Calculates the centroid of the image
-// ...move the image around...
+## Contributing
-// check the new corner positions.
-JSON.stringify(img.getCorners())
-=> "[{"lat":51.50685099607552,"lng":-0.06058305501937867},{"lat":51.50685099607552,"lng":-0.02058595418930054},{"lat":51.486652692081925,"lng":-0.06058305501937867},{"lat":51.486652692081925,"lng":-0.02058595418930054}]"
+1) This project uses `grunt` to do a lot of things, including concatenate source files from `/src/` to `/DistortableImageOverlay.js`:
-// note there is an added level of precision after dragging the image for debugging purposes
+```Bash
+#you may need to install grunt-cli first:
+$ npm install -g grunt-cli
+#run in root dir, and it'll watch for changes and concatenate them on the fly
+$ grunt
```
-We further added a `getCorner(idx)` method used the same way as its plural counterpart but with an index passed to it.
-## Contributing
-1. This project uses `grunt` to do a lot of things, including concatenate source files from /src/ to /DistortableImageOverlay.js. But you may need to install grunt-cli: `npm install -g grunt-cli` first.
-2. Run `grunt` in the root directory, and it will watch for changes and concatenate them on the fly.
+2) To build all files from `/src/` into the `/dist/` folder, run:
-To build all files from `/src/` into the `/dist/` folder, run `grunt concat:dist`.
+```Bash
+$ grunt concat:dist
+```
+3. _Optional_: We use SVG for our icon system. Please visit our wiki [SVG Icon System](https://github.com/publiclab/Leaflet.DistortableImage/wiki/SVG-Icon-System) if you are interested in making updates to them or just simply learning about our workflow.
****
### Contributors
diff --git a/assets/icons/svg-min/border_clear.svg b/assets/icons/svg-min/border_clear.svg
new file mode 100644
index 000000000..6eeb92a15
--- /dev/null
+++ b/assets/icons/svg-min/border_clear.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/svg-min/border_outer.svg b/assets/icons/svg-min/border_outer.svg
new file mode 100644
index 000000000..9f937c629
--- /dev/null
+++ b/assets/icons/svg-min/border_outer.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/svg-min/crop_rotate.svg b/assets/icons/svg-min/crop_rotate.svg
new file mode 100644
index 000000000..4ecf1bca7
--- /dev/null
+++ b/assets/icons/svg-min/crop_rotate.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/svg-min/delete_forever.svg b/assets/icons/svg-min/delete_forever.svg
new file mode 100644
index 000000000..391b8edb2
--- /dev/null
+++ b/assets/icons/svg-min/delete_forever.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/svg-min/explore.svg b/assets/icons/svg-min/explore.svg
new file mode 100644
index 000000000..9ae3c0900
--- /dev/null
+++ b/assets/icons/svg-min/explore.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/svg-min/flip_to_back.svg b/assets/icons/svg-min/flip_to_back.svg
new file mode 100644
index 000000000..83ed7a3cd
--- /dev/null
+++ b/assets/icons/svg-min/flip_to_back.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/svg-min/flip_to_front.svg b/assets/icons/svg-min/flip_to_front.svg
new file mode 100644
index 000000000..0b1a4fcbb
--- /dev/null
+++ b/assets/icons/svg-min/flip_to_front.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/svg-min/get_app.svg b/assets/icons/svg-min/get_app.svg
new file mode 100644
index 000000000..10505986a
--- /dev/null
+++ b/assets/icons/svg-min/get_app.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/svg-min/lock.svg b/assets/icons/svg-min/lock.svg
new file mode 100644
index 000000000..c4848937e
--- /dev/null
+++ b/assets/icons/svg-min/lock.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/svg-min/opacity-empty.svg b/assets/icons/svg-min/opacity-empty.svg
new file mode 100644
index 000000000..0553aae3e
--- /dev/null
+++ b/assets/icons/svg-min/opacity-empty.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/svg-min/opacity.svg b/assets/icons/svg-min/opacity.svg
new file mode 100644
index 000000000..79261755c
--- /dev/null
+++ b/assets/icons/svg-min/opacity.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/svg-min/restore.svg b/assets/icons/svg-min/restore.svg
new file mode 100644
index 000000000..96358036e
--- /dev/null
+++ b/assets/icons/svg-min/restore.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/svg-min/transform.svg b/assets/icons/svg-min/transform.svg
new file mode 100644
index 000000000..a7289f392
--- /dev/null
+++ b/assets/icons/svg-min/transform.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/svg-min/unlock.svg b/assets/icons/svg-min/unlock.svg
new file mode 100644
index 000000000..97c3b7612
--- /dev/null
+++ b/assets/icons/svg-min/unlock.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/svg/border_clear.svg b/assets/icons/svg/border_clear.svg
new file mode 100644
index 000000000..166ed0def
--- /dev/null
+++ b/assets/icons/svg/border_clear.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/assets/icons/svg/border_outer.svg b/assets/icons/svg/border_outer.svg
new file mode 100644
index 000000000..e1d39c735
--- /dev/null
+++ b/assets/icons/svg/border_outer.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/assets/icons/svg/crop_rotate.svg b/assets/icons/svg/crop_rotate.svg
new file mode 100644
index 000000000..114a3bed8
--- /dev/null
+++ b/assets/icons/svg/crop_rotate.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/assets/icons/svg/delete_forever.svg b/assets/icons/svg/delete_forever.svg
new file mode 100644
index 000000000..0104a72c2
--- /dev/null
+++ b/assets/icons/svg/delete_forever.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/assets/icons/svg/explore.svg b/assets/icons/svg/explore.svg
new file mode 100644
index 000000000..6300a54ac
--- /dev/null
+++ b/assets/icons/svg/explore.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/assets/icons/svg/flip_to_back.svg b/assets/icons/svg/flip_to_back.svg
new file mode 100644
index 000000000..6e9f99f59
--- /dev/null
+++ b/assets/icons/svg/flip_to_back.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/assets/icons/svg/flip_to_front.svg b/assets/icons/svg/flip_to_front.svg
new file mode 100644
index 000000000..a8dbc05b0
--- /dev/null
+++ b/assets/icons/svg/flip_to_front.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/assets/icons/svg/get_app.svg b/assets/icons/svg/get_app.svg
new file mode 100644
index 000000000..4f6090f8e
--- /dev/null
+++ b/assets/icons/svg/get_app.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/assets/icons/svg/lock.svg b/assets/icons/svg/lock.svg
new file mode 100644
index 000000000..b2ae8e67f
--- /dev/null
+++ b/assets/icons/svg/lock.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/assets/icons/svg/opacity-empty.svg b/assets/icons/svg/opacity-empty.svg
new file mode 100644
index 000000000..a5bdd598b
--- /dev/null
+++ b/assets/icons/svg/opacity-empty.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/assets/icons/svg/opacity.svg b/assets/icons/svg/opacity.svg
new file mode 100644
index 000000000..fd150cbde
--- /dev/null
+++ b/assets/icons/svg/opacity.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/assets/icons/svg/restore.svg b/assets/icons/svg/restore.svg
new file mode 100644
index 000000000..9e8049345
--- /dev/null
+++ b/assets/icons/svg/restore.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/assets/icons/svg/transform.svg b/assets/icons/svg/transform.svg
new file mode 100644
index 000000000..d501b7de6
--- /dev/null
+++ b/assets/icons/svg/transform.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/assets/icons/svg/unlock.svg b/assets/icons/svg/unlock.svg
new file mode 100644
index 000000000..14213c1f5
--- /dev/null
+++ b/assets/icons/svg/unlock.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/assets/icons/symbol/sprite.symbol.html b/assets/icons/symbol/sprite.symbol.html
new file mode 100644
index 000000000..35a6ccf0d
--- /dev/null
+++ b/assets/icons/symbol/sprite.symbol.html
@@ -0,0 +1,441 @@
+
+
+
+
+
+
+
+ SVG <symbol> sprite preview | svg-sprite
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ A) Inline SVG with embedded sprite
+
+
+
+
+
+
+
+
+
+
+
+ border_clear
+
+
+
+
+
+
+
+
+
+
+ border_outer
+
+
+
+
+
+
+
+
+
+
+ crop_rotate
+
+
+
+
+
+
+
+
+
+
+ delete_forever
+
+
+
+
+
+
+
+
+
+
+ explore
+
+
+
+
+
+
+
+
+
+
+ flip_to_back
+
+
+
+
+
+
+
+
+
+
+ flip_to_front
+
+
+
+
+
+
+
+
+
+
+ get_app
+
+
+
+
+
+
+
+
+
+
+ lock
+
+
+
+
+
+
+
+
+
+
+ opacity
+
+
+
+
+
+
+
+
+
+
+ opacity-empty
+
+
+
+
+
+
+
+
+
+
+ restore
+
+
+
+
+
+
+
+
+
+
+ transform
+
+
+
+
+
+
+
+
+
+
+ unlock
+
+
+
+
+
+
+
+
+
+
+ B) Inline SVG with external sprite (IE 9-11 with polyfill only)
+
+
+
+
+
+
+
+
+
+
+
+ border_clear
+
+
+
+
+
+
+
+
+
+
+ border_outer
+
+
+
+
+
+
+
+
+
+
+ crop_rotate
+
+
+
+
+
+
+
+
+
+
+ delete_forever
+
+
+
+
+
+
+
+
+
+
+ explore
+
+
+
+
+
+
+
+
+
+
+ flip_to_back
+
+
+
+
+
+
+
+
+
+
+ flip_to_front
+
+
+
+
+
+
+
+
+
+
+ get_app
+
+
+
+
+
+
+
+
+
+
+ lock
+
+
+
+
+
+
+
+
+
+
+ opacity
+
+
+
+
+
+
+
+
+
+
+ opacity-empty
+
+
+
+
+
+
+
+
+
+
+ restore
+
+
+
+
+
+
+
+
+
+
+ transform
+
+
+
+
+
+
+
+
+
+
+ unlock
+
+
+
+
+
+
+
+
+
diff --git a/assets/icons/symbol/sprite.symbol.svg b/assets/icons/symbol/sprite.symbol.svg
new file mode 100644
index 000000000..8d45507f9
--- /dev/null
+++ b/assets/icons/symbol/sprite.symbol.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dist/leaflet.distortableimage.css b/dist/leaflet.distortableimage.css
index 6e1986481..5861cb9e1 100644
--- a/dist/leaflet.distortableimage.css
+++ b/dist/leaflet.distortableimage.css
@@ -1,46 +1,7 @@
-@font-face {
- font-family: 'Material Icons';
- font-style: normal;
- font-weight: 400;
- src: url(https://fonts.gstatic.com/s/materialicons/v47/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2) format('woff2');
-}
-
-@font-face {
- font-family: 'Material Icons Outlined';
- font-style: normal;
- font-weight: 400;
- src: url(https://fonts.gstatic.com/s/materialiconsoutlined/v8/gok-H7zzDkdnRel8-DQ6KAXJ69wP1tGnf4ZGhUcel5euIg.woff2) format('woff2');
-}
-
-.material-icons {
- font-family: 'Material Icons';
-}
-
-.material-icons-outlined {
- font-family: 'Material Icons Outlined';
-}
-
-.material-icons, .material-icons-outlined {
- font-weight: normal;
- font-style: normal;
- font-size: 24px;
- line-height: 1;
- letter-spacing: normal;
- text-transform: none;
- display: inline-block;
- white-space: nowrap;
- word-wrap: normal;
- direction: ltr;
- -webkit-font-feature-settings: 'liga';
- font-feature-settings: 'liga';
- -webkit-font-smoothing: antialiased;
-}
-
-.material-icons.red { color: #c10d0d; }
-
-.material-icons.md-18, .material-icons-outlined.md-18 {
- font-size: 18px;
- line-height: inherit;
+svg {
+ width: 18px;
+ height: 18px;
+ vertical-align: middle;
}
#imgcontainer {
diff --git a/dist/leaflet.distortableimage.js b/dist/leaflet.distortableimage.js
index 34b13127f..3fe5a58af 100644
--- a/dist/leaflet.distortableimage.js
+++ b/dist/leaflet.distortableimage.js
@@ -1,1830 +1,1961 @@
-L.DomUtil = L.extend(L.DomUtil, {
- getMatrixString: function(m) {
- var is3d = L.Browser.webkit3d || L.Browser.gecko3d || L.Browser.ie3d,
-
- /*
- * Since matrix3d takes a 4*4 matrix, we add in an empty row and column, which act as the identity on the z-axis.
- * See:
- * http://franklinta.com/2014/09/08/computing-css-matrix3d-transforms/
- * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#M.C3.B6bius'_homogeneous_coordinates_in_projective_geometry
- */
- matrix = [
- m[0], m[3], 0, m[6],
- m[1], m[4], 0, m[7],
- 0, 0, 1, 0,
- m[2], m[5], 0, m[8]
- ],
-
- str = is3d ? 'matrix3d(' + matrix.join(',') + ')' : '';
-
- if (!is3d) {
- console.log('Your browser must support 3D CSS transforms in order to use DistortableImageOverlay.');
- }
-
- return str;
- },
-
- getRotateString: function(angle, units) {
- var is3d = L.Browser.webkit3d || L.Browser.gecko3d || L.Browser.ie3d,
- open = 'rotate' + (is3d ? '3d' : '') + '(',
- rotateString = (is3d ? '0, 0, 1, ' : '') + angle + units;
-
- return open + rotateString + ')';
- },
-
- toggleClass: function(el, className) {
- var c = className;
- return this.hasClass(el, c) ? this.removeClass(el, c) : this.addClass(el, c);
- }
-
-});
-
-L.Map.include({
- _newLayerPointToLatLng: function(point, newZoom, newCenter) {
- var topLeft = L.Map.prototype._getNewTopLeftPoint.call(this, newCenter, newZoom)
- .add(L.Map.prototype._getMapPanePos.call(this));
- return this.unproject(point.add(topLeft), newZoom);
- }
-});
-L.MatrixUtil = {
-
- // Compute the adjugate of m
- adj: function(m) {
- return [
- m[4]*m[8]-m[5]*m[7], m[2]*m[7]-m[1]*m[8], m[1]*m[5]-m[2]*m[4],
- m[5]*m[6]-m[3]*m[8], m[0]*m[8]-m[2]*m[6], m[2]*m[3]-m[0]*m[5],
- m[3]*m[7]-m[4]*m[6], m[1]*m[6]-m[0]*m[7], m[0]*m[4]-m[1]*m[3]
- ];
- },
-
- // multiply two 3*3 matrices
- multmm: function(a, b) {
- var c = [],
- i;
-
- for (i = 0; i < 3; i++) {
- for (var j = 0; j < 3; j++) {
- var cij = 0;
- for (var k = 0; k < 3; k++) {
- cij += a[3*i + k]*b[3*k + j];
- }
- c[3*i + j] = cij;
- }
- }
- return c;
- },
-
- // multiply a 3*3 matrix and a 3-vector
- multmv: function(m, v) {
- return [
- m[0]*v[0] + m[1]*v[1] + m[2]*v[2],
- m[3]*v[0] + m[4]*v[1] + m[5]*v[2],
- m[6]*v[0] + m[7]*v[1] + m[8]*v[2]
- ];
- },
-
- // multiply a scalar and a 3*3 matrix
- multsm: function(s, m) {
- var matrix = [];
-
- for (var i = 0, l = m.length; i < l; i++) {
- matrix.push(s*m[i]);
- }
-
- return matrix;
- },
-
- basisToPoints: function(x1, y1, x2, y2, x3, y3, x4, y4) {
- var m = [
- x1, x2, x3,
- y1, y2, y3,
- 1, 1, 1
- ],
- v = L.MatrixUtil.multmv(L.MatrixUtil.adj(m), [x4, y4, 1]);
-
- return L.MatrixUtil.multmm(m, [
- v[0], 0, 0,
- 0, v[1], 0,
- 0, 0, v[2]
- ]);
- },
-
-
- project: function(m, x, y) {
- var v = L.MatrixUtil.multmv(m, [x, y, 1]);
- return [v[0]/v[2], v[1]/v[2]];
- },
-
- general2DProjection: function(
- x1s, y1s, x1d, y1d,
- x2s, y2s, x2d, y2d,
- x3s, y3s, x3d, y3d,
- x4s, y4s, x4d, y4d
- ) {
- var s = L.MatrixUtil.basisToPoints(x1s, y1s, x2s, y2s, x3s, y3s, x4s, y4s),
- d = L.MatrixUtil.basisToPoints(x1d, y1d, x2d, y2d, x3d, y3d, x4d, y4d),
- m = L.MatrixUtil.multmm(d, L.MatrixUtil.adj(s));
-
- /*
- * Normalize to the unique matrix with m[8] == 1.
- * See: http://franklinta.com/2014/09/08/computing-css-matrix3d-transforms/
- */
- return L.MatrixUtil.multsm(1/m[8], m);
- }
-};
-L.TrigUtil = {
-
- calcAngleDegrees: function(x, y) {
- return Math.atan2(y, x) * 180 / Math.PI;
- }
-
-};
-L.DistortableImageOverlay = L.ImageOverlay.extend({
-
- options: {
- alt: "",
- height: 200,
- crossOrigin: true,
- // todo: find ideal number to prevent distortions during RotateScale, and make it dynamic (remove hardcoding)
- edgeMinWidth: 520
- },
-
- initialize: function(url, options) {
- this._toolArray = L.DistortableImage.EditToolbarDefaults;
- this.edgeMinWidth = this.options.edgeMinWidth;
- this._url = url;
- this._rotation = this.options.rotation;
- L.DistortableImage._options = options;
-
- L.Util.setOptions(this, options);
- },
-
- onAdd: function(map) {
- /* Copied from L.ImageOverlay */
- this._map = map;
-
- if (!this._image) { this._initImage(); }
- if (!this._events) { this._initEvents(); }
-
- map._panes.overlayPane.appendChild(this._image);
-
- map.on("viewreset", this._reset, this);
- /* End copied from L.ImageOverlay */
-
- /* Use provided corners if available */
- if (this.options.corners) {
- this._corners = this.options.corners;
- if (map.options.zoomAnimation && L.Browser.any3d) {
- map.on("zoomanim", this._animateZoom, this);
- }
-
- /* This reset happens before image load; it allows
- * us to place the image on the map earlier with
- * "guessed" dimensions. */
- this._reset();
- }
-
- /* Have to wait for the image to load because
- * we need to access its width and height. */
- L.DomEvent.on(this._image, "load", function() {
- this._initImageDimensions();
- this._reset();
- /* Initialize default corners if not already set */
- if (!this._corners) {
- if (map.options.zoomAnimation && L.Browser.any3d) {
- map.on("zoomanim", this._animateZoom, this);
- }
- }
- }, this);
-
- this.fire("add");
- },
-
- onRemove: function(map) {
- this.fire("remove");
-
- L.ImageOverlay.prototype.onRemove.call(this, map);
- },
-
- _initImage: function() {
- L.ImageOverlay.prototype._initImage.call(this);
-
- L.extend(this._image, {
- alt: this.options.alt
- });
- },
-
- _addTool: function(tool) {
- this._toolArray.push(tool);
- L.DistortableImage.EditToolbar = LeafletToolbar.Popup.extend({
- options: {
- actions: this._toolArray
- }
- });
- },
-
- _initImageDimensions: function() {
- var map = this._map,
- originalImageWidth = L.DomUtil.getStyle(this._image, "width"),
- originalImageHeight = L.DomUtil.getStyle(this._image, "height"),
- aspectRatio =
- parseInt(originalImageWidth) / parseInt(originalImageHeight),
- imageHeight = this.options.height,
- imageWidth = parseInt(aspectRatio * imageHeight),
- center = map.latLngToContainerPoint(map.getCenter()),
- offset = L.point(imageWidth, imageHeight).divideBy(2);
-
- if (this.options.corners) {
- this._corners = this.options.corners;
- } else {
- this._corners = [
- map.containerPointToLatLng(center.subtract(offset)),
- map.containerPointToLatLng(
- center.add(L.point(offset.x, -offset.y))
- ),
- map.containerPointToLatLng(
- center.add(L.point(-offset.x, offset.y))
- ),
- map.containerPointToLatLng(center.add(offset))
- ];
- }
- },
-
- _initEvents: function() {
- this._events = ["click"];
-
- for (var i = 0, l = this._events.length; i < l; i++) {
- L.DomEvent.on(this._image, this._events[i], this._fireMouseEvent, this);
- }
- },
-
- /* See src/layer/vector/Path.SVG.js in the Leaflet source. */
- _fireMouseEvent: function(event) {
- if (!this.hasEventListeners(event.type)) { return; }
-
- var map = this._map,
- containerPoint = map.mouseEventToContainerPoint(event),
- layerPoint = map.containerPointToLayerPoint(containerPoint),
- latlng = map.layerPointToLatLng(layerPoint);
-
- this.fire(event.type, {
- latlng: latlng,
- layerPoint: layerPoint,
- containerPoint: containerPoint,
- originalEvent: event
- });
- },
-
- _updateCorner: function(corner, latlng) {
- this._corners[corner] = latlng;
- this._reset();
- },
-
- // fires a reset after all corner positions are updated instead of after each one (above). Use for translating
- _updateCorners: function(latlngObj) {
- var i = 0;
- for (var k in latlngObj) {
- this._corners[i] = latlngObj[k];
- i += 1;
- }
-
- this._reset();
- },
-
- _updateCornersFromPoints: function(pointsObj) {
- var map = this._map;
- var i = 0;
- for (var k in pointsObj) {
- this._corners[i] = map.layerPointToLatLng(pointsObj[k]);
- i += 1;
- }
-
- this._reset();
- },
-
- /* Copied from Leaflet v0.7 https://github.com/Leaflet/Leaflet/blob/66282f14bcb180ec87d9818d9f3c9f75afd01b30/src/dom/DomUtil.js#L189-L199 */
- /* since L.DomUtil.getTranslateString() is deprecated in Leaflet v1.0 */
- _getTranslateString: function(point) {
- // on WebKit browsers (Chrome/Safari/iOS Safari/Android) using translate3d instead of translate
- // makes animation smoother as it ensures HW accel is used. Firefox 13 doesn't care
- // (same speed either way), Opera 12 doesn't support translate3d
-
- var is3d = L.Browser.webkit3d,
- open = "translate" + (is3d ? "3d" : "") + "(",
- close = (is3d ? ",0" : "") + ")";
-
- return open + point.x + "px," + point.y + "px" + close;
- },
-
- _reset: function() {
- var map = this._map,
- image = this._image,
- latLngToLayerPoint = L.bind(map.latLngToLayerPoint, map),
- transformMatrix = this._calculateProjectiveTransform(latLngToLayerPoint),
- topLeft = latLngToLayerPoint(this._corners[0]),
- warp = L.DomUtil.getMatrixString(transformMatrix),
- translation = this._getTranslateString(topLeft);
-
- /* See L.DomUtil.setPosition. Mainly for the purposes of L.Draggable. */
- image._leaflet_pos = topLeft;
-
- image.style[L.DomUtil.TRANSFORM] = [translation, warp].join(" ");
-
- /* Set origin to the upper-left corner rather than the center of the image, which is the default. */
- image.style[L.DomUtil.TRANSFORM + "-origin"] = "0 0 0";
- },
-
- /*
- * Calculates the transform string that will be correct *at the end* of zooming.
- * Leaflet then generates a CSS3 animation between the current transform and
- * future transform which makes the transition appear smooth.
- */
- _animateZoom: function(event) {
- var map = this._map,
- image = this._image,
- latLngToNewLayerPoint = function(latlng) {
- return map._latLngToNewLayerPoint(latlng, event.zoom, event.center);
- },
- transformMatrix = this._calculateProjectiveTransform(
- latLngToNewLayerPoint
- ),
- topLeft = latLngToNewLayerPoint(this._corners[0]),
- warp = L.DomUtil.getMatrixString(transformMatrix),
- translation = this._getTranslateString(topLeft);
-
- /* See L.DomUtil.setPosition. Mainly for the purposes of L.Draggable. */
- image._leaflet_pos = topLeft;
-
- if (!L.Browser.gecko) {
- image.style[L.DomUtil.TRANSFORM] = [translation, warp].join(" ");
- }
- },
-
- getCorners: function() {
- return this._corners;
- },
-
- getCorner: function(i) {
- return this._corners[i];
- },
-
- /*
- * Calculates the centroid of the image.
- * See http://stackoverflow.com/questions/6149175/logical-question-given-corners-find-center-of-quadrilateral
- */
- getCenter: function(ll2c, c2ll) {
- var map = this._map,
- latLngToCartesian = ll2c ? ll2c : map.latLngToLayerPoint,
- cartesianToLatLng = c2ll ? c2ll : map.layerPointToLatLng,
- nw = latLngToCartesian.call(map, this._corners[0]),
- ne = latLngToCartesian.call(map, this._corners[1]),
- se = latLngToCartesian.call(map, this._corners[2]),
- sw = latLngToCartesian.call(map, this._corners[3]),
- nmid = nw.add(ne.subtract(nw).divideBy(2)),
- smid = sw.add(se.subtract(sw).divideBy(2));
-
- return cartesianToLatLng.call(
- map,
- nmid.add(smid.subtract(nmid).divideBy(2))
- );
- },
-
- // Use for translation calculations - for translation the delta for 1 corner applies to all 4
- _calcCornerPointDelta: function() {
- return this._dragStartPoints[0].subtract(this._dragPoints[0]);
- },
-
- _calcCenterTwoCornerPoints: function(topLeft, topRight) {
- var toolPoint = { x: "", y: "" };
-
- toolPoint.x = topRight.x + (topLeft.x - topRight.x) / 2;
- toolPoint.y = topRight.y + (topLeft.y - topRight.y) / 2;
-
- return toolPoint;
- },
-
- _calculateProjectiveTransform: function(latLngToCartesian) {
- /* Setting reasonable but made-up image defaults
- * allow us to place images on the map before
- * they've finished downloading. */
- var offset = latLngToCartesian(this._corners[0]),
- w = this._image.offsetWidth || 500,
- h = this._image.offsetHeight || 375,
- c = [],
- j;
- /* Convert corners to container points (i.e. cartesian coordinates). */
- for (j = 0; j < this._corners.length; j++) {
- c.push(latLngToCartesian(this._corners[j])._subtract(offset));
- }
-
- /*
- * This matrix describes the action of the CSS transform on each corner of the image.
- * It maps from the coordinate system centered at the upper left corner of the image
- * to the region bounded by the latlngs in this._corners.
- * For example:
- * 0, 0, c[0].x, c[0].y
- * says that the upper-left corner of the image maps to the first latlng in this._corners.
- */
- return L.MatrixUtil.general2DProjection(
- 0, 0, c[0].x, c[0].y,
- w, 0, c[1].x, c[1].y,
- 0, h, c[2].x, c[2].y,
- w, h, c[3].x, c[3].y
- );
- },
-
- _getCmPerPixel: function() {
- var map = this._map;
-
- var dist = map.latLngToLayerPoint(this.getCorner(0))
- .distanceTo(map.latLngToLayerPoint(this.getCorner(1)));
-
- return (dist * 100) / this._image.width;
- }
-
-});
-
-L.distortableImageOverlay = function(id, options) {
- return new L.DistortableImageOverlay(id, options);
-};
-
-
-
-
-L.DistortableCollection = L.FeatureGroup.extend({
- onAdd: function(map) {
- L.FeatureGroup.prototype.onAdd.call(this, map);
-
- this._map = map;
-
- L.DomEvent.on(document, "keydown", this._onKeyDown, this);
- L.DomEvent.on(map, "click", this._deselectAll, this);
-
- /**
- * the box zoom override works, but there is a bug involving click event propogation.
- * keeping uncommented for now so that it isn't used as a multi-select mechanism
- */
-
- // L.DomEvent.on(map, "boxzoomend", this._addSelections, this);
-
- var lastSelected;
-
- this.eachLayer(function(layer) {
- L.DomEvent.on(layer._image, "mousedown", this._deselectOthers, this);
- L.DomEvent.on(layer, "dragstart", this._dragStartMultiple, this);
- L.DomEvent.on(layer, "drag", this._dragMultiple, this);
-
- if (layer.options.selected) {
- layer.editing._deselect();
- lastSelected = layer.editing;
- }
- }, this);
-
- if (lastSelected) {
- lastSelected._select();
- }
- },
-
- onRemove: function() {
- var map = this._map;
-
- L.DomEvent.off(document, "keydown", this._onKeyDown, this);
- L.DomEvent.off(map, "click", this._deselectAll, this);
- // L.DomEvent.off(map, "boxzoomend", this._addSelections, this);
-
- this.eachLayer(function(layer) {
- L.DomEvent.off(layer._image, "mousedown", this._deselectOthers, this);
- L.DomEvent.off(layer, "dragstart", this._dragStartMultiple, this);
- L.DomEvent.off(layer, "drag", this._dragMultiple, this);
- }, this);
- },
-
- isSelected: function(overlay) {
- return L.DomUtil.hasClass(overlay.getElement(), "selected");
- },
-
- _toggleMultiSelect: function(event, edit) {
- if (edit._mode === "lock") { return; }
-
- if (event.metaKey || event.ctrlKey) {
- L.DomUtil.toggleClass(event.target, "selected");
- }
- },
-
- _deselectOthers: function(event) {
- this.eachLayer(function(layer) {
- var edit = layer.editing;
- if (layer.getElement() !== event.target) {
- edit._deselect();
- } else {
- this._toggleMultiSelect(event, edit);
- }
- }, this);
-
- L.DomEvent.stopPropagation(event);
- },
-
- _addSelections: function(e) {
- var box = e.boxZoomBounds,
- i = 0;
-
- this.eachLayer(function(layer) {
- var edit = layer.editing;
-
- if (edit.toolbar) { edit._hideToolbar(); }
-
- for (i = 0; i < 4; i++) {
- if (box.contains(layer.getCorner(i)) && edit._mode !== "lock") {
- L.DomUtil.addClass(layer.getElement(), "selected");
- break;
- }
- }
- });
- },
-
- _getAvgCmPerPixel: function(imgs) {
- var reduce = imgs.reduce(function(sum, img) {
- return sum + img.cm_per_pixel;
- }, 0);
- return reduce / imgs.length;
- },
-
- _generateExportJson: function() {
- var json = {};
- json.images = [];
-
- this.eachLayer(function(layer) {
- if (this.isSelected(layer)) {
- json.images.push({
- id: this.getLayerId(layer),
- src: layer._image.src,
- nodes: layer.getCorners(),
- cm_per_pixel: layer._getCmPerPixel()
- });
- }
- }, this);
-
- json.avg_cm_per_pixel = this._getAvgCmPerPixel(json.images);
-
- return json;
- },
-
- _runExport: function(collection) {
- collection = collection || this._generateExportJson();
- $.ajax({
- url: "http://export.mapknitter.org/export",
- crossDomain: true,
- type: "POST",
- data: {
- collection: JSON.stringify(collection.images),
- scale: 30
- },
- success: function _getStatusJson(data) {
- console.log(data);
- $.ajax("http://export.mapknitter.org" + data, {
- type: "GET",
- crossDomain: true
- }).done(function(data) {
- console.log(data);
- });
- }
- });
- },
-
- _onKeyDown: function(e) {
- if (e.key === "Escape") {
- this._deselectAll(e);
- }
- if (e.key === "Backspace") {
- this._removeFromGroup(e);
- }
- },
-
- _dragStartMultiple: function(event) {
- var overlay = event.target,
- i;
-
- if (!this.isSelected(overlay)) { return; }
-
- this.eachLayer(function(layer) {
- var edit = layer.editing;
- edit._deselect();
-
- for (i = 0; i < 4; i++) {
- layer._dragStartPoints[i] = layer._map.latLngToLayerPoint(
- layer.getCorner(i)
- );
- }
- });
- },
-
- _dragMultiple: function(event) {
- var overlay = event.target,
- map = this._map,
- i;
-
- if (!this.isSelected(overlay)) { return; }
-
- overlay._dragPoints = {};
-
- for (i = 0; i < 4; i++) {
- overlay._dragPoints[i] = map.latLngToLayerPoint(overlay.getCorner(i));
- }
-
- var cpd = overlay._calcCornerPointDelta();
-
- this._updateCollectionFromPoints(cpd, overlay);
- },
-
- _deselectAll: function(event) {
- this.eachLayer(function(layer) {
- var edit = layer.editing;
- L.DomUtil.removeClass(layer.getElement(), "selected");
- edit._deselect();
- });
-
- L.DomEvent.stopPropagation(event);
- },
-
- _removeFromGroup: function(e) {
- this.eachLayer(function(layer) {
- var edit = layer.editing;
- if (edit._selected && edit._mode !== "lock") {
- var choice = edit.confirmDelete();
- if (choice) {
- edit._selected = false;
- this.removeLayer(layer);
- } else {
- L.DomEvent.stopPropagation(e);
- return;
- }
- }
- }, this);
- },
- /**
- * images in 'lock' mode are included in this feature group collection for functionalities
- * such as export, but are filtered out for editing / dragging here
- */
- _calcCollectionFromPoints: function(cpd, overlay) {
- var layersToMove = [],
- p = new L.Transformation(1, -cpd.x, 1, -cpd.y);
-
- this.eachLayer(function(layer) {
- if (
- layer !== overlay &&
- layer.editing._mode !== "lock" &&
- this.isSelected(layer)
- ) {
- layer._cpd = {};
-
- layer._cpd.val0 = p.transform(layer._dragStartPoints[0]);
- layer._cpd.val1 = p.transform(layer._dragStartPoints[1]);
- layer._cpd.val2 = p.transform(layer._dragStartPoints[2]);
- layer._cpd.val3 = p.transform(layer._dragStartPoints[3]);
-
- layersToMove.push(layer);
- }
- }, this);
-
- return layersToMove;
- },
-
- /**
- * cpd === cornerPointDelta
- */
- _updateCollectionFromPoints: function(cpd, overlay) {
- var layersToMove = this._calcCollectionFromPoints(cpd, overlay);
-
- layersToMove.forEach(function(layer) {
- layer._updateCornersFromPoints(layer._cpd);
- layer.fire("update");
- }, this);
- }
-});
-
-L.distortableCollection = function(id, options) {
- return new L.DistortableCollection(id, options);
-};
-L.EXIF = function getEXIFdata(img) {
- if (Object.keys(EXIF.getAllTags(img)).length !== 0) {
- console.log(EXIF.getAllTags(img));
- var GPS = EXIF.getAllTags(img),
- altitude;
-
- /* If the lat/lng is available. */
- if (
- typeof GPS.GPSLatitude !== "undefined" &&
- typeof GPS.GPSLongitude !== "undefined"
- ) {
- // sadly, encoded in [degrees,minutes,seconds]
- // primitive value = GPS.GPSLatitude[x].numerator
- var lat =
- GPS.GPSLatitude[0] +
- GPS.GPSLatitude[1] / 60 +
- GPS.GPSLatitude[2] / 3600;
- var lng =
- GPS.GPSLongitude[0] +
- GPS.GPSLongitude[1] / 60 +
- GPS.GPSLongitude[2] / 3600;
-
- if (GPS.GPSLatitudeRef !== "N") {
- lat = lat * -1;
- }
- if (GPS.GPSLongitudeRef === "W") {
- lng = lng * -1;
- }
- }
-
- // Attempt to use GPS compass heading; will require
- // some trig to calc corner points, which you can find below:
-
- var angle = 0;
- // "T" refers to "True north", so -90.
- if (GPS.GPSImgDirectionRef === "T") {
- angle =
- (Math.PI / 180) *
- (GPS.GPSImgDirection.numerator / GPS.GPSImgDirection.denominator - 90);
- }
- // "M" refers to "Magnetic north"
- else if (GPS.GPSImgDirectionRef === "M") {
- angle =
- (Math.PI / 180) *
- (GPS.GPSImgDirection.numerator / GPS.GPSImgDirection.denominator - 90);
- } else {
- console.log("No compass data found");
- }
-
- console.log("Orientation:", GPS.Orientation);
-
- /* If there is orientation data -- i.e. landscape/portrait etc */
- if (GPS.Orientation === 6) {
- //CCW
- angle += (Math.PI / 180) * -90;
- } else if (GPS.Orientation === 8) {
- //CW
- angle += (Math.PI / 180) * 90;
- } else if (GPS.Orientation === 3) {
- //180
- angle += (Math.PI / 180) * 180;
- }
-
- /* If there is altitude data */
- if (
- typeof GPS.GPSAltitude !== "undefined" &&
- typeof GPS.GPSAltitudeRef !== "undefined"
- ) {
- // Attempt to use GPS altitude:
- // (may eventually need to find EXIF field of view for correction)
- if (
- typeof GPS.GPSAltitude !== "undefined" &&
- typeof GPS.GPSAltitudeRef !== "undefined"
- ) {
- altitude =
- GPS.GPSAltitude.numerator / GPS.GPSAltitude.denominator +
- GPS.GPSAltitudeRef;
- } else {
- altitude = 0; // none
- }
- }
- } else {
- alert("EXIF initialized. Press again to view data in console.");
- }
-};
-
-L.EditHandle = L.Marker.extend({
- initialize: function(overlay, corner, options) {
- var markerOptions,
- latlng = overlay._corners[corner];
-
- L.setOptions(this, options);
-
- this._handled = overlay;
- this._corner = corner;
-
- markerOptions = {
- draggable: true,
- zIndexOffset: 10
- };
-
- if (options && options.hasOwnProperty("draggable")) {
- markerOptions.draggable = options.draggable;
- }
-
- L.Marker.prototype.initialize.call(this, latlng, markerOptions);
- },
-
- onAdd: function(map) {
- L.Marker.prototype.onAdd.call(this, map);
- this._bindListeners();
-
- this.updateHandle();
- },
-
- onRemove: function(map) {
- this._unbindListeners();
- L.Marker.prototype.onRemove.call(this, map);
- },
-
- _onHandleDragStart: function() {
- this._handled.fire("editstart");
- },
-
- _onHandleDragEnd: function() {
- this._fireEdit();
- },
-
- _fireEdit: function() {
- this._handled.edited = true;
- this._handled.fire("edit");
- },
-
- _bindListeners: function() {
- this.on(
- {
- dragstart: this._onHandleDragStart,
- drag: this._onHandleDrag,
- dragend: this._onHandleDragEnd
- },
- this
- );
-
- this._handled._map.on("zoomend", this.updateHandle, this);
-
- this._handled.on("update", this.updateHandle, this);
- },
-
- _unbindListeners: function() {
- this.off(
- {
- dragstart: this._onHandleDragStart,
- drag: this._onHandleDrag,
- dragend: this._onHandleDragEnd
- },
- this
- );
-
- this._handled._map.off("zoomend", this.updateHandle, this);
- this._handled.off("update", this.updateHandle, this);
- },
-
- /* Takes two latlngs and calculates the scaling difference. */
- _calculateScalingFactor: function(latlngA, latlngB) {
- var map = this._handled._map,
- centerPoint = map.latLngToLayerPoint(this._handled.getCenter()),
- formerPoint = map.latLngToLayerPoint(latlngA),
- newPoint = map.latLngToLayerPoint(latlngB),
-
- formerRadiusSquared = this._d2(centerPoint, formerPoint),
- newRadiusSquared = this._d2(centerPoint, newPoint);
-
- return Math.sqrt(newRadiusSquared / formerRadiusSquared);
- },
-
- /* Distance between two points in cartesian space, squared (distance formula). */
- _d2: function(a, b) {
- var dx = a.x - b.x,
- dy = a.y - b.y;
-
- return Math.pow(dx, 2) + Math.pow(dy, 2);
- },
-
- /* Takes two latlngs and calculates the angle between them. */
- _calculateAngle: function(latlngA, latlngB) {
- var map = this._handled._map,
-
- centerPoint = map.latLngToLayerPoint(this._handled.getCenter()),
- formerPoint = map.latLngToLayerPoint(latlngA),
- newPoint = map.latLngToLayerPoint(latlngB),
-
- initialAngle = Math.atan2(centerPoint.y - formerPoint.y, centerPoint.x - formerPoint.x),
- newAngle = Math.atan2(centerPoint.y - newPoint.y, centerPoint.x - newPoint.x);
-
- return newAngle - initialAngle;
- }
-});
-
-L.LockHandle = L.EditHandle.extend({
- options: {
- TYPE: 'lock',
- icon: L.icon({
- iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAklEQVR4AewaftIAAAD8SURBVO3BPU7CYAAA0AdfjIcQlRCQBG7C3gk2uIPG2RC3Dk16Gz0FTO1WZs/gwGCMP/2+xsSl7+n1er1Iz9LtRQjaPeMeO+TinLDCJV78YqjdA04YodKuxhUaPGoRxMmxwRQZSt87Yo4KExGCeAUyLLFB4bMacxywEClIU2KDKXbInTUYo8JCgoFuGoxQO5uiwY1EA91VmDqrcKeDoX8WdNNgjApvmGGLXKIgXY0xGkxQYItrrFFIEKQ5Yo4KEx9yrDFDhlKkIF6NOQ5Y+KpAhiXWKEQI4pxwiwoLPyuxwQw75FoE7fZYocFEuwI7jHCBV39gL92TXq/Xi/AOcmczZmaIMScAAAAASUVORK5CYII=',
- iconSize: [32, 32],
- iconAnchor: [16, 16]
- })
- },
-
- /* cannot be dragged */
- _onHandleDrag: function() {
- },
-
- updateHandle: function() {
- this.setLatLng(this._handled._corners[this._corner]);
- L.DomUtil.removeClass(this._handled.getElement(), 'selected');
- }
-
-});
-
-L.DistortHandle = L.EditHandle.extend({
- options: {
- TYPE: "distort",
- icon: L.icon({
- iconUrl:
- "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAklEQVR4AewaftIAAAChSURBVO3BIU4DURgGwNkvL2B6AkQTLBqP4QCoSm7DDXoBLBZHDbfgICAIZjEV3YTn9uVHdMZZtcnCfI13bIzxg0emg6Nm6QVbYz3jylEsXRrvwommb49X67jFkz80fR9Mb1YxTzqiWBSLYlEsikWxKBbFolgUi2JRLIpFsSgWxaJY03fHHOu40dH07bAzWCx9Ge/TiWbpHgdsjPGNB2f/yS+7xRCyiiZPJQAAAABJRU5ErkJggg==",
- iconSize: [32, 32],
- iconAnchor: [16, 16]
- })
- },
-
- updateHandle: function() {
- this.setLatLng(this._handled._corners[this._corner]);
- },
-
- _onHandleDrag: function() {
- this._handled._updateCorner(this._corner, this.getLatLng());
-
- this._handled.fire("update");
- this._handled.editing._showToolbar();
- }
-});
-
-L.RotateScaleHandle = L.EditHandle.extend({
- options: {
- TYPE: 'rotateScale',
- icon: L.icon({
- iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAklEQVR4AewaftIAAAHiSURBVMXBa3HbShgA0PMp/1sCCo8oCEpgTaCXgIXAJiDzyCJoAUTm4UVQAns1Y8+snWnTvJyeE16hkjDgDrfoNTMKcpC9UPiLSo8JyetkjEHxjPCMyoS199kFoz8Iv1HpMaN3qWDCHoegOKkkRwnJpRmroHgiPFEZ8IBekzEGxQtUEhKSS/fB7Ew4U+lxcGkVZG9QWWPSFAxBcdK59KApuA+yNwp2uEdx1GN25sZJZULSfAtm77SlbNjju6MvG75u+WHRWVR6rDVjMPsgwYyVZl3pLTpHkyYHOx8syMiayaJzlDTZ9YyaZNFVkiYH2ZUEBcVJJXVImuz6Js3Qofe59pq7DoOTILu+g+a288mCouk7/1iH4qTS+2QdDppbV1ZJmrnDXnPnc5UOs2Z0fUmTuyBr+krvSioJyUmQO0dZM7mepMkWnaNRkyrJB6uskTSjxY3Fll8bvmJwlDb83FJ8gMqAB80uyBY3Trb82PAfvjj6vuHnluIdKgMeNXOwctK5NKBoHitrb1RJeHRp5Ux4ojLg0aWMHGQvUOkxIWkKVsHsTPiNSo8HDC5lZIsgO6n0uMUdRvQuFQxB8UR4RmXC2vvsgtEfhL+o9JiQvE7GGBTPCK9QSUjoMWgKDthjDrIX+h/k0I7gth6N5gAAAABJRU5ErkJggg==',
- iconSize: [32, 32],
- iconAnchor: [16, 16]
- })
- },
-
- _onHandleDrag: function() {
- var overlay = this._handled,
- formerLatLng = this._handled._corners[this._corner],
- newLatLng = this.getLatLng(),
-
- angle = this._calculateAngle(formerLatLng, newLatLng),
- scale = this._calculateScalingFactor(formerLatLng, newLatLng);
-
- overlay.editing._rotateBy(angle);
-
- /*
- checks whether the "edgeMinWidth" property is set and tracks the minimum edge length;
- this enables preventing scaling to zero, but we might also add an overall scale limit
- */
- if (this._handled.hasOwnProperty('edgeMinWidth')){
- var edgeMinWidth = this._handled.edgeMinWidth,
- w = L.latLng(overlay._corners[0]).distanceTo(overlay._corners[1]),
- h = L.latLng(overlay._corners[1]).distanceTo(overlay._corners[2]);
- if ((w > edgeMinWidth && h > edgeMinWidth) || scale > 1) {
- overlay.editing._scaleBy(scale);
- }
- }
-
- overlay.fire('update');
-
- this._handled.editing._showToolbar();
-
- },
-
- updateHandle: function() {
- this.setLatLng(this._handled._corners[this._corner]);
- },
-
-});
-
-L.RotateHandle = L.EditHandle.extend({
- options: {
- TYPE: 'rotate',
- icon: L.icon({
- iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAklEQVR4AewaftIAAAHiSURBVMXBa3HbShgA0PMp/1sCCo8oCEpgTaCXgIXAJiDzyCJoAUTm4UVQAns1Y8+snWnTvJyeE16hkjDgDrfoNTMKcpC9UPiLSo8JyetkjEHxjPCMyoS199kFoz8Iv1HpMaN3qWDCHoegOKkkRwnJpRmroHgiPFEZ8IBekzEGxQtUEhKSS/fB7Ew4U+lxcGkVZG9QWWPSFAxBcdK59KApuA+yNwp2uEdx1GN25sZJZULSfAtm77SlbNjju6MvG75u+WHRWVR6rDVjMPsgwYyVZl3pLTpHkyYHOx8syMiayaJzlDTZ9YyaZNFVkiYH2ZUEBcVJJXVImuz6Js3Qofe59pq7DoOTILu+g+a288mCouk7/1iH4qTS+2QdDppbV1ZJmrnDXnPnc5UOs2Z0fUmTuyBr+krvSioJyUmQO0dZM7mepMkWnaNRkyrJB6uskTSjxY3Fll8bvmJwlDb83FJ8gMqAB80uyBY3Trb82PAfvjj6vuHnluIdKgMeNXOwctK5NKBoHitrb1RJeHRp5Ux4ojLg0aWMHGQvUOkxIWkKVsHsTPiNSo8HDC5lZIsgO6n0uMUdRvQuFQxB8UR4RmXC2vvsgtEfhL+o9JiQvE7GGBTPCK9QSUjoMWgKDthjDrIX+h/k0I7gth6N5gAAAABJRU5ErkJggg==',
- iconSize: [32, 32],
- iconAnchor: [16, 16]
- })
- },
-
- _onHandleDrag: function() {
- var overlay = this._handled,
- formerLatLng = this._handled._corners[this._corner],
- newLatLng = this.getLatLng(),
- angle = this._calculateAngle(formerLatLng, newLatLng);
-
- overlay.editing._rotateBy(angle);
-
- overlay.fire('update');
-
- this._handled.editing._showToolbar();
- },
-
- updateHandle: function() {
- this.setLatLng(this._handled._corners[this._corner]);
- }
-
-});
-
-L.ScaleHandle = L.EditHandle.extend({
- options: {
- TYPE: 'rotate',
- icon: L.icon({
- iconUrl:'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIHdpZHRoPSI0NTkiIGhlaWdodD0iNDY0IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA1MTIgNTEyIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iIj48cmVjdCBpZD0iYmFja2dyb3VuZHJlY3QiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIHg9IjAiIHk9IjAiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIgY2xhc3M9IiIgc3R5bGU9IiIvPjxnIGNsYXNzPSJjdXJyZW50TGF5ZXIiIHN0eWxlPSIiPjx0aXRsZT5MYXllciAxPC90aXRsZT48cGF0aCBkPSJNNDU5LjA0OTE1OTUzMDQ3MTM0LDg2LjkyNjIzNDUxMjU1MDAyIFYwIGgtODUuNzE0NTczMzU2MzEyMDkgdjI3LjA0MzcxNzQwMzkwNDQ1MiBIODUuNzE0NTczMzU2MzEyMDMgVjAgSDAgdjg2LjkyNjIzNDUxMjU1MDAyIGgyNS43MTQzNzIwMDY4OTM2MjYgdjI4OS43NTQxMTUwNDE4MzM0IEgwIHY4Ni45MjYyMzQ1MTI1NTAwMiBoODUuNzE0NTczMzU2MzEyMDkgdi0yNy4wNDM3MTc0MDM5MDQ0NTIgaDI4NS43MTUyNDQ1MjEwNDAzIHYyNy4wNDM3MTc0MDM5MDQ0NTIgaDg1LjcxNDU3MzM1NjMxMjA5IHYtODYuOTI2MjM0NTEyNTUwMDIgaC0yMy44MDk2MDM3MTAwODY2OSBWODYuOTI2MjM0NTEyNTUwMDIgSDQ1OS4wNDkxNTk1MzA0NzEzNCB6TTM4NC43NjMxOTU5NTUwMDA5LDEyLjU1NjAxMTY1MTgxMjc4MSBoNjEuOTA0OTY5NjQ2MjI1Mzk2IHY2Mi43ODAwNTgyNTkwNjM5MSBoLTYxLjkwNDk2OTY0NjIyNTM5NiBWMTIuNTU2MDExNjUxODEyNzgxIHpNMTIuMzgwOTkzOTI5MjQ1MDUsMTIuNTU2MDExNjUxODEyNzgxIGg2MS45MDQ5Njk2NDYyMjUzOTYgdjYyLjc4MDA1ODI1OTA2MzkxIEgxMi4zODA5OTM5MjkyNDUwNSBWMTIuNTU2MDExNjUxODEyNzgxIHpNNzQuMjg1OTYzNTc1NDcwNTMsNDUxLjA1MDU3MjQxNTEyMDY2IEgxMi4zODA5OTM5MjkyNDUwNSB2LTYyLjc4MDA1ODI1OTA2MzkxIGg2MS45MDQ5Njk2NDYyMjUzOTYgVjQ1MS4wNTA1NzI0MTUxMjA2NiB6TTQ0NS43MTU3ODE0NTI4MjI3NCw0NTEuMDUwNTcyNDE1MTIwNjYgaC02Mi44NTczNTM3OTQ2Mjg4NjQgdi02Mi43ODAwNTgyNTkwNjM5MSBoNjIuODU3MzUzNzk0NjI4ODY0IFY0NTEuMDUwNTcyNDE1MTIwNjYgek00MDcuNjIwNDE1NTE2Njg0MjYsMzc2LjY4MDM0OTU1NDM4MzQ0IGgtMzYuMTkwNTk3NjM5MzMxNzcgdjMyLjgzODc5OTcwNDc0MTEyIEg4NS43MTQ1NzMzNTYzMTIwMyB2LTMyLjgzODc5OTcwNDc0MTEyIEg0OS41MjM5NzU3MTY5ODAzMiBWODYuOTI2MjM0NTEyNTUwMDIgaDM2LjE5MDU5NzYzOTMzMTc3IFY1MC4yMjQwNDY2MDcyNTExMjUgaDI4Ny42MjAwMTI4MTc4NDcyIHYzNi43MDIxODc5MDUyOTg5IGgzNC4yODU4MjkzNDI1MjQ4MzUgVjM3Ni42ODAzNDk1NTQzODM0NCB6IiBpZD0ic3ZnXzIiIGNsYXNzPSIiIGZpbGw9IiMxYTFhZWIiIGZpbGwtb3BhY2l0eT0iMSIvPjwvZz48L3N2Zz4=',
- iconSize: [32, 32],
- iconAnchor: [16, 16]
- })
- },
-
- _onHandleDrag: function() {
- var overlay = this._handled,
- formerLatLng = this._handled._corners[this._corner],
- newLatLng = this.getLatLng(),
-
- scale = this._calculateScalingFactor(formerLatLng, newLatLng);
-
- overlay.editing._scaleBy(scale);
-
- overlay.fire('update');
-
- this._handled.editing._showToolbar();
- },
-
- updateHandle: function() {
- this.setLatLng(this._handled._corners[this._corner]);
- },
-
-
-});
-
-L.DistortableImage = L.DistortableImage || {};
-
-var EditOverlayAction = LeafletToolbar.ToolbarAction.extend({
- initialize: function(map, overlay, options) {
- this._overlay = overlay;
- this._map = map;
-
- LeafletToolbar.ToolbarAction.prototype.initialize.call(this, options);
- }
- }),
-
- ToggleTransparency = EditOverlayAction.extend({
- options: {
- toolbarIcon: {
- html: 'opacity ',
- tooltip: 'Toggle Transparency'
- }
- },
-
- addHooks: function() {
- var editing = this._overlay.editing;
-
- editing._toggleTransparency();
- this.disable();
- }
- }),
-
- ToggleOutline = EditOverlayAction.extend({
- initialize: function(map, overlay, options) {
- var edit = overlay.editing,
- icon = edit._outlined ? 'border_clear' : 'border_outer';
-
- options = options || {};
- options.toolbarIcon = {
- html: '' + icon + ' ',
- tooltip: 'Toggle Outline'
- };
-
- EditOverlayAction.prototype.initialize.call(this, map, overlay, options);
- },
-
- addHooks: function() {
- var editing = this._overlay.editing;
-
- editing._toggleOutline();
- this.disable();
- }
- }),
-
- Delete = EditOverlayAction.extend({
- options: {
- toolbarIcon: {
- html: 'delete_forever ',
- tooltip: 'Delete Image'
- }
- },
-
- addHooks: function() {
- var editing = this._overlay.editing;
-
- editing._removeOverlay();
- this.disable();
- }
- }),
-
- ToggleLock = EditOverlayAction.extend({
- initialize: function(map, overlay, options) {
- var edit = overlay.editing,
- icon,
- tooltip;
-
- if (edit._mode === 'lock') {
- icon = 'lock_open';
- tooltip = 'Unlock';
- } else {
- icon = 'lock';
- tooltip = 'Lock';
- }
-
- options = options || {};
- options.toolbarIcon = {
- html: '' + icon + ' ',
- tooltip: tooltip
- };
-
- EditOverlayAction.prototype.initialize.call(this, map, overlay, options);
- },
-
- addHooks: function() {
- var editing = this._overlay.editing;
-
- editing._toggleLock();
- this.disable();
- }
- }),
-
- ToggleRotateScale = EditOverlayAction.extend({
- initialize: function(map, overlay, options) {
- var edit = overlay.editing,
- icon,
- tooltip;
-
- if (edit._mode === 'rotateScale') {
- icon = 'transform';
- tooltip = 'Distort';
- } else {
- icon = 'crop_rotate';
- tooltip = 'Rotate+Scale';
- }
-
- options = options || {};
- options.toolbarIcon = {
- html: '' + icon + ' ',
- tooltip: tooltip
- };
-
- EditOverlayAction.prototype.initialize.call(this, map, overlay, options);
- },
-
- addHooks: function() {
- var editing = this._overlay.editing;
-
- editing._toggleRotateScale();
- this.disable();
- }
- }),
-
- Export = EditOverlayAction.extend({
- options: {
- toolbarIcon: {
- html: 'get_app ',
- tooltip: 'Export Image'
- }
- },
-
- addHooks: function() {
- var editing = this._overlay.editing;
-
- editing._toggleExport();
- this.disable();
- }
- }),
-
- ToggleOrder = EditOverlayAction.extend({
- initialize: function(map, overlay, options) {
- var edit = overlay.editing,
- icon,
- tooltip;
-
- if (edit._toggledImage) {
- icon = 'flip_to_front';
- tooltip = 'Stack to front';
- } else {
- icon = 'flip_to_back';
- tooltip = 'Stack to back';
- }
-
- options = options || {};
- options.toolbarIcon = {
- html: '' + icon + ' ',
- tooltip: tooltip
- };
-
- EditOverlayAction.prototype.initialize.call(this, map, overlay, options);
- },
-
- addHooks: function() {
- var editing = this._overlay.editing;
-
- editing._toggleOrder();
- this.disable();
- }
- }),
-
- EnableEXIF = EditOverlayAction.extend({
- options: {
- toolbarIcon: {
- html: 'explore ',
- tooltip: 'Geolocate Image'
- }
- },
-
- addHooks: function() {
- var image = this._overlay.getElement();
-
- EXIF.getData(image, L.EXIF(image));
- }
- });
-
-L.DistortableImage.EditToolbar = LeafletToolbar.Popup.extend({
- options: {
- actions: [
- ToggleTransparency,
- ToggleOutline,
- ToggleLock,
- ToggleRotateScale,
- ToggleOrder,
- EnableEXIF,
- Export,
- Delete
- ]
- },
-
- // todo: move to some sort of util class, these methods could be useful in future
- _rotateToolbarAngleDeg: function(angle) {
- var div = this._container,
- divStyle = div.style;
-
- var oldTransform = divStyle.transform;
-
- divStyle.transform = oldTransform + "rotate(" + angle + "deg)";
- divStyle.transformOrigin = "1080% 650%";
-
- this._rotateToolbarIcons(angle);
- },
-
- _rotateToolbarIcons: function(angle) {
- var icons = document.querySelectorAll(".fa");
-
- for (var i = 0; i < icons.length; i++) {
- icons.item(i).style.transform = "rotate(" + -angle + "deg)";
- }
- }
-});
-
-L.DistortableImage = L.DistortableImage || {};
-
-L.DistortableImage.Edit = L.Handler.extend({
- options: {
- opacity: 0.7,
- outline: "1px solid red",
- keymap: {
- 'Backspace': '_removeOverlay', // backspace windows / delete mac
- 'CapsLock': '_toggleRotate',
- 'Escape': '_deselect',
- 'd': '_toggleRotateScale',
- 'r': '_toggleRotateScale',
- 'j': '_toggleOrder',
- 'k': '_toggleOrder',
- 'l': '_toggleLock',
- 'o': '_toggleOutline',
- 's': '_toggleScale',
- 't': '_toggleTransparency',
- }
- },
-
- initialize: function(overlay) {
- this._overlay = overlay;
- this._toggledImage = false;
- /* Different actions. */
- var actions = ["distort", "lock", "rotate", "scale", "rotateScale"];
- /* Interaction modes. */
- this._mode = actions[actions.indexOf(this._overlay.options.mode)] || "distort";
- this._selected = this._overlay.options.selected || false;
- this._transparent = false;
- this._outlined = false;
-
- /* generate instance counts */
- this.instance_count = L.DistortableImage.Edit.prototype.instances =
- L.DistortableImage.Edit.prototype.instances ? L.DistortableImage.Edit.prototype.instances + 1 : 1;
- },
-
- /* Run on image selection. */
- addHooks: function() {
- var overlay = this._overlay,
- map = overlay._map,
- keymapper_position;
-
- /* instantiate and render keymapper for one instance only*/
- if (this.instance_count === 1 && overlay.options.keymapper !== false) {
- keymapper_position = overlay.options.keymapper_position || 'topright';
- map.addControl(new L.DistortableImage.Keymapper({ position: keymapper_position }));
- }
-
- /* bring the selected image into view */
- overlay.bringToFront();
-
- this._initHandles();
-
- this._appendHandlesandDragable(this._mode);
-
- if (this._selected) { this._initToolbar(); }
-
- this._overlay._dragStartPoints = {
- 0: L.point(0, 0),
- 1: L.point(0, 0),
- 2: L.point(0, 0),
- 3: L.point(0, 0)
- };
-
- L.DomEvent.on(map, "click", this._deselect, this);
- L.DomEvent.on(overlay._image, "click", this._select, this);
-
- /* Enable hotkeys. */
- L.DomEvent.on(window, "keydown", this._onKeyDown, this);
- },
-
- /* Run on image deselection. */
- removeHooks: function() {
- var overlay = this._overlay,
- map = overlay._map;
-
- L.DomEvent.off(map, "click", this._deselect, this);
- L.DomEvent.off(overlay._image, "click", this._select, this);
-
- // First, check if dragging exists - it may be off due to locking
- if (this.dragging) { this.dragging.disable(); }
- delete this.dragging;
-
- if (this.toolbar) { this._hideToolbar(); }
- if (this.editing) { this.editing.disable(); }
-
- map.removeLayer(this._handles[this._mode]);
-
- /* Disable hotkeys. */
- L.DomEvent.off(window, "keydown", this._onKeyDown, this);
- },
-
- _initHandles: function() {
- var overlay = this._overlay,
- i;
-
- this._lockHandles = L.layerGroup();
- for (i = 0; i < 4; i++) {
- this._lockHandles.addLayer(
- new L.LockHandle(overlay, i, { draggable: false })
- );
- }
-
- this._distortHandles = L.layerGroup();
- for (i = 0; i < 4; i++) {
- this._distortHandles.addLayer(new L.DistortHandle(overlay, i));
- }
-
- this._rotateHandles = L.layerGroup(); // individual rotate
- for (i = 0; i < 4; i++) {
- this._rotateHandles.addLayer(new L.RotateHandle(overlay, i));
- }
-
- this._scaleHandles = L.layerGroup();
- for (i = 0; i < 4; i++) {
- this._scaleHandles.addLayer(new L.ScaleHandle(overlay, i));
- }
-
- this._rotateScaleHandles = L.layerGroup(); // handle includes rotate AND scale
- for (i = 0; i < 4; i++) {
- this._rotateScaleHandles.addLayer(new L.RotateScaleHandle(overlay, i));
- }
-
- this._handles = {
- lock: this._lockHandles,
- distort: this._distortHandles,
- rotateScale: this._rotateScaleHandles,
- scale: this._scaleHandles,
- rotate: this._rotateHandles
- };
- },
-
- _appendHandlesandDragable: function (mode) {
- var overlay = this._overlay,
- map = overlay._map;
-
- map.addLayer(this._handles[mode]);
-
- if (mode !== 'lock') {
- if (!this._selected) {
- this._handles[mode].eachLayer(function (layer) {
- layer.setOpacity(0);
- layer.dragging.disable();
- layer.options.draggable = false;
- });
- }
-
- this._enableDragging();
- }
- },
-
-
- _initToolbar: function () {
- this._showToolbar();
- },
-
- confirmDelete: function() {
- return window.confirm("Are you sure you want to delete?");
- },
-
- _rotateBy: function(angle) {
- var overlay = this._overlay,
- map = overlay._map,
- center = map.latLngToLayerPoint(overlay.getCenter()),
- i,
- p,
- q;
-
- for (i = 0; i < 4; i++) {
- p = map.latLngToLayerPoint(overlay._corners[i]).subtract(center);
- q = L.point(
- Math.cos(angle) * p.x - Math.sin(angle) * p.y,
- Math.sin(angle) * p.x + Math.cos(angle) * p.y
- );
- overlay._corners[i] = map.layerPointToLatLng(q.add(center));
- }
-
- overlay._reset();
- },
-
- _scaleBy: function(scale) {
- var overlay = this._overlay,
- map = overlay._map,
- center = map.latLngToLayerPoint(overlay.getCenter()),
- i,
- p;
-
- for (i = 0; i < 4; i++) {
- p = map
- .latLngToLayerPoint(overlay._corners[i])
- .subtract(center)
- .multiplyBy(scale)
- .add(center);
- overlay._corners[i] = map.layerPointToLatLng(p);
- }
-
- overlay._reset();
- },
-
- _enableDragging: function() {
- var overlay = this._overlay,
- map = overlay._map;
-
- this.dragging = new L.Draggable(overlay._image);
- this.dragging.enable();
-
- /* Hide toolbars and markers while dragging; click will re-show it */
- this.dragging.on("dragstart", function() {
- overlay.fire("dragstart");
- this._hideToolbar();
- },this);
-
- /*
- * Adjust default behavior of L.Draggable.
- * By default, L.Draggable overwrites the CSS3 distort transform
- * that we want when it calls L.DomUtil.setPosition.
- */
- this.dragging._updatePosition = function() {
- var delta = this._newPos.subtract(
- map.latLngToLayerPoint(overlay._corners[0])
- ),
- currentPoint,
- i;
-
- this.fire("predrag");
-
- for (i = 0; i < 4; i++) {
- currentPoint = map.latLngToLayerPoint(overlay._corners[i]);
- overlay._corners[i] = map.layerPointToLatLng(currentPoint.add(delta));
- }
-
- overlay._reset();
- overlay.fire("update");
- overlay.fire("drag");
-
- this.fire("drag");
- };
- },
-
- _onKeyDown: function(event) {
- var keymap = this.options.keymap,
- handlerName = keymap[event.key];
-
- if (this[handlerName] !== undefined && this._overlay.options.suppressToolbar !== true) {
- if (this._selected) {
- this[handlerName].call(this);
- }
- }
- },
-
- _toggleRotateScale: function() {
- var map = this._overlay._map;
-
- if (this._mode === "lock") { return; }
-
- map.removeLayer(this._handles[this._mode]);
-
- /* Switch mode. */
- if (this._mode === "rotateScale") { this._mode = "distort"; }
- else { this._mode = "rotateScale"; }
-
- map.addLayer(this._handles[this._mode]);
-
- this._showToolbar();
- },
-
- _toggleScale: function() {
- var map = this._overlay._map;
-
- if (this._mode === "lock") { return; }
-
- map.removeLayer(this._handles[this._mode]);
-
- if (this._mode === "scale") { this._mode = "distort"; }
- else { this._mode = "scale"; }
-
- map.addLayer(this._handles[this._mode]);
-
- },
-
- _toggleRotate: function() {
- var map = this._overlay._map;
-
- if (this._mode === "lock") { return; }
-
- map.removeLayer(this._handles[this._mode]);
- if (this._mode === "rotate") { this._mode = "distort"; }
- else { this._mode = "rotate"; }
-
- map.addLayer(this._handles[this._mode]);
- },
-
- _toggleTransparency: function() {
- var image = this._overlay._image,
- opacity;
-
- this._transparent = !this._transparent;
- opacity = this._transparent ? this.options.opacity : 1;
-
- L.DomUtil.setOpacity(image, opacity);
- image.setAttribute("opacity", opacity);
- },
-
- _toggleOutline: function() {
- var image = this._overlay._image,
- opacity,
- outline;
-
- this._outlined = !this._outlined;
- outline = this._outlined ? this.options.outline : "none";
-
- L.DomUtil.setOpacity(image, opacity);
- image.setAttribute("opacity", opacity);
-
- image.style.outline = outline;
-
- this._showToolbar();
- },
-
- _sendUp: function() {
- this._overlay.bringToFront();
- },
-
- _sendDown: function() {
- this._overlay.bringToBack();
- },
-
- _toggleLock: function() {
- var map = this._overlay._map;
-
- map.removeLayer(this._handles[this._mode]);
- /* Switch mode. */
- if (this._mode === "lock") {
- this._mode = "distort";
- this._enableDragging();
- } else {
- this._mode = "lock";
- if (this.dragging) { this.dragging.disable(); }
- delete this.dragging;
- }
-
- map.addLayer(this._handles[this._mode]);
-
- this._showToolbar();
- },
-
- _select: function(event) {
- this._selected = true;
- this._showToolbar();
- this._showMarkers();
-
- if (event) { L.DomEvent.stopPropagation(event); }
- },
-
- _deselect: function() {
- this._selected = false;
- this._hideToolbar();
- if (this._mode !== "lock") {
- this._hideMarkers();
- }
- },
-
- _hideToolbar: function() {
- var map = this._overlay._map;
-
- if (this.toolbar) {
- map.removeLayer(this.toolbar);
- this.toolbar = false;
- }
- },
-
- _showMarkers: function() {
- if (this._mode === "lock") { return; }
-
- var currentHandle = this._handles[this._mode];
-
- currentHandle.eachLayer(function(layer) {
- var drag = layer.dragging,
- opts = layer.options;
-
- layer.setOpacity(1);
- if (drag) { drag.enable(); }
- if (opts.draggable) { opts.draggable = true; }
- });
- },
-
- _hideMarkers: function() {
- if (!this._handles) { this._initHandles(); } // workaround for race condition w/ feature group
-
- var mode = this._mode,
- currentHandle = this._handles[mode];
-
- currentHandle.eachLayer(function (layer) {
- var drag = layer.dragging,
- opts = layer.options;
-
- if (mode !== 'lock') {
- layer.setOpacity(0);
- }
- if (drag) { drag.disable(); }
- if (opts.draggable) { opts.draggable = false; }
- });
- },
-
- // TODO: toolbar for multiple image selection
- _showToolbar: function() {
- var overlay = this._overlay,
- map = overlay._map;
-
- //Find the topmost point on the image.
- var corners = overlay.getCorners();
- var maxLat = -Infinity;
- for (var i = 0; i < corners.length; i++) {
- if (corners[i].lat > maxLat) {
- maxLat = corners[i].lat;
- }
- }
-
- //Longitude is based on the centroid of the image.
- var raised_point = overlay.getCenter();
- raised_point.lat = maxLat;
-
- if (this._overlay.options.suppressToolbar !== true) {
- try {
- this.toolbar = new L.DistortableImage.EditToolbar(raised_point).addTo(map, overlay);
- overlay.fire('toolbar:created');
- }
- catch (e) {}
- }
- },
-
- _removeOverlay: function () {
- var overlay = this._overlay,
- eventParents = overlay._eventParents;
-
- if (this._mode === "lock") { return; }
-
- var choice = this.confirmDelete();
- if (!choice) { return; }
-
- this._hideToolbar();
- if (eventParents) {
- var eP = eventParents[Object.keys(eventParents)[0]];
- eP.removeLayer(overlay);
- } else {
- overlay._map.removeLayer(overlay);
- }
- },
-
- // compare this to using overlay zIndex
- _toggleOrder: function () {
- if (this._toggledImage) {
- this._toggledImage = false;
- this._overlay.bringToFront();
- } else {
- this._toggledImage = true;
- this._overlay.bringToBack();
- }
-
- this._showToolbar();
- },
-
- // Based on https://github.com/publiclab/mapknitter/blob/8d94132c81b3040ae0d0b4627e685ff75275b416/app/assets/javascripts/mapknitter/Map.js#L47-L82
- _toggleExport: function() {
- var map = this._overlay._map;
- var overlay = this._overlay;
-
- // make a new image
- var downloadable = new Image();
-
- downloadable.id = downloadable.id || "tempId12345";
- $("body").append(downloadable);
-
- downloadable.onload = function onLoadDownloadableImage() {
- var height = downloadable.height,
- width = downloadable.width,
- nw = map.latLngToLayerPoint(overlay._corners[0]),
- ne = map.latLngToLayerPoint(overlay._corners[1]),
- sw = map.latLngToLayerPoint(overlay._corners[2]),
- se = map.latLngToLayerPoint(overlay._corners[3]);
-
- // I think this is to move the image to the upper left corner,
- // jywarren: i think we may need these or the image goes off the edge of the canvas
- // jywarren: but these seem to break the distortion math...
-
- // jywarren: i think it should be rejiggered so it
- // finds the most negative values of x and y and then
- // adds those to all coordinates
-
- //nw.x -= nw.x;
- //ne.x -= nw.x;
- //se.x -= nw.x;
- //sw.x -= nw.x;
-
- //nw.y -= nw.y;
- //ne.y -= nw.y;
- //se.y -= nw.y;
- //sw.y -= nw.y;
-
- // run once warping is complete
- downloadable.onload = function() {
- $(downloadable).remove();
- };
-
- if (window && window.hasOwnProperty("warpWebGl")) {
- warpWebGl(
- downloadable.id,
- [0, 0, width, 0, width, height, 0, height],
- [nw.x, nw.y, ne.x, ne.y, se.x, se.y, sw.x, sw.y],
- true // trigger download
- );
- }
- };
-
- downloadable.src = overlay.options.fullResolutionSrc || overlay._image.src;
- },
-
- toggleIsolate: function() {
- // this.isolated = !this.isolated;
- // if (this.isolated) {
- // $.each($L.images,function(i,img) {
- // img.hidden = false;
- // img.setOpacity(1);
- // });
- // } else {
- // $.each($L.images,function(i,img) {
- // img.hidden = true;
- // img.setOpacity(0);
- // });
- // }
- // this.hidden = false;
- // this.setOpacity(1);
- }
-});
-
-L.DistortableImageOverlay.addInitHook(function() {
- this.editing = new L.DistortableImage.Edit(this);
-
- if (this.options.editable) {
- L.DomEvent.on(this._image, "load", this.editing.enable, this.editing);
- }
-
- this.on('remove', function () {
- if (this.editing) { this.editing.disable(); }
- });
-});
-
+L.DomUtil = L.extend(L.DomUtil, {
+ getMatrixString: function(m) {
+ var is3d = L.Browser.webkit3d || L.Browser.gecko3d || L.Browser.ie3d,
+
+ /*
+ * Since matrix3d takes a 4*4 matrix, we add in an empty row and column, which act as the identity on the z-axis.
+ * See:
+ * http://franklinta.com/2014/09/08/computing-css-matrix3d-transforms/
+ * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#M.C3.B6bius'_homogeneous_coordinates_in_projective_geometry
+ */
+ matrix = [
+ m[0], m[3], 0, m[6],
+ m[1], m[4], 0, m[7],
+ 0, 0, 1, 0,
+ m[2], m[5], 0, m[8]
+ ],
+
+ str = is3d ? 'matrix3d(' + matrix.join(',') + ')' : '';
+
+ if (!is3d) {
+ console.log('Your browser must support 3D CSS transforms in order to use DistortableImageOverlay.');
+ }
+
+ return str;
+ },
+
+ getRotateString: function(angle, units) {
+ var is3d = L.Browser.webkit3d || L.Browser.gecko3d || L.Browser.ie3d,
+ open = 'rotate' + (is3d ? '3d' : '') + '(',
+ rotateString = (is3d ? '0, 0, 1, ' : '') + angle + units;
+
+ return open + rotateString + ')';
+ },
+
+ toggleClass: function(el, className) {
+ var c = className;
+ return this.hasClass(el, c) ? this.removeClass(el, c) : this.addClass(el, c);
+ }
+
+});
+
+L.ImageUtil = {
+
+ getCmPerPixel: function(overlay) {
+ var map = overlay._map;
+
+ var dist = map
+ .latLngToLayerPoint(overlay.getCorner(0))
+ .distanceTo(map.latLngToLayerPoint(overlay.getCorner(1)));
+
+ return (dist * 100) / overlay._image.width;
+ }
+
+};
+L.Map.include({
+ _newLayerPointToLatLng: function(point, newZoom, newCenter) {
+ var topLeft = L.Map.prototype._getNewTopLeftPoint.call(this, newCenter, newZoom)
+ .add(L.Map.prototype._getMapPanePos.call(this));
+ return this.unproject(point.add(topLeft), newZoom);
+ }
+});
+L.MatrixUtil = {
+
+ // Compute the adjugate of m
+ adj: function(m) {
+ return [
+ m[4]*m[8]-m[5]*m[7], m[2]*m[7]-m[1]*m[8], m[1]*m[5]-m[2]*m[4],
+ m[5]*m[6]-m[3]*m[8], m[0]*m[8]-m[2]*m[6], m[2]*m[3]-m[0]*m[5],
+ m[3]*m[7]-m[4]*m[6], m[1]*m[6]-m[0]*m[7], m[0]*m[4]-m[1]*m[3]
+ ];
+ },
+
+ // multiply two 3*3 matrices
+ multmm: function(a, b) {
+ var c = [],
+ i;
+
+ for (i = 0; i < 3; i++) {
+ for (var j = 0; j < 3; j++) {
+ var cij = 0;
+ for (var k = 0; k < 3; k++) {
+ cij += a[3*i + k]*b[3*k + j];
+ }
+ c[3*i + j] = cij;
+ }
+ }
+ return c;
+ },
+
+ // multiply a 3*3 matrix and a 3-vector
+ multmv: function(m, v) {
+ return [
+ m[0]*v[0] + m[1]*v[1] + m[2]*v[2],
+ m[3]*v[0] + m[4]*v[1] + m[5]*v[2],
+ m[6]*v[0] + m[7]*v[1] + m[8]*v[2]
+ ];
+ },
+
+ // multiply a scalar and a 3*3 matrix
+ multsm: function(s, m) {
+ var matrix = [];
+
+ for (var i = 0, l = m.length; i < l; i++) {
+ matrix.push(s*m[i]);
+ }
+
+ return matrix;
+ },
+
+ basisToPoints: function(x1, y1, x2, y2, x3, y3, x4, y4) {
+ var m = [
+ x1, x2, x3,
+ y1, y2, y3,
+ 1, 1, 1
+ ],
+ v = L.MatrixUtil.multmv(L.MatrixUtil.adj(m), [x4, y4, 1]);
+
+ return L.MatrixUtil.multmm(m, [
+ v[0], 0, 0,
+ 0, v[1], 0,
+ 0, 0, v[2]
+ ]);
+ },
+
+
+ project: function(m, x, y) {
+ var v = L.MatrixUtil.multmv(m, [x, y, 1]);
+ return [v[0]/v[2], v[1]/v[2]];
+ },
+
+ general2DProjection: function(
+ x1s, y1s, x1d, y1d,
+ x2s, y2s, x2d, y2d,
+ x3s, y3s, x3d, y3d,
+ x4s, y4s, x4d, y4d
+ ) {
+ var s = L.MatrixUtil.basisToPoints(x1s, y1s, x2s, y2s, x3s, y3s, x4s, y4s),
+ d = L.MatrixUtil.basisToPoints(x1d, y1d, x2d, y2d, x3d, y3d, x4d, y4d),
+ m = L.MatrixUtil.multmm(d, L.MatrixUtil.adj(s));
+
+ /*
+ * Normalize to the unique matrix with m[8] == 1.
+ * See: http://franklinta.com/2014/09/08/computing-css-matrix3d-transforms/
+ */
+ return L.MatrixUtil.multsm(1/m[8], m);
+ }
+};
+L.TrigUtil = {
+
+ calcAngleDegrees: function(x, y) {
+ var pointAngle = Math.atan2(y, x);
+ return this.radiansToDegrees(pointAngle);
+ },
+
+ radiansToDegrees: function(angle) {
+ return angle * 180 / Math.PI;
+ },
+
+ degreesToRadians: function(angle) {
+ return angle * Math.PI / 180;
+ }
+
+};
+L.DistortableImageOverlay = L.ImageOverlay.extend({
+
+ options: {
+ alt: "",
+ height: 200,
+ crossOrigin: true,
+ // todo: find ideal number to prevent distortions during RotateScale, and make it dynamic (remove hardcoding)
+ edgeMinWidth: 520
+ },
+
+ initialize: function(url, options) {
+ this._toolArray = L.DistortableImage.EditToolbarDefaults;
+ this.edgeMinWidth = this.options.edgeMinWidth;
+ this._url = url;
+ this.rotation = 0;
+ // window.rotation = this.rotation;
+ L.DistortableImage._options = options;
+
+ L.setOptions(this, options);
+ },
+
+ onAdd: function(map) {
+ /* Copied from L.ImageOverlay */
+ this._map = map;
+
+ if (!this._image) { this._initImage(); }
+ if (!this._events) { this._initEvents(); }
+
+ map._panes.overlayPane.appendChild(this._image);
+
+ map.on("viewreset", this._reset, this);
+ /* End copied from L.ImageOverlay */
+
+ /* Use provided corners if available */
+ if (this.options.corners) {
+ this._corners = this.options.corners;
+ if (map.options.zoomAnimation && L.Browser.any3d) {
+ map.on("zoomanim", this._animateZoom, this);
+ }
+
+ /* This reset happens before image load; it allows
+ * us to place the image on the map earlier with
+ * "guessed" dimensions. */
+ this._reset();
+ }
+
+ /* Have to wait for the image to load because
+ * we need to access its width and height. */
+ L.DomEvent.on(this._image, "load", function() {
+ this._initImageDimensions();
+ this._reset();
+ /* Initialize default corners if not already set */
+ if (!this._corners) {
+ if (map.options.zoomAnimation && L.Browser.any3d) {
+ map.on("zoomanim", this._animateZoom, this);
+ }
+ }
+ }, this);
+
+ this.fire("add");
+ },
+
+ onRemove: function(map) {
+ this.fire("remove");
+
+ L.ImageOverlay.prototype.onRemove.call(this, map);
+ },
+
+ _initImage: function() {
+ L.ImageOverlay.prototype._initImage.call(this);
+
+ L.extend(this._image, {
+ alt: this.options.alt
+ });
+ },
+
+ _addTool: function(tool) {
+ this._toolArray.push(tool);
+ L.DistortableImage.EditToolbar = LeafletToolbar.Popup.extend({
+ options: {
+ actions: this._toolArray
+ }
+ });
+ },
+
+ _initImageDimensions: function() {
+ var map = this._map,
+ originalImageWidth = L.DomUtil.getStyle(this._image, "width"),
+ originalImageHeight = L.DomUtil.getStyle(this._image, "height"),
+ aspectRatio =
+ parseInt(originalImageWidth) / parseInt(originalImageHeight),
+ imageHeight = this.options.height,
+ imageWidth = parseInt(aspectRatio * imageHeight),
+ center = map.latLngToContainerPoint(map.getCenter()),
+ offset = L.point(imageWidth, imageHeight).divideBy(2);
+
+ if (this.options.corners) {
+ this._corners = this.options.corners;
+ } else {
+ this._corners = [
+ map.containerPointToLatLng(center.subtract(offset)),
+ map.containerPointToLatLng(
+ center.add(L.point(offset.x, -offset.y))
+ ),
+ map.containerPointToLatLng(
+ center.add(L.point(-offset.x, offset.y))
+ ),
+ map.containerPointToLatLng(center.add(offset))
+ ];
+ }
+ this._initialDimensions = { 'height': imageHeight, 'width': imageWidth, 'offset': offset };
+ },
+
+ _initEvents: function() {
+ this._events = ["click"];
+
+ for (var i = 0, l = this._events.length; i < l; i++) {
+ L.DomEvent.on(this._image, this._events[i], this._fireMouseEvent, this);
+ }
+ },
+
+ /* See src/layer/vector/Path.SVG.js in the Leaflet source. */
+ _fireMouseEvent: function(event) {
+ if (!this.hasEventListeners(event.type)) { return; }
+
+ var map = this._map,
+ containerPoint = map.mouseEventToContainerPoint(event),
+ layerPoint = map.containerPointToLayerPoint(containerPoint),
+ latlng = map.layerPointToLatLng(layerPoint);
+
+ this.fire(event.type, {
+ latlng: latlng,
+ layerPoint: layerPoint,
+ containerPoint: containerPoint,
+ originalEvent: event
+ });
+ },
+
+ _updateCorner: function(corner, latlng) {
+ this._corners[corner] = latlng;
+ this._reset();
+ },
+
+ // fires a reset after all corner positions are updated instead of after each one (above). Use for translating
+ _updateCorners: function(latlngObj) {
+ var i = 0;
+ for (var k in latlngObj) {
+ this._corners[i] = latlngObj[k];
+ i += 1;
+ }
+
+ this._reset();
+ },
+
+ _updateCornersFromPoints: function(pointsObj) {
+ var map = this._map;
+ var i = 0;
+ for (var k in pointsObj) {
+ this._corners[i] = map.layerPointToLatLng(pointsObj[k]);
+ i += 1;
+ }
+
+ this._reset();
+ },
+
+ /* Copied from Leaflet v0.7 https://github.com/Leaflet/Leaflet/blob/66282f14bcb180ec87d9818d9f3c9f75afd01b30/src/dom/DomUtil.js#L189-L199 */
+ /* since L.DomUtil.getTranslateString() is deprecated in Leaflet v1.0 */
+ _getTranslateString: function(point) {
+ // on WebKit browsers (Chrome/Safari/iOS Safari/Android) using translate3d instead of translate
+ // makes animation smoother as it ensures HW accel is used. Firefox 13 doesn't care
+ // (same speed either way), Opera 12 doesn't support translate3d
+
+ var is3d = L.Browser.webkit3d,
+ open = "translate" + (is3d ? "3d" : "") + "(",
+ close = (is3d ? ",0" : "") + ")";
+
+ return open + point.x + "px," + point.y + "px" + close;
+ },
+
+ _reset: function() {
+ var map = this._map,
+ image = this._image,
+ latLngToLayerPoint = L.bind(map.latLngToLayerPoint, map),
+ transformMatrix = this._calculateProjectiveTransform(latLngToLayerPoint),
+ topLeft = latLngToLayerPoint(this._corners[0]),
+ warp = L.DomUtil.getMatrixString(transformMatrix),
+ translation = this._getTranslateString(topLeft);
+
+ /* See L.DomUtil.setPosition. Mainly for the purposes of L.Draggable. */
+ image._leaflet_pos = topLeft;
+
+ image.style[L.DomUtil.TRANSFORM] = [translation, warp].join(" ");
+
+ /* Set origin to the upper-left corner rather than the center of the image, which is the default. */
+ image.style[L.DomUtil.TRANSFORM + "-origin"] = "0 0 0";
+ },
+
+ /*
+ * Calculates the transform string that will be correct *at the end* of zooming.
+ * Leaflet then generates a CSS3 animation between the current transform and
+ * future transform which makes the transition appear smooth.
+ */
+ _animateZoom: function(event) {
+ var map = this._map,
+ image = this._image,
+ latLngToNewLayerPoint = function(latlng) {
+ return map._latLngToNewLayerPoint(latlng, event.zoom, event.center);
+ },
+ transformMatrix = this._calculateProjectiveTransform(
+ latLngToNewLayerPoint
+ ),
+ topLeft = latLngToNewLayerPoint(this._corners[0]),
+ warp = L.DomUtil.getMatrixString(transformMatrix),
+ translation = this._getTranslateString(topLeft);
+
+ /* See L.DomUtil.setPosition. Mainly for the purposes of L.Draggable. */
+ image._leaflet_pos = topLeft;
+
+ if (!L.Browser.gecko) {
+ image.style[L.DomUtil.TRANSFORM] = [translation, warp].join(" ");
+ }
+ },
+
+ getCorners: function() {
+ return this._corners;
+ },
+
+ getCorner: function(i) {
+ return this._corners[i];
+ },
+
+ /*
+ * Calculates the centroid of the image.
+ * See http://stackoverflow.com/questions/6149175/logical-question-given-corners-find-center-of-quadrilateral
+ */
+ getCenter: function(ll2c, c2ll) {
+ var map = this._map,
+ latLngToCartesian = ll2c ? ll2c : map.latLngToLayerPoint,
+ cartesianToLatLng = c2ll ? c2ll : map.layerPointToLatLng,
+ nw = latLngToCartesian.call(map, this._corners[0]),
+ ne = latLngToCartesian.call(map, this._corners[1]),
+ se = latLngToCartesian.call(map, this._corners[2]),
+ sw = latLngToCartesian.call(map, this._corners[3]),
+ nmid = nw.add(ne.subtract(nw).divideBy(2)),
+ smid = sw.add(se.subtract(sw).divideBy(2));
+
+ return cartesianToLatLng.call(
+ map,
+ nmid.add(smid.subtract(nmid).divideBy(2))
+ );
+ },
+
+ // Use for translation calculations - for translation the delta for 1 corner applies to all 4
+ _calcCornerPointDelta: function() {
+ return this._dragStartPoints[0].subtract(this._dragPoints[0]);
+ },
+
+ _calcCenterTwoCornerPoints: function(topLeft, topRight) {
+ var toolPoint = { x: "", y: "" };
+
+ toolPoint.x = topRight.x + (topLeft.x - topRight.x) / 2;
+ toolPoint.y = topRight.y + (topLeft.y - topRight.y) / 2;
+
+ return toolPoint;
+ },
+
+ _calculateProjectiveTransform: function(latLngToCartesian) {
+ /* Setting reasonable but made-up image defaults
+ * allow us to place images on the map before
+ * they've finished downloading. */
+ var offset = latLngToCartesian(this._corners[0]),
+ w = this._image.offsetWidth || 500,
+ h = this._image.offsetHeight || 375,
+ c = [],
+ j;
+ /* Convert corners to container points (i.e. cartesian coordinates). */
+ for (j = 0; j < this._corners.length; j++) {
+ c.push(latLngToCartesian(this._corners[j])._subtract(offset));
+ }
+
+ /*
+ * This matrix describes the action of the CSS transform on each corner of the image.
+ * It maps from the coordinate system centered at the upper left corner of the image
+ * to the region bounded by the latlngs in this._corners.
+ * For example:
+ * 0, 0, c[0].x, c[0].y
+ * says that the upper-left corner of the image maps to the first latlng in this._corners.
+ */
+ return L.MatrixUtil.general2DProjection(
+ 0, 0, c[0].x, c[0].y,
+ w, 0, c[1].x, c[1].y,
+ 0, h, c[2].x, c[2].y,
+ w, h, c[3].x, c[3].y
+ );
+ }
+
+});
+
+L.distortableImageOverlay = function(id, options) {
+ return new L.DistortableImageOverlay(id, options);
+};
+
+
+
+
+L.DistortableCollection = L.FeatureGroup.extend({
+ onAdd: function(map) {
+ L.FeatureGroup.prototype.onAdd.call(this, map);
+
+ this._map = map;
+
+ L.DomEvent.on(document, "keydown", this._onKeyDown, this);
+ L.DomEvent.on(map, "click", this._deselectAll, this);
+
+ /**
+ * the box zoom override works, but there is a bug involving click event propogation.
+ * keeping uncommented for now so that it isn't used as a multi-select mechanism
+ */
+
+ // L.DomEvent.on(map, "boxzoomend", this._addSelections, this);
+
+ var lastSelected;
+
+ this.eachLayer(function(layer) {
+ L.DomEvent.on(layer._image, "mousedown", this._deselectOthers, this);
+ L.DomEvent.on(layer, "dragstart", this._dragStartMultiple, this);
+ L.DomEvent.on(layer, "drag", this._dragMultiple, this);
+
+ if (layer.options.selected) {
+ layer.editing._deselect();
+ lastSelected = layer.editing;
+ }
+ }, this);
+
+ if (lastSelected) {
+ lastSelected._select();
+ }
+ },
+
+ onRemove: function() {
+ var map = this._map;
+
+ L.DomEvent.off(document, "keydown", this._onKeyDown, this);
+ L.DomEvent.off(map, "click", this._deselectAll, this);
+ // L.DomEvent.off(map, "boxzoomend", this._addSelections, this);
+
+ this.eachLayer(function(layer) {
+ L.DomEvent.off(layer._image, "mousedown", this._deselectOthers, this);
+ L.DomEvent.off(layer, "dragstart", this._dragStartMultiple, this);
+ L.DomEvent.off(layer, "drag", this._dragMultiple, this);
+ }, this);
+ },
+
+ isSelected: function(overlay) {
+ return L.DomUtil.hasClass(overlay.getElement(), "selected");
+ },
+
+ _toggleMultiSelect: function(event, edit) {
+ if (edit._mode === "lock") { return; }
+
+ if (event.metaKey || event.ctrlKey) {
+ L.DomUtil.toggleClass(event.target, "selected");
+ }
+ },
+
+ _deselectOthers: function(event) {
+ this.eachLayer(function(layer) {
+ var edit = layer.editing;
+ if (layer.getElement() !== event.target) {
+ edit._deselect();
+ } else {
+ this._toggleMultiSelect(event, edit);
+ }
+ }, this);
+
+ L.DomEvent.stopPropagation(event);
+ },
+
+ _addSelections: function(e) {
+ var box = e.boxZoomBounds,
+ i = 0;
+
+ this.eachLayer(function(layer) {
+ var edit = layer.editing;
+
+ if (edit.toolbar) { edit._hideToolbar(); }
+
+ for (i = 0; i < 4; i++) {
+ if (box.contains(layer.getCorner(i)) && edit._mode !== "lock") {
+ L.DomUtil.addClass(layer.getElement(), "selected");
+ break;
+ }
+ }
+ });
+ },
+
+ _getAvgCmPerPixel: function(imgs) {
+ var reduce = imgs.reduce(function(sum, img) {
+ return sum + img.cm_per_pixel;
+ }, 0);
+ return reduce / imgs.length;
+ },
+
+ _generateExportJson: function() {
+ var json = {};
+ json.images = [];
+
+ this.eachLayer(function(layer) {
+ if (this.isSelected(layer)) {
+ json.images.push({
+ id: this.getLayerId(layer),
+ src: layer._image.src,
+ nodes: layer.getCorners(),
+ cm_per_pixel: L.ImageUtil.getCmPerPixel(layer)
+ });
+ }
+ }, this);
+
+ json.avg_cm_per_pixel = this._getAvgCmPerPixel(json.images);
+
+ return json;
+ },
+
+ _runExport: function(collection) {
+ collection = collection || this._generateExportJson();
+ $.ajax({
+ url: "http://export.mapknitter.org/export",
+ crossDomain: true,
+ type: "POST",
+ data: {
+ collection: JSON.stringify(collection.images),
+ scale: 30
+ },
+ success: function _getStatusJson(data) {
+ console.log(data);
+ $.ajax("http://export.mapknitter.org" + data, {
+ type: "GET",
+ crossDomain: true
+ }).done(function(data) {
+ console.log(data);
+ });
+ }
+ });
+ },
+
+ _onKeyDown: function(e) {
+ if (e.key === "Escape") {
+ this._deselectAll(e);
+ }
+ if (e.key === "Backspace") {
+ this._removeFromGroup(e);
+ }
+ },
+
+ _dragStartMultiple: function(event) {
+ var overlay = event.target,
+ i;
+
+ if (!this.isSelected(overlay)) { return; }
+
+ this.eachLayer(function(layer) {
+ var edit = layer.editing;
+ edit._deselect();
+
+ for (i = 0; i < 4; i++) {
+ layer._dragStartPoints[i] = layer._map.latLngToLayerPoint(
+ layer.getCorner(i)
+ );
+ }
+ });
+ },
+
+ _dragMultiple: function(event) {
+ var overlay = event.target,
+ map = this._map,
+ i;
+
+ if (!this.isSelected(overlay)) { return; }
+
+ overlay._dragPoints = {};
+
+ for (i = 0; i < 4; i++) {
+ overlay._dragPoints[i] = map.latLngToLayerPoint(overlay.getCorner(i));
+ }
+
+ var cpd = overlay._calcCornerPointDelta();
+
+ this._updateCollectionFromPoints(cpd, overlay);
+ },
+
+ _deselectAll: function(event) {
+ this.eachLayer(function(layer) {
+ var edit = layer.editing;
+ L.DomUtil.removeClass(layer.getElement(), "selected");
+ edit._deselect();
+ });
+
+ L.DomEvent.stopPropagation(event);
+ },
+
+ _removeFromGroup: function(e) {
+ this.eachLayer(function(layer) {
+ var edit = layer.editing;
+ if (edit._selected && edit._mode !== "lock") {
+ var choice = edit.confirmDelete();
+ if (choice) {
+ edit._selected = false;
+ this.removeLayer(layer);
+ } else {
+ L.DomEvent.stopPropagation(e);
+ return;
+ }
+ }
+ }, this);
+ },
+ /**
+ * images in 'lock' mode are included in this feature group collection for functionalities
+ * such as export, but are filtered out for editing / dragging here
+ */
+ _calcCollectionFromPoints: function(cpd, overlay) {
+ var layersToMove = [],
+ p = new L.Transformation(1, -cpd.x, 1, -cpd.y);
+
+ this.eachLayer(function(layer) {
+ if (
+ layer !== overlay &&
+ layer.editing._mode !== "lock" &&
+ this.isSelected(layer)
+ ) {
+ layer._cpd = {};
+
+ layer._cpd.val0 = p.transform(layer._dragStartPoints[0]);
+ layer._cpd.val1 = p.transform(layer._dragStartPoints[1]);
+ layer._cpd.val2 = p.transform(layer._dragStartPoints[2]);
+ layer._cpd.val3 = p.transform(layer._dragStartPoints[3]);
+
+ layersToMove.push(layer);
+ }
+ }, this);
+
+ return layersToMove;
+ },
+
+ /**
+ * cpd === cornerPointDelta
+ */
+ _updateCollectionFromPoints: function(cpd, overlay) {
+ var layersToMove = this._calcCollectionFromPoints(cpd, overlay);
+
+ layersToMove.forEach(function(layer) {
+ layer._updateCornersFromPoints(layer._cpd);
+ layer.fire("update");
+ }, this);
+ }
+});
+
+L.distortableCollection = function(id, options) {
+ return new L.DistortableCollection(id, options);
+};
+
+L.EXIF = function getEXIFdata(img) {
+ if (Object.keys(EXIF.getAllTags(img)).length !== 0) {
+ console.log(EXIF.getAllTags(img));
+ var GPS = EXIF.getAllTags(img),
+ altitude;
+
+ /* If the lat/lng is available. */
+ if (
+ typeof GPS.GPSLatitude !== "undefined" &&
+ typeof GPS.GPSLongitude !== "undefined"
+ ) {
+ // sadly, encoded in [degrees,minutes,seconds]
+ // primitive value = GPS.GPSLatitude[x].numerator
+ var lat =
+ GPS.GPSLatitude[0] +
+ GPS.GPSLatitude[1] / 60 +
+ GPS.GPSLatitude[2] / 3600;
+ var lng =
+ GPS.GPSLongitude[0] +
+ GPS.GPSLongitude[1] / 60 +
+ GPS.GPSLongitude[2] / 3600;
+
+ if (GPS.GPSLatitudeRef !== "N") {
+ lat = lat * -1;
+ }
+ if (GPS.GPSLongitudeRef === "W") {
+ lng = lng * -1;
+ }
+ }
+
+ // Attempt to use GPS compass heading; will require
+ // some trig to calc corner points, which you can find below:
+
+ var angle = 0;
+ // "T" refers to "True north", so -90.
+ if (GPS.GPSImgDirectionRef === "T") {
+ angle =
+ (Math.PI / 180) *
+ (GPS.GPSImgDirection.numerator / GPS.GPSImgDirection.denominator - 90);
+ }
+ // "M" refers to "Magnetic north"
+ else if (GPS.GPSImgDirectionRef === "M") {
+ angle =
+ (Math.PI / 180) *
+ (GPS.GPSImgDirection.numerator / GPS.GPSImgDirection.denominator - 90);
+ } else {
+ console.log("No compass data found");
+ }
+
+ console.log("Orientation:", GPS.Orientation);
+
+ /* If there is orientation data -- i.e. landscape/portrait etc */
+ if (GPS.Orientation === 6) {
+ //CCW
+ angle += (Math.PI / 180) * -90;
+ } else if (GPS.Orientation === 8) {
+ //CW
+ angle += (Math.PI / 180) * 90;
+ } else if (GPS.Orientation === 3) {
+ //180
+ angle += (Math.PI / 180) * 180;
+ }
+
+ /* If there is altitude data */
+ if (
+ typeof GPS.GPSAltitude !== "undefined" &&
+ typeof GPS.GPSAltitudeRef !== "undefined"
+ ) {
+ // Attempt to use GPS altitude:
+ // (may eventually need to find EXIF field of view for correction)
+ if (
+ typeof GPS.GPSAltitude !== "undefined" &&
+ typeof GPS.GPSAltitudeRef !== "undefined"
+ ) {
+ altitude =
+ GPS.GPSAltitude.numerator / GPS.GPSAltitude.denominator +
+ GPS.GPSAltitudeRef;
+ } else {
+ altitude = 0; // none
+ }
+ }
+ } else {
+ alert("EXIF initialized. Press again to view data in console.");
+ }
+};
+
+L.EditHandle = L.Marker.extend({
+ initialize: function(overlay, corner, options) {
+ var markerOptions,
+ latlng = overlay.getCorner(corner);
+
+ L.setOptions(this, options);
+
+ this._handled = overlay;
+ this._corner = corner;
+
+ markerOptions = {
+ draggable: true,
+ zIndexOffset: 10
+ };
+
+ if (options && options.hasOwnProperty("draggable")) {
+ markerOptions.draggable = options.draggable;
+ }
+
+ L.Marker.prototype.initialize.call(this, latlng, markerOptions);
+ },
+
+ onAdd: function(map) {
+ L.Marker.prototype.onAdd.call(this, map);
+ this._bindListeners();
+
+ this.updateHandle();
+ },
+
+ onRemove: function(map) {
+ this._unbindListeners();
+ L.Marker.prototype.onRemove.call(this, map);
+ },
+
+ _onHandleDragStart: function() {
+ this._handled.fire("editstart");
+ },
+
+ _onHandleDragEnd: function() {
+ this._fireEdit();
+ },
+
+ _fireEdit: function() {
+ this._handled.edited = true;
+ this._handled.fire("edit");
+ },
+
+ _bindListeners: function() {
+ this.on(
+ {
+ dragstart: this._onHandleDragStart,
+ drag: this._onHandleDrag,
+ dragend: this._onHandleDragEnd
+ },
+ this
+ );
+
+ this._handled._map.on("zoomend", this.updateHandle, this);
+
+ this._handled.on("update", this.updateHandle, this);
+ },
+
+ _unbindListeners: function() {
+ this.off(
+ {
+ dragstart: this._onHandleDragStart,
+ drag: this._onHandleDrag,
+ dragend: this._onHandleDragEnd
+ },
+ this
+ );
+
+ this._handled._map.off("zoomend", this.updateHandle, this);
+ this._handled.off("update", this.updateHandle, this);
+ },
+
+ /* Takes two latlngs and calculates the scaling difference. */
+ _calculateScalingFactor: function(latlngA, latlngB) {
+ var overlay = this._handled,
+ map = overlay._map,
+
+ centerPoint = map.latLngToLayerPoint(overlay.getCenter()),
+ formerPoint = map.latLngToLayerPoint(latlngA),
+ newPoint = map.latLngToLayerPoint(latlngB),
+ formerRadiusSquared = this._d2(centerPoint, formerPoint),
+ newRadiusSquared = this._d2(centerPoint, newPoint);
+
+ return Math.sqrt(newRadiusSquared / formerRadiusSquared);
+ },
+
+ /* Distance between two points in cartesian space, squared (distance formula). */
+ _d2: function(a, b) {
+ var dx = a.x - b.x,
+ dy = a.y - b.y;
+
+ return Math.pow(dx, 2) + Math.pow(dy, 2);
+ },
+
+ /* Takes two latlngs and calculates the angle between them. */
+ calculateAngleDelta: function(latlngA, latlngB) {
+ var overlay = this._handled,
+ map = overlay._map,
+
+ centerPoint = map.latLngToLayerPoint(overlay.getCenter()),
+ formerPoint = map.latLngToLayerPoint(latlngA),
+ newPoint = map.latLngToLayerPoint(latlngB),
+
+ initialAngle = Math.atan2(centerPoint.y - formerPoint.y, centerPoint.x - formerPoint.x),
+ newAngle = Math.atan2(centerPoint.y - newPoint.y, centerPoint.x - newPoint.x);
+
+ return newAngle - initialAngle;
+ }
+});
+
+L.LockHandle = L.EditHandle.extend({
+ options: {
+ TYPE: 'lock',
+ icon: L.icon({
+ iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAklEQVR4AewaftIAAAD8SURBVO3BPU7CYAAA0AdfjIcQlRCQBG7C3gk2uIPG2RC3Dk16Gz0FTO1WZs/gwGCMP/2+xsSl7+n1er1Iz9LtRQjaPeMeO+TinLDCJV78YqjdA04YodKuxhUaPGoRxMmxwRQZSt87Yo4KExGCeAUyLLFB4bMacxywEClIU2KDKXbInTUYo8JCgoFuGoxQO5uiwY1EA91VmDqrcKeDoX8WdNNgjApvmGGLXKIgXY0xGkxQYItrrFFIEKQ5Yo4KEx9yrDFDhlKkIF6NOQ5Y+KpAhiXWKEQI4pxwiwoLPyuxwQw75FoE7fZYocFEuwI7jHCBV39gL92TXq/Xi/AOcmczZmaIMScAAAAASUVORK5CYII=',
+ iconSize: [32, 32],
+ iconAnchor: [16, 16]
+ })
+ },
+
+ /* cannot be dragged */
+ _onHandleDrag: function() {
+ },
+
+ updateHandle: function() {
+ this.setLatLng(this._handled.getCorner(this._corner));
+ L.DomUtil.removeClass(this._handled.getElement(), 'selected');
+ }
+
+});
+
+L.DistortHandle = L.EditHandle.extend({
+ options: {
+ TYPE: "distort",
+ icon: L.icon({
+ iconUrl:
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAklEQVR4AewaftIAAAChSURBVO3BIU4DURgGwNkvL2B6AkQTLBqP4QCoSm7DDXoBLBZHDbfgICAIZjEV3YTn9uVHdMZZtcnCfI13bIzxg0emg6Nm6QVbYz3jylEsXRrvwommb49X67jFkz80fR9Mb1YxTzqiWBSLYlEsikWxKBbFolgUi2JRLIpFsSgWxaJY03fHHOu40dH07bAzWCx9Ge/TiWbpHgdsjPGNB2f/yS+7xRCyiiZPJQAAAABJRU5ErkJggg==",
+ iconSize: [32, 32],
+ iconAnchor: [16, 16]
+ })
+ },
+
+ _onHandleDrag: function() {
+ var overlay = this._handled;
+
+ overlay._updateCorner(this._corner, this.getLatLng());
+
+ overlay.fire("update");
+ overlay.editing._updateToolbarPos();
+ },
+
+ updateHandle: function() {
+ this.setLatLng(this._handled.getCorner(this._corner));
+ },
+
+});
+
+L.RotateScaleHandle = L.EditHandle.extend({
+ options: {
+ TYPE: 'rotateScale',
+ icon: L.icon({
+ iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAklEQVR4AewaftIAAAHiSURBVMXBa3HbShgA0PMp/1sCCo8oCEpgTaCXgIXAJiDzyCJoAUTm4UVQAns1Y8+snWnTvJyeE16hkjDgDrfoNTMKcpC9UPiLSo8JyetkjEHxjPCMyoS199kFoz8Iv1HpMaN3qWDCHoegOKkkRwnJpRmroHgiPFEZ8IBekzEGxQtUEhKSS/fB7Ew4U+lxcGkVZG9QWWPSFAxBcdK59KApuA+yNwp2uEdx1GN25sZJZULSfAtm77SlbNjju6MvG75u+WHRWVR6rDVjMPsgwYyVZl3pLTpHkyYHOx8syMiayaJzlDTZ9YyaZNFVkiYH2ZUEBcVJJXVImuz6Js3Qofe59pq7DoOTILu+g+a288mCouk7/1iH4qTS+2QdDppbV1ZJmrnDXnPnc5UOs2Z0fUmTuyBr+krvSioJyUmQO0dZM7mepMkWnaNRkyrJB6uskTSjxY3Fll8bvmJwlDb83FJ8gMqAB80uyBY3Trb82PAfvjj6vuHnluIdKgMeNXOwctK5NKBoHitrb1RJeHRp5Ux4ojLg0aWMHGQvUOkxIWkKVsHsTPiNSo8HDC5lZIsgO6n0uMUdRvQuFQxB8UR4RmXC2vvsgtEfhL+o9JiQvE7GGBTPCK9QSUjoMWgKDthjDrIX+h/k0I7gth6N5gAAAABJRU5ErkJggg==',
+ iconSize: [32, 32],
+ iconAnchor: [16, 16]
+ })
+ },
+
+ _onHandleDrag: function() {
+ var overlay = this._handled,
+ edit = overlay.editing,
+ formerLatLng = overlay.getCorner(this._corner),
+ newLatLng = this.getLatLng(),
+
+ angle = this.calculateAngleDelta(formerLatLng, newLatLng),
+ scale = this._calculateScalingFactor(formerLatLng, newLatLng);
+
+ if (angle !== 0) { edit._rotateBy(angle); }
+
+ /*
+ checks whether the "edgeMinWidth" property is set and tracks the minimum edge length;
+ this enables preventing scaling to zero, but we might also add an overall scale limit
+ */
+ if (overlay.hasOwnProperty('edgeMinWidth')){
+ var edgeMinWidth = overlay.edgeMinWidth,
+ w = L.latLng(overlay.getCorner(0)).distanceTo(overlay.getCorner(1)),
+ h = L.latLng(overlay.getCorner(1)).distanceTo(overlay.getCorner(2));
+ if ((w > edgeMinWidth && h > edgeMinWidth) || scale > 1) {
+ edit._scaleBy(scale);
+ }
+ }
+
+ overlay.fire('update');
+ edit._updateToolbarPos();
+ },
+
+ updateHandle: function() {
+ this.setLatLng(this._handled.getCorner(this._corner));
+ },
+});
+
+L.RotateHandle = L.EditHandle.extend({
+ options: {
+ TYPE: 'rotate',
+ icon: L.icon({
+ iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAklEQVR4AewaftIAAAHiSURBVMXBa3HbShgA0PMp/1sCCo8oCEpgTaCXgIXAJiDzyCJoAUTm4UVQAns1Y8+snWnTvJyeE16hkjDgDrfoNTMKcpC9UPiLSo8JyetkjEHxjPCMyoS199kFoz8Iv1HpMaN3qWDCHoegOKkkRwnJpRmroHgiPFEZ8IBekzEGxQtUEhKSS/fB7Ew4U+lxcGkVZG9QWWPSFAxBcdK59KApuA+yNwp2uEdx1GN25sZJZULSfAtm77SlbNjju6MvG75u+WHRWVR6rDVjMPsgwYyVZl3pLTpHkyYHOx8syMiayaJzlDTZ9YyaZNFVkiYH2ZUEBcVJJXVImuz6Js3Qofe59pq7DoOTILu+g+a288mCouk7/1iH4qTS+2QdDppbV1ZJmrnDXnPnc5UOs2Z0fUmTuyBr+krvSioJyUmQO0dZM7mepMkWnaNRkyrJB6uskTSjxY3Fll8bvmJwlDb83FJ8gMqAB80uyBY3Trb82PAfvjj6vuHnluIdKgMeNXOwctK5NKBoHitrb1RJeHRp5Ux4ojLg0aWMHGQvUOkxIWkKVsHsTPiNSo8HDC5lZIsgO6n0uMUdRvQuFQxB8UR4RmXC2vvsgtEfhL+o9JiQvE7GGBTPCK9QSUjoMWgKDthjDrIX+h/k0I7gth6N5gAAAABJRU5ErkJggg==',
+ iconSize: [32, 32],
+ iconAnchor: [16, 16]
+ })
+ },
+
+ _onHandleDrag: function() {
+ var overlay = this._handled,
+ formerLatLng = overlay.getCorner(this._corner),
+ newLatLng = this.getLatLng(),
+ angle = this.calculateAngleDelta(formerLatLng, newLatLng);
+
+ if (angle !== 0) { overlay.editing._rotateBy(angle); }
+
+ overlay.fire('update');
+ overlay.editing._updateToolbarPos();
+ },
+
+ updateHandle: function() {
+ this.setLatLng(this._handled.getCorner(this._corner));
+ }
+
+});
+
+L.ScaleHandle = L.EditHandle.extend({
+ options: {
+ TYPE: 'rotate',
+ icon: L.icon({
+ iconUrl:'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIHdpZHRoPSI0NTkiIGhlaWdodD0iNDY0IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA1MTIgNTEyIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iIj48cmVjdCBpZD0iYmFja2dyb3VuZHJlY3QiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIHg9IjAiIHk9IjAiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIgY2xhc3M9IiIgc3R5bGU9IiIvPjxnIGNsYXNzPSJjdXJyZW50TGF5ZXIiIHN0eWxlPSIiPjx0aXRsZT5MYXllciAxPC90aXRsZT48cGF0aCBkPSJNNDU5LjA0OTE1OTUzMDQ3MTM0LDg2LjkyNjIzNDUxMjU1MDAyIFYwIGgtODUuNzE0NTczMzU2MzEyMDkgdjI3LjA0MzcxNzQwMzkwNDQ1MiBIODUuNzE0NTczMzU2MzEyMDMgVjAgSDAgdjg2LjkyNjIzNDUxMjU1MDAyIGgyNS43MTQzNzIwMDY4OTM2MjYgdjI4OS43NTQxMTUwNDE4MzM0IEgwIHY4Ni45MjYyMzQ1MTI1NTAwMiBoODUuNzE0NTczMzU2MzEyMDkgdi0yNy4wNDM3MTc0MDM5MDQ0NTIgaDI4NS43MTUyNDQ1MjEwNDAzIHYyNy4wNDM3MTc0MDM5MDQ0NTIgaDg1LjcxNDU3MzM1NjMxMjA5IHYtODYuOTI2MjM0NTEyNTUwMDIgaC0yMy44MDk2MDM3MTAwODY2OSBWODYuOTI2MjM0NTEyNTUwMDIgSDQ1OS4wNDkxNTk1MzA0NzEzNCB6TTM4NC43NjMxOTU5NTUwMDA5LDEyLjU1NjAxMTY1MTgxMjc4MSBoNjEuOTA0OTY5NjQ2MjI1Mzk2IHY2Mi43ODAwNTgyNTkwNjM5MSBoLTYxLjkwNDk2OTY0NjIyNTM5NiBWMTIuNTU2MDExNjUxODEyNzgxIHpNMTIuMzgwOTkzOTI5MjQ1MDUsMTIuNTU2MDExNjUxODEyNzgxIGg2MS45MDQ5Njk2NDYyMjUzOTYgdjYyLjc4MDA1ODI1OTA2MzkxIEgxMi4zODA5OTM5MjkyNDUwNSBWMTIuNTU2MDExNjUxODEyNzgxIHpNNzQuMjg1OTYzNTc1NDcwNTMsNDUxLjA1MDU3MjQxNTEyMDY2IEgxMi4zODA5OTM5MjkyNDUwNSB2LTYyLjc4MDA1ODI1OTA2MzkxIGg2MS45MDQ5Njk2NDYyMjUzOTYgVjQ1MS4wNTA1NzI0MTUxMjA2NiB6TTQ0NS43MTU3ODE0NTI4MjI3NCw0NTEuMDUwNTcyNDE1MTIwNjYgaC02Mi44NTczNTM3OTQ2Mjg4NjQgdi02Mi43ODAwNTgyNTkwNjM5MSBoNjIuODU3MzUzNzk0NjI4ODY0IFY0NTEuMDUwNTcyNDE1MTIwNjYgek00MDcuNjIwNDE1NTE2Njg0MjYsMzc2LjY4MDM0OTU1NDM4MzQ0IGgtMzYuMTkwNTk3NjM5MzMxNzcgdjMyLjgzODc5OTcwNDc0MTEyIEg4NS43MTQ1NzMzNTYzMTIwMyB2LTMyLjgzODc5OTcwNDc0MTEyIEg0OS41MjM5NzU3MTY5ODAzMiBWODYuOTI2MjM0NTEyNTUwMDIgaDM2LjE5MDU5NzYzOTMzMTc3IFY1MC4yMjQwNDY2MDcyNTExMjUgaDI4Ny42MjAwMTI4MTc4NDcyIHYzNi43MDIxODc5MDUyOTg5IGgzNC4yODU4MjkzNDI1MjQ4MzUgVjM3Ni42ODAzNDk1NTQzODM0NCB6IiBpZD0ic3ZnXzIiIGNsYXNzPSIiIGZpbGw9IiMxYTFhZWIiIGZpbGwtb3BhY2l0eT0iMSIvPjwvZz48L3N2Zz4=',
+ iconSize: [32, 32],
+ iconAnchor: [16, 16]
+ })
+ },
+
+ _onHandleDrag: function() {
+ var overlay = this._handled,
+ formerLatLng = overlay.getCorner(this._corner),
+ newLatLng = this.getLatLng(),
+
+ scale = this._calculateScalingFactor(formerLatLng, newLatLng);
+
+ overlay.editing._scaleBy(scale);
+
+ overlay.fire('update');
+ overlay.editing._updateToolbarPos();
+ },
+
+ updateHandle: function() {
+ this.setLatLng(this._handled.getCorner(this._corner));
+ },
+});
+
+L.DistortableImage = L.DistortableImage || {};
+
+var EditOverlayAction = LeafletToolbar.ToolbarAction.extend({
+ initialize: function(map, overlay, options) {
+ this._overlay = overlay;
+ this._map = map;
+
+ LeafletToolbar.ToolbarAction.prototype.initialize.call(this, options);
+ }
+}),
+
+ ToggleTransparency = EditOverlayAction.extend({
+ initialize: function(map, overlay, options) {
+ var edit = overlay.editing,
+ href,
+ tooltip;
+
+ if (edit._transparent) {
+ href = ' ';
+ tooltip = 'Make Image Opaque';
+ } else {
+ href = ' ';
+ tooltip = 'Make Image Transparent';
+ }
+
+ options = options || {};
+ options.toolbarIcon = {
+ html: '' + href + ' ',
+ tooltip: tooltip
+ };
+
+ EditOverlayAction.prototype.initialize.call(this, map, overlay, options);
+ },
+
+ addHooks: function() {
+ var editing = this._overlay.editing;
+
+ editing._toggleTransparency();
+ this.disable();
+ }
+ }),
+
+ ToggleOutline = EditOverlayAction.extend({
+ initialize: function(map, overlay, options) {
+ var edit = overlay.editing,
+ href,
+ tooltip;
+
+ if (edit._outlined) {
+ href = ' ';
+ tooltip = 'Remove Border';
+ } else {
+ href = ' ';
+ tooltip = 'Add Border';
+ }
+
+ options = options || {};
+ options.toolbarIcon = {
+ html: '' + href + ' ',
+ tooltip: tooltip
+ };
+
+ EditOverlayAction.prototype.initialize.call(this, map, overlay, options);
+ },
+
+ addHooks: function() {
+ var editing = this._overlay.editing;
+
+ editing._toggleOutline();
+ this.disable();
+ }
+ }),
+
+ Delete = EditOverlayAction.extend({
+ initialize: function(map, overlay, options) {
+ var href = ' ';
+
+ options = options || {};
+ options.toolbarIcon = {
+ html: '' + href + ' ',
+ tooltip: 'Delete Image'
+ };
+
+ EditOverlayAction.prototype.initialize.call(this, map, overlay, options);
+ },
+
+ addHooks: function() {
+ var editing = this._overlay.editing;
+
+ editing._removeOverlay();
+ this.disable();
+ }
+ }),
+
+ ToggleLock = EditOverlayAction.extend({
+ initialize: function(map, overlay, options) {
+ var edit = overlay.editing,
+ href,
+ tooltip;
+
+ if (edit._mode === 'lock') {
+ href = ' ';
+ tooltip = 'Unlock';
+ } else {
+ href = ' ';
+ tooltip = 'Lock';
+ }
+
+ options = options || {};
+ options.toolbarIcon = {
+ html: '' + href + ' ',
+ tooltip: tooltip
+ };
+
+ EditOverlayAction.prototype.initialize.call(this, map, overlay, options);
+ },
+
+ addHooks: function() {
+ var editing = this._overlay.editing;
+
+ editing._toggleLock();
+ this.disable();
+ }
+ }),
+
+ ToggleRotateScale = EditOverlayAction.extend({
+ initialize: function(map, overlay, options) {
+ var edit = overlay.editing,
+ href,
+ tooltip;
+
+ if (edit._mode === 'rotateScale') {
+ href = ' ';
+ tooltip = 'Distort';
+ } else {
+ href = ' ';
+ tooltip = 'Rotate+Scale';
+ }
+
+ options = options || {};
+ options.toolbarIcon = {
+ html: '' + href + ' ',
+ tooltip: tooltip
+ };
+
+ EditOverlayAction.prototype.initialize.call(this, map, overlay, options);
+ },
+
+ addHooks: function() {
+ var editing = this._overlay.editing;
+
+ editing._toggleRotateScale();
+ this.disable();
+ }
+ }),
+
+ Export = EditOverlayAction.extend({
+ initialize: function(map, overlay, options) {
+ var href = ' ';
+
+ options = options || {};
+ options.toolbarIcon = {
+ html: '' + href + ' ',
+ tooltip: 'Export Image'
+ };
+
+ EditOverlayAction.prototype.initialize.call(this, map, overlay, options);
+ },
+
+ addHooks: function() {
+ var editing = this._overlay.editing;
+
+ editing._toggleExport();
+ this.disable();
+ }
+ }),
+
+ ToggleOrder = EditOverlayAction.extend({
+ initialize: function(map, overlay, options) {
+ var edit = overlay.editing,
+ href,
+ tooltip;
+
+ if (edit._toggledImage) {
+ href = ' ';
+ tooltip = 'Stack to Front';
+ } else {
+ href = ' ';
+ tooltip = 'Stack to Back';
+ }
+
+ options = options || {};
+ options.toolbarIcon = {
+ html: '' + href + ' ',
+ tooltip: tooltip
+ };
+
+ EditOverlayAction.prototype.initialize.call(this, map, overlay, options);
+ },
+
+ addHooks: function() {
+ var editing = this._overlay.editing;
+
+ editing._toggleOrder();
+ this.disable();
+ }
+ }),
+
+ EnableEXIF = EditOverlayAction.extend({
+ initialize: function(map, overlay, options) {
+ var href = ' ';
+
+ options = options || {};
+ options.toolbarIcon = {
+ html: '' + href + ' ',
+ tooltip: 'Geolocate Image'
+ };
+
+ EditOverlayAction.prototype.initialize.call(this, map, overlay, options);
+ },
+
+ addHooks: function() {
+ var image = this._overlay.getElement();
+
+ EXIF.getData(image, L.EXIF(image));
+ }
+ }),
+
+ Restore = EditOverlayAction.extend({
+ initialize: function(map, overlay, options) {
+ var href = ' ';
+
+ options = options || {};
+ options.toolbarIcon = {
+ html: '' + href + ' ',
+ tooltip: 'Restore'
+ };
+
+ EditOverlayAction.prototype.initialize.call(this, map, overlay, options);
+ },
+
+ addHooks: function() {
+ var editing = this._overlay.editing;
+
+ editing._restore();
+ this.disable();
+ }
+ });
+
+L.DistortableImage.EditToolbar = LeafletToolbar.Popup.extend({
+ options: {
+ actions: [
+ ToggleTransparency,
+ ToggleOutline,
+ ToggleLock,
+ ToggleRotateScale,
+ ToggleOrder,
+ EnableEXIF,
+ Restore,
+ Export,
+ Delete
+ ]
+ },
+
+ // todo: move to some sort of util class, these methods could be useful in future
+ _rotateToolbarAngleDeg: function(angle) {
+ var div = this._container,
+ divStyle = div.style;
+
+ var oldTransform = divStyle.transform;
+
+ divStyle.transform = oldTransform + "rotate(" + angle + "deg)";
+ divStyle.transformOrigin = "1080% 650%";
+
+ this._rotateToolbarIcons(angle);
+ },
+
+ _rotateToolbarIcons: function(angle) {
+ var icons = document.querySelectorAll(".fa");
+
+ for (var i = 0; i < icons.length; i++) {
+ icons.item(i).style.transform = "rotate(" + -angle + "deg)";
+ }
+ }
+});
+
+L.DistortableImage = L.DistortableImage || {};
+
+L.DistortableImage.Edit = L.Handler.extend({
+ options: {
+ opacity: 0.7,
+ outline: "1px solid red",
+ keymap: {
+ 'Backspace': '_removeOverlay', // backspace windows / delete mac
+ 'CapsLock': '_toggleRotate',
+ 'Escape': '_deselect',
+ 'd': '_toggleRotateScale',
+ 'r': '_toggleRotateScale',
+ 'j': '_toggleOrder',
+ 'k': '_toggleOrder',
+ 'l': '_toggleLock',
+ 'o': '_toggleOutline',
+ 's': '_toggleScale',
+ 't': '_toggleTransparency',
+ }
+ },
+
+ initialize: function(overlay) {
+ this._overlay = overlay;
+ this._toggledImage = false;
+ /* Different actions. */
+ var actions = ["distort", "lock", "rotate", "scale", "rotateScale"];
+ /* Interaction modes. */
+ this._mode = actions[actions.indexOf(this._overlay.options.mode)] || "distort";
+ this._selected = this._overlay.options.selected || false;
+ this._transparent = false;
+ this._outlined = false;
+
+ /* generate instance counts */
+ this.instance_count = L.DistortableImage.Edit.prototype.instances =
+ L.DistortableImage.Edit.prototype.instances ? L.DistortableImage.Edit.prototype.instances + 1 : 1;
+ },
+
+ /* Run on image selection. */
+ addHooks: function() {
+ var overlay = this._overlay,
+ map = overlay._map,
+ keymapper_position;
+
+ /* instantiate and render keymapper for one instance only*/
+ if (this.instance_count === 1 && overlay.options.keymapper !== false) {
+ keymapper_position = overlay.options.keymapper_position || 'topright';
+ map.addControl(new L.DistortableImage.Keymapper({ position: keymapper_position }));
+ }
+
+ /* bring the selected image into view */
+ overlay.bringToFront();
+
+ this._initHandles();
+
+ this._appendHandlesandDragable(this._mode);
+
+ if (this._selected) { this._initToolbar(); }
+
+ this._overlay._dragStartPoints = {
+ 0: L.point(0, 0),
+ 1: L.point(0, 0),
+ 2: L.point(0, 0),
+ 3: L.point(0, 0)
+ };
+
+ L.DomEvent.on(map, "click", this._deselect, this);
+ L.DomEvent.on(overlay._image, "click", this._select, this);
+
+ /* Enable hotkeys. */
+ L.DomEvent.on(window, "keydown", this._onKeyDown, this);
+ },
+
+ /* Run on image deselection. */
+ removeHooks: function() {
+ var overlay = this._overlay,
+ map = overlay._map;
+
+ L.DomEvent.off(map, "click", this._deselect, this);
+ L.DomEvent.off(overlay._image, "click", this._select, this);
+
+ // First, check if dragging exists - it may be off due to locking
+ if (this.dragging) { this.dragging.disable(); }
+ delete this.dragging;
+
+ if (this.toolbar) { this._hideToolbar(); }
+ if (this.editing) { this.editing.disable(); }
+
+ map.removeLayer(this._handles[this._mode]);
+
+ /* Disable hotkeys. */
+ L.DomEvent.off(window, "keydown", this._onKeyDown, this);
+ },
+
+ _initHandles: function() {
+ var overlay = this._overlay,
+ i;
+
+ this._lockHandles = L.layerGroup();
+ for (i = 0; i < 4; i++) {
+ this._lockHandles.addLayer(
+ new L.LockHandle(overlay, i, { draggable: false })
+ );
+ }
+
+ this._distortHandles = L.layerGroup();
+ for (i = 0; i < 4; i++) {
+ this._distortHandles.addLayer(new L.DistortHandle(overlay, i));
+ }
+
+ this._rotateHandles = L.layerGroup(); // individual rotate
+ for (i = 0; i < 4; i++) {
+ this._rotateHandles.addLayer(new L.RotateHandle(overlay, i));
+ }
+
+ this._scaleHandles = L.layerGroup();
+ for (i = 0; i < 4; i++) {
+ this._scaleHandles.addLayer(new L.ScaleHandle(overlay, i));
+ }
+
+ this._rotateScaleHandles = L.layerGroup(); // handle includes rotate AND scale
+ for (i = 0; i < 4; i++) {
+ this._rotateScaleHandles.addLayer(new L.RotateScaleHandle(overlay, i));
+ }
+
+ this._handles = {
+ lock: this._lockHandles,
+ distort: this._distortHandles,
+ rotateScale: this._rotateScaleHandles,
+ scale: this._scaleHandles,
+ rotate: this._rotateHandles
+ };
+ },
+
+ _appendHandlesandDragable: function (mode) {
+ var overlay = this._overlay,
+ map = overlay._map;
+
+ map.addLayer(this._handles[mode]);
+
+ if (mode !== 'lock') {
+ if (!this._selected) {
+ this._handles[mode].eachLayer(function (layer) {
+ layer.setOpacity(0);
+ layer.dragging.disable();
+ layer.options.draggable = false;
+ });
+ }
+
+ this._enableDragging();
+ }
+ },
+
+
+ _initToolbar: function () {
+ this._showToolbar();
+ },
+
+ confirmDelete: function() {
+ return window.confirm("Are you sure you want to delete?");
+ },
+
+ _rotateBy: function(angle) {
+ var overlay = this._overlay,
+ map = overlay._map,
+ center = map.latLngToLayerPoint(overlay.getCenter()),
+ i,
+ p,
+ q;
+
+ for (i = 0; i < 4; i++) {
+ p = map.latLngToLayerPoint(overlay._corners[i]).subtract(center);
+ q = L.point(
+ Math.cos(angle) * p.x - Math.sin(angle) * p.y,
+ Math.sin(angle) * p.x + Math.cos(angle) * p.y
+ );
+ overlay._updateCorner(i, map.layerPointToLatLng(q.add(center)));
+ }
+
+ // window.angle = L.TrigUtil.radiansToDegrees(angle);
+
+ this._overlay.rotation -= L.TrigUtil.radiansToDegrees(angle);
+
+ overlay._reset();
+ },
+
+ _restore: function() {
+ var overlay = this._overlay;
+ var angle = overlay.rotation;
+ var map = overlay._map;
+ var center = map.latLngToLayerPoint(overlay.getCenter());
+ var offset = overlay._initialDimensions.offset;
+
+ var corners = {
+ 0: map.layerPointToLatLng(center.subtract(offset)),
+ 1: map.layerPointToLatLng(center.add(L.point(offset.x, -offset.y))),
+ 2: map.layerPointToLatLng(center.add(L.point(-offset.x, offset.y))),
+ 3: map.layerPointToLatLng(center.add(offset))
+ };
+
+ map.removeLayer(this._handles[this._mode]);
+
+ overlay._updateCorners(corners);
+
+ if (angle !== 0) { this._rotateBy(L.TrigUtil.degreesToRadians(360 - angle)); }
+
+ map.addLayer(this._handles[this._mode]);
+
+ this._showToolbar();
+
+ this._overlay.rotation = angle;
+ },
+
+ _scaleBy: function(scale) {
+ var overlay = this._overlay,
+ map = overlay._map,
+ center = map.latLngToLayerPoint(overlay.getCenter()),
+ i,
+ p;
+
+ for (i = 0; i < 4; i++) {
+ p = map
+ .latLngToLayerPoint(overlay.getCorner(i))
+ .subtract(center)
+ .multiplyBy(scale)
+ .add(center);
+ overlay._updateCorner(i, map.layerPointToLatLng(p));
+ }
+
+ overlay._reset();
+ },
+
+ _enableDragging: function() {
+ var overlay = this._overlay,
+ map = overlay._map;
+
+ this.dragging = new L.Draggable(overlay._image);
+ this.dragging.enable();
+
+ /* Hide toolbars and markers while dragging; click will re-show it */
+ this.dragging.on("dragstart", function() {
+ overlay.fire("dragstart");
+ this._hideToolbar();
+ },this);
+
+ /*
+ * Adjust default behavior of L.Draggable.
+ * By default, L.Draggable overwrites the CSS3 distort transform
+ * that we want when it calls L.DomUtil.setPosition.
+ */
+ this.dragging._updatePosition = function() {
+ var delta = this._newPos.subtract(
+ map.latLngToLayerPoint(overlay._corners[0])
+ ),
+ currentPoint,
+ i;
+
+ this.fire("predrag");
+
+ for (i = 0; i < 4; i++) {
+ currentPoint = map.latLngToLayerPoint(overlay._corners[i]);
+ overlay._corners[i] = map.layerPointToLatLng(currentPoint.add(delta));
+ }
+
+ overlay._reset();
+ overlay.fire("update");
+ overlay.fire("drag");
+
+ this.fire("drag");
+ };
+ },
+
+ _onKeyDown: function(event) {
+ var keymap = this.options.keymap,
+ handlerName = keymap[event.key];
+
+ if (this[handlerName] !== undefined && this._overlay.options.suppressToolbar !== true) {
+ if (this._selected) {
+ this[handlerName].call(this);
+ }
+ }
+ },
+
+ _toggleRotateScale: function() {
+ var map = this._overlay._map;
+
+ if (this._mode === "lock") { return; }
+
+ map.removeLayer(this._handles[this._mode]);
+
+ /* Switch mode. */
+ if (this._mode === "rotateScale") { this._mode = "distort"; }
+ else { this._mode = "rotateScale"; }
+
+ map.addLayer(this._handles[this._mode]);
+
+ this._showToolbar();
+ },
+
+ _toggleScale: function() {
+ var map = this._overlay._map;
+
+ if (this._mode === "lock") { return; }
+
+ map.removeLayer(this._handles[this._mode]);
+
+ if (this._mode === "scale") { this._mode = "distort"; }
+ else { this._mode = "scale"; }
+
+ map.addLayer(this._handles[this._mode]);
+
+ },
+
+ _toggleRotate: function() {
+ var map = this._overlay._map;
+
+ if (this._mode === "lock") { return; }
+
+ map.removeLayer(this._handles[this._mode]);
+ if (this._mode === "rotate") { this._mode = "distort"; }
+ else { this._mode = "rotate"; }
+
+ map.addLayer(this._handles[this._mode]);
+ },
+
+ _toggleTransparency: function() {
+ var image = this._overlay._image,
+ opacity;
+
+ this._transparent = !this._transparent;
+ opacity = this._transparent ? this.options.opacity : 1;
+
+ L.DomUtil.setOpacity(image, opacity);
+ image.setAttribute("opacity", opacity);
+
+ this._showToolbar();
+ },
+
+ _toggleOutline: function() {
+ var image = this._overlay._image,
+ opacity,
+ outline;
+
+ this._outlined = !this._outlined;
+ outline = this._outlined ? this.options.outline : "none";
+
+ L.DomUtil.setOpacity(image, opacity);
+ image.setAttribute("opacity", opacity);
+
+ image.style.outline = outline;
+
+ this._showToolbar();
+ },
+
+ _sendUp: function() {
+ this._overlay.bringToFront();
+ },
+
+ _sendDown: function() {
+ this._overlay.bringToBack();
+ },
+
+ _toggleLock: function() {
+ var map = this._overlay._map;
+
+ map.removeLayer(this._handles[this._mode]);
+ /* Switch mode. */
+ if (this._mode === "lock") {
+ this._mode = "distort";
+ this._enableDragging();
+ } else {
+ this._mode = "lock";
+ if (this.dragging) { this.dragging.disable(); }
+ delete this.dragging;
+ }
+
+ map.addLayer(this._handles[this._mode]);
+
+ this._showToolbar();
+ },
+
+ _select: function(event) {
+ this._selected = true;
+ this._showToolbar();
+ this._showMarkers();
+
+ if (event) { L.DomEvent.stopPropagation(event); }
+ },
+
+ _deselect: function() {
+ this._selected = false;
+ this._hideToolbar();
+ if (this._mode !== "lock") {
+ this._hideMarkers();
+ }
+ },
+
+ _hideToolbar: function() {
+ var map = this._overlay._map;
+
+ if (this.toolbar) {
+ map.removeLayer(this.toolbar);
+ this.toolbar = false;
+ }
+ },
+
+ _showMarkers: function() {
+ if (this._mode === "lock") { return; }
+
+ var currentHandle = this._handles[this._mode];
+
+ currentHandle.eachLayer(function(layer) {
+ var drag = layer.dragging,
+ opts = layer.options;
+
+ layer.setOpacity(1);
+ if (drag) { drag.enable(); }
+ if (opts.draggable) { opts.draggable = true; }
+ });
+ },
+
+ _hideMarkers: function() {
+ if (!this._handles) { this._initHandles(); } // workaround for race condition w/ feature group
+
+ var mode = this._mode,
+ currentHandle = this._handles[mode];
+
+ currentHandle.eachLayer(function (layer) {
+ var drag = layer.dragging,
+ opts = layer.options;
+
+ if (mode !== 'lock') {
+ layer.setOpacity(0);
+ }
+ if (drag) { drag.disable(); }
+ if (opts.draggable) { opts.draggable = false; }
+ });
+ },
+
+ // TODO: toolbar for multiple image selection
+ _showToolbar: function() {
+ var overlay = this._overlay,
+ map = overlay._map,
+ //Find the topmost point on the image.
+ corners = overlay.getCorners(),
+ maxLat = -Infinity;
+
+ for (var i = 0; i < corners.length; i++) {
+ if (corners[i].lat > maxLat) {
+ maxLat = corners[i].lat;
+ }
+ }
+
+ //Longitude is based on the centroid of the image.
+ var raised_point = overlay.getCenter();
+ raised_point.lat = maxLat;
+
+ if (overlay.options.suppressToolbar !== true) {
+ try {
+ this.toolbar = new L.DistortableImage.EditToolbar(raised_point).addTo(map, overlay);
+ overlay.fire('toolbar:created');
+ }
+ catch (e) {}
+ }
+ },
+
+ _updateToolbarPos: function() {
+ var overlay = this._overlay,
+ //Find the topmost point on the image.
+ corners = overlay.getCorners(),
+ maxLat = -Infinity;
+
+ for (var i = 0; i < corners.length; i++) {
+ if (corners[i].lat > maxLat) {
+ maxLat = corners[i].lat;
+ }
+ }
+
+ //Longitude is based on the centroid of the image.
+ var raised_point = overlay.getCenter();
+ raised_point.lat = maxLat;
+
+ if (overlay.options.suppressToolbar !== true) {
+ this.toolbar.setLatLng(raised_point);
+ }
+
+ },
+
+ _removeOverlay: function () {
+ var overlay = this._overlay,
+ eventParents = overlay._eventParents;
+
+ if (this._mode === "lock") { return; }
+
+ var choice = this.confirmDelete();
+ if (!choice) { return; }
+
+ this._hideToolbar();
+ if (eventParents) {
+ var eP = eventParents[Object.keys(eventParents)[0]];
+ eP.removeLayer(overlay);
+ } else {
+ overlay._map.removeLayer(overlay);
+ }
+ },
+
+ // compare this to using overlay zIndex
+ _toggleOrder: function () {
+ if (this._toggledImage) {
+ this._toggledImage = false;
+ this._overlay.bringToFront();
+ } else {
+ this._toggledImage = true;
+ this._overlay.bringToBack();
+ }
+
+ this._showToolbar();
+ },
+
+ // Based on https://github.com/publiclab/mapknitter/blob/8d94132c81b3040ae0d0b4627e685ff75275b416/app/assets/javascripts/mapknitter/Map.js#L47-L82
+ _toggleExport: function() {
+ var map = this._overlay._map;
+ var overlay = this._overlay;
+
+ // make a new image
+ var downloadable = new Image();
+
+ downloadable.id = downloadable.id || "tempId12345";
+ $("body").append(downloadable);
+
+ downloadable.onload = function onLoadDownloadableImage() {
+ var height = downloadable.height,
+ width = downloadable.width,
+ nw = map.latLngToLayerPoint(overlay.getCorner(0)),
+ ne = map.latLngToLayerPoint(overlay.getCorner(1)),
+ sw = map.latLngToLayerPoint(overlay.getCorner(2)),
+ se = map.latLngToLayerPoint(overlay.getCorner(3));
+
+ // I think this is to move the image to the upper left corner,
+ // jywarren: i think we may need these or the image goes off the edge of the canvas
+ // jywarren: but these seem to break the distortion math...
+
+ // jywarren: i think it should be rejiggered so it
+ // finds the most negative values of x and y and then
+ // adds those to all coordinates
+
+ //nw.x -= nw.x;
+ //ne.x -= nw.x;
+ //se.x -= nw.x;
+ //sw.x -= nw.x;
+
+ //nw.y -= nw.y;
+ //ne.y -= nw.y;
+ //se.y -= nw.y;
+ //sw.y -= nw.y;
+
+ // run once warping is complete
+ downloadable.onload = function() {
+ $(downloadable).remove();
+ };
+
+ if (window && window.hasOwnProperty("warpWebGl")) {
+ warpWebGl(
+ downloadable.id,
+ [0, 0, width, 0, width, height, 0, height],
+ [nw.x, nw.y, ne.x, ne.y, se.x, se.y, sw.x, sw.y],
+ true // trigger download
+ );
+ }
+ };
+
+ downloadable.src = overlay.options.fullResolutionSrc || overlay._image.src;
+ },
+
+ toggleIsolate: function() {
+ // this.isolated = !this.isolated;
+ // if (this.isolated) {
+ // $.each($L.images,function(i,img) {
+ // img.hidden = false;
+ // img.setOpacity(1);
+ // });
+ // } else {
+ // $.each($L.images,function(i,img) {
+ // img.hidden = true;
+ // img.setOpacity(0);
+ // });
+ // }
+ // this.hidden = false;
+ // this.setOpacity(1);
+ }
+});
+
+L.DistortableImageOverlay.addInitHook(function() {
+ this.editing = new L.DistortableImage.Edit(this);
+
+ if (this.options.editable) {
+ L.DomEvent.on(this._image, "load", this.editing.enable, this.editing);
+ }
+
+ this.on('remove', function () {
+ if (this.editing) { this.editing.disable(); }
+ });
+});
+
L.DomUtil = L.DomUtil || {};
L.DistortableImage = L.DistortableImage || {};
@@ -1851,96 +1982,96 @@ L.DistortableImage.Keymapper = L.Control.extend({
"";
return el_wrapper;
}
-});
-L.Map.mergeOptions({ boxSelector: true, boxZoom: false });
-
-// used for multiple image select. Temporarily disabled until click
-// propagation issue is fixed
-
-L.Map.BoxSelectHandle = L.Map.BoxZoom.extend({
-
- initialize: function (map) {
- this._map = map;
- this._container = map._container;
- this._pane = map._panes.overlayPane;
- },
-
- addHooks: function () {
- L.DomEvent.on(this._container, 'mousedown', this._onMouseDown, this);
- },
-
- removeHooks: function () {
- L.DomEvent.off(this._container, 'mousedown', this._onMouseDown, this);
- },
-
- _onMouseDown: function (e) {
- if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
-
- L.DomUtil.disableTextSelection();
- L.DomUtil.disableImageDrag();
-
- this._startLayerPoint = this._map.mouseEventToLayerPoint(e);
-
- this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._pane);
- L.DomUtil.setPosition(this._box, this._startLayerPoint);
-
- this._container.style.cursor = 'crosshair';
-
- L.DomEvent
- .on(document, 'mousemove', this._onMouseMove, this)
- .on(document, 'mouseup', this._onMouseUp, this)
- .preventDefault(e);
-
- this._map.fire('boxzoomstart');
- },
-
- _onMouseMove: function (e) {
- var startPoint = this._startLayerPoint,
- box = this._box,
-
- layerPoint = this._map.mouseEventToLayerPoint(e),
- offset = layerPoint.subtract(startPoint),
-
- newPos = L.point(
- Math.min(layerPoint.x, startPoint.x),
- Math.min(layerPoint.y, startPoint.y));
-
- L.DomUtil.setPosition(box, newPos);
-
- box.style.width = (Math.max(0, Math.abs(offset.x) - 4)) + 'px';
- box.style.height = (Math.max(0, Math.abs(offset.y) - 4)) + 'px';
- },
-
- _onMouseUp: function (e) {
- var map = this._map,
- layerPoint = map.mouseEventToLayerPoint(e);
-
- if (this._startLayerPoint.equals(layerPoint)) { return; }
-
- this._boxBounds = new L.LatLngBounds(
- map.layerPointToLatLng(this._startLayerPoint),
- map.layerPointToLatLng(layerPoint));
-
- this._finish();
-
- map.fire('boxzoomend', { boxZoomBounds: this._boxBounds });
-
- // this._finish();
- },
-
- _finish: function () {
- $(this._map.boxSelector._box).remove();
- // L.DomUtil.remove(this._box);
- // L.DomUtil.remove(this._map.boxSelector);
- this._container.style.cursor = '';
-
- L.DomUtil.enableTextSelection();
- L.DomUtil.enableImageDrag();
-
- L.DomEvent
- .off(document, 'mousemove', this._onMouseMove)
- .off(document, 'mouseup', this._onMouseUp);
- },
-});
-
-L.Map.addInitHook('addHandler', 'boxSelector', L.Map.BoxSelectHandle);
+});
+L.Map.mergeOptions({ boxSelector: true, boxZoom: false });
+
+// used for multiple image select. Temporarily disabled until click
+// propagation issue is fixed
+
+L.Map.BoxSelectHandle = L.Map.BoxZoom.extend({
+
+ initialize: function (map) {
+ this._map = map;
+ this._container = map._container;
+ this._pane = map._panes.overlayPane;
+ },
+
+ addHooks: function () {
+ L.DomEvent.on(this._container, 'mousedown', this._onMouseDown, this);
+ },
+
+ removeHooks: function () {
+ L.DomEvent.off(this._container, 'mousedown', this._onMouseDown, this);
+ },
+
+ _onMouseDown: function (e) {
+ if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
+
+ L.DomUtil.disableTextSelection();
+ L.DomUtil.disableImageDrag();
+
+ this._startLayerPoint = this._map.mouseEventToLayerPoint(e);
+
+ this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._pane);
+ L.DomUtil.setPosition(this._box, this._startLayerPoint);
+
+ this._container.style.cursor = 'crosshair';
+
+ L.DomEvent
+ .on(document, 'mousemove', this._onMouseMove, this)
+ .on(document, 'mouseup', this._onMouseUp, this)
+ .preventDefault(e);
+
+ this._map.fire('boxzoomstart');
+ },
+
+ _onMouseMove: function (e) {
+ var startPoint = this._startLayerPoint,
+ box = this._box,
+
+ layerPoint = this._map.mouseEventToLayerPoint(e),
+ offset = layerPoint.subtract(startPoint),
+
+ newPos = L.point(
+ Math.min(layerPoint.x, startPoint.x),
+ Math.min(layerPoint.y, startPoint.y));
+
+ L.DomUtil.setPosition(box, newPos);
+
+ box.style.width = (Math.max(0, Math.abs(offset.x) - 4)) + 'px';
+ box.style.height = (Math.max(0, Math.abs(offset.y) - 4)) + 'px';
+ },
+
+ _onMouseUp: function (e) {
+ var map = this._map,
+ layerPoint = map.mouseEventToLayerPoint(e);
+
+ if (this._startLayerPoint.equals(layerPoint)) { return; }
+
+ this._boxBounds = new L.LatLngBounds(
+ map.layerPointToLatLng(this._startLayerPoint),
+ map.layerPointToLatLng(layerPoint));
+
+ this._finish();
+
+ map.fire('boxzoomend', { boxZoomBounds: this._boxBounds });
+
+ // this._finish();
+ },
+
+ _finish: function () {
+ $(this._map.boxSelector._box).remove();
+ // L.DomUtil.remove(this._box);
+ // L.DomUtil.remove(this._map.boxSelector);
+ this._container.style.cursor = '';
+
+ L.DomUtil.enableTextSelection();
+ L.DomUtil.enableImageDrag();
+
+ L.DomEvent
+ .off(document, 'mousemove', this._onMouseMove)
+ .off(document, 'mouseup', this._onMouseUp);
+ },
+});
+
+L.Map.addInitHook('addHandler', 'boxSelector', L.Map.BoxSelectHandle);
\ No newline at end of file
diff --git a/examples/index.html b/examples/index.html
index a42f617de..66b235f05 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -1,10 +1,10 @@
-
+
Leaflet.DistortableImage Example
-
-
-
+
+
+
@@ -17,27 +17,21 @@
-
-
+
+
+
+
+
+
+
-
+
diff --git a/examples/listeners.html b/examples/listeners.html
index 9e87f4605..226973220 100644
--- a/examples/listeners.html
+++ b/examples/listeners.html
@@ -1,15 +1,15 @@
-
+
Leaflet.DistortableImage Example
-
-
-
+
+
+
-
+
@@ -18,6 +18,10 @@
+
+
+
+
diff --git a/examples/select.html b/examples/select.html
index 1362a422f..b803442d2 100644
--- a/examples/select.html
+++ b/examples/select.html
@@ -1,10 +1,10 @@
-
+
Leaflet.DistortableImage Example
-
-
-
+
+
+
@@ -16,8 +16,15 @@
+
+
+
+
+
+
+
@@ -47,10 +54,10 @@
img = L.distortableImageOverlay(
'example.png', {
corners: [
+ L.latLng(51.52, -0.14),
L.latLng(51.52,-0.10),
- L.latLng(51.52,-0.14),
- L.latLng(51.50,-0.10),
- L.latLng(51.50,-0.14)
+ L.latLng(51.50, -0.14),
+ L.latLng(51.50,-0.10)
],
mode: 'lock'
}).addTo(map);
@@ -59,10 +66,10 @@
img2 = L.distortableImageOverlay(
'example.png', {
corners: [
+ L.latLng(51.51, -0.20),
L.latLng(51.51,-0.16),
- L.latLng(51.51,-0.20),
- L.latLng(51.49,-0.17),
- L.latLng(51.49,-0.21)
+ L.latLng(51.49, -0.21),
+ L.latLng(51.49,-0.17)
],
mode: 'rotateScale'
}).addTo(map);
@@ -70,20 +77,20 @@
img3 = L.distortableImageOverlay(
'example.png', {
corners: [
+ L.latLng(51.50, -0.13),
L.latLng(51.50,-0.09),
- L.latLng(51.50,-0.13),
+ L.latLng(51.48, -0.14),
L.latLng(51.48,-0.10),
- L.latLng(51.48,-0.14)
],
}).addTo(map);
img4 = L.distortableImageOverlay(
'example.png', {
corners: [
+ L.latLng(51.51, -0.07),
L.latLng(51.51,-0.03),
- L.latLng(51.51,-0.07),
- L.latLng(51.49,-0.04),
- L.latLng(51.49,-0.08)
+ L.latLng(51.49, -0.08),
+ L.latLng(51.49,-0.04)
],
}).addTo(map);
diff --git a/package-lock.json b/package-lock.json
index 41a0e4ef9..aa5f06312 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -40,6 +40,12 @@
"integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==",
"dev": true
},
+ "@types/q": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz",
+ "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==",
+ "dev": true
+ },
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@@ -414,6 +420,12 @@
}
}
},
+ "boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
+ "dev": true
+ },
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -672,6 +684,72 @@
}
}
},
+ "clone": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+ "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
+ "dev": true
+ },
+ "clone-buffer": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz",
+ "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=",
+ "dev": true
+ },
+ "clone-stats": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz",
+ "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=",
+ "dev": true
+ },
+ "cloneable-readable": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz",
+ "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "process-nextick-args": "^2.0.0",
+ "readable-stream": "^2.3.5"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "coa": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz",
+ "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==",
+ "dev": true,
+ "requires": {
+ "@types/q": "^1.5.1",
+ "chalk": "^2.4.1",
+ "q": "^1.1.2"
+ }
+ },
"code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
@@ -693,6 +771,16 @@
"object-visit": "^1.0.0"
}
},
+ "color": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz",
+ "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.1",
+ "color-string": "^1.5.2"
+ }
+ },
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -708,12 +796,38 @@
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
},
+ "color-string": {
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
+ "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
+ "dev": true,
+ "requires": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "colornames": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz",
+ "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=",
+ "dev": true
+ },
"colors": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
"integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
"dev": true
},
+ "colorspace": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz",
+ "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==",
+ "dev": true,
+ "requires": {
+ "color": "3.0.x",
+ "text-hex": "1.0.x"
+ }
+ },
"combined-stream": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
@@ -860,6 +974,97 @@
"which": "^1.2.9"
}
},
+ "css-select": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.0.2.tgz",
+ "integrity": "sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ==",
+ "dev": true,
+ "requires": {
+ "boolbase": "^1.0.0",
+ "css-what": "^2.1.2",
+ "domutils": "^1.7.0",
+ "nth-check": "^1.0.2"
+ },
+ "dependencies": {
+ "domutils": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
+ "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==",
+ "dev": true,
+ "requires": {
+ "dom-serializer": "0",
+ "domelementtype": "1"
+ }
+ }
+ }
+ },
+ "css-select-base-adapter": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz",
+ "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==",
+ "dev": true
+ },
+ "css-selector-parser": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-1.3.0.tgz",
+ "integrity": "sha1-XxrUPi2O77/cME/NOaUhZklD4+s=",
+ "dev": true
+ },
+ "css-tree": {
+ "version": "1.0.0-alpha.28",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.28.tgz",
+ "integrity": "sha512-joNNW1gCp3qFFzj4St6zk+Wh/NBv0vM5YbEreZk0SD4S23S+1xBKb6cLDg2uj4P4k/GUMlIm6cKIDqIG+vdt0w==",
+ "dev": true,
+ "requires": {
+ "mdn-data": "~1.1.0",
+ "source-map": "^0.5.3"
+ }
+ },
+ "css-url-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/css-url-regex/-/css-url-regex-1.1.0.tgz",
+ "integrity": "sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w=",
+ "dev": true
+ },
+ "css-what": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
+ "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==",
+ "dev": true
+ },
+ "cssmin": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/cssmin/-/cssmin-0.4.3.tgz",
+ "integrity": "sha1-yRlAd+Dr2s1pHV9ZAVudgZ840BU=",
+ "dev": true
+ },
+ "csso": {
+ "version": "3.5.1",
+ "resolved": "https://registry.npmjs.org/csso/-/csso-3.5.1.tgz",
+ "integrity": "sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg==",
+ "dev": true,
+ "requires": {
+ "css-tree": "1.0.0-alpha.29"
+ },
+ "dependencies": {
+ "css-tree": {
+ "version": "1.0.0-alpha.29",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.29.tgz",
+ "integrity": "sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==",
+ "dev": true,
+ "requires": {
+ "mdn-data": "~1.1.0",
+ "source-map": "^0.5.3"
+ }
+ }
+ }
+ },
+ "cssom": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz",
+ "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==",
+ "dev": true
+ },
"currently-unhandled": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
@@ -1009,6 +1214,17 @@
"integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=",
"dev": true
},
+ "diagnostics": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz",
+ "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==",
+ "dev": true,
+ "requires": {
+ "colorspace": "1.1.x",
+ "enabled": "1.0.x",
+ "kuler": "1.0.x"
+ }
+ },
"diff": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
@@ -1092,6 +1308,15 @@
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
"dev": true
},
+ "enabled": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz",
+ "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=",
+ "dev": true,
+ "requires": {
+ "env-variable": "0.0.x"
+ }
+ },
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
@@ -1193,6 +1418,12 @@
"integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=",
"dev": true
},
+ "env-variable": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz",
+ "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==",
+ "dev": true
+ },
"error": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/error/-/error-7.0.2.tgz",
@@ -1502,6 +1733,12 @@
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
"dev": true
},
+ "fast-safe-stringify": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz",
+ "integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==",
+ "dev": true
+ },
"faye-websocket": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
@@ -1520,6 +1757,12 @@
"pend": "~1.2.0"
}
},
+ "fecha": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz",
+ "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==",
+ "dev": true
+ },
"fill-range": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
@@ -2619,6 +2862,29 @@
"which": "~1.3.0"
}
},
+ "grunt-svg-sprite": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/grunt-svg-sprite/-/grunt-svg-sprite-1.5.0.tgz",
+ "integrity": "sha512-E7q84qIYLygO/CRlqH2t5EdsaLdblqNs2JSpY+d1XEyNIlOU0tsUuG/cro36Nd3IGSnouzIJAS4yurpPUHUrRA==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.1",
+ "prettysize": "^1.1.0",
+ "svg-sprite": "^1.5.0"
+ }
+ },
+ "grunt-svgmin": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-svgmin/-/grunt-svgmin-6.0.0.tgz",
+ "integrity": "sha512-gSVIzJ9TNUDpsTpZELaAdBkQ5DRMios6SlFKc+S7XwUPFvqx8scCsiYTEiGLeYDEvE9+a2GGsSdJUd5ufIFlGQ==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.2",
+ "log-symbols": "^2.2.0",
+ "pretty-bytes": "^5.1.0",
+ "svgo": "^1.2.0"
+ }
+ },
"handlebars": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz",
@@ -3428,6 +3694,15 @@
"graceful-fs": "^4.1.9"
}
},
+ "kuler": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz",
+ "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==",
+ "dev": true,
+ "requires": {
+ "colornames": "^1.1.1"
+ }
+ },
"lcid": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
@@ -3517,6 +3792,134 @@
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
"dev": true
},
+ "lodash._arraymap": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lodash._arraymap/-/lodash._arraymap-3.0.0.tgz",
+ "integrity": "sha1-Go/Q9MDfS2HeoHbXF83Jfwo8PmY=",
+ "dev": true
+ },
+ "lodash._basecallback": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/lodash._basecallback/-/lodash._basecallback-3.3.1.tgz",
+ "integrity": "sha1-t7K7Q9whYEJKIczybFfkQ3cqjic=",
+ "dev": true,
+ "requires": {
+ "lodash._baseisequal": "^3.0.0",
+ "lodash._bindcallback": "^3.0.0",
+ "lodash.isarray": "^3.0.0",
+ "lodash.pairs": "^3.0.0"
+ }
+ },
+ "lodash._baseeach": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/lodash._baseeach/-/lodash._baseeach-3.0.4.tgz",
+ "integrity": "sha1-z4cGVyyhROjZ11InyZDamC+TKvM=",
+ "dev": true,
+ "requires": {
+ "lodash.keys": "^3.0.0"
+ }
+ },
+ "lodash._baseget": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/lodash._baseget/-/lodash._baseget-3.7.2.tgz",
+ "integrity": "sha1-G2rh1frPPCVTI1ChPBGXy4u2dPQ=",
+ "dev": true
+ },
+ "lodash._baseisequal": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/lodash._baseisequal/-/lodash._baseisequal-3.0.7.tgz",
+ "integrity": "sha1-2AJfdjOdKTQnZ9zIh85cuVpbUfE=",
+ "dev": true,
+ "requires": {
+ "lodash.isarray": "^3.0.0",
+ "lodash.istypedarray": "^3.0.0",
+ "lodash.keys": "^3.0.0"
+ }
+ },
+ "lodash._bindcallback": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz",
+ "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=",
+ "dev": true
+ },
+ "lodash._getnative": {
+ "version": "3.9.1",
+ "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
+ "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=",
+ "dev": true
+ },
+ "lodash._topath": {
+ "version": "3.8.1",
+ "resolved": "https://registry.npmjs.org/lodash._topath/-/lodash._topath-3.8.1.tgz",
+ "integrity": "sha1-PsXiYGAU9MuX91X+aRTt2L/ADqw=",
+ "dev": true,
+ "requires": {
+ "lodash.isarray": "^3.0.0"
+ }
+ },
+ "lodash.isarguments": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
+ "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=",
+ "dev": true
+ },
+ "lodash.isarray": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
+ "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=",
+ "dev": true
+ },
+ "lodash.istypedarray": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz",
+ "integrity": "sha1-yaR3SYYHUB2OhJTSg7h8OSgc72I=",
+ "dev": true
+ },
+ "lodash.keys": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
+ "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=",
+ "dev": true,
+ "requires": {
+ "lodash._getnative": "^3.0.0",
+ "lodash.isarguments": "^3.0.0",
+ "lodash.isarray": "^3.0.0"
+ }
+ },
+ "lodash.map": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-3.1.4.tgz",
+ "integrity": "sha1-tIOs0beGxce0ksSV97UmYim8AMI=",
+ "dev": true,
+ "requires": {
+ "lodash._arraymap": "^3.0.0",
+ "lodash._basecallback": "^3.0.0",
+ "lodash._baseeach": "^3.0.0",
+ "lodash.isarray": "^3.0.0",
+ "lodash.keys": "^3.0.0"
+ }
+ },
+ "lodash.pairs": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.pairs/-/lodash.pairs-3.0.1.tgz",
+ "integrity": "sha1-u+CNV4bu6qCaFckevw3LfSvjJqk=",
+ "dev": true,
+ "requires": {
+ "lodash.keys": "^3.0.0"
+ }
+ },
+ "lodash.pluck": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.pluck/-/lodash.pluck-3.1.2.tgz",
+ "integrity": "sha1-s0fwN0wBafDusE1nLYnOyGMsIjE=",
+ "dev": true,
+ "requires": {
+ "lodash._baseget": "^3.0.0",
+ "lodash._topath": "^3.0.0",
+ "lodash.isarray": "^3.0.0",
+ "lodash.map": "^3.0.0"
+ }
+ },
"log-symbols": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
@@ -3556,6 +3959,33 @@
}
}
},
+ "logform": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz",
+ "integrity": "sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==",
+ "dev": true,
+ "requires": {
+ "colors": "^1.2.1",
+ "fast-safe-stringify": "^2.0.4",
+ "fecha": "^2.3.3",
+ "ms": "^2.1.1",
+ "triple-beam": "^1.3.0"
+ },
+ "dependencies": {
+ "colors": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz",
+ "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
"lolex": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lolex/-/lolex-4.0.1.tgz",
@@ -3630,6 +4060,12 @@
"stack-trace": "0.0.10"
}
},
+ "mdn-data": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz",
+ "integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==",
+ "dev": true
+ },
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -3854,6 +4290,12 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
+ "mustache": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mustache/-/mustache-3.0.1.tgz",
+ "integrity": "sha512-jFI/4UVRsRYdUbuDTKT7KzfOp7FiD5WzYmmwNwXyUVypC0xjoTL78Fqc0jHUPIvvGD+6DQSPHIt1NE7D1ArsqA==",
+ "dev": true
+ },
"nan": {
"version": "2.13.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz",
@@ -3964,6 +4406,15 @@
"path-key": "^2.0.0"
}
},
+ "nth-check": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
+ "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
+ "dev": true,
+ "requires": {
+ "boolbase": "~1.0.0"
+ }
+ },
"number-is-nan": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
@@ -4080,6 +4531,18 @@
"isobject": "^3.0.1"
}
},
+ "object.values": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz",
+ "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.12.0",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3"
+ }
+ },
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
@@ -4098,6 +4561,12 @@
"wrappy": "1"
}
},
+ "one-time": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz",
+ "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=",
+ "dev": true
+ },
"optimist": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
@@ -4425,6 +4894,18 @@
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
"dev": true
},
+ "pretty-bytes": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.2.0.tgz",
+ "integrity": "sha512-ujANBhiUsl9AhREUDUEY1GPOharMGm8x8juS7qOHybcLi7XsKfrYQ88hSly1l2i0klXHTDYrlL8ihMCG55Dc3w==",
+ "dev": true
+ },
+ "prettysize": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/prettysize/-/prettysize-1.1.0.tgz",
+ "integrity": "sha512-U5Noa+FYV1dGkICyLJz8IWlDUehPF4Bk9tZRO8YqPhLA9EoiHuFqtnpWY2mvMjHh5eOLo82HipeLn4RIiSsGqQ==",
+ "dev": true
+ },
"process-nextick-args": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
@@ -4465,6 +4946,12 @@
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
},
+ "q": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
+ "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
+ "dev": true
+ },
"qjobs": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz",
@@ -4623,6 +5110,12 @@
"is-finite": "^1.0.0"
}
},
+ "replace-ext": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz",
+ "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=",
+ "dev": true
+ },
"request": {
"version": "2.88.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
@@ -4770,6 +5263,12 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
+ "sax": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+ "dev": true
+ },
"semver": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
@@ -4830,12 +5329,83 @@
"integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=",
"dev": true
},
+ "should": {
+ "version": "13.2.3",
+ "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz",
+ "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==",
+ "dev": true,
+ "requires": {
+ "should-equal": "^2.0.0",
+ "should-format": "^3.0.3",
+ "should-type": "^1.4.0",
+ "should-type-adaptors": "^1.0.1",
+ "should-util": "^1.0.0"
+ }
+ },
+ "should-equal": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz",
+ "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==",
+ "dev": true,
+ "requires": {
+ "should-type": "^1.4.0"
+ }
+ },
+ "should-format": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz",
+ "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=",
+ "dev": true,
+ "requires": {
+ "should-type": "^1.3.0",
+ "should-type-adaptors": "^1.0.1"
+ }
+ },
+ "should-type": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz",
+ "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=",
+ "dev": true
+ },
+ "should-type-adaptors": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz",
+ "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==",
+ "dev": true,
+ "requires": {
+ "should-type": "^1.3.0",
+ "should-util": "^1.0.0"
+ }
+ },
+ "should-util": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.0.tgz",
+ "integrity": "sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM=",
+ "dev": true
+ },
"signal-exit": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"dev": true
},
+ "simple-swizzle": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+ "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.3.1"
+ },
+ "dependencies": {
+ "is-arrayish": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
+ "dev": true
+ }
+ }
+ },
"sinon": {
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/sinon/-/sinon-7.3.2.tgz",
@@ -5137,6 +5707,12 @@
"tweetnacl": "~0.14.0"
}
},
+ "stable": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
+ "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
+ "dev": true
+ },
"stack-trace": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
@@ -5294,6 +5870,216 @@
"has-flag": "^3.0.0"
}
},
+ "svg-sprite": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/svg-sprite/-/svg-sprite-1.5.0.tgz",
+ "integrity": "sha512-0mE5BLY3K8wg3+HrYfzpiKbIM44IGcg8uINED8ri22EdQbLvGecOHjRtkrNAlphbiU5kyGyqoBlIaukL45fs2Q==",
+ "dev": true,
+ "requires": {
+ "async": "^2.6.1",
+ "css-selector-parser": "^1.3.0",
+ "cssmin": "^0.4.3",
+ "cssom": "^0.3.4",
+ "dateformat": "^3.0.3",
+ "glob": "^7.1.3",
+ "js-yaml": "^3.12.0",
+ "lodash": "^4.17.11",
+ "lodash.pluck": "^3.1.2",
+ "mkdirp": "^0.5.1",
+ "mocha": "^5.2.0",
+ "mustache": "^3.0.0",
+ "phantomjs-prebuilt": "^2.1.16",
+ "prettysize": "^1.1.0",
+ "should": "^13.2.3",
+ "svgo": "^1.1.1",
+ "vinyl": "^2.2.0",
+ "winston": "^3.1.0",
+ "xmldom": "0.1.27",
+ "xpath": "^0.0.27",
+ "yargs": "^12.0.2"
+ },
+ "dependencies": {
+ "async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
+ "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.11"
+ }
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "commander": {
+ "version": "2.15.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
+ "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
+ "dev": true
+ },
+ "dateformat": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
+ "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==",
+ "dev": true
+ },
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "get-caller-file": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
+ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
+ "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "he": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
+ "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
+ "dev": true
+ },
+ "mocha": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz",
+ "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==",
+ "dev": true,
+ "requires": {
+ "browser-stdout": "1.3.1",
+ "commander": "2.15.1",
+ "debug": "3.1.0",
+ "diff": "3.5.0",
+ "escape-string-regexp": "1.0.5",
+ "glob": "7.1.2",
+ "growl": "1.10.5",
+ "he": "1.1.1",
+ "minimatch": "3.0.4",
+ "mkdirp": "0.5.1",
+ "supports-color": "5.4.0"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
+ }
+ },
+ "require-main-filename": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
+ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
+ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "yargs": {
+ "version": "12.0.5",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
+ "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^4.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^1.0.1",
+ "os-locale": "^3.0.0",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^1.0.1",
+ "set-blocking": "^2.0.0",
+ "string-width": "^2.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^3.2.1 || ^4.0.0",
+ "yargs-parser": "^11.1.1"
+ }
+ },
+ "yargs-parser": {
+ "version": "11.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
+ "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
+ "svgo": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.2.2.tgz",
+ "integrity": "sha512-rAfulcwp2D9jjdGu+0CuqlrAUin6bBWrpoqXWwKDZZZJfXcUXQSxLJOFJCQCSA0x0pP2U0TxSlJu2ROq5Bq6qA==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.1",
+ "coa": "^2.0.2",
+ "css-select": "^2.0.0",
+ "css-select-base-adapter": "^0.1.1",
+ "css-tree": "1.0.0-alpha.28",
+ "css-url-regex": "^1.1.0",
+ "csso": "^3.5.1",
+ "js-yaml": "^3.13.1",
+ "mkdirp": "~0.5.1",
+ "object.values": "^1.1.0",
+ "sax": "~1.2.4",
+ "stable": "^0.1.8",
+ "unquote": "~1.1.1",
+ "util.promisify": "~1.0.0"
+ }
+ },
+ "text-hex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
+ "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==",
+ "dev": true
+ },
"throttleit": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz",
@@ -5414,6 +6200,12 @@
"integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
"dev": true
},
+ "triple-beam": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
+ "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==",
+ "dev": true
+ },
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@@ -5545,6 +6337,12 @@
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
"dev": true
},
+ "unquote": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz",
+ "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=",
+ "dev": true
+ },
"unset-value": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
@@ -5622,6 +6420,16 @@
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"dev": true
},
+ "util.promisify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz",
+ "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.2",
+ "object.getownpropertydescriptors": "^2.0.3"
+ }
+ },
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@@ -5663,6 +6471,20 @@
"extsprintf": "^1.2.0"
}
},
+ "vinyl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz",
+ "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==",
+ "dev": true,
+ "requires": {
+ "clone": "^2.1.1",
+ "clone-buffer": "^1.0.0",
+ "clone-stats": "^1.0.0",
+ "cloneable-readable": "^1.0.0",
+ "remove-trailing-separator": "^1.0.1",
+ "replace-ext": "^1.0.0"
+ }
+ },
"void-elements": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
@@ -5714,6 +6536,90 @@
"string-width": "^1.0.2 || 2"
}
},
+ "winston": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz",
+ "integrity": "sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==",
+ "dev": true,
+ "requires": {
+ "async": "^2.6.1",
+ "diagnostics": "^1.1.1",
+ "is-stream": "^1.1.0",
+ "logform": "^2.1.1",
+ "one-time": "0.0.4",
+ "readable-stream": "^3.1.1",
+ "stack-trace": "0.0.x",
+ "triple-beam": "^1.3.0",
+ "winston-transport": "^4.3.0"
+ },
+ "dependencies": {
+ "async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
+ "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.11"
+ }
+ },
+ "readable-stream": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
+ "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz",
+ "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "winston-transport": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz",
+ "integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.3.6",
+ "triple-beam": "^1.2.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
"wordwrap": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
@@ -5769,12 +6675,24 @@
"ultron": "~1.1.0"
}
},
+ "xmldom": {
+ "version": "0.1.27",
+ "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz",
+ "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=",
+ "dev": true
+ },
"xmlhttprequest-ssl": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
"integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=",
"dev": true
},
+ "xpath": {
+ "version": "0.0.27",
+ "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz",
+ "integrity": "sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ==",
+ "dev": true
+ },
"xtend": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
diff --git a/package.json b/package.json
index fc687c827..cd35d6001 100644
--- a/package.json
+++ b/package.json
@@ -40,6 +40,8 @@
"grunt-contrib-jshint": "^2.0.0",
"grunt-contrib-watch": "^1.0.0",
"grunt-karma": "^3.0.1",
+ "grunt-svg-sprite": "^1.5.0",
+ "grunt-svgmin": "^6.0.0",
"jquery": "~> 3.4.0",
"karma": "^4.1.0",
"karma-coverage": "^1.1.1",
diff --git a/src/DistortableCollection.js b/src/DistortableCollection.js
index 6b8123438..b48a56ed3 100644
--- a/src/DistortableCollection.js
+++ b/src/DistortableCollection.js
@@ -106,7 +106,7 @@ L.DistortableCollection = L.FeatureGroup.extend({
id: this.getLayerId(layer),
src: layer._image.src,
nodes: layer.getCorners(),
- cm_per_pixel: layer._getCmPerPixel()
+ cm_per_pixel: L.ImageUtil.getCmPerPixel(layer)
});
}
}, this);
diff --git a/src/DistortableImageOverlay.js b/src/DistortableImageOverlay.js
index ae30773e5..ae480e249 100644
--- a/src/DistortableImageOverlay.js
+++ b/src/DistortableImageOverlay.js
@@ -12,10 +12,11 @@ L.DistortableImageOverlay = L.ImageOverlay.extend({
this._toolArray = L.DistortableImage.EditToolbarDefaults;
this.edgeMinWidth = this.options.edgeMinWidth;
this._url = url;
- this._rotation = this.options.rotation;
+ this.rotation = 0;
+ // window.rotation = this.rotation;
L.DistortableImage._options = options;
- L.Util.setOptions(this, options);
+ L.setOptions(this, options);
},
onAdd: function(map) {
@@ -107,6 +108,7 @@ L.DistortableImageOverlay = L.ImageOverlay.extend({
map.containerPointToLatLng(center.add(offset))
];
}
+ this._initialDimensions = { 'height': imageHeight, 'width': imageWidth, 'offset': offset };
},
_initEvents: function() {
@@ -290,15 +292,6 @@ L.DistortableImageOverlay = L.ImageOverlay.extend({
0, h, c[2].x, c[2].y,
w, h, c[3].x, c[3].y
);
- },
-
- _getCmPerPixel: function() {
- var map = this._map;
-
- var dist = map.latLngToLayerPoint(this.getCorner(0))
- .distanceTo(map.latLngToLayerPoint(this.getCorner(1)));
-
- return (dist * 100) / this._image.width;
}
});
diff --git a/src/edit/DistortHandle.js b/src/edit/DistortHandle.js
index f760e3257..ae2f02183 100644
--- a/src/edit/DistortHandle.js
+++ b/src/edit/DistortHandle.js
@@ -9,14 +9,17 @@ L.DistortHandle = L.EditHandle.extend({
})
},
+ _onHandleDrag: function() {
+ var overlay = this._handled;
+
+ overlay._updateCorner(this._corner, this.getLatLng());
+
+ overlay.fire("update");
+ overlay.editing._updateToolbarPos();
+ },
+
updateHandle: function() {
- this.setLatLng(this._handled._corners[this._corner]);
+ this.setLatLng(this._handled.getCorner(this._corner));
},
- _onHandleDrag: function() {
- this._handled._updateCorner(this._corner, this.getLatLng());
-
- this._handled.fire("update");
- this._handled.editing._showToolbar();
- }
});
diff --git a/src/edit/DistortableImage.Edit.js b/src/edit/DistortableImage.Edit.js
index f391a1abb..61679eddb 100644
--- a/src/edit/DistortableImage.Edit.js
+++ b/src/edit/DistortableImage.Edit.js
@@ -173,12 +173,43 @@ L.DistortableImage.Edit = L.Handler.extend({
Math.cos(angle) * p.x - Math.sin(angle) * p.y,
Math.sin(angle) * p.x + Math.cos(angle) * p.y
);
- overlay._corners[i] = map.layerPointToLatLng(q.add(center));
+ overlay._updateCorner(i, map.layerPointToLatLng(q.add(center)));
}
+ // window.angle = L.TrigUtil.radiansToDegrees(angle);
+
+ this._overlay.rotation -= L.TrigUtil.radiansToDegrees(angle);
+
overlay._reset();
},
+ _restore: function() {
+ var overlay = this._overlay;
+ var angle = overlay.rotation;
+ var map = overlay._map;
+ var center = map.latLngToLayerPoint(overlay.getCenter());
+ var offset = overlay._initialDimensions.offset;
+
+ var corners = {
+ 0: map.layerPointToLatLng(center.subtract(offset)),
+ 1: map.layerPointToLatLng(center.add(L.point(offset.x, -offset.y))),
+ 2: map.layerPointToLatLng(center.add(L.point(-offset.x, offset.y))),
+ 3: map.layerPointToLatLng(center.add(offset))
+ };
+
+ map.removeLayer(this._handles[this._mode]);
+
+ overlay._updateCorners(corners);
+
+ if (angle !== 0) { this._rotateBy(L.TrigUtil.degreesToRadians(360 - angle)); }
+
+ map.addLayer(this._handles[this._mode]);
+
+ this._showToolbar();
+
+ this._overlay.rotation = angle;
+ },
+
_scaleBy: function(scale) {
var overlay = this._overlay,
map = overlay._map,
@@ -188,11 +219,11 @@ L.DistortableImage.Edit = L.Handler.extend({
for (i = 0; i < 4; i++) {
p = map
- .latLngToLayerPoint(overlay._corners[i])
+ .latLngToLayerPoint(overlay.getCorner(i))
.subtract(center)
.multiplyBy(scale)
.add(center);
- overlay._corners[i] = map.layerPointToLatLng(p);
+ overlay._updateCorner(i, map.layerPointToLatLng(p));
}
overlay._reset();
@@ -300,6 +331,8 @@ L.DistortableImage.Edit = L.Handler.extend({
L.DomUtil.setOpacity(image, opacity);
image.setAttribute("opacity", opacity);
+
+ this._showToolbar();
},
_toggleOutline: function() {
@@ -406,11 +439,11 @@ L.DistortableImage.Edit = L.Handler.extend({
// TODO: toolbar for multiple image selection
_showToolbar: function() {
var overlay = this._overlay,
- map = overlay._map;
+ map = overlay._map,
+ //Find the topmost point on the image.
+ corners = overlay.getCorners(),
+ maxLat = -Infinity;
- //Find the topmost point on the image.
- var corners = overlay.getCorners();
- var maxLat = -Infinity;
for (var i = 0; i < corners.length; i++) {
if (corners[i].lat > maxLat) {
maxLat = corners[i].lat;
@@ -421,14 +454,36 @@ L.DistortableImage.Edit = L.Handler.extend({
var raised_point = overlay.getCenter();
raised_point.lat = maxLat;
- if (this._overlay.options.suppressToolbar !== true) {
+ if (overlay.options.suppressToolbar !== true) {
try {
this.toolbar = new L.DistortableImage.EditToolbar(raised_point).addTo(map, overlay);
overlay.fire('toolbar:created');
}
catch (e) {}
}
- },
+ },
+
+ _updateToolbarPos: function() {
+ var overlay = this._overlay,
+ //Find the topmost point on the image.
+ corners = overlay.getCorners(),
+ maxLat = -Infinity;
+
+ for (var i = 0; i < corners.length; i++) {
+ if (corners[i].lat > maxLat) {
+ maxLat = corners[i].lat;
+ }
+ }
+
+ //Longitude is based on the centroid of the image.
+ var raised_point = overlay.getCenter();
+ raised_point.lat = maxLat;
+
+ if (overlay.options.suppressToolbar !== true) {
+ this.toolbar.setLatLng(raised_point);
+ }
+
+ },
_removeOverlay: function () {
var overlay = this._overlay,
@@ -475,10 +530,10 @@ L.DistortableImage.Edit = L.Handler.extend({
downloadable.onload = function onLoadDownloadableImage() {
var height = downloadable.height,
width = downloadable.width,
- nw = map.latLngToLayerPoint(overlay._corners[0]),
- ne = map.latLngToLayerPoint(overlay._corners[1]),
- sw = map.latLngToLayerPoint(overlay._corners[2]),
- se = map.latLngToLayerPoint(overlay._corners[3]);
+ nw = map.latLngToLayerPoint(overlay.getCorner(0)),
+ ne = map.latLngToLayerPoint(overlay.getCorner(1)),
+ sw = map.latLngToLayerPoint(overlay.getCorner(2)),
+ se = map.latLngToLayerPoint(overlay.getCorner(3));
// I think this is to move the image to the upper left corner,
// jywarren: i think we may need these or the image goes off the edge of the canvas
diff --git a/src/edit/DistortableImage.EditToolbar.js b/src/edit/DistortableImage.EditToolbar.js
index 3f25931f2..c37612c28 100644
--- a/src/edit/DistortableImage.EditToolbar.js
+++ b/src/edit/DistortableImage.EditToolbar.js
@@ -1,20 +1,35 @@
L.DistortableImage = L.DistortableImage || {};
var EditOverlayAction = LeafletToolbar.ToolbarAction.extend({
- initialize: function(map, overlay, options) {
- this._overlay = overlay;
- this._map = map;
+ initialize: function(map, overlay, options) {
+ this._overlay = overlay;
+ this._map = map;
- LeafletToolbar.ToolbarAction.prototype.initialize.call(this, options);
- }
- }),
-
+ LeafletToolbar.ToolbarAction.prototype.initialize.call(this, options);
+ }
+}),
+
ToggleTransparency = EditOverlayAction.extend({
- options: {
- toolbarIcon: {
- html: 'opacity ',
- tooltip: 'Toggle Transparency'
+ initialize: function(map, overlay, options) {
+ var edit = overlay.editing,
+ href,
+ tooltip;
+
+ if (edit._transparent) {
+ href = ' ';
+ tooltip = 'Make Image Opaque';
+ } else {
+ href = ' ';
+ tooltip = 'Make Image Transparent';
}
+
+ options = options || {};
+ options.toolbarIcon = {
+ html: '' + href + ' ',
+ tooltip: tooltip
+ };
+
+ EditOverlayAction.prototype.initialize.call(this, map, overlay, options);
},
addHooks: function() {
@@ -23,17 +38,26 @@ var EditOverlayAction = LeafletToolbar.ToolbarAction.extend({
editing._toggleTransparency();
this.disable();
}
- }),
-
+ }),
+
ToggleOutline = EditOverlayAction.extend({
initialize: function(map, overlay, options) {
var edit = overlay.editing,
- icon = edit._outlined ? 'border_clear' : 'border_outer';
+ href,
+ tooltip;
+
+ if (edit._outlined) {
+ href = ' ';
+ tooltip = 'Remove Border';
+ } else {
+ href = ' ';
+ tooltip = 'Add Border';
+ }
options = options || {};
options.toolbarIcon = {
- html: '' + icon + ' ',
- tooltip: 'Toggle Outline'
+ html: '' + href + ' ',
+ tooltip: tooltip
};
EditOverlayAction.prototype.initialize.call(this, map, overlay, options);
@@ -45,14 +69,19 @@ var EditOverlayAction = LeafletToolbar.ToolbarAction.extend({
editing._toggleOutline();
this.disable();
}
- }),
-
+ }),
+
Delete = EditOverlayAction.extend({
- options: {
- toolbarIcon: {
- html: 'delete_forever ',
+ initialize: function(map, overlay, options) {
+ var href = ' ';
+
+ options = options || {};
+ options.toolbarIcon = {
+ html: '' + href + ' ',
tooltip: 'Delete Image'
- }
+ };
+
+ EditOverlayAction.prototype.initialize.call(this, map, overlay, options);
},
addHooks: function() {
@@ -61,25 +90,25 @@ var EditOverlayAction = LeafletToolbar.ToolbarAction.extend({
editing._removeOverlay();
this.disable();
}
- }),
-
+ }),
+
ToggleLock = EditOverlayAction.extend({
initialize: function(map, overlay, options) {
var edit = overlay.editing,
- icon,
+ href,
tooltip;
if (edit._mode === 'lock') {
- icon = 'lock_open';
+ href = ' ';
tooltip = 'Unlock';
} else {
- icon = 'lock';
+ href = ' ';
tooltip = 'Lock';
}
options = options || {};
options.toolbarIcon = {
- html: '' + icon + ' ',
+ html: '' + href + ' ',
tooltip: tooltip
};
@@ -92,25 +121,25 @@ var EditOverlayAction = LeafletToolbar.ToolbarAction.extend({
editing._toggleLock();
this.disable();
}
- }),
-
+ }),
+
ToggleRotateScale = EditOverlayAction.extend({
initialize: function(map, overlay, options) {
- var edit = overlay.editing,
- icon,
- tooltip;
-
- if (edit._mode === 'rotateScale') {
- icon = 'transform';
- tooltip = 'Distort';
- } else {
- icon = 'crop_rotate';
- tooltip = 'Rotate+Scale';
- }
+ var edit = overlay.editing,
+ href,
+ tooltip;
+
+ if (edit._mode === 'rotateScale') {
+ href = ' ';
+ tooltip = 'Distort';
+ } else {
+ href = ' ';
+ tooltip = 'Rotate+Scale';
+ }
options = options || {};
options.toolbarIcon = {
- html: '' + icon + ' ',
+ html: '' + href + ' ',
tooltip: tooltip
};
@@ -123,14 +152,19 @@ var EditOverlayAction = LeafletToolbar.ToolbarAction.extend({
editing._toggleRotateScale();
this.disable();
}
- }),
-
+ }),
+
Export = EditOverlayAction.extend({
- options: {
- toolbarIcon: {
- html: 'get_app ',
+ initialize: function(map, overlay, options) {
+ var href = ' ';
+
+ options = options || {};
+ options.toolbarIcon = {
+ html: '' + href + ' ',
tooltip: 'Export Image'
- }
+ };
+
+ EditOverlayAction.prototype.initialize.call(this, map, overlay, options);
},
addHooks: function() {
@@ -139,25 +173,25 @@ var EditOverlayAction = LeafletToolbar.ToolbarAction.extend({
editing._toggleExport();
this.disable();
}
- }),
-
+ }),
+
ToggleOrder = EditOverlayAction.extend({
initialize: function(map, overlay, options) {
var edit = overlay.editing,
- icon,
+ href,
tooltip;
- if (edit._toggledImage) {
- icon = 'flip_to_front';
- tooltip = 'Stack to front';
- } else {
- icon = 'flip_to_back';
- tooltip = 'Stack to back';
- }
+ if (edit._toggledImage) {
+ href = ' ';
+ tooltip = 'Stack to Front';
+ } else {
+ href = ' ';
+ tooltip = 'Stack to Back';
+ }
options = options || {};
options.toolbarIcon = {
- html: '' + icon + ' ',
+ html: '' + href + ' ',
tooltip: tooltip
};
@@ -170,14 +204,19 @@ var EditOverlayAction = LeafletToolbar.ToolbarAction.extend({
editing._toggleOrder();
this.disable();
}
- }),
-
+ }),
+
EnableEXIF = EditOverlayAction.extend({
- options: {
- toolbarIcon: {
- html: 'explore ',
+ initialize: function(map, overlay, options) {
+ var href = ' ';
+
+ options = options || {};
+ options.toolbarIcon = {
+ html: '' + href + ' ',
tooltip: 'Geolocate Image'
- }
+ };
+
+ EditOverlayAction.prototype.initialize.call(this, map, overlay, options);
},
addHooks: function() {
@@ -185,6 +224,27 @@ var EditOverlayAction = LeafletToolbar.ToolbarAction.extend({
EXIF.getData(image, L.EXIF(image));
}
+ }),
+
+ Restore = EditOverlayAction.extend({
+ initialize: function(map, overlay, options) {
+ var href = ' ';
+
+ options = options || {};
+ options.toolbarIcon = {
+ html: '' + href + ' ',
+ tooltip: 'Restore'
+ };
+
+ EditOverlayAction.prototype.initialize.call(this, map, overlay, options);
+ },
+
+ addHooks: function() {
+ var editing = this._overlay.editing;
+
+ editing._restore();
+ this.disable();
+ }
});
L.DistortableImage.EditToolbar = LeafletToolbar.Popup.extend({
@@ -196,6 +256,7 @@ L.DistortableImage.EditToolbar = LeafletToolbar.Popup.extend({
ToggleRotateScale,
ToggleOrder,
EnableEXIF,
+ Restore,
Export,
Delete
]
diff --git a/src/edit/EditHandle.js b/src/edit/EditHandle.js
index 5d5a83232..f043bb24c 100644
--- a/src/edit/EditHandle.js
+++ b/src/edit/EditHandle.js
@@ -1,7 +1,7 @@
L.EditHandle = L.Marker.extend({
initialize: function(overlay, corner, options) {
var markerOptions,
- latlng = overlay._corners[corner];
+ latlng = overlay.getCorner(corner);
L.setOptions(this, options);
@@ -76,13 +76,14 @@ L.EditHandle = L.Marker.extend({
/* Takes two latlngs and calculates the scaling difference. */
_calculateScalingFactor: function(latlngA, latlngB) {
- var map = this._handled._map,
- centerPoint = map.latLngToLayerPoint(this._handled.getCenter()),
- formerPoint = map.latLngToLayerPoint(latlngA),
- newPoint = map.latLngToLayerPoint(latlngB),
+ var overlay = this._handled,
+ map = overlay._map,
- formerRadiusSquared = this._d2(centerPoint, formerPoint),
- newRadiusSquared = this._d2(centerPoint, newPoint);
+ centerPoint = map.latLngToLayerPoint(overlay.getCenter()),
+ formerPoint = map.latLngToLayerPoint(latlngA),
+ newPoint = map.latLngToLayerPoint(latlngB),
+ formerRadiusSquared = this._d2(centerPoint, formerPoint),
+ newRadiusSquared = this._d2(centerPoint, newPoint);
return Math.sqrt(newRadiusSquared / formerRadiusSquared);
},
@@ -96,10 +97,11 @@ L.EditHandle = L.Marker.extend({
},
/* Takes two latlngs and calculates the angle between them. */
- _calculateAngle: function(latlngA, latlngB) {
- var map = this._handled._map,
+ calculateAngleDelta: function(latlngA, latlngB) {
+ var overlay = this._handled,
+ map = overlay._map,
- centerPoint = map.latLngToLayerPoint(this._handled.getCenter()),
+ centerPoint = map.latLngToLayerPoint(overlay.getCenter()),
formerPoint = map.latLngToLayerPoint(latlngA),
newPoint = map.latLngToLayerPoint(latlngB),
diff --git a/src/edit/LockHandle.js b/src/edit/LockHandle.js
index e166c8b54..d23dc1f5e 100644
--- a/src/edit/LockHandle.js
+++ b/src/edit/LockHandle.js
@@ -13,7 +13,7 @@ L.LockHandle = L.EditHandle.extend({
},
updateHandle: function() {
- this.setLatLng(this._handled._corners[this._corner]);
+ this.setLatLng(this._handled.getCorner(this._corner));
L.DomUtil.removeClass(this._handled.getElement(), 'selected');
}
diff --git a/src/edit/RotateHandle.js b/src/edit/RotateHandle.js
index f3dfa23ba..1b4533ec3 100644
--- a/src/edit/RotateHandle.js
+++ b/src/edit/RotateHandle.js
@@ -10,19 +10,18 @@ L.RotateHandle = L.EditHandle.extend({
_onHandleDrag: function() {
var overlay = this._handled,
- formerLatLng = this._handled._corners[this._corner],
+ formerLatLng = overlay.getCorner(this._corner),
newLatLng = this.getLatLng(),
- angle = this._calculateAngle(formerLatLng, newLatLng);
+ angle = this.calculateAngleDelta(formerLatLng, newLatLng);
- overlay.editing._rotateBy(angle);
+ if (angle !== 0) { overlay.editing._rotateBy(angle); }
overlay.fire('update');
-
- this._handled.editing._showToolbar();
+ overlay.editing._updateToolbarPos();
},
updateHandle: function() {
- this.setLatLng(this._handled._corners[this._corner]);
+ this.setLatLng(this._handled.getCorner(this._corner));
}
});
diff --git a/src/edit/RotateScaleHandle.js b/src/edit/RotateScaleHandle.js
index deaa78da0..142006098 100644
--- a/src/edit/RotateScaleHandle.js
+++ b/src/edit/RotateScaleHandle.js
@@ -10,35 +10,33 @@ L.RotateScaleHandle = L.EditHandle.extend({
_onHandleDrag: function() {
var overlay = this._handled,
- formerLatLng = this._handled._corners[this._corner],
+ edit = overlay.editing,
+ formerLatLng = overlay.getCorner(this._corner),
newLatLng = this.getLatLng(),
- angle = this._calculateAngle(formerLatLng, newLatLng),
+ angle = this.calculateAngleDelta(formerLatLng, newLatLng),
scale = this._calculateScalingFactor(formerLatLng, newLatLng);
-
- overlay.editing._rotateBy(angle);
+
+ if (angle !== 0) { edit._rotateBy(angle); }
/*
checks whether the "edgeMinWidth" property is set and tracks the minimum edge length;
this enables preventing scaling to zero, but we might also add an overall scale limit
*/
- if (this._handled.hasOwnProperty('edgeMinWidth')){
- var edgeMinWidth = this._handled.edgeMinWidth,
- w = L.latLng(overlay._corners[0]).distanceTo(overlay._corners[1]),
- h = L.latLng(overlay._corners[1]).distanceTo(overlay._corners[2]);
+ if (overlay.hasOwnProperty('edgeMinWidth')){
+ var edgeMinWidth = overlay.edgeMinWidth,
+ w = L.latLng(overlay.getCorner(0)).distanceTo(overlay.getCorner(1)),
+ h = L.latLng(overlay.getCorner(1)).distanceTo(overlay.getCorner(2));
if ((w > edgeMinWidth && h > edgeMinWidth) || scale > 1) {
- overlay.editing._scaleBy(scale);
+ edit._scaleBy(scale);
}
}
overlay.fire('update');
-
- this._handled.editing._showToolbar();
-
+ edit._updateToolbarPos();
},
updateHandle: function() {
- this.setLatLng(this._handled._corners[this._corner]);
+ this.setLatLng(this._handled.getCorner(this._corner));
},
-
});
diff --git a/src/edit/ScaleHandle.js b/src/edit/ScaleHandle.js
index 401c84adc..470c8d371 100644
--- a/src/edit/ScaleHandle.js
+++ b/src/edit/ScaleHandle.js
@@ -10,7 +10,7 @@ L.ScaleHandle = L.EditHandle.extend({
_onHandleDrag: function() {
var overlay = this._handled,
- formerLatLng = this._handled._corners[this._corner],
+ formerLatLng = overlay.getCorner(this._corner),
newLatLng = this.getLatLng(),
scale = this._calculateScalingFactor(formerLatLng, newLatLng);
@@ -18,13 +18,10 @@ L.ScaleHandle = L.EditHandle.extend({
overlay.editing._scaleBy(scale);
overlay.fire('update');
-
- this._handled.editing._showToolbar();
+ overlay.editing._updateToolbarPos();
},
updateHandle: function() {
- this.setLatLng(this._handled._corners[this._corner]);
+ this.setLatLng(this._handled.getCorner(this._corner));
},
-
-
});
diff --git a/src/util/ImageUtil.js b/src/util/ImageUtil.js
new file mode 100644
index 000000000..85aa4fb2c
--- /dev/null
+++ b/src/util/ImageUtil.js
@@ -0,0 +1,13 @@
+L.ImageUtil = {
+
+ getCmPerPixel: function(overlay) {
+ var map = overlay._map;
+
+ var dist = map
+ .latLngToLayerPoint(overlay.getCorner(0))
+ .distanceTo(map.latLngToLayerPoint(overlay.getCorner(1)));
+
+ return (dist * 100) / overlay._image.width;
+ }
+
+};
\ No newline at end of file
diff --git a/src/util/TrigUtil.js b/src/util/TrigUtil.js
index 74ac10773..43108542c 100644
--- a/src/util/TrigUtil.js
+++ b/src/util/TrigUtil.js
@@ -1,7 +1,16 @@
L.TrigUtil = {
calcAngleDegrees: function(x, y) {
- return Math.atan2(y, x) * 180 / Math.PI;
+ var pointAngle = Math.atan2(y, x);
+ return this.radiansToDegrees(pointAngle);
+ },
+
+ radiansToDegrees: function(angle) {
+ return angle * 180 / Math.PI;
+ },
+
+ degreesToRadians: function(angle) {
+ return angle * Math.PI / 180;
}
};
\ No newline at end of file
diff --git a/test/src/DistortableCollectionSpec.js b/test/src/DistortableCollectionSpec.js
index b0c331d70..30737b027 100644
--- a/test/src/DistortableCollectionSpec.js
+++ b/test/src/DistortableCollectionSpec.js
@@ -11,8 +11,8 @@ describe("L.DistortableCollection", function () {
corners: [
L.latLng(41.7934, -87.6052),
L.latLng(41.7934, -87.5852),
- L.latLng(41.7834, -87.5852),
- L.latLng(41.7834, -87.6052)
+ L.latLng(41.7834, -87.6052),
+ L.latLng(41.7834, -87.5852)
]
}).addTo(map);
@@ -20,8 +20,8 @@ describe("L.DistortableCollection", function () {
corners: [
L.latLng(41.7934, -87.6050),
L.latLng(41.7934, -87.5850),
- L.latLng(41.7834, -87.5850),
- L.latLng(41.7834, -87.6050)
+ L.latLng(41.7834, -87.6050),
+ L.latLng(41.7834, -87.5850)
]
}).addTo(map);
diff --git a/test/src/DistortableImageOverlaySpec.js b/test/src/DistortableImageOverlaySpec.js
index 62a12c7d8..fc79988d0 100644
--- a/test/src/DistortableImageOverlaySpec.js
+++ b/test/src/DistortableImageOverlaySpec.js
@@ -18,8 +18,8 @@ describe("L.DistortableImageOverlay", function() {
corners: [
L.latLng(41.7934, -87.6052),
L.latLng(41.7934, -87.5852),
- L.latLng(41.7834, -87.5852),
- L.latLng(41.7834, -87.6052)
+ L.latLng(41.7834, -87.6052),
+ L.latLng(41.7834, -87.5852)
]
});
});
@@ -37,7 +37,7 @@ describe("L.DistortableImageOverlay", function() {
});
});
- describe("#_getCenter", function() {
+ describe("#getCenter", function() {
it("Should return the center when the outline of the image is a rectangle.", function(done) {
distortable.addTo(map);
diff --git a/test/src/edit/RotateHandleSpec.js b/test/src/edit/RotateHandleSpec.js
index a2848b81a..78826664d 100644
--- a/test/src/edit/RotateHandleSpec.js
+++ b/test/src/edit/RotateHandleSpec.js
@@ -7,10 +7,10 @@ describe("L.RotateHandle", function() {
map = L.map(L.DomUtil.create('div', '', document.body)).setView([41.7896,-87.5996], 15);
distortable = L.distortableImageOverlay('/examples/example.jpg', {
corners: [
- new L.LatLng(41.7934, -87.6052),
- new L.LatLng(41.7934, -87.5852),
- new L.LatLng(41.7834, -87.5852),
- new L.LatLng(41.7834, -87.6052)
+ L.latLng(41.7934, -87.6052),
+ L.latLng(41.7934, -87.5852),
+ L.latLng(41.7834, -87.5852),
+ L.latLng(41.7834, -87.6052)
]
}).addTo(map);
@@ -22,8 +22,8 @@ describe("L.RotateHandle", function() {
describe("_calculateRotation", function() {
it("Should return 0 when given the same latlng twice.", function() {
- var latlng = distortable._corners[0],
- angle = rotateHandle._calculateAngle(latlng, latlng);
+ var latlng = distortable.getCorner(0),
+ angle = rotateHandle.calculateAngleDelta(latlng, latlng);
expect(angle).to.equal(0);
});
diff --git a/test/src/edit/RotateScaleHandleSpec.js b/test/src/edit/RotateScaleHandleSpec.js
index 23876a6d7..c2f5ea5b2 100644
--- a/test/src/edit/RotateScaleHandleSpec.js
+++ b/test/src/edit/RotateScaleHandleSpec.js
@@ -7,10 +7,10 @@ describe("L.RotateScaleHandle", function() {
map = L.map(L.DomUtil.create('div', '', document.body)).setView([41.7896,-87.5996], 15);
distortable = L.distortableImageOverlay('/examples/example.png', {
corners: [
- new L.LatLng(41.7934, -87.6052),
- new L.LatLng(41.7934, -87.5852),
- new L.LatLng(41.7834, -87.5852),
- new L.LatLng(41.7834, -87.6052)
+ L.latLng(41.7934, -87.6052),
+ L.latLng(41.7934, -87.5852),
+ L.latLng(41.7834, -87.5852),
+ L.latLng(41.7834, -87.6052)
]
}).addTo(map);
@@ -26,8 +26,8 @@ describe("L.RotateScaleHandle", function() {
describe("_calculateRotation", function() {
it("Should return 0 when given the same latlng twice.", function() {
- var latlng = distortable._corners[0],
- angle = rotateHandle._calculateAngle(latlng, latlng);
+ var latlng = distortable.getCorner(0),
+ angle = rotateHandle.calculateAngleDelta(latlng, latlng);
expect(angle).to.equal(0);
});
@@ -35,7 +35,7 @@ describe("L.RotateScaleHandle", function() {
describe("_calculateScalingFactor", function() {
it("Should return 1 when given the same latlng twice.", function() {
- var latlng = distortable._corners[0],
+ var latlng = distortable.getCorner(0),
scale = rotateHandle._calculateScalingFactor(latlng, latlng);
expect(scale).to.equal(1);
diff --git a/test/src/edit/ScaleHandleSpec.js b/test/src/edit/ScaleHandleSpec.js
index 72de986ff..6e370b484 100644
--- a/test/src/edit/ScaleHandleSpec.js
+++ b/test/src/edit/ScaleHandleSpec.js
@@ -7,10 +7,10 @@ describe("L.ScaleHandle", function() {
map = L.map(L.DomUtil.create('div', '', document.body)).setView([41.7896,-87.5996], 15);
distortable = L.distortableImageOverlay('/examples/example.jpg', {
corners: [
- new L.LatLng(41.7934, -87.6052),
- new L.LatLng(41.7934, -87.5852),
- new L.LatLng(41.7834, -87.5852),
- new L.LatLng(41.7834, -87.6052)
+ L.latLng(41.7934, -87.6052),
+ L.latLng(41.7934, -87.5852),
+ L.latLng(41.7834, -87.5852),
+ L.latLng(41.7834, -87.6052)
]
}).addTo(map);
@@ -22,7 +22,7 @@ describe("L.ScaleHandle", function() {
describe("_calculateScalingFactor", function() {
it("Should return 1 when given the same latlng twice.", function() {
- var latlng = distortable._corners[0],
+ var latlng = distortable.getCorner(0),
scale = scaleHandle._calculateScalingFactor(latlng, latlng);
expect(scale).to.equal(1);