diff --git a/web_widget_darkroom/README.rst b/web_widget_darkroom/README.rst index befb0c9f..02e280d6 100755 --- a/web_widget_darkroom/README.rst +++ b/web_widget_darkroom/README.rst @@ -1,70 +1,72 @@ -.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg +.. image:: https://img.shields.io/badge/license-LGPL--3-blue.svg :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html :alt: License: LGPL-3 -====================== -Odoo DarkroomJS Widget -====================== +================================ +DarkroomJS Image Editing for Web +================================ -This module provides a `DarkroomJS`_ web widget for use with images fields. +This module provides a `DarkroomJS`_ (v2.0.1) web widget for use with image +fields. It also adds a Darkroom button to the normal image widget, which can +be used to edit the image via Darkroom in a modal. .. _DarkroomJS: https://github.com/MattKetmo/darkroomjs -This widget will allow you to perform the following actions on images: +The widget currently supports the following operations and can be extended to +allow others: - * Zoom - * Rotate - * Crop - * Step back in history client-side (before save) +* Zoom and pan +* Rotate +* Crop +* Step back in history client-side (before save) - Usage ===== -To use this module, you need to: - -* Install web_widget_darkroom -* Add the to any One2many image relation by using the `darkroom` widget. Options can be passed through to Darkroom using the `options` key:: +After installing the module, you can use it in the following ways: - +* Specify the ``darkroom`` widget when adding an image field to a view. + Configuration values can be provided using the ``options`` attribute:: -The Odoo DarkroomJS widget passes options directly through to Darkroom, which are copied from the source below:: + - // Default options - defaults: { - // Canvas properties (dimension, ratio, color) - minWidth: null, - minHeight: null, - maxWidth: null, - maxHeight: null, - ratio: null, - backgroundColor: '#fff', + The widget passes options directly through to DarkroomJS, which supports the + following: - // Plugins options - plugins: {}, + * minWidth + * minHeight + * maxWidth + * maxHeight + * ratio (aspect ratio) + * backgroundColor - // Post-initialisation callback - initialize: function() { /* noop */ } - }, +* Open a form view that contains an image in edit mode and hover over the + image widget. You should see a Darkoom button that can be clicked to open + the image in a Darkroom modal, where it can be edited and the changes can be + saved. + .. image:: /web_widget_darkroom/static/description/modal_screenshot_1.png + :alt: Darkroom Modal Screenshot 1 + :class: img-thumbnail + :height: 260 + .. image:: /web_widget_darkroom/static/description/modal_screenshot_2.png + :alt: Darkroom Modal Screenshot 2 + :class: img-thumbnail col-xs-offset-1 + :height: 260 -Known Issues/Roadmap -==================== - -* Plugins are not able to be added without inheriting, then redefining the widget in the registry due to JS inheritance. - ** This is not scalable because there would need to be an explicit dependency chain in order to avoid registry overwrite. - +Known Issues / Roadmap +====================== +* Darkroom modals are currently not supported during record creation Bug Tracker =========== -Bugs are tracked on `GitHub Issues -`_. In case of trouble, please -check there if your issue has already been reported. If you spotted it first, -help us smashing it by providing a detailed and welcomed feedback. +Bugs are tracked on `GitHub Issues `_. In +case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smash it by providing detailed and welcome +feedback. Credits ======= @@ -72,12 +74,14 @@ Credits Images ------ -* Odoo Community Association: `Icon `_. +* Odoo Community Association: + `Icon `_. Contributors ------------ * Dave Lasley +* Oleg Bulkin Maintainer ---------- diff --git a/web_widget_darkroom/__init__.py b/web_widget_darkroom/__init__.py old mode 100755 new mode 100644 index 08d9d6b0..c1a869f5 --- a/web_widget_darkroom/__init__.py +++ b/web_widget_darkroom/__init__.py @@ -1,3 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2016 LasLabs Inc. +# Copyright 2016-2017 LasLabs Inc. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from . import wizards diff --git a/web_widget_darkroom/__openerp__.py b/web_widget_darkroom/__openerp__.py old mode 100755 new mode 100644 index 9bc1c568..47a4b742 --- a/web_widget_darkroom/__openerp__.py +++ b/web_widget_darkroom/__openerp__.py @@ -1,28 +1,26 @@ # -*- coding: utf-8 -*- -# Copyright 2016 LasLabs Inc. +# Copyright 2016-2017 LasLabs Inc. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). { - "name": "Web Darkroom Image Widget", - "summary": "Widget provides a dynamic, editable canvas for use on any" - " One2many image field in backend form views.", - "version": "9.0.1.0.1", - "category": "Web", - "website": "https://laslabs.com/", - "author": "LasLabs, Odoo Community Association (OCA)", - "license": "LGPL-3", - "application": False, - "installable": True, - "depends": [ - "web", + 'name': 'Web DarkroomJS Image Editing', + 'summary': 'Provides web widget for image editing and adds it to standard' + ' image widget as modal', + 'version': '9.0.1.0.1', + 'category': 'Web', + 'website': 'https://laslabs.com/', + 'author': 'LasLabs, Odoo Community Association (OCA)', + 'license': 'LGPL-3', + 'application': False, + 'installable': True, + 'depends': [ + 'web', ], - "data": [ + 'data': [ 'views/assets.xml', + 'wizards/darkroom_modal.xml', ], 'qweb': [ - "static/src/xml/field_templates.xml", + 'static/src/xml/field_templates.xml', ], - 'demo': [ - 'demo/res_users.xml', - ] } diff --git a/web_widget_darkroom/demo/res_users.xml b/web_widget_darkroom/demo/res_users.xml deleted file mode 100644 index 43a17825..00000000 --- a/web_widget_darkroom/demo/res_users.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - res.users.form.darkroom - res.users - - - - - - - - - - - - diff --git a/web_widget_darkroom/static/description/modal_screenshot_1.png b/web_widget_darkroom/static/description/modal_screenshot_1.png new file mode 100644 index 00000000..5505d68b Binary files /dev/null and b/web_widget_darkroom/static/description/modal_screenshot_1.png differ diff --git a/web_widget_darkroom/static/description/modal_screenshot_2.png b/web_widget_darkroom/static/description/modal_screenshot_2.png new file mode 100644 index 00000000..6c0ce569 Binary files /dev/null and b/web_widget_darkroom/static/description/modal_screenshot_2.png differ diff --git a/web_widget_darkroom/static/lib/darkroomjs/.editorconfig b/web_widget_darkroom/static/lib/darkroomjs/.editorconfig deleted file mode 100755 index 8c98bb17..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/.editorconfig +++ /dev/null @@ -1,9 +0,0 @@ -root = true - -[*] -charset = utf-8 -end_of_line = LF -indent_style = space -trim_trailing_whitespace = true -insert_final_newline = true -indent_size = 2 diff --git a/web_widget_darkroom/static/lib/darkroomjs/.gitignore b/web_widget_darkroom/static/lib/darkroomjs/.gitignore deleted file mode 100755 index abad6da3..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/.sass-cache/ -/bower_components/ -/build/ -/node_modules/ -/docs/ diff --git a/web_widget_darkroom/static/lib/darkroomjs/CHANGELOG.md b/web_widget_darkroom/static/lib/darkroomjs/CHANGELOG.md deleted file mode 100755 index 17b13903..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/CHANGELOG.md +++ /dev/null @@ -1,23 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. - -## Unreleased - -- Add type "button" to avoid html5 submit validation (#24) - -## 2.0.0 (2015-08-01) - -- Use of **Gulp** for the build process -- Replace the webfont by **SVG symbols** (which are direclty included in the compiled javascript) -- Ability to change **canvas ratio** -- Original image is kept and changes are done on a clone - -## 1.0.x (2014) - -Initial release. - -- Create canvas with FabricJS from an image element -- Plugins: Crop, History, Rotate, Save -- Build process via Grunt -- Build webfont from SVG files to display the icons diff --git a/web_widget_darkroom/static/lib/darkroomjs/LICENSE b/web_widget_darkroom/static/lib/darkroomjs/LICENSE deleted file mode 100755 index 35541ed0..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -Copyright (c) 2013 Matthieu Moquet - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/web_widget_darkroom/static/lib/darkroomjs/README.md b/web_widget_darkroom/static/lib/darkroomjs/README.md deleted file mode 100755 index 5ba31b0b..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# DarkroomJS - -DarkroomJS is a JavaScript library which provides basic image editing tools in -your browser, such as **rotation** or **cropping**. It is based on the awesome -[FabricJS](http://fabricjs.com/) library to handle images in HTML5 canvas. - -## Demo - -Try the online demo at [http://mattketmo.github.io/darkroomjs](http://mattketmo.github.io/darkroomjs/) - -The library is currently *work in progress*. -I know there is some bug especially when resizing the crop zone. -Feel free to fork the project or report issues on GitHub. -All ideas are also welcome. - -## Building - -- Install [Node](http://nodejs.org/) & `npm`. -- Run `npm install` to build dependencies. -- Run `npm start` to build the assets and start the demo webserver. - -## Usage - -Simply instanciate a new Darkroom object with a reference to the image element: - -```html - - -``` - -You can also pass some options: - -```javascript -new Darkroom('#target', { - // Canvas initialization size - minWidth: 100, - minHeight: 100, - maxWidth: 500, - maxHeight: 500, - - // Plugins options - plugins: { - crop: { - minHeight: 50, - minWidth: 50, - ratio: 1 - }, - save: false // disable plugin - }, - - // Post initialization method - initialize: function() { - // Active crop selection - this.plugins['crop'].requireFocus(); - - // Add custom listener - this.addEventListener('core:transformation', function() { /* ... */ }); - } -}); -``` - -## Why? - -It's easy to get a javascript script to crop an image in a web page. -But if your want more features like rotation or brightness adjustment, then you -will have to do it yourself. No more jQuery plugins here. -It only uses the power of HTML5 canvas to make what ever you want with your image. - -## The concept - -The library is designed to be easily extendable. The core script only transforms -the target image to a canvas with a FabricJS instance, and creates an empty toolbar. -All the features are then implemented in separate plugins. - -Each plugin is responsible for creating its own functionality. -Buttons can easily be added to the toolbar and binded with those features. - -## Contributing - -Run `npm develop` to build and watch the files while developing. - -## License - -DarkroomJS is released under the MIT License. See the [bundled LICENSE file](LICENSE) -for details. - diff --git a/web_widget_darkroom/static/lib/darkroomjs/bower.json b/web_widget_darkroom/static/lib/darkroomjs/bower.json deleted file mode 100755 index aa38ffe7..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/bower.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "darkroom", - "description": "Extensible image editing tool via HTML canvas", - "version": "2.0.0", - "homepage": "http://mattketmo.github.io/darkroomjs/", - "authors": [ - "Matthieu Moquet " - ], - "license": "MIT", - "dependencies": { - "fabric": "~1.4.*" - }, - "main": [ - "build/darkroom.css", - "build/darkroom.js" - ], - "moduleType": [ - "globals" - ], - "keywords": [ - "image", - "canvas", - "crop" - ], - "ignore": [ - "node_modules", - "bower_components", - "test", - "tests" - ] -} diff --git a/web_widget_darkroom/static/lib/darkroomjs/core/darkroom.js b/web_widget_darkroom/static/lib/darkroomjs/core/darkroom.js new file mode 100755 index 00000000..328fed20 --- /dev/null +++ b/web_widget_darkroom/static/lib/darkroomjs/core/darkroom.js @@ -0,0 +1,356 @@ +/** +* Copyright 2013 Matthieu Moquet +* Copyright 2016-2017 LasLabs Inc. +* License MIT (https://opensource.org/licenses/MIT) +**/ + +(function() { + 'use strict'; + + window.Darkroom = Darkroom; + + // Core object of DarkroomJS. + // Basically it's a single object, instanciable via an element + // (it could be a CSS selector or a DOM element), some custom options, + // and a list of plugin objects (or none to use default ones). + function Darkroom(element, options, plugins) { + return this.constructor(element, options, plugins); + } + + // Create an empty list of plugin objects, which will be filled by + // other plugin scripts. This is the default plugin list if none is + // specified in Darkroom's constructor. + Darkroom.plugins = []; + + Darkroom.prototype = { + // Reference to the main container element + containerElement: null, + + // Reference to the Fabric canvas object + canvas: null, + + // Reference to the Fabric image object + image: null, + + // Reference to the Fabric source canvas object + sourceCanvas: null, + + // Reference to the Fabric source image object + sourceImage: null, + + // Track of the original image element + originalImageElement: null, + + // Stack of transformations to apply to the image source + transformations: [], + + // Default options + defaults: { + // Canvas properties (dimension, ratio, color) + minWidth: null, + minHeight: null, + maxWidth: null, + maxHeight: null, + ratio: null, + backgroundColor: '#fff', + + // Plugins options + plugins: {}, + + // Post-initialisation callback + initialize: function() { /* noop */ } + }, + + // List of the instancied plugins + plugins: {}, + + // This options are a merge between `defaults` and the options passed + // through the constructor + options: {}, + + constructor: function(element, options) { + this.options = Darkroom.Utils.extend(options, this.defaults); + + if (typeof element === 'string') + element = document.querySelector(element); + if (null === element) + return; + + var image = new Image(); + var parent = element.parentElement; + image.onload = function() { + // Initialize the DOM/Fabric elements + this._initializeDOM(element, parent); + this._initializeImage(); + + // Then initialize the plugins + this._initializePlugins(Darkroom.plugins); + + // Public method to adjust image according to the canvas + this.refresh(function() { + // Execute a custom callback after initialization + this.options.initialize.bind(this).call(); + }.bind(this)); + }.bind(this); + + image.src = element.src; + }, + + selfDestroy: function() { + var container = this.containerElement; + var image = new Image(); + image.onload = function() { + container.parentNode.replaceChild(image, container); + }; + + image.src = this.sourceImage.toDataURL(); + }, + + // Add ability to attach event listener on the core object. + // It uses the canvas element to process events. + addEventListener: function(eventName, callback) { + var el = this.canvas.getElement(); + if (el.addEventListener) { + el.addEventListener(eventName, callback); + } else if (el.attachEvent) { + el.attachEvent('on' + eventName, callback); + } + }, + + dispatchEvent: function(eventName) { + // Use the old way of creating event to be IE compatible + // See https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events + var event = document.createEvent('Event'); + event.initEvent(eventName, true, true); + + this.canvas.getElement().dispatchEvent(event); + }, + + // Adjust image & canvas dimension according to min/max width/height + // and ratio specified in the options. + // This method should be called after each image transformation. + refresh: function(next) { + var clone = new Image(); + clone.onload = function() { + this._replaceCurrentImage(new fabric.Image(clone)); + if (next) next(); + }.bind(this); + clone.src = this.sourceImage.toDataURL(); + }, + + _replaceCurrentImage: function(newImage) { + if (this.image) { + this.image.remove(); + } + + this.image = newImage; + this.image.selectable = false; + + // Adjust width or height according to specified ratio + var viewport = Darkroom.Utils.computeImageViewPort(this.image); + var canvasWidth = viewport.width; + var canvasHeight = viewport.height; + + if (null !== this.options.ratio) { + var canvasRatio = +this.options.ratio; + var currentRatio = canvasWidth / canvasHeight; + + if (currentRatio > canvasRatio) { + canvasHeight = canvasWidth / canvasRatio; + } else if (currentRatio < canvasRatio) { + canvasWidth = canvasHeight * canvasRatio; + } + } + + // Then scale the image to fit into dimension limits + var scaleMin = 1; + var scaleMax = 1; + var scaleX = 1; + var scaleY = 1; + + if (null !== this.options.maxWidth && this.options.maxWidth < canvasWidth) { + scaleX = this.options.maxWidth / canvasWidth; + } + if (null !== this.options.maxHeight && this.options.maxHeight < canvasHeight) { + scaleY = this.options.maxHeight / canvasHeight; + } + scaleMin = Math.min(scaleX, scaleY); + + scaleX = 1; + scaleY = 1; + if (null !== this.options.minWidth && this.options.minWidth > canvasWidth) { + scaleX = this.options.minWidth / canvasWidth; + } + if (null !== this.options.minHeight && this.options.minHeight > canvasHeight) { + scaleY = this.options.minHeight / canvasHeight; + } + scaleMax = Math.max(scaleX, scaleY); + + var scale = scaleMax * scaleMin; // one should be equals to 1 + + canvasWidth *= scale; + canvasHeight *= scale; + + // Finally place the image in the center of the canvas + this.image.setScaleX(1 * scale); + this.image.setScaleY(1 * scale); + this.canvas.add(this.image); + this.canvas.setWidth(canvasWidth); + this.canvas.setHeight(canvasHeight); + this.canvas.centerObject(this.image); + this.image.setCoords(); + }, + + // Apply the transformation on the current image and save it in the + // transformations stack (in order to reconstitute the previous states + // of the image). + applyTransformation: function(transformation) { + this.transformations.push(transformation); + + transformation.applyTransformation( + this.sourceCanvas, + this.sourceImage, + this._postTransformation.bind(this) + ); + }, + + _postTransformation: function(newImage) { + if (newImage) + this.sourceImage = newImage; + + this.refresh(function() { + this.dispatchEvent('core:transformation'); + }.bind(this)); + }, + + // Initialize image from original element plus re-apply every + // transformations. + reinitializeImage: function() { + this.sourceImage.remove(); + this._initializeImage(); + this._popTransformation(this.transformations.slice()); + }, + + _popTransformation: function(transformations) { + if (0 === transformations.length) { + this.dispatchEvent('core:reinitialized'); + this.refresh(); + return; + } + + var transformation = transformations.shift(); + + var next = function(newImage) { + if (newImage) this.sourceImage = newImage; + this._popTransformation(transformations); + }; + + transformation.applyTransformation( + this.sourceCanvas, + this.sourceImage, + next.bind(this) + ); + }, + + // Create the DOM elements and instanciate the Fabric canvas. + // The image element is replaced by a new `div` element. + // However the original image is re-injected in order to keep a trace of it. + _initializeDOM: function(imageElement) { + // Container + var mainContainerElement = document.createElement('div'); + mainContainerElement.className = 'darkroom-container'; + + // Toolbar + var toolbarElement = document.createElement('div'); + toolbarElement.className = 'darkroom-toolbar'; + mainContainerElement.appendChild(toolbarElement); + + // Viewport canvas + var canvasContainerElement = document.createElement('div'); + canvasContainerElement.className = 'darkroom-image-container'; + var canvasElement = document.createElement('canvas'); + canvasContainerElement.appendChild(canvasElement); + mainContainerElement.appendChild(canvasContainerElement); + + // Source canvas + var sourceCanvasContainerElement = document.createElement('div'); + sourceCanvasContainerElement.className = 'darkroom-source-container'; + sourceCanvasContainerElement.style.display = 'none'; + var sourceCanvasElement = document.createElement('canvas'); + sourceCanvasContainerElement.appendChild(sourceCanvasElement); + mainContainerElement.appendChild(sourceCanvasContainerElement); + + // Original image + imageElement.parentNode.replaceChild(mainContainerElement, imageElement); + imageElement.style.display = 'none'; + mainContainerElement.appendChild(imageElement); + + // Instanciate object from elements + this.containerElement = mainContainerElement; + this.originalImageElement = imageElement; + + this.toolbar = new Darkroom.UI.Toolbar(toolbarElement); + + this.canvas = new fabric.Canvas(canvasElement, { + selection: false, + backgroundColor: this.options.backgroundColor, + }); + + this.sourceCanvas = new fabric.Canvas(sourceCanvasElement, { + selection: false, + backgroundColor: this.options.backgroundColor, + }); + }, + + // Instanciate the Fabric image object. + // The image is created as a static element with no control, + // then it is add in the Fabric canvas object. + _initializeImage: function() { + this.sourceImage = new fabric.Image(this.originalImageElement, { + // Some options to make the image static + selectable: false, + evented: false, + lockMovementX: true, + lockMovementY: true, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, + lockUniScaling: true, + hasControls: false, + hasBorders: false, + }); + + this.sourceCanvas.add(this.sourceImage); + + // Adjust width or height according to specified ratio + var viewport = Darkroom.Utils.computeImageViewPort(this.sourceImage); + var canvasWidth = viewport.width; + var canvasHeight = viewport.height; + + this.sourceCanvas.setWidth(canvasWidth); + this.sourceCanvas.setHeight(canvasHeight); + this.sourceCanvas.centerObject(this.sourceImage); + this.sourceImage.setCoords(); + }, + + // Initialize every plugins. + // Note that plugins are instanciated in the same order than they + // are declared in the parameter object. + _initializePlugins: function(plugins) { + for (var name in plugins) { + var plugin = plugins[name]; + var options = this.options.plugins[name]; + + // Setting false into the plugin options will disable the plugin + if (options === false) + continue; + + // Avoid any issues with _proto_ + if (!plugins.hasOwnProperty(name)) + continue; + + this.plugins[name] = new plugin(this, options); + } + }, + }; +})(); diff --git a/web_widget_darkroom/static/lib/darkroomjs/core/plugin.js b/web_widget_darkroom/static/lib/darkroomjs/core/plugin.js new file mode 100755 index 00000000..03334418 --- /dev/null +++ b/web_widget_darkroom/static/lib/darkroomjs/core/plugin.js @@ -0,0 +1,47 @@ +/** +* Copyright 2013 Matthieu Moquet +* Copyright 2016-2017 LasLabs Inc. +* License MIT (https://opensource.org/licenses/MIT) +**/ + +(function() { + 'use strict'; + + Darkroom.Plugin = Plugin; + + // Define a plugin object. This is the (abstract) parent class which + // has to be extended for each plugin. + function Plugin(darkroom, options) { + this.darkroom = darkroom; + this.options = Darkroom.Utils.extend(options, this.defaults); + this.initialize(); + } + + Plugin.prototype = { + defaults: {}, + initialize: function() { /* no-op */ } + }; + + // Inspired by Backbone.js extend capability. + Plugin.extend = function(protoProps) { + var parent = this; + var child; + + if (protoProps && protoProps.hasOwnProperty('constructor')) { + child = protoProps.constructor; + } else { + child = function() { return parent.apply(this, arguments); }; + } + + Darkroom.Utils.extend(child, parent); + + var Surrogate = function() { this.constructor = child; }; + Surrogate.prototype = parent.prototype; + child.prototype = new Surrogate(); + + if (protoProps) Darkroom.Utils.extend(child.prototype, protoProps); + child.__super__ = parent.prototype; + + return child; + }; +})(); diff --git a/web_widget_darkroom/static/lib/darkroomjs/core/transformation.js b/web_widget_darkroom/static/lib/darkroomjs/core/transformation.js new file mode 100755 index 00000000..8ea8441a --- /dev/null +++ b/web_widget_darkroom/static/lib/darkroomjs/core/transformation.js @@ -0,0 +1,43 @@ +/** +* Copyright 2013 Matthieu Moquet +* Copyright 2016-2017 LasLabs Inc. +* License MIT (https://opensource.org/licenses/MIT) +**/ + +(function() { + 'use strict'; + + Darkroom.Transformation = Transformation; + + function Transformation(options) { + this.options = options; + } + + Transformation.prototype = { + applyTransformation: function() { /* no-op */ } + }; + + // Inspired by Backbone.js extend capability. + Transformation.extend = function(protoProps) { + var parent = this; + var child; + + if (protoProps && protoProps.hasOwnProperty('constructor')) { + child = protoProps.constructor; + } else { + child = function() { return parent.apply(this, arguments); }; + } + + Darkroom.Utils.extend(child, parent); + + var Surrogate = function() { this.constructor = child; }; + Surrogate.prototype = parent.prototype; + child.prototype = new Surrogate(); + + if (protoProps) Darkroom.Utils.extend(child.prototype, protoProps); + + child.__super__ = parent.prototype; + + return child; + }; +})(); diff --git a/web_widget_darkroom/static/lib/darkroomjs/core/utils.js b/web_widget_darkroom/static/lib/darkroomjs/core/utils.js new file mode 100755 index 00000000..2e0a5e21 --- /dev/null +++ b/web_widget_darkroom/static/lib/darkroomjs/core/utils.js @@ -0,0 +1,36 @@ +/** +* Copyright 2013 Matthieu Moquet +* Copyright 2016-2017 LasLabs Inc. +* License MIT (https://opensource.org/licenses/MIT) +**/ + +(function() { + 'use strict'; + + Darkroom.Utils = { + extend: extend, + computeImageViewPort: computeImageViewPort, + }; + + // Utility method to easily extend objects. + function extend(b, a) { + var prop; + if (b === undefined) { + return a; + } + + for (prop in a) { + if (a.hasOwnProperty(prop) && b.hasOwnProperty(prop) === false) { + b[prop] = a[prop]; + } + } + return b; + } + + function computeImageViewPort(image) { + return { + height: Math.abs(image.getWidth() * (Math.sin(image.getAngle() * Math.PI/180))) + Math.abs(image.getHeight() * (Math.cos(image.getAngle() * Math.PI/180))), + width: Math.abs(image.getHeight() * (Math.sin(image.getAngle() * Math.PI/180))) + Math.abs(image.getWidth() * (Math.cos(image.getAngle() * Math.PI/180))), + }; + } +})(); diff --git a/web_widget_darkroom/static/lib/darkroomjs/gh-pages.sh b/web_widget_darkroom/static/lib/darkroomjs/gh-pages.sh deleted file mode 100755 index 2ae99a52..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/gh-pages.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# Update gh-pages branch -git branch -D gh-pages -git checkout -b gh-pages HEAD - -# Build assets -rm -rf build -gulp build --prod - -# Put build into demo folder -rm demo/build -cp -r build demo/build - -# Commit -git add -f demo -git commit -m "Build GH pages" - -# Push & reset -git push origin `git subtree split --prefix demo HEAD`:gh-pages --force -git checkout - diff --git a/web_widget_darkroom/static/lib/darkroomjs/gulpfile.js b/web_widget_darkroom/static/lib/darkroomjs/gulpfile.js deleted file mode 100755 index dced1e88..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/gulpfile.js +++ /dev/null @@ -1,112 +0,0 @@ -var concat = require('gulp-concat') -var connect = require('gulp-connect') -var gulp = require('gulp') -var gutil = require('gulp-util') -var htmlJsStr = require('js-string-escape') -var inject = require('gulp-inject') -var plumber = require('gulp-plumber') -var rimraf = require('rimraf') -var sass = require('gulp-sass') -var sourcemaps = require('gulp-sourcemaps') -var spawn = require("child_process").spawn -var streamqueue = require('streamqueue') -var svgmin = require('gulp-svgmin') -var svgstore = require('gulp-svgstore') -var uglify = require('gulp-uglify') - - -// -// Variables -// -var srcDir = './lib'; -var distDir = './build'; -var isDebug = !gutil.env.prod; - -// -// Default -// -gulp.task('default', ['build'], function() { - gulp.start('watch'); -}); - -// -// Clean -// -gulp.task('clean', function(cb) { - rimraf(distDir, cb); -}); - -// -// Build -// -gulp.task('build', ['clean'], function() { - gulp.start('scripts', 'styles'); -}); - -// -// Watch -// -gulp.task('watch', ['server'], function() { - gulp.watch(srcDir + '/js/**/*.js', ['scripts']); - - gulp.watch(srcDir + '/css/**/*.scss', ['styles']); -}); - -// -// Server -// -gulp.task('server', function() { - connect.server({ - root: './demo', - port: 2222, - livereload: false - }); -}); - -// -// Javascript -// -gulp.task('scripts', function () { - var svgs = gulp.src(srcDir + '/icons/*.svg') - .pipe(svgmin()) - .pipe(svgstore({inlineSvg: true})) - // .pipe(gulp.dest(distDir)); - - function fileContents (filePath, file) { - return file.contents.toString(); - } - - var files = [ - srcDir + '/js/core/bootstrap.js', - srcDir + '/js/core/darkroom.js', - srcDir + '/js/core/*.js', - // srcDir + '/js/plugins/*.js', - srcDir + '/js/plugins/darkroom.history.js', - srcDir + '/js/plugins/darkroom.rotate.js', - srcDir + '/js/plugins/darkroom.crop.js', - srcDir + '/js/plugins/darkroom.save.js', - ]; - - gulp.src(files) - .pipe(plumber()) - .pipe(isDebug ? sourcemaps.init() : gutil.noop()) - .pipe(concat('darkroom.js', {newLine: ';'})) - .pipe(inject(svgs, { transform: fileContents })) - .pipe(isDebug ? gutil.noop() : uglify({mangle: false})) - .pipe(isDebug ? sourcemaps.write() : gutil.noop()) - .pipe(gulp.dest(distDir)) -}) - -// -// Stylesheet -// -gulp.task('styles', function () { - gulp.src(srcDir + '/css/darkroom.scss') - .pipe(plumber()) - .pipe(isDebug ? sourcemaps.init() : gutil.noop()) - .pipe(sass({ - outputStyle: isDebug ? 'nested' : 'compressed' - })) - .pipe(isDebug ? sourcemaps.write() : gutil.noop()) - .pipe(gulp.dest(distDir)) -}) diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/css/_layout.scss b/web_widget_darkroom/static/lib/darkroomjs/lib/css/_layout.scss deleted file mode 100755 index 693cd38a..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/css/_layout.scss +++ /dev/null @@ -1,12 +0,0 @@ -.darkroom-container { - position: relative; -} - -.darkroom-image-container { - top: 0; - left: 0; -} - -.darkroom-image-container img { - // display: none; -} diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/css/_toolbar.scss b/web_widget_darkroom/static/lib/darkroomjs/lib/css/_toolbar.scss deleted file mode 100755 index f90aaac5..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/css/_toolbar.scss +++ /dev/null @@ -1,99 +0,0 @@ -// -// Toolbar -// -.darkroom-toolbar { - display: block; - position: absolute; - top: -45px; - left: 0; - background: #444; - height: 40px; - min-width: 40px; - z-index: 99; - border-radius: 2px; - white-space: nowrap; - padding: 0 5px; - - // Triangle - &:before { - content: ""; - position: absolute; - bottom: -7px; - left: 20px; - width: 0; - height: 0; - border-left: 7px solid transparent; - border-right: 7px solid transparent; - border-top: 7px solid #444; - } -} - -// -// Button Group -// -.darkroom-button-group { - display: inline-block; - margin: 0; - padding: 0; - // border-right: 1px solid #777; - - &:last-child { - border-right: none; - } -} - - -// -// Button -// -.darkroom-button { - box-sizing: border-box; - background: transparent; - border: none; - outline: none; - padding: 2px 0 0 0; - width: 40px; - height: 40px; - - &:hover { - cursor: pointer; - background: #555; - } - &:active { - cursor: pointer; - background: #333; - } - - &:disabled .darkroom-icon { - fill: #666; - } - &:disabled:hover { - cursor: default; - /*cursor: not-allowed;*/ - background: transparent; - } - &.darkroom-button-active .darkroom-icon { - fill: #33b5e5; - } - &.darkroom-button-hidden { - display: none; - } - &.darkroom-button-success .darkroom-icon { - fill: #99cc00; - } - &.darkroom-button-warning .darkroom-icon { - fill: #FFBB33; - } - &.darkroom-button-danger .darkroom-icon { - fill: #FF4444; - } -} - -// -// Icon -// -.darkroom-icon { - width: 24px; - height: 24px; - fill: #fff; -} diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/css/darkroom.scss b/web_widget_darkroom/static/lib/darkroomjs/lib/css/darkroom.scss deleted file mode 100755 index 22d3411c..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/css/darkroom.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'layout'; -@import 'toolbar'; diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/icons/close.svg b/web_widget_darkroom/static/lib/darkroomjs/lib/icons/close.svg deleted file mode 100755 index 6c375ca8..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/icons/close.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/icons/crop.svg b/web_widget_darkroom/static/lib/darkroomjs/lib/icons/crop.svg deleted file mode 100755 index f3affcc4..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/icons/crop.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/icons/done.svg b/web_widget_darkroom/static/lib/darkroomjs/lib/icons/done.svg deleted file mode 100755 index 0af0fb9f..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/icons/done.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/icons/redo.svg b/web_widget_darkroom/static/lib/darkroomjs/lib/icons/redo.svg deleted file mode 100755 index fb1cc3a1..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/icons/redo.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/icons/rotate-left.svg b/web_widget_darkroom/static/lib/darkroomjs/lib/icons/rotate-left.svg deleted file mode 100755 index dd87ec4f..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/icons/rotate-left.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/icons/rotate-right.svg b/web_widget_darkroom/static/lib/darkroomjs/lib/icons/rotate-right.svg deleted file mode 100755 index fdfae402..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/icons/rotate-right.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/icons/save.svg b/web_widget_darkroom/static/lib/darkroomjs/lib/icons/save.svg deleted file mode 100755 index 2a4e62bb..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/icons/save.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/icons/undo.svg b/web_widget_darkroom/static/lib/darkroomjs/lib/icons/undo.svg deleted file mode 100755 index 273f4918..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/icons/undo.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/bootstrap.js b/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/bootstrap.js deleted file mode 100755 index 34f32795..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/bootstrap.js +++ /dev/null @@ -1,14 +0,0 @@ -(function() { -'use strict'; - -// Inject SVG icons into the DOM -var element = document.createElement('div'); -element.id = 'darkroom-icons'; -element.style.height = 0; -element.style.width = 0; -element.style.position = 'absolute'; -element.style.visibility = 'hidden'; -element.innerHTML = ''; -document.body.appendChild(element); - -})(); diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/darkroom.js b/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/darkroom.js deleted file mode 100755 index 7ea5b363..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/darkroom.js +++ /dev/null @@ -1,354 +0,0 @@ -(function() { -'use strict'; - -window.Darkroom = Darkroom; - -// Core object of DarkroomJS. -// Basically it's a single object, instanciable via an element -// (it could be a CSS selector or a DOM element), some custom options, -// and a list of plugin objects (or none to use default ones). -function Darkroom(element, options, plugins) { - return this.constructor(element, options, plugins); -} - -// Create an empty list of plugin objects, which will be filled by -// other plugin scripts. This is the default plugin list if none is -// specified in Darkroom'ss constructor. -Darkroom.plugins = []; - -Darkroom.prototype = { - // Refenrece to the main container element - containerElement: null, - - // Reference to the Fabric canvas object - canvas: null, - - // Reference to the Fabric image object - image: null, - - // Reference to the Fabric source canvas object - sourceCanvas: null, - - // Reference to the Fabric source image object - sourceImage: null, - - // Track of the original image element - originalImageElement: null, - - // Stack of transformations to apply to the image source - transformations: [], - - // Default options - defaults: { - // Canvas properties (dimension, ratio, color) - minWidth: null, - minHeight: null, - maxWidth: null, - maxHeight: null, - ratio: null, - backgroundColor: '#fff', - - // Plugins options - plugins: {}, - - // Post-initialisation callback - initialize: function() { /* noop */ } - }, - - // List of the instancied plugins - plugins: {}, - - // This options are a merge between `defaults` and the options passed - // through the constructor - options: {}, - - constructor: function(element, options, plugins) { - this.options = Darkroom.Utils.extend(options, this.defaults); - - if (typeof element === 'string') - element = document.querySelector(element); - if (null === element) - return; - - var image = new Image(); - var parent = element.parentElement; - image.onload = function() { - // Initialize the DOM/Fabric elements - this._initializeDOM(element, parent); - this._initializeImage(); - - // Then initialize the plugins - this._initializePlugins(Darkroom.plugins); - - // Public method to adjust image according to the canvas - this.refresh(function() { - // Execute a custom callback after initialization - this.options.initialize.bind(this).call(); - }.bind(this)); - - }.bind(this) - - //image.crossOrigin = 'anonymous'; - image.src = element.src; - }, - - selfDestroy: function() { - var container = this.containerElement; - var image = new Image(); - image.onload = function() { - container.parentNode.replaceChild(image, container); - } - - image.src = this.sourceImage.toDataURL(); - }, - - // Add ability to attach event listener on the core object. - // It uses the canvas element to process events. - addEventListener: function(eventName, callback) { - var el = this.canvas.getElement(); - if (el.addEventListener){ - el.addEventListener(eventName, callback); - } else if (el.attachEvent) { - el.attachEvent('on' + eventName, callback); - } - }, - - dispatchEvent: function(eventName) { - // Use the old way of creating event to be IE compatible - // See https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events - var event = document.createEvent('Event'); - event.initEvent(eventName, true, true); - - this.canvas.getElement().dispatchEvent(event); - }, - - // Adjust image & canvas dimension according to min/max width/height - // and ratio specified in the options. - // This method should be called after each image transformation. - refresh: function(next) { - var clone = new Image(); - clone.onload = function() { - this._replaceCurrentImage(new fabric.Image(clone)); - - if (next) next(); - }.bind(this); - clone.src = this.sourceImage.toDataURL(); - }, - - _replaceCurrentImage: function(newImage) { - if (this.image) { - this.image.remove(); - } - - this.image = newImage; - this.image.selectable = false; - - // Adjust width or height according to specified ratio - var viewport = Darkroom.Utils.computeImageViewPort(this.image); - var canvasWidth = viewport.width; - var canvasHeight = viewport.height; - - if (null !== this.options.ratio) { - var canvasRatio = +this.options.ratio; - var currentRatio = canvasWidth / canvasHeight; - - if (currentRatio > canvasRatio) { - canvasHeight = canvasWidth / canvasRatio; - } else if (currentRatio < canvasRatio) { - canvasWidth = canvasHeight * canvasRatio; - } - } - - // Then scale the image to fit into dimension limits - var scaleMin = 1; - var scaleMax = 1; - var scaleX = 1; - var scaleY = 1; - - if (null !== this.options.maxWidth && this.options.maxWidth < canvasWidth) { - scaleX = this.options.maxWidth / canvasWidth; - } - if (null !== this.options.maxHeight && this.options.maxHeight < canvasHeight) { - scaleY = this.options.maxHeight / canvasHeight; - } - scaleMin = Math.min(scaleX, scaleY); - - scaleX = 1; - scaleY = 1; - if (null !== this.options.minWidth && this.options.minWidth > canvasWidth) { - scaleX = this.options.minWidth / canvasWidth; - } - if (null !== this.options.minHeight && this.options.minHeight > canvasHeight) { - scaleY = this.options.minHeight / canvasHeight; - } - scaleMax = Math.max(scaleX, scaleY); - - var scale = scaleMax * scaleMin; // one should be equals to 1 - - canvasWidth *= scale; - canvasHeight *= scale; - - // Finally place the image in the center of the canvas - this.image.setScaleX(1 * scale); - this.image.setScaleY(1 * scale); - this.canvas.add(this.image); - this.canvas.setWidth(canvasWidth); - this.canvas.setHeight(canvasHeight); - this.canvas.centerObject(this.image); - this.image.setCoords(); - }, - - // Apply the transformation on the current image and save it in the - // transformations stack (in order to reconstitute the previous states - // of the image). - applyTransformation: function(transformation) { - this.transformations.push(transformation); - - transformation.applyTransformation( - this.sourceCanvas, - this.sourceImage, - this._postTransformation.bind(this) - ); - }, - - _postTransformation: function(newImage) { - if (newImage) - this.sourceImage = newImage; - - this.refresh(function() { - this.dispatchEvent('core:transformation'); - }.bind(this)); - }, - - // Initialize image from original element plus re-apply every - // transformations. - reinitializeImage: function() { - this.sourceImage.remove(); - this._initializeImage(); - this._popTransformation(this.transformations.slice()) - }, - - _popTransformation: function(transformations) { - if (0 === transformations.length) { - this.dispatchEvent('core:reinitialized'); - this.refresh(); - return; - } - - var transformation = transformations.shift(); - - var next = function(newImage) { - if (newImage) this.sourceImage = newImage; - this._popTransformation(transformations) - }; - - transformation.applyTransformation( - this.sourceCanvas, - this.sourceImage, - next.bind(this) - ); - }, - - // Create the DOM elements and instanciate the Fabric canvas. - // The image element is replaced by a new `div` element. - // However the original image is re-injected in order to keep a trace of it. - _initializeDOM: function(imageElement) { - // Container - var mainContainerElement = document.createElement('div'); - mainContainerElement.className = 'darkroom-container'; - - // Toolbar - var toolbarElement = document.createElement('div'); - toolbarElement.className = 'darkroom-toolbar'; - mainContainerElement.appendChild(toolbarElement); - - // Viewport canvas - var canvasContainerElement = document.createElement('div'); - canvasContainerElement.className = 'darkroom-image-container'; - var canvasElement = document.createElement('canvas'); - canvasContainerElement.appendChild(canvasElement); - mainContainerElement.appendChild(canvasContainerElement); - - // Source canvas - var sourceCanvasContainerElement = document.createElement('div'); - sourceCanvasContainerElement.className = 'darkroom-source-container'; - sourceCanvasContainerElement.style.display = 'none'; - var sourceCanvasElement = document.createElement('canvas'); - sourceCanvasContainerElement.appendChild(sourceCanvasElement); - mainContainerElement.appendChild(sourceCanvasContainerElement); - - // Original image - imageElement.parentNode.replaceChild(mainContainerElement, imageElement); - imageElement.style.display = 'none'; - mainContainerElement.appendChild(imageElement); - - // Instanciate object from elements - this.containerElement = mainContainerElement; - this.originalImageElement = imageElement; - - this.toolbar = new Darkroom.UI.Toolbar(toolbarElement); - - this.canvas = new fabric.Canvas(canvasElement, { - selection: false, - backgroundColor: this.options.backgroundColor - }); - - this.sourceCanvas = new fabric.Canvas(sourceCanvasElement, { - selection: false, - backgroundColor: this.options.backgroundColor - }); - }, - - // Instanciate the Fabric image object. - // The image is created as a static element with no control, - // then it is add in the Fabric canvas object. - _initializeImage: function() { - this.sourceImage = new fabric.Image(this.originalImageElement, { - // Some options to make the image static - selectable: false, - evented: false, - lockMovementX: true, - lockMovementY: true, - lockRotation: true, - lockScalingX: true, - lockScalingY: true, - lockUniScaling: true, - hasControls: false, - hasBorders: false, - }); - - this.sourceCanvas.add(this.sourceImage); - - // Adjust width or height according to specified ratio - var viewport = Darkroom.Utils.computeImageViewPort(this.sourceImage); - var canvasWidth = viewport.width; - var canvasHeight = viewport.height; - - this.sourceCanvas.setWidth(canvasWidth); - this.sourceCanvas.setHeight(canvasHeight); - this.sourceCanvas.centerObject(this.sourceImage); - this.sourceImage.setCoords(); - }, - - // Initialize every plugins. - // Note that plugins are instanciated in the same order than they - // are declared in the parameter object. - _initializePlugins: function(plugins) { - for (var name in plugins) { - var plugin = plugins[name]; - var options = this.options.plugins[name]; - - // Setting false into the plugin options will disable the plugin - if (options === false) - continue; - - // Avoid any issues with _proto_ - if (!plugins.hasOwnProperty(name)) - continue; - - this.plugins[name] = new plugin(this, options); - } - }, -} - -})(); diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/plugin.js b/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/plugin.js deleted file mode 100755 index 07b35a05..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/plugin.js +++ /dev/null @@ -1,43 +0,0 @@ -(function() { -'use strict'; - -Darkroom.Plugin = Plugin; - -// Define a plugin object. This is the (abstract) parent class which -// has to be extended for each plugin. -function Plugin(darkroom, options) { - this.darkroom = darkroom; - this.options = Darkroom.Utils.extend(options, this.defaults); - this.initialize(); -} - -Plugin.prototype = { - defaults: {}, - initialize: function() { } -} - -// Inspired by Backbone.js extend capability. -Plugin.extend = function(protoProps) { - var parent = this; - var child; - - if (protoProps && protoProps.hasOwnProperty('constructor')) { - child = protoProps.constructor; - } else { - child = function(){ return parent.apply(this, arguments); }; - } - - Darkroom.Utils.extend(child, parent); - - var Surrogate = function(){ this.constructor = child; }; - Surrogate.prototype = parent.prototype; - child.prototype = new Surrogate; - - if (protoProps) Darkroom.Utils.extend(child.prototype, protoProps); - - child.__super__ = parent.prototype; - - return child; -} - -})(); diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/transformation.js b/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/transformation.js deleted file mode 100755 index e0d27461..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/transformation.js +++ /dev/null @@ -1,38 +0,0 @@ -(function() { -'use strict'; - -Darkroom.Transformation = Transformation; - -function Transformation(options) { - this.options = options; -} - -Transformation.prototype = { - applyTransformation: function(image) { /* no-op */ } -} - -// Inspired by Backbone.js extend capability. -Transformation.extend = function(protoProps) { - var parent = this; - var child; - - if (protoProps && protoProps.hasOwnProperty('constructor')) { - child = protoProps.constructor; - } else { - child = function(){ return parent.apply(this, arguments); }; - } - - Darkroom.Utils.extend(child, parent); - - var Surrogate = function(){ this.constructor = child; }; - Surrogate.prototype = parent.prototype; - child.prototype = new Surrogate; - - if (protoProps) Darkroom.Utils.extend(child.prototype, protoProps); - - child.__super__ = parent.prototype; - - return child; -} - -})(); diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/ui.js b/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/ui.js deleted file mode 100755 index b4d752a8..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/ui.js +++ /dev/null @@ -1,91 +0,0 @@ -(function() { -'use strict'; - -Darkroom.UI = { - Toolbar: Toolbar, - ButtonGroup: ButtonGroup, - Button: Button, -}; - -// Toolbar object. -function Toolbar(element) { - this.element = element; -} - -Toolbar.prototype = { - createButtonGroup: function(options) { - var buttonGroup = document.createElement('div'); - buttonGroup.className = 'darkroom-button-group'; - this.element.appendChild(buttonGroup); - - return new ButtonGroup(buttonGroup); - } -}; - -// ButtonGroup object. -function ButtonGroup(element) { - this.element = element; -} - -ButtonGroup.prototype = { - createButton: function(options) { - var defaults = { - image: 'help', - type: 'default', - group: 'default', - hide: false, - disabled: false - }; - - options = Darkroom.Utils.extend(options, defaults); - - var buttonElement = document.createElement('button'); - buttonElement.type = 'button'; - buttonElement.className = 'darkroom-button darkroom-button-' + options.type; - buttonElement.innerHTML = ''; - this.element.appendChild(buttonElement); - - var button = new Button(buttonElement); - button.hide(options.hide); - button.disable(options.disabled); - - return button; - } -} - -// Button object. -function Button(element) { - this.element = element; -} - -Button.prototype = { - addEventListener: function(eventName, listener) { - if (this.element.addEventListener){ - this.element.addEventListener(eventName, listener); - } else if (this.element.attachEvent) { - this.element.attachEvent('on' + eventName, listener); - } - }, - removeEventListener: function(eventName, listener) { - if (this.element.removeEventListener){ - this.element.removeEventListener(eventName, listener); - } - }, - active: function(value) { - if (value) - this.element.classList.add('darkroom-button-active'); - else - this.element.classList.remove('darkroom-button-active'); - }, - hide: function(value) { - if (value) - this.element.classList.add('darkroom-button-hidden'); - else - this.element.classList.remove('darkroom-button-hidden'); - }, - disable: function(value) { - this.element.disabled = (value) ? true : false; - } -}; - -})(); diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/utils.js b/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/utils.js deleted file mode 100755 index f4de5f9a..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/utils.js +++ /dev/null @@ -1,31 +0,0 @@ -(function() { -'use strict'; - -Darkroom.Utils = { - extend: extend, - computeImageViewPort: computeImageViewPort, -}; - - -// Utility method to easily extend objects. -function extend(b, a) { - var prop; - if (b === undefined) { - return a; - } - for (prop in a) { - if (a.hasOwnProperty(prop) && b.hasOwnProperty(prop) === false) { - b[prop] = a[prop]; - } - } - return b; -} - -function computeImageViewPort(image) { - return { - height: Math.abs(image.getWidth() * (Math.sin(image.getAngle() * Math.PI/180))) + Math.abs(image.getHeight() * (Math.cos(image.getAngle() * Math.PI/180))), - width: Math.abs(image.getHeight() * (Math.sin(image.getAngle() * Math.PI/180))) + Math.abs(image.getWidth() * (Math.cos(image.getAngle() * Math.PI/180))), - } -} - -})(); diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/js/plugins/darkroom.crop.js b/web_widget_darkroom/static/lib/darkroomjs/lib/js/plugins/darkroom.crop.js deleted file mode 100755 index 9c441823..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/js/plugins/darkroom.crop.js +++ /dev/null @@ -1,669 +0,0 @@ -(function() { -'use strict'; - -var Crop = Darkroom.Transformation.extend({ - applyTransformation: function(canvas, image, next) { - // Snapshot the image delimited by the crop zone - var snapshot = new Image(); - snapshot.onload = function() { - // Validate image - if (height < 1 || width < 1) - return; - - var imgInstance = new fabric.Image(this, { - // options to make the image static - selectable: false, - evented: false, - lockMovementX: true, - lockMovementY: true, - lockRotation: true, - lockScalingX: true, - lockScalingY: true, - lockUniScaling: true, - hasControls: false, - hasBorders: false - }); - - var width = this.width; - var height = this.height; - - // Update canvas size - canvas.setWidth(width); - canvas.setHeight(height); - - // Add image - image.remove(); - canvas.add(imgInstance); - - next(imgInstance); - }; - - var viewport = Darkroom.Utils.computeImageViewPort(image); - var imageWidth = viewport.width; - var imageHeight = viewport.height; - - var left = this.options.left * imageWidth; - var top = this.options.top * imageHeight; - var width = Math.min(this.options.width * imageWidth, imageWidth - left); - var height = Math.min(this.options.height * imageHeight, imageHeight - top); - - snapshot.src = canvas.toDataURL({ - left: left, - top: top, - width: width, - height: height, - }); - } -}); - -var CropZone = fabric.util.createClass(fabric.Rect, { - _render: function(ctx) { - this.callSuper('_render', ctx); - - var canvas = ctx.canvas; - var dashWidth = 7; - - // Set original scale - var flipX = this.flipX ? -1 : 1; - var flipY = this.flipY ? -1 : 1; - var scaleX = flipX / this.scaleX; - var scaleY = flipY / this.scaleY; - - ctx.scale(scaleX, scaleY); - - // Overlay rendering - ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; - this._renderOverlay(ctx); - - // Set dashed borders - if (ctx.setLineDash !== undefined) - ctx.setLineDash([dashWidth, dashWidth]); - else if (ctx.mozDash !== undefined) - ctx.mozDash = [dashWidth, dashWidth]; - - // First lines rendering with black - ctx.strokeStyle = 'rgba(0, 0, 0, 0.2)'; - this._renderBorders(ctx); - this._renderGrid(ctx); - - // Re render lines in white - ctx.lineDashOffset = dashWidth; - ctx.strokeStyle = 'rgba(255, 255, 255, 0.4)'; - this._renderBorders(ctx); - this._renderGrid(ctx); - - // Reset scale - ctx.scale(1/scaleX, 1/scaleY); - }, - - _renderOverlay: function(ctx) { - var canvas = ctx.canvas; - var borderOffset = 0; - - // - // x0 x1 x2 x3 - // y0 +------------------------+ - // |\\\\\\\\\\\\\\\\\\\\\\\\| - // |\\\\\\\\\\\\\\\\\\\\\\\\| - // y1 +------+---------+-------+ - // |\\\\\\| |\\\\\\\| - // |\\\\\\| 0 |\\\\\\\| - // |\\\\\\| |\\\\\\\| - // y2 +------+---------+-------+ - // |\\\\\\\\\\\\\\\\\\\\\\\\| - // |\\\\\\\\\\\\\\\\\\\\\\\\| - // y3 +------------------------+ - // - - var x0 = Math.ceil(-this.getWidth() / 2 - this.getLeft()); - var x1 = Math.ceil(-this.getWidth() / 2); - var x2 = Math.ceil(this.getWidth() / 2); - var x3 = Math.ceil(this.getWidth() / 2 + (canvas.width - this.getWidth() - this.getLeft())); - - var y0 = Math.ceil(-this.getHeight() / 2 - this.getTop()); - var y1 = Math.ceil(-this.getHeight() / 2); - var y2 = Math.ceil(this.getHeight() / 2); - var y3 = Math.ceil(this.getHeight() / 2 + (canvas.height - this.getHeight() - this.getTop())); - - // Upper rect - ctx.fillRect(x0, y0, x3 - x0, y1 - y0 + borderOffset); - - // Left rect - ctx.fillRect(x0, y1, x1 - x0, y2 - y1 + borderOffset); - - // Right rect - ctx.fillRect(x2, y1, x3 - x2, y2 - y1 + borderOffset); - - // Down rect - ctx.fillRect(x0, y2, x3 - x0, y3 - y2); - }, - - _renderBorders: function(ctx) { - ctx.beginPath(); - ctx.moveTo(-this.getWidth()/2, -this.getHeight()/2); // upper left - ctx.lineTo(this.getWidth()/2, -this.getHeight()/2); // upper right - ctx.lineTo(this.getWidth()/2, this.getHeight()/2); // down right - ctx.lineTo(-this.getWidth()/2, this.getHeight()/2); // down left - ctx.lineTo(-this.getWidth()/2, -this.getHeight()/2); // upper left - ctx.stroke(); - }, - - _renderGrid: function(ctx) { - // Vertical lines - ctx.beginPath(); - ctx.moveTo(-this.getWidth()/2 + 1/3 * this.getWidth(), -this.getHeight()/2); - ctx.lineTo(-this.getWidth()/2 + 1/3 * this.getWidth(), this.getHeight()/2); - ctx.stroke(); - ctx.beginPath(); - ctx.moveTo(-this.getWidth()/2 + 2/3 * this.getWidth(), -this.getHeight()/2); - ctx.lineTo(-this.getWidth()/2 + 2/3 * this.getWidth(), this.getHeight()/2); - ctx.stroke(); - // Horizontal lines - ctx.beginPath(); - ctx.moveTo(-this.getWidth()/2, -this.getHeight()/2 + 1/3 * this.getHeight()); - ctx.lineTo(this.getWidth()/2, -this.getHeight()/2 + 1/3 * this.getHeight()); - ctx.stroke(); - ctx.beginPath(); - ctx.moveTo(-this.getWidth()/2, -this.getHeight()/2 + 2/3 * this.getHeight()); - ctx.lineTo(this.getWidth()/2, -this.getHeight()/2 + 2/3 * this.getHeight()); - ctx.stroke(); - } -}); - -Darkroom.plugins['crop'] = Darkroom.Plugin.extend({ - // Init point - startX: null, - startY: null, - - // Keycrop - isKeyCroping: false, - isKeyLeft: false, - isKeyUp: false, - - defaults: { - // min crop dimension - minHeight: 1, - minWidth: 1, - // ensure crop ratio - ratio: null, - // quick crop feature (set a key code to enable it) - quickCropKey: false - }, - - initialize: function InitDarkroomCropPlugin() { - var buttonGroup = this.darkroom.toolbar.createButtonGroup(); - - this.cropButton = buttonGroup.createButton({ - image: 'crop' - }); - this.okButton = buttonGroup.createButton({ - image: 'done', - type: 'success', - hide: true - }); - this.cancelButton = buttonGroup.createButton({ - image: 'close', - type: 'danger', - hide: true - }); - - // Buttons click - this.cropButton.addEventListener('click', this.toggleCrop.bind(this)); - this.okButton.addEventListener('click', this.cropCurrentZone.bind(this)); - this.cancelButton.addEventListener('click', this.releaseFocus.bind(this)); - - // Canvas events - this.darkroom.canvas.on('mouse:down', this.onMouseDown.bind(this)); - this.darkroom.canvas.on('mouse:move', this.onMouseMove.bind(this)); - this.darkroom.canvas.on('mouse:up', this.onMouseUp.bind(this)); - this.darkroom.canvas.on('object:moving', this.onObjectMoving.bind(this)); - this.darkroom.canvas.on('object:scaling', this.onObjectScaling.bind(this)); - - fabric.util.addListener(fabric.document, 'keydown', this.onKeyDown.bind(this)); - fabric.util.addListener(fabric.document, 'keyup', this.onKeyUp.bind(this)); - - this.darkroom.addEventListener('core:transformation', this.releaseFocus.bind(this)); - }, - - // Avoid crop zone to go beyond the canvas edges - onObjectMoving: function(event) { - if (!this.hasFocus()) { - return; - } - - var currentObject = event.target; - if (currentObject !== this.cropZone) - return; - - var canvas = this.darkroom.canvas; - var x = currentObject.getLeft(), y = currentObject.getTop(); - var w = currentObject.getWidth(), h = currentObject.getHeight(); - var maxX = canvas.getWidth() - w; - var maxY = canvas.getHeight() - h; - - if (x < 0) - currentObject.set('left', 0); - if (y < 0) - currentObject.set('top', 0); - if (x > maxX) - currentObject.set('left', maxX); - if (y > maxY) - currentObject.set('top', maxY); - - this.darkroom.dispatchEvent('crop:update'); - }, - - // Prevent crop zone from going beyond the canvas edges (like mouseMove) - onObjectScaling: function(event) { - if (!this.hasFocus()) { - return; - } - - var preventScaling = false; - var currentObject = event.target; - if (currentObject !== this.cropZone) - return; - - var canvas = this.darkroom.canvas; - var pointer = canvas.getPointer(event.e); - var x = pointer.x; - var y = pointer.y; - - var minX = currentObject.getLeft(); - var minY = currentObject.getTop(); - var maxX = currentObject.getLeft() + currentObject.getWidth(); - var maxY = currentObject.getTop() + currentObject.getHeight(); - - if (null !== this.options.ratio) { - if (minX < 0 || maxX > canvas.getWidth() || minY < 0 || maxY > canvas.getHeight()) { - preventScaling = true; - } - } - - if (minX < 0 || maxX > canvas.getWidth() || preventScaling) { - var lastScaleX = this.lastScaleX || 1; - currentObject.setScaleX(lastScaleX); - } - if (minX < 0) { - currentObject.setLeft(0); - } - - if (minY < 0 || maxY > canvas.getHeight() || preventScaling) { - var lastScaleY = this.lastScaleY || 1; - currentObject.setScaleY(lastScaleY); - } - if (minY < 0) { - currentObject.setTop(0); - } - - if (currentObject.getWidth() < this.options.minWidth) { - currentObject.scaleToWidth(this.options.minWidth); - } - if (currentObject.getHeight() < this.options.minHeight) { - currentObject.scaleToHeight(this.options.minHeight); - } - - this.lastScaleX = currentObject.getScaleX(); - this.lastScaleY = currentObject.getScaleY(); - - this.darkroom.dispatchEvent('crop:update'); - }, - - // Init crop zone - onMouseDown: function(event) { - if (!this.hasFocus()) { - return; - } - - var canvas = this.darkroom.canvas; - - // recalculate offset, in case canvas was manipulated since last `calcOffset` - canvas.calcOffset(); - var pointer = canvas.getPointer(event.e); - var x = pointer.x; - var y = pointer.y; - var point = new fabric.Point(x, y); - - // Check if user want to scale or drag the crop zone. - var activeObject = canvas.getActiveObject(); - if (activeObject === this.cropZone || this.cropZone.containsPoint(point)) { - return; - } - - canvas.discardActiveObject(); - this.cropZone.setWidth(0); - this.cropZone.setHeight(0); - this.cropZone.setScaleX(1); - this.cropZone.setScaleY(1); - - this.startX = x; - this.startY = y; - }, - - // Extend crop zone - onMouseMove: function(event) { - // Quick crop feature - if (this.isKeyCroping) - return this.onMouseMoveKeyCrop(event); - - if (null === this.startX || null === this.startY) { - return; - } - - var canvas = this.darkroom.canvas; - var pointer = canvas.getPointer(event.e); - var x = pointer.x; - var y = pointer.y; - - this._renderCropZone(this.startX, this.startY, x, y); - }, - - onMouseMoveKeyCrop: function(event) { - var canvas = this.darkroom.canvas; - var zone = this.cropZone; - - var pointer = canvas.getPointer(event.e); - var x = pointer.x; - var y = pointer.y; - - if (!zone.left || !zone.top) { - zone.setTop(y); - zone.setLeft(x); - } - - this.isKeyLeft = x < zone.left + zone.width / 2 ; - this.isKeyUp = y < zone.top + zone.height / 2 ; - - this._renderCropZone( - Math.min(zone.left, x), - Math.min(zone.top, y), - Math.max(zone.left+zone.width, x), - Math.max(zone.top+zone.height, y) - ); - }, - - // Finish crop zone - onMouseUp: function(event) { - if (null === this.startX || null === this.startY) { - return; - } - - var canvas = this.darkroom.canvas; - this.cropZone.setCoords(); - canvas.setActiveObject(this.cropZone); - canvas.calcOffset(); - - this.startX = null; - this.startY = null; - }, - - onKeyDown: function(event) { - if (false === this.options.quickCropKey || event.keyCode !== this.options.quickCropKey || this.isKeyCroping) - return; - - // Active quick crop flow - this.isKeyCroping = true ; - this.darkroom.canvas.discardActiveObject(); - this.cropZone.setWidth(0); - this.cropZone.setHeight(0); - this.cropZone.setScaleX(1); - this.cropZone.setScaleY(1); - this.cropZone.setTop(0); - this.cropZone.setLeft(0); - }, - - onKeyUp: function(event) { - if (false === this.options.quickCropKey || event.keyCode !== this.options.quickCropKey || !this.isKeyCroping) - return; - - // Unactive quick crop flow - this.isKeyCroping = false; - this.startX = 1; - this.startY = 1; - this.onMouseUp(); - }, - - selectZone: function(x, y, width, height, forceDimension) { - if (!this.hasFocus()) - this.requireFocus(); - - if (!forceDimension) { - this._renderCropZone(x, y, x+width, y+height); - } else { - this.cropZone.set({ - 'left': x, - 'top': y, - 'width': width, - 'height': height - }); - } - - var canvas = this.darkroom.canvas; - canvas.bringToFront(this.cropZone); - this.cropZone.setCoords(); - canvas.setActiveObject(this.cropZone); - canvas.calcOffset(); - - this.darkroom.dispatchEvent('crop:update'); - }, - - toggleCrop: function() { - if (!this.hasFocus()) - this.requireFocus(); - else - this.releaseFocus(); - }, - - cropCurrentZone: function() { - if (!this.hasFocus()) - return; - - // Avoid croping empty zone - if (this.cropZone.width < 1 && this.cropZone.height < 1) - return; - - var image = this.darkroom.image; - - // Compute crop zone dimensions - var top = this.cropZone.getTop() - image.getTop(); - var left = this.cropZone.getLeft() - image.getLeft(); - var width = this.cropZone.getWidth(); - var height = this.cropZone.getHeight(); - - // Adjust dimensions to image only - if (top < 0) { - height += top; - top = 0; - } - - if (left < 0) { - width += left; - left = 0; - } - - // Apply crop transformation. - // Make sure to use relative dimension since the crop will be applied - // on the source image. - this.darkroom.applyTransformation(new Crop({ - top: top / image.getHeight(), - left: left / image.getWidth(), - width: width / image.getWidth(), - height: height / image.getHeight(), - })); - }, - - // Test wether crop zone is set - hasFocus: function() { - return this.cropZone !== undefined; - }, - - // Create the crop zone - requireFocus: function() { - this.cropZone = new CropZone({ - fill: 'transparent', - hasBorders: false, - originX: 'left', - originY: 'top', - //stroke: '#444', - //strokeDashArray: [5, 5], - //borderColor: '#444', - cornerColor: '#444', - cornerSize: 8, - transparentCorners: false, - lockRotation: true, - hasRotatingPoint: false, - }); - - if (null !== this.options.ratio) { - this.cropZone.set('lockUniScaling', true); - } - - this.darkroom.canvas.add(this.cropZone); - this.darkroom.canvas.defaultCursor = 'crosshair'; - - this.cropButton.active(true); - this.okButton.hide(false); - this.cancelButton.hide(false); - }, - - // Remove the crop zone - releaseFocus: function() { - if (undefined === this.cropZone) - return; - - this.cropZone.remove(); - this.cropZone = undefined; - - this.cropButton.active(false); - this.okButton.hide(true); - this.cancelButton.hide(true); - - this.darkroom.canvas.defaultCursor = 'default'; - - this.darkroom.dispatchEvent('crop:update'); - }, - - _renderCropZone: function(fromX, fromY, toX, toY) { - var canvas = this.darkroom.canvas; - - var isRight = (toX > fromX); - var isLeft = !isRight; - var isDown = (toY > fromY); - var isUp = !isDown; - - var minWidth = Math.min(+this.options.minWidth, canvas.getWidth()); - var minHeight = Math.min(+this.options.minHeight, canvas.getHeight()); - - // Define corner coordinates - var leftX = Math.min(fromX, toX); - var rightX = Math.max(fromX, toX); - var topY = Math.min(fromY, toY); - var bottomY = Math.max(fromY, toY); - - // Replace current point into the canvas - leftX = Math.max(0, leftX); - rightX = Math.min(canvas.getWidth(), rightX); - topY = Math.max(0, topY) - bottomY = Math.min(canvas.getHeight(), bottomY); - - // Recalibrate coordinates according to given options - if (rightX - leftX < minWidth) { - if (isRight) - rightX = leftX + minWidth; - else - leftX = rightX - minWidth; - } - if (bottomY - topY < minHeight) { - if (isDown) - bottomY = topY + minHeight; - else - topY = bottomY - minHeight; - } - - // Truncate truncate according to canvas dimensions - if (leftX < 0) { - // Translate to the left - rightX += Math.abs(leftX); - leftX = 0 - } - if (rightX > canvas.getWidth()) { - // Translate to the right - leftX -= (rightX - canvas.getWidth()); - rightX = canvas.getWidth(); - } - if (topY < 0) { - // Translate to the bottom - bottomY += Math.abs(topY); - topY = 0 - } - if (bottomY > canvas.getHeight()) { - // Translate to the right - topY -= (bottomY - canvas.getHeight()); - bottomY = canvas.getHeight(); - } - - var width = rightX - leftX; - var height = bottomY - topY; - var currentRatio = width / height; - - if (this.options.ratio && +this.options.ratio !== currentRatio) { - var ratio = +this.options.ratio; - - if(this.isKeyCroping) { - isLeft = this.isKeyLeft; - isUp = this.isKeyUp; - } - - if (currentRatio < ratio) { - var newWidth = height * ratio; - if (isLeft) { - leftX -= (newWidth - width); - } - width = newWidth; - } else if (currentRatio > ratio) { - var newHeight = height / (ratio * height/width); - if (isUp) { - topY -= (newHeight - height); - } - height = newHeight; - } - - if (leftX < 0) { - leftX = 0; - //TODO - } - if (topY < 0) { - topY = 0; - //TODO - } - if (leftX + width > canvas.getWidth()) { - var newWidth = canvas.getWidth() - leftX; - height = newWidth * height / width; - width = newWidth; - if (isUp) { - topY = fromY - height; - } - } - if (topY + height > canvas.getHeight()) { - var newHeight = canvas.getHeight() - topY; - width = width * newHeight / height; - height = newHeight; - if (isLeft) { - leftX = fromX - width; - } - } - } - - // Apply coordinates - this.cropZone.left = leftX; - this.cropZone.top = topY; - this.cropZone.width = width; - this.cropZone.height = height; - - this.darkroom.canvas.bringToFront(this.cropZone); - - this.darkroom.dispatchEvent('crop:update'); - } -}); - -})(); diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/js/plugins/darkroom.history.js b/web_widget_darkroom/static/lib/darkroomjs/lib/js/plugins/darkroom.history.js deleted file mode 100755 index 81bea34a..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/js/plugins/darkroom.history.js +++ /dev/null @@ -1,66 +0,0 @@ -;(function(window, document, Darkroom, fabric) { - 'use strict'; - - Darkroom.plugins['history'] = Darkroom.Plugin.extend({ - undoTransformations: [], - - initialize: function InitDarkroomHistoryPlugin() { - this._initButtons(); - - this.darkroom.addEventListener('core:transformation', this._onTranformationApplied.bind(this)); - }, - - undo: function() { - if (this.darkroom.transformations.length === 0) { - return; - } - - var lastTransformation = this.darkroom.transformations.pop(); - this.undoTransformations.unshift(lastTransformation); - - this.darkroom.reinitializeImage(); - this._updateButtons(); - }, - - redo: function() { - if (this.undoTransformations.length === 0) { - return; - } - - var cancelTransformation = this.undoTransformations.shift(); - this.darkroom.transformations.push(cancelTransformation); - - this.darkroom.reinitializeImage(); - this._updateButtons(); - }, - - _initButtons: function() { - var buttonGroup = this.darkroom.toolbar.createButtonGroup(); - - this.backButton = buttonGroup.createButton({ - image: 'undo', - disabled: true - }); - - this.forwardButton = buttonGroup.createButton({ - image: 'redo', - disabled: true - }); - - this.backButton.addEventListener('click', this.undo.bind(this)); - this.forwardButton.addEventListener('click', this.redo.bind(this)); - - return this; - }, - - _updateButtons: function() { - this.backButton.disable((this.darkroom.transformations.length === 0)) - this.forwardButton.disable((this.undoTransformations.length === 0)) - }, - - _onTranformationApplied: function() { - this.undoTransformations = []; - this._updateButtons(); - } - }); -})(window, document, Darkroom, fabric); diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/js/plugins/darkroom.rotate.js b/web_widget_darkroom/static/lib/darkroomjs/lib/js/plugins/darkroom.rotate.js deleted file mode 100755 index 1f2fbe92..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/js/plugins/darkroom.rotate.js +++ /dev/null @@ -1,57 +0,0 @@ -(function() { -'use strict'; - -var Rotation = Darkroom.Transformation.extend({ - applyTransformation: function(canvas, image, next) { - var angle = (image.getAngle() + this.options.angle) % 360; - image.rotate(angle); - - var width, height; - height = Math.abs(image.getWidth()*(Math.sin(angle*Math.PI/180)))+Math.abs(image.getHeight()*(Math.cos(angle*Math.PI/180))); - width = Math.abs(image.getHeight()*(Math.sin(angle*Math.PI/180)))+Math.abs(image.getWidth()*(Math.cos(angle*Math.PI/180))); - - canvas.setWidth(width); - canvas.setHeight(height); - - canvas.centerObject(image); - image.setCoords(); - canvas.renderAll(); - - next(); - } -}); - -Darkroom.plugins['rotate'] = Darkroom.Plugin.extend({ - - initialize: function InitDarkroomRotatePlugin() { - var buttonGroup = this.darkroom.toolbar.createButtonGroup(); - - var leftButton = buttonGroup.createButton({ - image: 'rotate-left' - }); - - var rightButton = buttonGroup.createButton({ - image: 'rotate-right' - }); - - leftButton.addEventListener('click', this.rotateLeft.bind(this)); - rightButton.addEventListener('click', this.rotateRight.bind(this)); - }, - - rotateLeft: function rotateLeft() { - this.rotate(-90); - }, - - rotateRight: function rotateRight() { - this.rotate(90); - }, - - rotate: function rotate(angle) { - this.darkroom.applyTransformation( - new Rotation({angle: angle}) - ); - } - -}); - -})(); diff --git a/web_widget_darkroom/static/lib/darkroomjs/lib/js/plugins/darkroom.save.js b/web_widget_darkroom/static/lib/darkroomjs/lib/js/plugins/darkroom.save.js deleted file mode 100755 index d12eeadf..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/lib/js/plugins/darkroom.save.js +++ /dev/null @@ -1,23 +0,0 @@ -(function() { -'use strict'; - -Darkroom.plugins['save'] = Darkroom.Plugin.extend({ - - defaults: { - callback: function() { - this.darkroom.selfDestroy(); - } - }, - - initialize: function InitializeDarkroomSavePlugin() { - var buttonGroup = this.darkroom.toolbar.createButtonGroup(); - - this.destroyButton = buttonGroup.createButton({ - image: 'save' - }); - - this.destroyButton.addEventListener('click', this.options.callback.bind(this)); - }, -}); - -})(); diff --git a/web_widget_darkroom/static/lib/darkroomjs/package.json b/web_widget_darkroom/static/lib/darkroomjs/package.json deleted file mode 100755 index 4684f431..00000000 --- a/web_widget_darkroom/static/lib/darkroomjs/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "darkroom", - "description": "Extensible image editing tool via HTML canvas", - "version": "2.0.1", - "license": "MIT", - "homepage": "https://mattketmo.github.io/darkroomjs", - "repository": { - "type": "git", - "url": "https://github.com/mattketmo/darkroomjs.git" - }, - "author": "Matthieu Moquet (http://moquet.net/)", - "dependencies": {}, - "devDependencies": { - "cheerio": "^0.18.0", - "gulp": "^3.8.6", - "gulp-concat": "^2.3.4", - "gulp-connect": "^2.0.6", - "gulp-inject": "^1.2.0", - "gulp-plumber": "^0.6.4", - "gulp-sass": "^0.7.2", - "gulp-sourcemaps": "^1.1.0", - "gulp-svgmin": "^1.1.1", - "gulp-svgstore": "^5.0.0", - "gulp-uglify": "^0.3.1", - "gulp-util": "^3.0.0", - "js-string-escape": "^1.0.0", - "rimraf": "^2.2.8", - "streamqueue": "^0.1.1" - }, - "scripts": { - "start": "node_modules/.bin/gulp server build --prod", - "develop": "node_modules/.bin/gulp" - }, - "ignore": [ - "**/.*", - "node_modules", - "bower_components" - ] -} diff --git a/web_widget_darkroom/static/src/css/darkroom.css b/web_widget_darkroom/static/src/css/darkroom.css deleted file mode 100755 index 21d4668a..00000000 --- a/web_widget_darkroom/static/src/css/darkroom.css +++ /dev/null @@ -1,11 +0,0 @@ -/*.darkroom-container{ - padding-top: 50px; -} -.darkroom-toolbar{ - top: 5px; -} -*/ - -.darkroom-button-group{ - display: inline; -} diff --git a/web_widget_darkroom/static/src/js/darkroom_plugins.js b/web_widget_darkroom/static/src/js/darkroom_plugins.js deleted file mode 100644 index b7dd01c1..00000000 --- a/web_widget_darkroom/static/src/js/darkroom_plugins.js +++ /dev/null @@ -1,20 +0,0 @@ -/* Copyright 2016 LasLabs Inc. - * License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). - */ - -odoo.define('web_widget_darkroom.darkroom_plugins', function(require){ - "use strict"; - - var DarkroomPlugins = Object; - DarkroomPlugins.extend = function(destination, source) { - for (var property in source) { - if (source.hasOwnProperty(property)) { - destination[property] = source[property]; - } - } - return destination; - }; - - return DarkroomPlugins - -}); diff --git a/web_widget_darkroom/static/src/js/plugins/darkroom.crop.js b/web_widget_darkroom/static/src/js/plugins/darkroom.crop.js index 8819a57c..0ae738f0 100755 --- a/web_widget_darkroom/static/src/js/plugins/darkroom.crop.js +++ b/web_widget_darkroom/static/src/js/plugins/darkroom.crop.js @@ -1,683 +1,693 @@ -/* Adapted from https://github.com/MattKetmo/darkroomjs/tree/master/lib/js/plugins - * License https://github.com/MattKetmo/darkroomjs/blob/master/LICENSE - */ - -odoo.define('web_widget_darkroom.darkroom_crop', function(require){ - - 'use strict'; - - var DarkroomPluginCrop = function(){ - - var Crop = Darkroom.Transformation.extend({ - applyTransformation: function(canvas, image, next) { - // Snapshot the image delimited by the crop zone - var snapshot = new Image(); - snapshot.onload = function() { - // Validate image - if (height < 1 || width < 1) - return; - - var imgInstance = new fabric.Image(this, { - // options to make the image static - selectable: false, - evented: false, - lockMovementX: true, - lockMovementY: true, - lockRotation: true, - lockScalingX: true, - lockScalingY: true, - lockUniScaling: true, - hasControls: false, - hasBorders: false - }); - - var width = this.width; - var height = this.height; - - // Update canvas size - canvas.setWidth(width); - canvas.setHeight(height); - - // Add image - image.remove(); - canvas.add(imgInstance); - - next(imgInstance); - }; - - var viewport = Darkroom.Utils.computeImageViewPort(image); - var imageWidth = viewport.width; - var imageHeight = viewport.height; - - var left = this.options.left * imageWidth; - var top = this.options.top * imageHeight; - var width = Math.min(this.options.width * imageWidth, imageWidth - left); - var height = Math.min(this.options.height * imageHeight, imageHeight - top); - - snapshot.src = canvas.toDataURL({ - left: left, - top: top, - width: width, - height: height, - }); - } - }); - - var CropZone = fabric.util.createClass(fabric.Rect, { - _render: function(ctx) { - this.callSuper('_render', ctx); - - var canvas = ctx.canvas; - var dashWidth = 7; - - // Set original scale - var flipX = this.flipX ? -1 : 1; - var flipY = this.flipY ? -1 : 1; - var scaleX = flipX / this.scaleX; - var scaleY = flipY / this.scaleY; - - ctx.scale(scaleX, scaleY); - - // Overlay rendering - ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; - this._renderOverlay(ctx); - - // Set dashed borders - if (ctx.setLineDash !== undefined) - ctx.setLineDash([dashWidth, dashWidth]); - else if (ctx.mozDash !== undefined) - ctx.mozDash = [dashWidth, dashWidth]; - - // First lines rendering with black - ctx.strokeStyle = 'rgba(0, 0, 0, 0.2)'; - this._renderBorders(ctx); - this._renderGrid(ctx); - - // Re render lines in white - ctx.lineDashOffset = dashWidth; - ctx.strokeStyle = 'rgba(255, 255, 255, 0.4)'; - this._renderBorders(ctx); - this._renderGrid(ctx); - - // Reset scale - ctx.scale(1/scaleX, 1/scaleY); - }, - - _renderOverlay: function(ctx) { - var canvas = ctx.canvas; - var borderOffset = 0; - - // - // x0 x1 x2 x3 - // y0 +------------------------+ - // |\\\\\\\\\\\\\\\\\\\\\\\\| - // |\\\\\\\\\\\\\\\\\\\\\\\\| - // y1 +------+---------+-------+ - // |\\\\\\| |\\\\\\\| - // |\\\\\\| 0 |\\\\\\\| - // |\\\\\\| |\\\\\\\| - // y2 +------+---------+-------+ - // |\\\\\\\\\\\\\\\\\\\\\\\\| - // |\\\\\\\\\\\\\\\\\\\\\\\\| - // y3 +------------------------+ - // - - var x0 = Math.ceil(-this.getWidth() / 2 - this.getLeft()); - var x1 = Math.ceil(-this.getWidth() / 2); - var x2 = Math.ceil(this.getWidth() / 2); - var x3 = Math.ceil(this.getWidth() / 2 + (canvas.width - this.getWidth() - this.getLeft())); - - var y0 = Math.ceil(-this.getHeight() / 2 - this.getTop()); - var y1 = Math.ceil(-this.getHeight() / 2); - var y2 = Math.ceil(this.getHeight() / 2); - var y3 = Math.ceil(this.getHeight() / 2 + (canvas.height - this.getHeight() - this.getTop())); - - // Upper rect - ctx.fillRect(x0, y0, x3 - x0, y1 - y0 + borderOffset); - - // Left rect - ctx.fillRect(x0, y1, x1 - x0, y2 - y1 + borderOffset); - - // Right rect - ctx.fillRect(x2, y1, x3 - x2, y2 - y1 + borderOffset); - - // Down rect - ctx.fillRect(x0, y2, x3 - x0, y3 - y2); - }, - - _renderBorders: function(ctx) { - ctx.beginPath(); - ctx.moveTo(-this.getWidth()/2, -this.getHeight()/2); // upper left - ctx.lineTo(this.getWidth()/2, -this.getHeight()/2); // upper right - ctx.lineTo(this.getWidth()/2, this.getHeight()/2); // down right - ctx.lineTo(-this.getWidth()/2, this.getHeight()/2); // down left - ctx.lineTo(-this.getWidth()/2, -this.getHeight()/2); // upper left - ctx.stroke(); - }, - - _renderGrid: function(ctx) { - // Vertical lines - ctx.beginPath(); - ctx.moveTo(-this.getWidth()/2 + 1/3 * this.getWidth(), -this.getHeight()/2); - ctx.lineTo(-this.getWidth()/2 + 1/3 * this.getWidth(), this.getHeight()/2); - ctx.stroke(); - ctx.beginPath(); - ctx.moveTo(-this.getWidth()/2 + 2/3 * this.getWidth(), -this.getHeight()/2); - ctx.lineTo(-this.getWidth()/2 + 2/3 * this.getWidth(), this.getHeight()/2); - ctx.stroke(); - // Horizontal lines - ctx.beginPath(); - ctx.moveTo(-this.getWidth()/2, -this.getHeight()/2 + 1/3 * this.getHeight()); - ctx.lineTo(this.getWidth()/2, -this.getHeight()/2 + 1/3 * this.getHeight()); - ctx.stroke(); - ctx.beginPath(); - ctx.moveTo(-this.getWidth()/2, -this.getHeight()/2 + 2/3 * this.getHeight()); - ctx.lineTo(this.getWidth()/2, -this.getHeight()/2 + 2/3 * this.getHeight()); - ctx.stroke(); - } - }); - - Darkroom.plugins['crop'] = Darkroom.Plugin.extend({ - // Init point - startX: null, - startY: null, - - // Keycrop - isKeyCroping: false, - isKeyLeft: false, - isKeyUp: false, - - defaults: { - // min crop dimension - minHeight: 1, - minWidth: 1, - // ensure crop ratio - ratio: null, - // quick crop feature (set a key code to enable it) - quickCropKey: false - }, - - initialize: function InitDarkroomCropPlugin() { - var buttonGroup = this.darkroom.toolbar.createButtonGroup(); - - this.cropButton = buttonGroup.createButton({ - image: 'fa fa-crop', - editOnly: true, - }); - this.okButton = buttonGroup.createButton({ - image: 'fa fa-check', - editOnly: true, - type: 'success', - hide: true - }); - this.cancelButton = buttonGroup.createButton({ - image: 'fa fa-times', - editOnly: true, - type: 'danger', - hide: true +/** +* Copyright 2013 Matthieu Moquet +* Copyright 2016-2017 LasLabs Inc. +* License MIT (https://opensource.org/licenses/MIT) +**/ + +odoo.define('web_widget_darkroom.darkroom_crop', function(){ + 'use strict'; + + var DarkroomPluginCrop = function() { + var Crop = Darkroom.Transformation.extend({ + applyTransformation: function(canvas, image, next) { + // Snapshot the image delimited by the crop zone + var snapshot = new Image(); + snapshot.onload = function() { + var width = this.width; + var height = this.height; + + // Validate image + if (height < 1 || width < 1) { + return; + } + + var imgInstance = new fabric.Image(this, { + // Options to make the image static + selectable: false, + evented: false, + lockMovementX: true, + lockMovementY: true, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, + lockUniScaling: true, + hasControls: false, + hasBorders: false, + }); + + // Update canvas size + canvas.setWidth(width); + canvas.setHeight(height); + + // Add image + image.remove(); + canvas.add(imgInstance); + + next(imgInstance); + }; + + var viewport = Darkroom.Utils.computeImageViewPort(image); + var imageWidth = viewport.width; + var imageHeight = viewport.height; + + var left = this.options.left * imageWidth; + var top = this.options.top * imageHeight; + var width = Math.min(this.options.width * imageWidth, imageWidth - left); + var height = Math.min(this.options.height * imageHeight, imageHeight - top); + + snapshot.src = canvas.toDataURL({ + left: left, + top: top, + width: width, + height: height, + }); + }, }); - - // Buttons click - this.cropButton.addEventListener('click', this.toggleCrop.bind(this)); - this.okButton.addEventListener('click', this.cropCurrentZone.bind(this)); - this.cancelButton.addEventListener('click', this.releaseFocus.bind(this)); - - // Canvas events - this.darkroom.canvas.on('mouse:down', this.onMouseDown.bind(this)); - this.darkroom.canvas.on('mouse:move', this.onMouseMove.bind(this)); - this.darkroom.canvas.on('mouse:up', this.onMouseUp.bind(this)); - this.darkroom.canvas.on('object:moving', this.onObjectMoving.bind(this)); - this.darkroom.canvas.on('object:scaling', this.onObjectScaling.bind(this)); - - fabric.util.addListener(fabric.document, 'keydown', this.onKeyDown.bind(this)); - fabric.util.addListener(fabric.document, 'keyup', this.onKeyUp.bind(this)); - - this.darkroom.addEventListener('core:transformation', this.releaseFocus.bind(this)); - }, - - // Avoid crop zone to go beyond the canvas edges - onObjectMoving: function(event) { - if (!this.hasFocus()) { - return; - } - - var currentObject = event.target; - if (currentObject !== this.cropZone) - return; - - var canvas = this.darkroom.canvas; - var x = currentObject.getLeft(), y = currentObject.getTop(); - var w = currentObject.getWidth(), h = currentObject.getHeight(); - var maxX = canvas.getWidth() - w; - var maxY = canvas.getHeight() - h; - - if (x < 0) - currentObject.set('left', 0); - if (y < 0) - currentObject.set('top', 0); - if (x > maxX) - currentObject.set('left', maxX); - if (y > maxY) - currentObject.set('top', maxY); - - this.darkroom.dispatchEvent('crop:update'); - }, - - // Prevent crop zone from going beyond the canvas edges (like mouseMove) - onObjectScaling: function(event) { - if (!this.hasFocus()) { - return; - } - - var preventScaling = false; - var currentObject = event.target; - if (currentObject !== this.cropZone) - return; - - var canvas = this.darkroom.canvas; - var pointer = canvas.getPointer(event.e); - var x = pointer.x; - var y = pointer.y; - - var minX = currentObject.getLeft(); - var minY = currentObject.getTop(); - var maxX = currentObject.getLeft() + currentObject.getWidth(); - var maxY = currentObject.getTop() + currentObject.getHeight(); - - if (null !== this.options.ratio) { - if (minX < 0 || maxX > canvas.getWidth() || minY < 0 || maxY > canvas.getHeight()) { - preventScaling = true; - } - } - - if (minX < 0 || maxX > canvas.getWidth() || preventScaling) { - var lastScaleX = this.lastScaleX || 1; - currentObject.setScaleX(lastScaleX); - } - if (minX < 0) { - currentObject.setLeft(0); - } - - if (minY < 0 || maxY > canvas.getHeight() || preventScaling) { - var lastScaleY = this.lastScaleY || 1; - currentObject.setScaleY(lastScaleY); - } - if (minY < 0) { - currentObject.setTop(0); - } - - if (currentObject.getWidth() < this.options.minWidth) { - currentObject.scaleToWidth(this.options.minWidth); - } - if (currentObject.getHeight() < this.options.minHeight) { - currentObject.scaleToHeight(this.options.minHeight); - } - - this.lastScaleX = currentObject.getScaleX(); - this.lastScaleY = currentObject.getScaleY(); - - this.darkroom.dispatchEvent('crop:update'); - }, - - // Init crop zone - onMouseDown: function(event) { - if (!this.hasFocus()) { - return; - } - - var canvas = this.darkroom.canvas; - - // recalculate offset, in case canvas was manipulated since last `calcOffset` - canvas.calcOffset(); - var pointer = canvas.getPointer(event.e); - var x = pointer.x; - var y = pointer.y; - var point = new fabric.Point(x, y); - - // Check if user want to scale or drag the crop zone. - var activeObject = canvas.getActiveObject(); - if (activeObject === this.cropZone || this.cropZone.containsPoint(point)) { - return; - } - - canvas.discardActiveObject(); - this.cropZone.setWidth(0); - this.cropZone.setHeight(0); - this.cropZone.setScaleX(1); - this.cropZone.setScaleY(1); - - this.startX = x; - this.startY = y; - }, - - // Extend crop zone - onMouseMove: function(event) { - // Quick crop feature - if (this.isKeyCroping) - return this.onMouseMoveKeyCrop(event); - - if (null === this.startX || null === this.startY) { - return; - } - - var canvas = this.darkroom.canvas; - var pointer = canvas.getPointer(event.e); - var x = pointer.x; - var y = pointer.y; - - this._renderCropZone(this.startX, this.startY, x, y); - }, - - onMouseMoveKeyCrop: function(event) { - var canvas = this.darkroom.canvas; - var zone = this.cropZone; - - var pointer = canvas.getPointer(event.e); - var x = pointer.x; - var y = pointer.y; - - if (!zone.left || !zone.top) { - zone.setTop(y); - zone.setLeft(x); - } - - this.isKeyLeft = x < zone.left + zone.width / 2 ; - this.isKeyUp = y < zone.top + zone.height / 2 ; - - this._renderCropZone( - Math.min(zone.left, x), - Math.min(zone.top, y), - Math.max(zone.left+zone.width, x), - Math.max(zone.top+zone.height, y) - ); - }, - - // Finish crop zone - onMouseUp: function(event) { - if (null === this.startX || null === this.startY) { - return; - } - - var canvas = this.darkroom.canvas; - this.cropZone.setCoords(); - canvas.setActiveObject(this.cropZone); - canvas.calcOffset(); - - this.startX = null; - this.startY = null; - }, - - onKeyDown: function(event) { - if (false === this.options.quickCropKey || event.keyCode !== this.options.quickCropKey || this.isKeyCroping) - return; - - // Active quick crop flow - this.isKeyCroping = true ; - this.darkroom.canvas.discardActiveObject(); - this.cropZone.setWidth(0); - this.cropZone.setHeight(0); - this.cropZone.setScaleX(1); - this.cropZone.setScaleY(1); - this.cropZone.setTop(0); - this.cropZone.setLeft(0); - }, - - onKeyUp: function(event) { - if (false === this.options.quickCropKey || event.keyCode !== this.options.quickCropKey || !this.isKeyCroping) - return; - - // Unactive quick crop flow - this.isKeyCroping = false; - this.startX = 1; - this.startY = 1; - this.onMouseUp(); - }, - - selectZone: function(x, y, width, height, forceDimension) { - if (!this.hasFocus()) - this.requireFocus(); - - if (!forceDimension) { - this._renderCropZone(x, y, x+width, y+height); - } else { - this.cropZone.set({ - 'left': x, - 'top': y, - 'width': width, - 'height': height - }); - } - - var canvas = this.darkroom.canvas; - canvas.bringToFront(this.cropZone); - this.cropZone.setCoords(); - canvas.setActiveObject(this.cropZone); - canvas.calcOffset(); - - this.darkroom.dispatchEvent('crop:update'); - }, - - toggleCrop: function() { - if (!this.hasFocus()) - this.requireFocus(); - else - this.releaseFocus(); - }, - - cropCurrentZone: function() { - if (!this.hasFocus()) - return; - - // Avoid croping empty zone - if (this.cropZone.width < 1 && this.cropZone.height < 1) - return; - - var image = this.darkroom.image; - - // Compute crop zone dimensions - var top = this.cropZone.getTop() - image.getTop(); - var left = this.cropZone.getLeft() - image.getLeft(); - var width = this.cropZone.getWidth(); - var height = this.cropZone.getHeight(); - - // Adjust dimensions to image only - if (top < 0) { - height += top; - top = 0; - } - - if (left < 0) { - width += left; - left = 0; - } - - // Apply crop transformation. - // Make sure to use relative dimension since the crop will be applied - // on the source image. - this.darkroom.applyTransformation(new Crop({ - top: top / image.getHeight(), - left: left / image.getWidth(), - width: width / image.getWidth(), - height: height / image.getHeight(), - })); - }, - - // Test wether crop zone is set - hasFocus: function() { - return this.cropZone !== undefined; - }, - - // Create the crop zone - requireFocus: function() { - this.cropZone = new CropZone({ - fill: 'transparent', - hasBorders: false, - originX: 'left', - originY: 'top', - //stroke: '#444', - //strokeDashArray: [5, 5], - //borderColor: '#444', - cornerColor: '#444', - cornerSize: 8, - transparentCorners: false, - lockRotation: true, - hasRotatingPoint: false, + + var CropZone = fabric.util.createClass(fabric.Rect, { + _render: function(ctx) { + this.callSuper('_render', ctx); + var dashWidth = 7; + + // Set original scale + var flipX = this.flipX ? -1 : 1; + var flipY = this.flipY ? -1 : 1; + var scaleX = flipX / this.scaleX; + var scaleY = flipY / this.scaleY; + ctx.scale(scaleX, scaleY); + + // Overlay rendering + ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; + this._renderOverlay(ctx); + + // Set dashed borders + if (typeof ctx.setLineDash !== 'undefined') { + ctx.setLineDash([dashWidth, dashWidth]); + } else if (typeof ctx.mozDash !== 'undefined') { + ctx.mozDash = [dashWidth, dashWidth]; + } + + // First lines rendering with black + ctx.strokeStyle = 'rgba(0, 0, 0, 0.2)'; + this._renderBorders(ctx); + this._renderGrid(ctx); + + // Re render lines in white + ctx.lineDashOffset = dashWidth; + ctx.strokeStyle = 'rgba(255, 255, 255, 0.4)'; + this._renderBorders(ctx); + this._renderGrid(ctx); + + // Reset scale + ctx.scale(1/scaleX, 1/scaleY); + }, + + _renderOverlay: function(ctx) { + var canvas = ctx.canvas; + var borderOffset = 0; + + // + // x0 x1 x2 x3 + // y0 +------------------------+ + // |\\\\\\\\\\\\\\\\\\\\\\\\| + // |\\\\\\\\\\\\\\\\\\\\\\\\| + // y1 +------+---------+-------+ + // |\\\\\\| |\\\\\\\| + // |\\\\\\| 0 |\\\\\\\| + // |\\\\\\| |\\\\\\\| + // y2 +------+---------+-------+ + // |\\\\\\\\\\\\\\\\\\\\\\\\| + // |\\\\\\\\\\\\\\\\\\\\\\\\| + // y3 +------------------------+ + // + + var x0 = Math.ceil(-this.getWidth() / 2 - this.getLeft()); + var x1 = Math.ceil(-this.getWidth() / 2); + var x2 = Math.ceil(this.getWidth() / 2); + var x3 = Math.ceil(this.getWidth() / 2 + (canvas.width - this.getWidth() - this.getLeft())); + + var y0 = Math.ceil(-this.getHeight() / 2 - this.getTop()); + var y1 = Math.ceil(-this.getHeight() / 2); + var y2 = Math.ceil(this.getHeight() / 2); + var y3 = Math.ceil(this.getHeight() / 2 + (canvas.height - this.getHeight() - this.getTop())); + + // Upper rect + ctx.fillRect(x0, y0, x3 - x0, y1 - y0 + borderOffset); + + // Left rect + ctx.fillRect(x0, y1, x1 - x0, y2 - y1 + borderOffset); + + // Right rect + ctx.fillRect(x2, y1, x3 - x2, y2 - y1 + borderOffset); + + // Down rect + ctx.fillRect(x0, y2, x3 - x0, y3 - y2); + }, + + _renderBorders: function(ctx) { + ctx.beginPath(); + // upper left + ctx.moveTo(-this.getWidth()/2, -this.getHeight()/2); + // upper right + ctx.lineTo(this.getWidth()/2, -this.getHeight()/2); + // down right + ctx.lineTo(this.getWidth()/2, this.getHeight()/2); + // down left + ctx.lineTo(-this.getWidth()/2, this.getHeight()/2); + // upper left + ctx.lineTo(-this.getWidth()/2, -this.getHeight()/2); + ctx.stroke(); + }, + + _renderGrid: function(ctx) { + // Vertical lines + ctx.beginPath(); + ctx.moveTo(-this.getWidth()/2 + 1/3 * this.getWidth(), -this.getHeight()/2); + ctx.lineTo(-this.getWidth()/2 + 1/3 * this.getWidth(), this.getHeight()/2); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(-this.getWidth()/2 + 2/3 * this.getWidth(), -this.getHeight()/2); + ctx.lineTo(-this.getWidth()/2 + 2/3 * this.getWidth(), this.getHeight()/2); + ctx.stroke(); + + // Horizontal lines + ctx.beginPath(); + ctx.moveTo(-this.getWidth()/2, -this.getHeight()/2 + 1/3 * this.getHeight()); + ctx.lineTo(this.getWidth()/2, -this.getHeight()/2 + 1/3 * this.getHeight()); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(-this.getWidth()/2, -this.getHeight()/2 + 2/3 * this.getHeight()); + ctx.lineTo(this.getWidth()/2, -this.getHeight()/2 + 2/3 * this.getHeight()); + ctx.stroke(); + }, }); - - if (null !== this.options.ratio) { - this.cropZone.set('lockUniScaling', true); - } - - this.darkroom.canvas.add(this.cropZone); - this.darkroom.canvas.defaultCursor = 'crosshair'; - - this.cropButton.active(true); - this.okButton.hide(false); - this.cancelButton.hide(false); - }, - - // Remove the crop zone - releaseFocus: function() { - if (undefined === this.cropZone) - return; - - this.cropZone.remove(); - this.cropZone = undefined; - - this.cropButton.active(false); - this.okButton.hide(true); - this.cancelButton.hide(true); - - this.darkroom.canvas.defaultCursor = 'default'; - - this.darkroom.dispatchEvent('crop:update'); - }, - - _renderCropZone: function(fromX, fromY, toX, toY) { - var canvas = this.darkroom.canvas; - - var isRight = (toX > fromX); - var isLeft = !isRight; - var isDown = (toY > fromY); - var isUp = !isDown; - - var minWidth = Math.min(+this.options.minWidth, canvas.getWidth()); - var minHeight = Math.min(+this.options.minHeight, canvas.getHeight()); - - // Define corner coordinates - var leftX = Math.min(fromX, toX); - var rightX = Math.max(fromX, toX); - var topY = Math.min(fromY, toY); - var bottomY = Math.max(fromY, toY); - - // Replace current point into the canvas - leftX = Math.max(0, leftX); - rightX = Math.min(canvas.getWidth(), rightX); - topY = Math.max(0, topY) - bottomY = Math.min(canvas.getHeight(), bottomY); - - // Recalibrate coordinates according to given options - if (rightX - leftX < minWidth) { - if (isRight) - rightX = leftX + minWidth; - else - leftX = rightX - minWidth; - } - if (bottomY - topY < minHeight) { - if (isDown) - bottomY = topY + minHeight; - else - topY = bottomY - minHeight; - } - - // Truncate truncate according to canvas dimensions - if (leftX < 0) { - // Translate to the left - rightX += Math.abs(leftX); - leftX = 0 - } - if (rightX > canvas.getWidth()) { - // Translate to the right - leftX -= (rightX - canvas.getWidth()); - rightX = canvas.getWidth(); - } - if (topY < 0) { - // Translate to the bottom - bottomY += Math.abs(topY); - topY = 0 - } - if (bottomY > canvas.getHeight()) { - // Translate to the right - topY -= (bottomY - canvas.getHeight()); - bottomY = canvas.getHeight(); - } - - var width = rightX - leftX; - var height = bottomY - topY; - var currentRatio = width / height; - - if (this.options.ratio && +this.options.ratio !== currentRatio) { - var ratio = +this.options.ratio; - - if(this.isKeyCroping) { - isLeft = this.isKeyLeft; - isUp = this.isKeyUp; - } - - if (currentRatio < ratio) { - var newWidth = height * ratio; - if (isLeft) { - leftX -= (newWidth - width); - } - width = newWidth; - } else if (currentRatio > ratio) { - var newHeight = height / (ratio * height/width); - if (isUp) { - topY -= (newHeight - height); - } - height = newHeight; - } - - if (leftX < 0) { - leftX = 0; - //TODO - } - if (topY < 0) { - topY = 0; - //TODO - } - if (leftX + width > canvas.getWidth()) { - var newWidth = canvas.getWidth() - leftX; - height = newWidth * height / width; - width = newWidth; - if (isUp) { - topY = fromY - height; - } - } - if (topY + height > canvas.getHeight()) { - var newHeight = canvas.getHeight() - topY; - width = width * newHeight / height; - height = newHeight; - if (isLeft) { - leftX = fromX - width; + + Darkroom.plugins.crop = Darkroom.Plugin.extend({ + // Init point + startX: null, + startY: null, + + // Keycrop + isKeyCroping: false, + isKeyLeft: false, + isKeyUp: false, + + defaults: { + // Min crop dimensions + minHeight: 1, + minWidth: 1, + // Ensure crop ratio + ratio: null, + // Quick crop feature (set a key code to enable it) + quickCropKey: false, + }, + + initialize: function InitDarkroomCropPlugin() { + var buttonGroup = this.darkroom.toolbar.createButtonGroup(); + + this.cropButton = buttonGroup.createButton({ + image: 'fa fa-crop', + editOnly: true, + }); + this.okButton = buttonGroup.createButton({ + image: 'fa fa-check', + editOnly: true, + type: 'success', + hide: true + }); + this.cancelButton = buttonGroup.createButton({ + image: 'fa fa-times', + editOnly: true, + type: 'danger', + hide: true + }); + + // Button click events + this.cropButton.addEventListener('click', this.toggleCrop.bind(this)); + this.okButton.addEventListener('click', this.cropCurrentZone.bind(this)); + this.cancelButton.addEventListener('click', this.releaseFocus.bind(this)); + + // Canvas events + this.darkroom.canvas.on('mouse:down', this.onMouseDown.bind(this)); + this.darkroom.canvas.on('mouse:move', this.onMouseMove.bind(this)); + this.darkroom.canvas.on('mouse:up', this.onMouseUp.bind(this)); + this.darkroom.canvas.on('object:moving', this.onObjectMoving.bind(this)); + this.darkroom.canvas.on('object:scaling', this.onObjectScaling.bind(this)); + + fabric.util.addListener(fabric.document, 'keydown', this.onKeyDown.bind(this)); + fabric.util.addListener(fabric.document, 'keyup', this.onKeyUp.bind(this)); + + this.darkroom.addEventListener('core:transformation', this.releaseFocus.bind(this)); + }, + + // Avoid crop zone to go beyond the canvas edges + onObjectMoving: function(event) { + if (!this.hasFocus()) { + return; + } + + var currentObject = event.target; + if (currentObject !== this.cropZone) { + return; + } + + var canvas = this.darkroom.canvas; + var x = currentObject.getLeft(), y = currentObject.getTop(); + var w = currentObject.getWidth(), h = currentObject.getHeight(); + var maxX = canvas.getWidth() - w; + var maxY = canvas.getHeight() - h; + + if (x < 0) { + currentObject.set('left', 0); + } + if (y < 0) { + currentObject.set('top', 0); + } + if (x > maxX) { + currentObject.set('left', maxX); + } + if (y > maxY) { + currentObject.set('top', maxY); + } + + this.darkroom.dispatchEvent('crop:update'); + }, + + // Prevent crop zone from going beyond the canvas edges (like mouseMove) + onObjectScaling: function(event) { + if (!this.hasFocus()) { + return; + } + + var preventScaling = false; + var currentObject = event.target; + if (currentObject !== this.cropZone) { + return; + } + + var canvas = this.darkroom.canvas; + + var minX = currentObject.getLeft(); + var minY = currentObject.getTop(); + var maxX = currentObject.getLeft() + currentObject.getWidth(); + var maxY = currentObject.getTop() + currentObject.getHeight(); + + if (this.options.ratio !== null) { + if (minX < 0 || maxX > canvas.getWidth() || minY < 0 || maxY > canvas.getHeight()) { + preventScaling = true; + } + } + + if (minX < 0 || maxX > canvas.getWidth() || preventScaling) { + var lastScaleX = this.lastScaleX || 1; + currentObject.setScaleX(lastScaleX); + } + if (minX < 0) { + currentObject.setLeft(0); + } + + if (minY < 0 || maxY > canvas.getHeight() || preventScaling) { + var lastScaleY = this.lastScaleY || 1; + currentObject.setScaleY(lastScaleY); + } + if (minY < 0) { + currentObject.setTop(0); + } + + if (currentObject.getWidth() < this.options.minWidth) { + currentObject.scaleToWidth(this.options.minWidth); + } + if (currentObject.getHeight() < this.options.minHeight) { + currentObject.scaleToHeight(this.options.minHeight); + } + + this.lastScaleX = currentObject.getScaleX(); + this.lastScaleY = currentObject.getScaleY(); + + this.darkroom.dispatchEvent('crop:update'); + }, + + // Init crop zone + onMouseDown: function(event) { + if (!this.hasFocus()) { + return; + } + + var canvas = this.darkroom.canvas; + + // Recalculate offset, in case canvas was manipulated since last `calcOffset` + canvas.calcOffset(); + var pointer = canvas.getPointer(event.e); + var x = pointer.x; + var y = pointer.y; + var point = new fabric.Point(x, y); + + // Check if user want to scale or drag the crop zone. + var activeObject = canvas.getActiveObject(); + if (activeObject === this.cropZone || this.cropZone.containsPoint(point)) { + return; + } + + canvas.discardActiveObject(); + this.cropZone.setWidth(0); + this.cropZone.setHeight(0); + this.cropZone.setScaleX(1); + this.cropZone.setScaleY(1); + + this.startX = x; + this.startY = y; + }, + + // Extend crop zone + onMouseMove: function(event) { + // Quick crop feature + if (this.isKeyCroping) { + return this.onMouseMoveKeyCrop(event); + } + if (this.startX === null || this.startY === null) { + return; + } + + var canvas = this.darkroom.canvas; + var pointer = canvas.getPointer(event.e); + var x = pointer.x; + var y = pointer.y; + + this._renderCropZone(this.startX, this.startY, x, y); + }, + + onMouseMoveKeyCrop: function(event) { + var canvas = this.darkroom.canvas; + var zone = this.cropZone; + + var pointer = canvas.getPointer(event.e); + var x = pointer.x; + var y = pointer.y; + + if (!zone.left || !zone.top) { + zone.setTop(y); + zone.setLeft(x); + } + + this.isKeyLeft = x < zone.left + zone.width / 2; + this.isKeyUp = y < zone.top + zone.height / 2; + + this._renderCropZone( + Math.min(zone.left, x), + Math.min(zone.top, y), + Math.max(zone.left+zone.width, x), + Math.max(zone.top+zone.height, y) + ); + }, + + // Finish crop zone + onMouseUp: function() { + if (this.startX === null || this.startY === null) { + return; + } + + var canvas = this.darkroom.canvas; + this.cropZone.setCoords(); + canvas.setActiveObject(this.cropZone); + canvas.calcOffset(); + + this.startX = null; + this.startY = null; + }, + + onKeyDown: function(event) { + if (this.options.quickCropKey === false || event.keyCode !== this.options.quickCropKey || this.isKeyCroping) { + return; + } + + // Active quick crop flow + this.isKeyCroping = true ; + this.darkroom.canvas.discardActiveObject(); + this.cropZone.setWidth(0); + this.cropZone.setHeight(0); + this.cropZone.setScaleX(1); + this.cropZone.setScaleY(1); + this.cropZone.setTop(0); + this.cropZone.setLeft(0); + }, + + onKeyUp: function(event) { + if (this.options.quickCropKey === false || event.keyCode !== this.options.quickCropKey || !this.isKeyCroping) { + return; + } + + // Inactive quick crop flow + this.isKeyCroping = false; + this.startX = 1; + this.startY = 1; + this.onMouseUp(); + }, + + selectZone: function(x, y, width, height, forceDimension) { + if (!this.hasFocus()) { + this.requireFocus(); + } + + if (forceDimension) { + this.cropZone.set({ + 'left': x, + 'top': y, + 'width': width, + 'height': height, + }); + } else { + this._renderCropZone(x, y, x+width, y+height); + } + + var canvas = this.darkroom.canvas; + canvas.bringToFront(this.cropZone); + this.cropZone.setCoords(); + canvas.setActiveObject(this.cropZone); + canvas.calcOffset(); + + this.darkroom.dispatchEvent('crop:update'); + }, + + toggleCrop: function() { + if (this.hasFocus()) { + this.releaseFocus(); + } else { + this.requireFocus(); + } + }, + + cropCurrentZone: function() { + if (!this.hasFocus()) { + return; + } + + // Avoid croping empty zone + if (this.cropZone.width < 1 && this.cropZone.height < 1) { + return; + } + + var image = this.darkroom.image; + + // Compute crop zone dimensions + var top = this.cropZone.getTop() - image.getTop(); + var left = this.cropZone.getLeft() - image.getLeft(); + var width = this.cropZone.getWidth(); + var height = this.cropZone.getHeight(); + + // Adjust dimensions to image only + if (top < 0) { + height += top; + top = 0; + } + + if (left < 0) { + width += left; + left = 0; + } + + // Apply crop transformation. Make sure to use relative + // dimension since the crop will be applied on the source image. + this.darkroom.applyTransformation(new Crop({ + top: top / image.getHeight(), + left: left / image.getWidth(), + width: width / image.getWidth(), + height: height / image.getHeight(), + })); + }, + + // Test whether crop zone is set + hasFocus: function() { + return typeof this.cropZone !== 'undefined'; + }, + + // Create the crop zone + requireFocus: function() { + this.cropZone = new CropZone({ + fill: 'transparent', + hasBorders: false, + originX: 'left', + originY: 'top', + cornerColor: '#444', + cornerSize: 8, + transparentCorners: false, + lockRotation: true, + hasRotatingPoint: false, + }); + + if (this.options.ratio !== null) { + this.cropZone.set('lockUniScaling', true); + } + + this.darkroom.canvas.add(this.cropZone); + this.darkroom.canvas.defaultCursor = 'crosshair'; + + this.cropButton.active(true); + this.okButton.hide(false); + this.cancelButton.hide(false); + }, + + // Remove the crop zone + releaseFocus: function() { + if (typeof this.cropZone === 'undefined') { + return; + } + + this.cropZone.remove(); + this.cropZone = undefined; + + this.cropButton.active(false); + this.okButton.hide(true); + this.cancelButton.hide(true); + + this.darkroom.canvas.defaultCursor = 'default'; + this.darkroom.dispatchEvent('crop:update'); + }, + + _renderCropZone: function(fromX, fromY, toX, toY) { + var canvas = this.darkroom.canvas; + + var isRight = toX > fromX; + var isLeft = !isRight; + var isDown = toY > fromY; + var isUp = !isDown; + + var minWidth = Math.min(Number(this.options.minWidth), canvas.getWidth()); + var minHeight = Math.min(Number(this.options.minHeight), canvas.getHeight()); + + // Define corner coordinates + var leftX = Math.min(fromX, toX); + var rightX = Math.max(fromX, toX); + var topY = Math.min(fromY, toY); + var bottomY = Math.max(fromY, toY); + + // Replace current point into the canvas + leftX = Math.max(0, leftX); + rightX = Math.min(canvas.getWidth(), rightX); + topY = Math.max(0, topY); + bottomY = Math.min(canvas.getHeight(), bottomY); + + // Recalibrate coordinates according to given options + if (rightX - leftX < minWidth) { + if (isRight) { + rightX = leftX + minWidth; + } else { + leftX = rightX - minWidth; + } + } + if (bottomY - topY < minHeight) { + if (isDown) { + bottomY = topY + minHeight; + } else { + topY = bottomY - minHeight; + } + } + + // Truncate truncate according to canvas dimensions + if (leftX < 0) { + // Translate to the left + rightX += Math.abs(leftX); + leftX = 0; + } + if (rightX > canvas.getWidth()) { + // Translate to the right + leftX -= rightX - canvas.getWidth(); + rightX = canvas.getWidth(); + } + if (topY < 0) { + // Translate to the bottom + bottomY += Math.abs(topY); + topY = 0; + } + if (bottomY > canvas.getHeight()) { + // Translate to the right + topY -= bottomY - canvas.getHeight(); + bottomY = canvas.getHeight(); + } + + var width = rightX - leftX; + var height = bottomY - topY; + var currentRatio = width / height; + + if (this.options.ratio && Number(this.options.ratio) !== currentRatio) { + var ratio = Number(this.options.ratio); + var newWidth = 0, newHeight = 0; + + if(this.isKeyCroping) { + isLeft = this.isKeyLeft; + isUp = this.isKeyUp; + } + + if (currentRatio < ratio) { + newWidth = height * ratio; + if (isLeft) { + leftX -= newWidth - width; + } + width = newWidth; + } else if (currentRatio > ratio) { + newHeight = height / (ratio * height/width); + if (isUp) { + topY -= newHeight - height; + } + height = newHeight; + } + + if (leftX < 0) { + leftX = 0; + //TODO + } + if (topY < 0) { + topY = 0; + //TODO + } + if (leftX + width > canvas.getWidth()) { + newWidth = canvas.getWidth() - leftX; + height = newWidth * height / width; + width = newWidth; + if (isUp) { + topY = fromY - height; + } + } + if (topY + height > canvas.getHeight()) { + newHeight = canvas.getHeight() - topY; + width = width * newHeight / height; + height = newHeight; + if (isLeft) { + leftX = fromX - width; + } + } + } + + // Apply coordinates + this.cropZone.left = leftX; + this.cropZone.top = topY; + this.cropZone.width = width; + this.cropZone.height = height; + + this.darkroom.canvas.bringToFront(this.cropZone); + this.darkroom.dispatchEvent('crop:update'); } - } - } - - // Apply coordinates - this.cropZone.left = leftX; - this.cropZone.top = topY; - this.cropZone.width = width; - this.cropZone.height = height; - - this.darkroom.canvas.bringToFront(this.cropZone); - - this.darkroom.dispatchEvent('crop:update'); - } - }); - - } - - return {DarkroomPluginCrop: DarkroomPluginCrop}; + }); + }; + return {DarkroomPluginCrop: DarkroomPluginCrop}; }); diff --git a/web_widget_darkroom/static/src/js/plugins/darkroom.history.js b/web_widget_darkroom/static/src/js/plugins/darkroom.history.js index 99bc07f6..11b2569c 100755 --- a/web_widget_darkroom/static/src/js/plugins/darkroom.history.js +++ b/web_widget_darkroom/static/src/js/plugins/darkroom.history.js @@ -1,80 +1,76 @@ -/* Adapted from https://github.com/MattKetmo/darkroomjs/tree/master/lib/js/plugins - * License https://github.com/MattKetmo/darkroomjs/blob/master/LICENSE - */ - -odoo.define('web_widget_darkroom.darkroom_history', function(require){ - - 'use strict'; - - var DarkroomPluginHistory = function() { - - Darkroom.plugins['history'] = Darkroom.Plugin.extend({ - undoTransformations: [], - - initialize: function InitDarkroomHistoryPlugin() { - this._initButtons(); - - this.darkroom.addEventListener('core:transformation', this._onTranformationApplied.bind(this)); - }, - - undo: function() { - if (this.darkroom.transformations.length === 0) { - return; - } - - var lastTransformation = this.darkroom.transformations.pop(); - this.undoTransformations.unshift(lastTransformation); - - this.darkroom.reinitializeImage(); - this._updateButtons(); - }, - - redo: function() { - if (this.undoTransformations.length === 0) { - return; - } - - var cancelTransformation = this.undoTransformations.shift(); - this.darkroom.transformations.push(cancelTransformation); - - this.darkroom.reinitializeImage(); - this._updateButtons(); - }, - - _initButtons: function() { - var buttonGroup = this.darkroom.toolbar.createButtonGroup(); - - this.backButton = buttonGroup.createButton({ - image: 'fa fa-step-backward', - disabled: true, - editOnly: true, - }); - - this.forwardButton = buttonGroup.createButton({ - image: 'fa fa-step-forward', - disabled: true, - editOnly: true, +/** +* Copyright 2013 Matthieu Moquet +* Copyright 2016-2017 LasLabs Inc. +* License MIT (https://opensource.org/licenses/MIT) +**/ + +odoo.define('web_widget_darkroom.darkroom_history', function() { + 'use strict'; + + var DarkroomPluginHistory = function() { + Darkroom.plugins.history = Darkroom.Plugin.extend({ + undoTransformations: [], + + initialize: function InitDarkroomHistoryPlugin() { + this._initButtons(); + this.darkroom.addEventListener('core:transformation', this._onTranformationApplied.bind(this)); + }, + + undo: function() { + if (this.darkroom.transformations.length === 0) { + return; + } + + var lastTransformation = this.darkroom.transformations.pop(); + this.undoTransformations.unshift(lastTransformation); + + this.darkroom.reinitializeImage(); + this._updateButtons(); + }, + + redo: function() { + if (this.undoTransformations.length === 0) { + return; + } + + var cancelTransformation = this.undoTransformations.shift(); + this.darkroom.transformations.push(cancelTransformation); + + this.darkroom.reinitializeImage(); + this._updateButtons(); + }, + + _initButtons: function() { + var buttonGroup = this.darkroom.toolbar.createButtonGroup(); + + this.backButton = buttonGroup.createButton({ + image: 'fa fa-step-backward', + disabled: true, + editOnly: true, + }); + this.forwardButton = buttonGroup.createButton({ + image: 'fa fa-step-forward', + disabled: true, + editOnly: true, + }); + + this.backButton.addEventListener('click', this.undo.bind(this)); + this.forwardButton.addEventListener('click', this.redo.bind(this)); + + return this; + }, + + _updateButtons: function() { + this.backButton.disable(this.darkroom.transformations.length === 0); + this.forwardButton.disable(this.undoTransformations.length === 0); + }, + + _onTranformationApplied: function() { + this.undoTransformations = []; + this._updateButtons(); + }, }); - - this.backButton.addEventListener('click', this.undo.bind(this)); - this.forwardButton.addEventListener('click', this.redo.bind(this)); - - return this; - }, - - _updateButtons: function() { - this.backButton.disable((this.darkroom.transformations.length === 0)) - this.forwardButton.disable((this.undoTransformations.length === 0)) - }, - - _onTranformationApplied: function() { - this.undoTransformations = []; - this._updateButtons(); - } - }); - - }; - - return {DarkroomPluginHistory: DarkroomPluginHistory}; - + }; + + return {DarkroomPluginHistory: DarkroomPluginHistory}; }); diff --git a/web_widget_darkroom/static/src/js/plugins/darkroom.rotate.js b/web_widget_darkroom/static/src/js/plugins/darkroom.rotate.js index e50a0d5f..0a02629a 100755 --- a/web_widget_darkroom/static/src/js/plugins/darkroom.rotate.js +++ b/web_widget_darkroom/static/src/js/plugins/darkroom.rotate.js @@ -1,70 +1,64 @@ -/* Adapted from https://github.com/MattKetmo/darkroomjs/tree/master/lib/js/plugins - * License https://github.com/MattKetmo/darkroomjs/blob/master/LICENSE - */ +/** +* Copyright 2013 Matthieu Moquet +* Copyright 2016-2017 LasLabs Inc. +* License MIT (https://opensource.org/licenses/MIT) +**/ -odoo.define('web_widget_darkroom.darkroom_rotate', function(require){ - - 'use strict'; - - var DarkroomPluginRotate = function() { +odoo.define('web_widget_darkroom.darkroom_rotate', function() { + 'use strict'; - var Rotation = Darkroom.Transformation.extend({ - applyTransformation: function(canvas, image, next) { - var angle = (image.getAngle() + this.options.angle) % 360; - image.rotate(angle); - - var width, height; - height = Math.abs(image.getWidth()*(Math.sin(angle*Math.PI/180)))+Math.abs(image.getHeight()*(Math.cos(angle*Math.PI/180))); - width = Math.abs(image.getHeight()*(Math.sin(angle*Math.PI/180)))+Math.abs(image.getWidth()*(Math.cos(angle*Math.PI/180))); - - canvas.setWidth(width); - canvas.setHeight(height); - - canvas.centerObject(image); - image.setCoords(); - canvas.renderAll(); - - next(); - } - }); - - Darkroom.plugins['rotate'] = Darkroom.Plugin.extend({ - - initialize: function InitDarkroomRotatePlugin() { - var buttonGroup = this.darkroom.toolbar.createButtonGroup(); - - var leftButton = buttonGroup.createButton({ - image: 'fa fa-undo oe_edit_only', - editOnly: true, + var DarkroomPluginRotate = function() { + var Rotation = Darkroom.Transformation.extend({ + applyTransformation: function(canvas, image, next) { + var angle = (image.getAngle() + this.options.angle) % 360; + image.rotate(angle); + + var height = Math.abs(image.getWidth()*Math.sin(angle*Math.PI/180))+Math.abs(image.getHeight()*Math.cos(angle*Math.PI/180)); + var width = Math.abs(image.getHeight()*Math.sin(angle*Math.PI/180))+Math.abs(image.getWidth()*Math.cos(angle*Math.PI/180)); + + canvas.setWidth(width); + canvas.setHeight(height); + + canvas.centerObject(image); + image.setCoords(); + canvas.renderAll(); + + next(); + }, }); - - var rightButton = buttonGroup.createButton({ - image: 'fa fa-repeat oe_edit_only', - editOnly: true, + + Darkroom.plugins.rotate = Darkroom.Plugin.extend({ + initialize: function InitDarkroomRotatePlugin() { + var buttonGroup = this.darkroom.toolbar.createButtonGroup(); + + var leftButton = buttonGroup.createButton({ + image: 'fa fa-undo oe_edit_only', + editOnly: true, + }); + var rightButton = buttonGroup.createButton({ + image: 'fa fa-repeat oe_edit_only', + editOnly: true, + }); + + leftButton.addEventListener('click', this.rotateLeft.bind(this)); + rightButton.addEventListener('click', this.rotateRight.bind(this)); + }, + + rotateLeft: function rotateLeft() { + this.rotate(-90); + }, + + rotateRight: function rotateRight() { + this.rotate(90); + }, + + rotate: function rotate(angle) { + this.darkroom.applyTransformation( + new Rotation({angle: angle}) + ); + } }); - - leftButton.addEventListener('click', this.rotateLeft.bind(this)); - rightButton.addEventListener('click', this.rotateRight.bind(this)); - }, - - rotateLeft: function rotateLeft() { - this.rotate(-90); - }, - - rotateRight: function rotateRight() { - this.rotate(90); - }, - - rotate: function rotate(angle) { - this.darkroom.applyTransformation( - new Rotation({angle: angle}) - ); - } - - }); - - } + }; - return {DarkroomPluginRotate: DarkroomPluginRotate}; - + return {DarkroomPluginRotate: DarkroomPluginRotate}; }); diff --git a/web_widget_darkroom/static/src/js/plugins/darkroom.zoom.js b/web_widget_darkroom/static/src/js/plugins/darkroom.zoom.js index 65085aee..c4849196 100755 --- a/web_widget_darkroom/static/src/js/plugins/darkroom.zoom.js +++ b/web_widget_darkroom/static/src/js/plugins/darkroom.zoom.js @@ -1,198 +1,148 @@ -/* Copyright 2016 LasLabs Inc. - * License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). - */ - -odoo.define('web_widget_darkroom.darkroom_zoom', function(require){ - - 'use strict'; - - var DarkroomPluginZoom = function(){ - - Darkroom.plugins['zoom'] = Darkroom.Plugin.extend({ - - inZoom: false, - zoomLevel: 0, - zoomFactor: .1, - - initialize: function() { - var self = this; - var buttonGroup = this.darkroom.toolbar.createButtonGroup(); - - this.zoomButton = buttonGroup.createButton({ - image: 'fa fa-search', - }) - this.zoomInButton = buttonGroup.createButton({ - image: 'fa fa-plus', - }) - this.zoomOutButton = buttonGroup.createButton({ - image: 'fa fa-minus', - }) - this.okButton = buttonGroup.createButton({ - image: 'fa fa-check', - type: 'success', - hide: true, - editOnly: true, - }); - this.cancelButton = buttonGroup.createButton({ - image: 'fa fa-times', - type: 'danger', - hide: true - }); - - // Buttons click - this.zoomButton.addEventListener('click', this.toggleZoom.bind(this)); - this.zoomInButton.addEventListener('click', this.zoomIn.bind(this)); - this.zoomOutButton.addEventListener('click', this.zoomOut.bind(this)); - //this.okButton.addEventListener('click', this.saveZoom.bind(this)); - this.cancelButton.addEventListener('click', this.releaseFocus.bind(this)); - - // Canvas events - this.darkroom.canvas.on('mouse:down', this.onMouseDown.bind(this)); - this.darkroom.canvas.on('mouse:move', this.onMouseMove.bind(this)); - this.darkroom.canvas.on('mouse:up', this.onMouseUp.bind(this)); - //this.darkroom.canvas.on('object:moving', this.onObjectMoving.bind(this)); - //this.darkroom.canvas.on('object:scaling', this.onObjectScaling.bind(this)); - $(this.darkroom.canvas.wrapperEl).on('mousewheel', function(event){ - self.onMouseWheel(event); +/** +* Copyright 2013 Matthieu Moquet +* Copyright 2016-2017 LasLabs Inc. +* License MIT (https://opensource.org/licenses/MIT) +**/ + +odoo.define('web_widget_darkroom.darkroom_zoom', function() { + 'use strict'; + + var DarkroomPluginZoom = function() { + Darkroom.plugins.zoom = Darkroom.Plugin.extend({ + inZoom: false, + zoomLevel: 0, + zoomFactor: 0.1, + + initialize: function() { + var self = this; + var buttonGroup = this.darkroom.toolbar.createButtonGroup(); + + this.zoomButton = buttonGroup.createButton({ + image: 'fa fa-search', + }); + this.zoomInButton = buttonGroup.createButton({ + image: 'fa fa-plus', + }); + this.zoomOutButton = buttonGroup.createButton({ + image: 'fa fa-minus', + }); + this.cancelButton = buttonGroup.createButton({ + image: 'fa fa-times', + type: 'danger', + hide: true + }); + + // Button click events + this.zoomButton.addEventListener('click', this.toggleZoom.bind(this)); + this.zoomInButton.addEventListener('click', this.zoomIn.bind(this)); + this.zoomOutButton.addEventListener('click', this.zoomOut.bind(this)); + this.cancelButton.addEventListener('click', this.releaseFocus.bind(this)); + + // Canvas events + this.darkroom.canvas.on('mouse:down', this.onMouseDown.bind(this)); + this.darkroom.canvas.on('mouse:move', this.onMouseMove.bind(this)); + this.darkroom.canvas.on('mouse:up', this.onMouseUp.bind(this)); + $(this.darkroom.canvas.wrapperEl).on('mousewheel', function(event){ + self.onMouseWheel(event); + }); + + this.toggleElements(false); + }, + + toggleZoom: function() { + if (this.hasFocus()) { + this.releaseFocus(); + } else { + this.requireFocus(); + } + }, + + hasFocus: function() { + return this.inZoom; + }, + + releaseFocus: function() { + this.toggleElements(false); + }, + + requireFocus: function() { + this.toggleElements(true); + }, + + toggleElements: function(bool) { + var toggle = bool; + if (typeof bool === 'undefined') { + toggle = !this.hasFocus(); + } + + this.zoomButton.active(toggle); + this.inZoom = toggle; + this.zoomInButton.hide(!toggle); + this.zoomOutButton.hide(!toggle); + this.cancelButton.hide(!toggle); + this.darkroom.canvas.default_cursor = toggle ? 'move' : 'default'; + }, + + zoomIn: function() { + return this.setZoomLevel(this.zoomFactor, this.getCenterPoint()); + }, + + zoomOut: function() { + return this.setZoomLevel(-this.zoomFactor, this.getCenterPoint()); + }, + + // Return fabric.Point object for center of canvas + getCenterPoint: function() { + var center = this.darkroom.canvas.getCenter(); + return new fabric.Point(center.left, center.top); + }, + + // Set internal zoom + setZoomLevel: function(factor, point) { + var zoomLevel = this.zoomLevel + factor; + if (zoomLevel < 0) { + zoomLevel = 0; + } + if (zoomLevel === this.zoomLevel) { + return false; + } + if (point) { + var canvas = this.darkroom.canvas; + // Add one for zero index + canvas.zoomToPoint(point, zoomLevel + 1); + this.zoomLevel = zoomLevel; + } + return true; + }, + + onMouseWheel: function(event) { + if (this.hasFocus() && event && event.originalEvent) { + var modifier = event.originalEvent.wheelDelta < 0 ? -1 : 1; + var pointer = this.darkroom.canvas.getPointer(event.originalEvent); + var mousePoint = new fabric.Point(pointer.x, pointer.y); + this.setZoomLevel(modifier * this.zoomFactor, mousePoint); + return event.preventDefault(); + } + }, + + onMouseDown: function() { + if (this.hasFocus()) { + this.panning = true; + } + }, + + onMouseUp: function() { + this.panning = false; + }, + + onMouseMove: function(event) { + if (this.panning && event && event.e) { + var delta = new fabric.Point(event.e.movementX, event.e.movementY); + this.darkroom.canvas.relativePan(delta); + } + }, }); - - //fabric.util.addListener(fabric.document, 'keydown', this.onKeyDown.bind(this)); - //fabric.util.addListener(fabric.document, 'keyup', this.onKeyUp.bind(this)); - this.toggleElements(false); - - }, - - toggleZoom: function() { - if (this.hasFocus()) { - this.releaseFocus(); - } else { - this.requireFocus(); - } - }, - - hasFocus: function() { - return this.inZoom; - }, - - releaseFocus: function() { - this.toggleElements(false); - }, - - requireFocus: function() { - this.toggleElements(true); - }, - - toggleElements: function(activate) { - if (activate === 'undefined') { - activate = !this.hasFocus(); - } - this.zoomButton.active(!activate); - this.inZoom = activate; - this.zoomInButton.hide(!activate); - this.zoomOutButton.hide(!activate); - this.okButton.hide(!activate); - this.cancelButton.hide(!activate); - this.darkroom.canvas.default_cursor = activate ? "move" : "default"; - }, - - // Return fabric.Point object for center of canvas - getCenterPoint: function() { - var center = this.darkroom.canvas.getCenter(); - return new fabric.Point(center.left, center.top); - }, - - // Set internal zoom - setZoomLevel: function(factor, point) { - var zoomLevel = this.zoomLevel + factor; - if (zoomLevel < 0) zoomLevel = 0; - if (zoomLevel == this.zoomLevel) return false; - console.log('Setting zoom factor'); - console.log(zoomLevel); - console.log(point); - if (point) { - var canvas = this.darkroom.canvas; - canvas.zoomToPoint(point, zoomLevel + 1); // Add one for zero index - this.zoomLevel = zoomLevel; - } - return true; - }, - - getObjectBounds: function() { - var canvas = this.darkroom.canvas; - var objects = canvas.getObjects(); - var top = 0, bottom = 0, left = 0, right = 0; - for (var idx in objects) { - var obj = objects[idx]; - var objRight = obj.left + obj.getWidth(); - var objBottom = obj.top + obj.getHeight(); - if (obj.left < left) left = obj.left; - if (objRight > right) right = objRight; - if (obj.top < top) top = obj.top; - if (objBottom > bottom) bottom = objBottom; - } - return { - top: top, - bottom: bottom, - left: left, - right: right, - height: (bottom - top), - width: (right - left), - } - }, - - zoomIn: function() { - return this.setZoomLevel(this.zoomFactor, this.getCenterPoint()); - }, - - zoomOut: function() { - return this.setZoomLevel(-this.zoomFactor, this.getCenterPoint()); - }, - - onMouseWheel: function(event) { - if (this.hasFocus() && event && event.originalEvent) { - var modifier = event.originalEvent.wheelDelta < 0 ? -1 : 1; - var pointer = this.darkroom.canvas.getPointer(event.originalEvent); - var mousePoint = new fabric.Point(pointer.x, pointer.y); - this.setZoomLevel(modifier * this.zoomFactor, mousePoint); - return event.preventDefault(); - } - }, - - onMouseDown: function(event) { - if (this.hasFocus()) { - this.panning = true; - } - }, - - onMouseUp: function(event) { - this.panning = false; - }, - - onMouseMove: function(event) { - if (this.panning && event && event.e) { - var delta = new fabric.Point(event.e.movementX, - event.e.movementY); - var canvas = this.darkroom.canvas; - var objBounds = this.getObjectBounds(); - var newPoint = new fabric.Point( - -delta.x - canvas.viewportTransform[4], - -delta.y - canvas.viewportTransform[5] - ) - if (newPoint.x < objBounds.left || newPoint.x > objBounds.right) { - return; - } - if (newPoint.y < objBounds.top || newPoint.y > objBounds.bottom) { - return; - } - canvas.absolutePan(newPoint); - //canvas.setCoords(); - } - }, - - }); - - } - - return {DarkroomPluginZoom: DarkroomPluginZoom}; + }; + return {DarkroomPluginZoom: DarkroomPluginZoom}; }); diff --git a/web_widget_darkroom/static/src/js/widget_darkroom.js b/web_widget_darkroom/static/src/js/widget_darkroom.js index 582f8997..c9814b58 100644 --- a/web_widget_darkroom/static/src/js/widget_darkroom.js +++ b/web_widget_darkroom/static/src/js/widget_darkroom.js @@ -1,238 +1,224 @@ -/* Copyright 2016 LasLabs Inc. - * License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). - */ - -odoo.define('web_widget_darkroom.darkroom_widget', function(require){ - "use strict"; - - var core = require('web.core'); - var common = require('web.form_common'); - var session = require('web.session'); - var utils = require('web.utils'); - var framework = require('web.framework'); - var crash_manager = require('web.crash_manager'); - - var QWeb = core.qweb; - var _t = core._t; - - var FieldDarkroomImage = common.AbstractField.extend(common.ReinitializeFieldMixin, { - className: 'darkroom-widget', - template: 'FieldDarkroomImage', - placeholder: "/web/static/src/img/placeholder.png", - darkroom: null, - no_rerender: false, - - defaults: { - // Canvas initialization size - minWidth: 100, - minHeight: 100, - maxWidth: 700, - maxHeight: 500, - - // Plugins options - plugins: { - crop: { - minHeight: 50, - minWidth: 50, - ratio: 1 +/** +* Copyright 2013 Matthieu Moquet +* Copyright 2016-2017 LasLabs Inc. +* License MIT (https://opensource.org/licenses/MIT) +**/ + +odoo.define('web_widget_darkroom.darkroom_widget', function(require) { + 'use strict'; + + var core = require('web.core'); + var common = require('web.form_common'); + var session = require('web.session'); + var utils = require('web.utils'); + var _ = require('_'); + + var QWeb = core.qweb; + + var FieldDarkroomImage = common.AbstractField.extend(common.ReinitializeFieldMixin, { + className: 'darkroom-widget', + template: 'FieldDarkroomImage', + placeholder: "/web/static/src/img/placeholder.png", + darkroom: null, + no_rerender: false, + + defaults: { + // Canvas initialization size + minWidth: 100, + minHeight: 100, + maxWidth: 700, + maxHeight: 500, + + // Plugin options + plugins: { + crop: { + minHeight: 50, + minWidth: 50, + ratio: 1 + }, + }, + }, + + init: function(field_manager, node) { + this._super(field_manager, node); + this.options = _.defaults(this.options, this.defaults); + }, + + _init_darkroom: function() { + if (!this.darkroom) { + this._init_darkroom_icons(); + this._init_darkroom_ui(); + this._init_darkroom_plugins(); + } + }, + + _init_darkroom_icons: function() { + var element = document.createElement('div'); + element.id = 'darkroom-icons'; + element.style.height = 0; + element.style.width = 0; + element.style.position = 'absolute'; + element.style.visibility = 'hidden'; + element.innerHTML = ''; + this.el.appendChild(element); + }, + + _init_darkroom_plugins: function() { + require('web_widget_darkroom.darkroom_crop').DarkroomPluginCrop(); + require('web_widget_darkroom.darkroom_history').DarkroomPluginHistory(); + require('web_widget_darkroom.darkroom_rotate').DarkroomPluginRotate(); + require('web_widget_darkroom.darkroom_zoom').DarkroomPluginZoom(); + }, + + _init_darkroom_ui: function() { + // Button object + function Button(element) { + this.element = element; + } + + Button.prototype = { + addEventListener: function(eventName, listener) { + if (this.element.addEventListener) { + this.element.addEventListener(eventName, listener); + } else if (this.element.attachEvent) { + this.element.attachEvent('on' + eventName, listener); + } + }, + removeEventListener: function(eventName, listener) { + if (this.element.removeEventListener) { + this.element.removeEventListener(eventName, listener); + } else if (this.element.detachEvent) { + this.element.detachEvent('on' + eventName, listener); + } + }, + active: function(bool) { + if (bool) { + this.element.classList.add('darkroom-button-active'); + } else { + this.element.classList.remove('darkroom-button-active'); + } + }, + hide: function(bool) { + if (bool) { + this.element.classList.add('hidden'); + } else { + this.element.classList.remove('hidden'); + } + }, + disable: function(bool) { + this.element.disabled = bool; + }, + }; + + // ButtonGroup object + function ButtonGroup(element) { + this.element = element; + } + + ButtonGroup.prototype = { + createButton: function(options) { + var defaults = { + image: 'fa fa-question-circle', + type: 'default', + group: 'default', + hide: false, + disabled: false, + editOnly: false, + addClass: '', + }; + var optionsMerged = Darkroom.Utils.extend(options, defaults); + + var buttonElement = document.createElement('button'); + buttonElement.type = 'button'; + buttonElement.className = 'darkroom-button darkroom-button-' + optionsMerged.type; + buttonElement.innerHTML = ''; + if (optionsMerged.editOnly) { + buttonElement.classList.add('oe_edit_only'); + } + if (optionsMerged.addClass) { + buttonElement.classList.add(optionsMerged.addClass); + } + this.element.appendChild(buttonElement); + + var button = new Button(buttonElement); + button.hide(optionsMerged.hide); + button.disable(optionsMerged.disabled); + + return button; + } + }; + + // Toolbar object + function Toolbar(element) { + this.element = element; + } + + Toolbar.prototype = { + createButtonGroup: function() { + var buttonGroupElement = document.createElement('div'); + buttonGroupElement.className = 'darkroom-button-group'; + this.element.appendChild(buttonGroupElement); + + return new ButtonGroup(buttonGroupElement); + } + }; + + Darkroom.UI = { + Toolbar: Toolbar, + ButtonGroup: ButtonGroup, + Button: Button, + }; }, - }, - - // Post initialization method - initialize: function() { - // Active crop selection - // this.plugins['crop'].requireFocus(); - // Add custom listener - // this.addEventListener('core:transformation', function() { /* ... */ }); - } - - }, - - init: function(field_manager, node) { - this._super(field_manager, node); - this.options = _.defaults(this.options, this.defaults); - }, - - _init_darkroom_icons: function() { - var element = document.createElement('div'); - element.id = 'darkroom-icons'; - element.style.height = 0; - element.style.width = 0; - element.style.position = 'absolute'; - element.style.visibility = 'hidden'; - element.innerHTML = ''; - this.el.appendChild(element); - }, - - _init_darkroom_plugins: function(){ - require('web_widget_darkroom.darkroom_crop').DarkroomPluginCrop(); - require('web_widget_darkroom.darkroom_history').DarkroomPluginHistory(); - require('web_widget_darkroom.darkroom_rotate').DarkroomPluginRotate(); - require('web_widget_darkroom.darkroom_zoom').DarkroomPluginZoom(); - }, - - _init_darkroom: function() { - if (!this.darkroom) { - this._init_darkroom_icons(); - this._init_darkroom_ui(); - this._init_darkroom_plugins(); - } - }, - - _init_darkroom_ui: function() { - - Darkroom.UI = { - Toolbar: Toolbar, - ButtonGroup: ButtonGroup, - Button: Button, - }; - - // Toolbar object. - function Toolbar(element) { - this.element = element; - } - - Toolbar.prototype = { - createButtonGroup: function(options) { - var buttonGroup = document.createElement('div'); - buttonGroup.className = 'darkroom-button-group'; - this.element.appendChild(buttonGroup); - - return new ButtonGroup(buttonGroup); - } - }; - - // ButtonGroup object. - function ButtonGroup(element) { - this.element = element; - } - - ButtonGroup.prototype = { - createButton: function(options) { - var defaults = { - image: 'fa fa-question-circle', - type: 'default', - group: 'default', - hide: false, - disabled: false, - editOnly: false, - addClass: '', - }; - - options = Darkroom.Utils.extend(options, defaults); - - var buttonElement = document.createElement('button'); - buttonElement.type = 'button'; - buttonElement.className = 'darkroom-button darkroom-button-' + options.type; - buttonElement.innerHTML = ''; - if (options.editOnly) { - buttonElement.classList.add('oe_edit_only'); - } - if (options.addClass) { - buttonElement.classList.add(options.addClass); - } - // buttonElement.innerHTML = ''; - this.element.appendChild(buttonElement); - - var button = new Button(buttonElement); - button.hide(options.hide); - button.disable(options.disabled); - - return button; - } - } - - // Button object. - function Button(element) { - this.element = element; - } - - Button.prototype = { - addEventListener: function(eventName, listener) { - if (this.element.addEventListener){ - this.element.addEventListener(eventName, listener); - } else if (this.element.attachEvent) { - this.element.attachEvent('on' + eventName, listener); - } + + destroy_content: function() { + if (this.darkroom && this.darkroom.containerElement) { + this.darkroom.containerElement.remove(); + this.darkroom = null; + } }, - removeEventListener: function(eventName, listener) { - if (this.element.removeEventListener){ - this.element.removeEventListener(eventName, listener); - } + + set_value: function(value) { + return this._super(value); }, - active: function(value) { - if (value){ - this.element.classList.add('darkroom-button-active'); - this.element.disabled = false; - } else { - this.element.classList.remove('darkroom-button-active'); - this.element.disabled = true; - } + + render_value: function() { + this.destroy_content(); + this._init_darkroom(); + + var url = null; + if (this.get('value') && !utils.is_bin_size(this.get('value'))) { + url = 'data:image/png;base64,' + this.get('value'); + } else if (this.get('value')) { + var id = JSON.stringify(this.view.datarecord.id || null); + var field = this.name; + if (this.options.preview_image) { + field = this.options.preview_image; + } + url = session.url('/web/image', { + model: this.view.dataset.model, + id: id, + field: field, + unique: (this.view.datarecord.__last_update || '').replace(/[^0-9]/g, ''), + }); + } else { + url = this.placeholder; + } + + var $img = $(QWeb.render("FieldBinaryImage-img", {widget: this, url: url})); + this.$el.find('> img').remove(); + this.$el.append($img); + this.darkroom = new Darkroom($img.get(0), this.options); + this.darkroom.widget = this; }, - hide: function(value) { - if (value) - this.element.classList.add('hidden'); - else - this.element.classList.remove('hidden'); + + commit_value: function() { + if (this.darkroom.sourceImage) { + this.set_value(this.darkroom.sourceImage.toDataURL().split(',')[1]); + } }, - disable: function(value) { - this.element.disabled = (value) ? true : false; - } - }; - - }, - - destroy_content: function() { - if (this.darkroom && this.darkroom.containerElement) { - this.darkroom.containerElement.remove(); - this.darkroom = null; - } - }, - - set_value: function(value){ - this.destroy_content(); - return this._super(value); - }, - - render_value: function() { - this._init_darkroom(); - var url; - if (this.get('value') && !utils.is_bin_size(this.get('value'))) { - url = 'data:image/png;base64,' + this.get('value'); - } else if (this.get('value')) { - var id = JSON.stringify(this.view.datarecord.id || null); - var field = this.name; - if (this.options.preview_image) - field = this.options.preview_image; - url = session.url('/web/image', { - model: this.view.dataset.model, - id: id, - field: field, - unique: (this.view.datarecord.__last_update || '').replace(/[^0-9]/g, ''), - }); - } else { - url = this.placeholder; - } - - var $img = $(QWeb.render("FieldBinaryImage-img", { widget: this, url: url })); - this.$el.find('> img').remove(); - this.$el.append($img); - this.darkroom = new Darkroom($img.get(0), this.options); - this.darkroom.widget = this; - }, - - commit_value: function(callback) { - this.set_value( - this.darkroom.sourceImage.toDataURL().split(',')[1] - ); - }, - - }); - - core.form_widget_registry.add("darkroom", FieldDarkroomImage); - - return { - FieldDarkroomImage: FieldDarkroomImage, - } - + }); + + core.form_widget_registry.add("darkroom", FieldDarkroomImage); + + return {FieldDarkroomImage: FieldDarkroomImage}; }); diff --git a/web_widget_darkroom/static/src/js/widget_darkroom.js.orig b/web_widget_darkroom/static/src/js/widget_darkroom.js.orig deleted file mode 100644 index 4b0f04f1..00000000 --- a/web_widget_darkroom/static/src/js/widget_darkroom.js.orig +++ /dev/null @@ -1,246 +0,0 @@ -/* © 2016-TODAY LasLabs Inc. - * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - */ - -odoo.define('web_widget_darkroom.darkroom_widget', function(require){ - "use strict"; - - var core = require('web.core'); - var common = require('web.form_common'); - var session = require('web.session'); - var utils = require('web.utils'); - var framework = require('web.framework'); - var crash_manager = require('web.crash_manager'); - - var QWeb = core.qweb; - var _t = core._t; - - var FieldDarkroomImage = common.AbstractField.extend(common.ReinitializeFieldMixin, { - className: 'darkroom-widget', - template: 'FieldDarkroomImage', - placeholder: "/web/static/src/img/placeholder.png", - darkroom: null, - no_rerender: false, - - _init_darkroom_icons: function() { - var element = document.createElement('div'); - element.id = 'darkroom-icons'; - element.style.height = 0; - element.style.width = 0; - element.style.position = 'absolute'; - element.style.visibility = 'hidden'; - element.innerHTML = ''; - this.el.appendChild(element); - }, - - _init_darkroom_plugins: function(){ - require('web_widget_darkroom.darkroom_crop').DarkroomPluginCrop(); - require('web_widget_darkroom.darkroom_history').DarkroomPluginHistory(); - require('web_widget_darkroom.darkroom_rotate').DarkroomPluginRotate(); - require('web_widget_darkroom.darkroom_zoom').DarkroomPluginZoom(); - require('web_widget_darkroom.darkroom_save').DarkroomPluginSave(); - }, - - _init_darkroom_ui: function() { - - this._init_darkroom_icons(); - - Darkroom.UI = { - Toolbar: Toolbar, - ButtonGroup: ButtonGroup, - Button: Button, - }; - - // Toolbar object. - function Toolbar(element) { - this.element = element; - } - - Toolbar.prototype = { - createButtonGroup: function(options) { - var buttonGroup = document.createElement('div'); - buttonGroup.className = 'darkroom-button-group'; - this.element.appendChild(buttonGroup); - - return new ButtonGroup(buttonGroup); - } - }; - - // ButtonGroup object. - function ButtonGroup(element) { - this.element = element; - } - - ButtonGroup.prototype = { - createButton: function(options) { - var defaults = { - image: 'fa fa-question-circle', - type: 'default', - group: 'default', - hide: false, - disabled: false, - editOnly: false, - addClass: '', - }; - - options = Darkroom.Utils.extend(options, defaults); - - var buttonElement = document.createElement('button'); - buttonElement.type = 'button'; - buttonElement.className = 'darkroom-button darkroom-button-' + options.type; - buttonElement.innerHTML = ''; - if (options.editOnly) { - buttonElement.classList.add('oe_edit_only'); - } -<<<<<<< Updated upstream - if (options.addClass) { - buttonElement.classList.add(options.addClass); - } - // buttonElement.innerHTML = ''; -======= ->>>>>>> Stashed changes - this.element.appendChild(buttonElement); - - var button = new Button(buttonElement); - button.hide(options.hide); - button.disable(options.disabled); - - return button; - } - } - - // Button object. - function Button(element) { - this.element = element; - } - - Button.prototype = { - addEventListener: function(eventName, listener) { - if (this.element.addEventListener){ - this.element.addEventListener(eventName, listener); - } else if (this.element.attachEvent) { - this.element.attachEvent('on' + eventName, listener); - } - }, - removeEventListener: function(eventName, listener) { - if (this.element.removeEventListener){ - this.element.removeEventListener(eventName, listener); - } - }, - active: function(value) { - if (value){ - this.element.classList.add('darkroom-button-active'); - this.element.disabled = false; - } else { - this.element.classList.remove('darkroom-button-active'); - this.element.disabled = true; - } - }, - hide: function(value) { - if (value) - this.element.classList.add('hidden'); - else - this.element.classList.remove('hidden'); - }, - disable: function(value) { - this.element.disabled = (value) ? true : false; - } - }; - - }, - - destroy_content: function() { - console.log('Destroying Darkroom Obj'); - this.darkroom.selfDestroy(); - }, - - render_value: function() { - console.log('Rerendering'); - var url; - if (this.get('value') && !utils.is_bin_size(this.get('value'))) { - url = 'data:image/png;base64,' + this.get('value'); - } else if (this.get('value')) { - var id = JSON.stringify(this.view.datarecord.id || null); - var field = this.name; - if (this.options.preview_image) - field = this.options.preview_image; - url = session.url('/web/image', { - model: this.view.dataset.model, - id: id, - field: field, - unique: (this.view.datarecord.__last_update || '').replace(/[^0-9]/g, ''), - }); - } else { - url = this.placeholder; - } - - var $img = $(QWeb.render("FieldBinaryImage-img", { widget: this, url: url })); - this.$el.find('> img').remove(); - this.$el.append($img); - - if (!this.darkroom) { - this._init_darkroom_ui(); - this._init_darkroom_plugins(); - } - this.darkroom = new Darkroom($img.get(0)); - this.darkroom.widget = this; - }, - - on_save_as: function(e) { - - framework.blockUI(); - var value = this.darkroom.sourceImage.toDataURL(); - var c = crash_manager; - var filename_fieldname = this.node.attrs.filename; - var filename_field = this.view.fields && this.view.fields[filename_fieldname]; - - var filereader = new FileReader(); - filereader.onload = function(upload) { - var data = upload.target.result; - data = data.split(',')[1]; - $.post({ - url: '/web/binary/upload', - - }) - }; - filereader.readAsDataURL(new Blob(value)); - - this.$el.find('form.o_form_darkroom_form input[name=ufile]').val(value); - this.$el.find('form.o_form_darkroom_form input[name=session_id]').val(this.session.session_id); - this.$el.find('form.o_form_darkroom_form').submit(); - - var $form = $(parentEl).find('form'); - $form.find('input[name=ufile]').val(value); - - }, - - init: function(field_manager, node) { - var self = this; - this._super(field_manager, node); - this.binary_value = false; - this.useFileAPI = !!window.FileReader; - this.max_upload_size = 25 * 1024 * 1024; // 25Mo - if (!this.useFileAPI) { - this.fileupload_id = _.uniqueId('oe_fileupload'); - $(window).on(this.fileupload_id, function() { - var args = [].slice.call(arguments).slice(1); - self.on_file_uploaded.apply(self, args); - }); - } - }, - stop: function() { - if (!this.useFileAPI) { - $(window).off(this.fileupload_id); - } - this._super.apply(this, arguments); - }, - - }); - - core.form_widget_registry.add("darkroom", FieldDarkroomImage); - - return { - FieldDarkroomImage: FieldDarkroomImage, - } - -}); diff --git a/web_widget_darkroom/static/src/js/widget_darkroom_modal.js b/web_widget_darkroom/static/src/js/widget_darkroom_modal.js new file mode 100644 index 00000000..118fd102 --- /dev/null +++ b/web_widget_darkroom/static/src/js/widget_darkroom_modal.js @@ -0,0 +1,64 @@ +/** +* Copyright 2017 LasLabs Inc. +* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +**/ + +odoo.define('web_widget_darkroom.darkroom_modal_button', function(require) { + 'use strict'; + + var core = require('web.core'); + var DataModel = require('web.DataModel'); + + core.form_widget_registry.get('image').include({ + // Used in template to prevent Darkroom buttons from being added to + // forms for new records, which are not supported + darkroom_supported: function() { + if (this.field_manager.dataset.index === null) { + return false; + } + return true; + }, + + render_value: function() { + this._super(); + + var imageWidget = this; + var activeModel = imageWidget.field_manager.dataset._model.name; + var activeRecordId = imageWidget.field_manager.datarecord.id; + var activeField = imageWidget.node.attrs.name; + + var updateImage = function() { + var ActiveModel = new DataModel(activeModel); + ActiveModel.query([activeField]). + filter([['id', '=', activeRecordId]]). + all(). + then(function(result) { + imageWidget.set_value(result[0].image); + }); + }; + + var openModal = function() { + var context = { + active_model: activeModel, + active_record_id: activeRecordId, + active_field: activeField, + }; + var modalAction = { + type: 'ir.actions.act_window', + res_model: 'darkroom.modal', + name: 'Darkroom', + views: [[false, 'form']], + target: 'new', + context: context, + }; + var options = {on_close: updateImage}; + imageWidget.do_action(modalAction, options); + }; + + var $button = this.$('.oe_form_binary_image_darkroom_modal'); + if ($button.length > 0) { + $button.click(openModal); + } + }, + }); +}); diff --git a/web_widget_darkroom/static/src/less/darkroom.less b/web_widget_darkroom/static/src/less/darkroom.less new file mode 100755 index 00000000..1c8a1b85 --- /dev/null +++ b/web_widget_darkroom/static/src/less/darkroom.less @@ -0,0 +1,11 @@ +.darkroom-button-group { + display: inline; +} + +.darkroom-button-active { + color: @odoo-brand-primary; +} + +.oe_form_field_image_controls i { + margin: 0 5%; +} diff --git a/web_widget_darkroom/static/src/xml/field_templates.xml b/web_widget_darkroom/static/src/xml/field_templates.xml index 746426be..2692cb7e 100644 --- a/web_widget_darkroom/static/src/xml/field_templates.xml +++ b/web_widget_darkroom/static/src/xml/field_templates.xml @@ -1,17 +1,30 @@ - + -
+
+ + + + + + + + + + + + + + diff --git a/web_widget_darkroom/tests/__init__.py b/web_widget_darkroom/tests/__init__.py new file mode 100644 index 00000000..3773eddd --- /dev/null +++ b/web_widget_darkroom/tests/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from . import test_darkroom_modal diff --git a/web_widget_darkroom/tests/test_darkroom_modal.py b/web_widget_darkroom/tests/test_darkroom_modal.py new file mode 100644 index 00000000..f299939e --- /dev/null +++ b/web_widget_darkroom/tests/test_darkroom_modal.py @@ -0,0 +1,203 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from openerp.tests.common import TransactionCase + + +class TestDarkroomModal(TransactionCase): + + def test_default_res_model_id_model_in_context(self): + """Should return correct ir.model record when context has model name""" + active_model = 'res.users' + test_model = self.env['darkroom.modal'].with_context({ + 'active_model': active_model, + }) + test_result = test_model._default_res_model_id() + + expected = self.env['ir.model'].search([('model', '=', active_model)]) + self.assertEqual(test_result, expected) + + def test_default_res_model_id_no_valid_info_in_context(self): + """Should return empty ir.model recordset when missing/invalid info""" + test_model = self.env['darkroom.modal'].with_context({}) + test_result = test_model._default_res_model_id() + + self.assertEqual(test_result, self.env['ir.model']) + + def test_default_res_record_id_id_in_context(self): + """Should return correct value when ID in context""" + active_record_id = 5 + test_model = self.env['darkroom.modal'].with_context({ + 'active_record_id': active_record_id, + }) + test_result = test_model._default_res_record_id() + + self.assertEqual(test_result, active_record_id) + + def test_default_res_record_id_no_id_in_context(self): + """Should return 0 when no ID in context""" + test_model = self.env['darkroom.modal'].with_context({}) + test_result = test_model._default_res_record_id() + + self.assertEqual(test_result, 0) + + def test_default_res_record_model_and_id_in_context(self): + """Should return correct record when context has model name and ID""" + active_model = 'res.users' + active_record_id = 1 + test_model = self.env['darkroom.modal'].with_context({ + 'active_model': active_model, + 'active_record_id': active_record_id, + }) + test_result = test_model._default_res_record() + + expected = self.env[active_model].browse(active_record_id) + self.assertEqual(test_result, expected) + + def test_default_res_record_model_but_no_id_in_context(self): + """Should return right empty recordset if model but no ID in context""" + active_model = 'res.users' + test_model = self.env['darkroom.modal'].with_context({ + 'active_model': active_model, + }) + test_result = test_model._default_res_record() + + self.assertEqual(test_result, self.env[active_model]) + + def test_default_res_record_no_valid_model_info_in_context(self): + """Should return None if context has missing/invalid model info""" + active_model = 'bad.model.name' + test_model = self.env['darkroom.modal'].with_context({ + 'active_model': active_model, + }) + test_result = test_model._default_res_record() + + self.assertIsNone(test_result) + + def test_default_res_field_id_model_and_field_in_context(self): + """Should return correct ir.model.fields record when info in context""" + active_model = 'res.users' + active_field = 'name' + test_model = self.env['darkroom.modal'].with_context({ + 'active_model': active_model, + 'active_field': active_field, + }) + test_result = test_model._default_res_field_id() + + self.assertEqual(test_result.name, active_field) + self.assertEqual(test_result.model_id.model, active_model) + + def test_default_res_field_id_no_valid_field_in_context(self): + """Should return empty recordset if field info missing/invalid""" + active_model = 'res.users' + active_field = 'totally.not.a.real.field.name' + test_model = self.env['darkroom.modal'].with_context({ + 'active_model': active_model, + 'active_field': active_field, + }) + test_result = test_model._default_res_field_id() + + self.assertEqual(test_result, self.env['ir.model.fields']) + + def test_default_res_field_id_no_valid_model_in_context(self): + """Should return empty recordset if model info missing/invalid""" + active_field = 'name' + test_model = self.env['darkroom.modal'].with_context({ + 'active_field': active_field, + }) + test_result = test_model._default_res_field_id() + + self.assertEqual(test_result, self.env['ir.model.fields']) + + def test_default_image_all_info_in_context(self): + """Should return value of correct field if all info in context""" + active_model = 'res.users' + active_record_id = 1 + active_field = 'name' + test_model = self.env['darkroom.modal'].with_context({ + 'active_model': active_model, + 'active_record_id': active_record_id, + 'active_field': active_field, + }) + test_result = test_model._default_image() + + expected = self.env[active_model].browse(active_record_id).name + self.assertEqual(test_result, expected) + + def test_default_image_no_valid_field_in_context(self): + """Should return None if missing/invalid field info in context""" + active_model = 'res.users' + active_record_id = 1 + test_model = self.env['darkroom.modal'].with_context({ + 'active_model': active_model, + 'active_record_id': active_record_id, + }) + test_result = test_model._default_image() + + self.assertIsNone(test_result) + + def test_default_image_no_valid_id_in_context(self): + """Should return False/None if missing/invalid record ID in context""" + active_model = 'res.users' + active_field = 'name' + test_model = self.env['darkroom.modal'].with_context({ + 'active_model': active_model, + 'active_field': active_field, + }) + test_result = test_model._default_image() + + self.assertFalse(test_result) + + def test_default_image_no_valid_model_in_context(self): + """Should return None if missing/invalid model info in context""" + active_record_id = 1 + active_field = 'name' + test_model = self.env['darkroom.modal'].with_context({ + 'active_record_id': active_record_id, + 'active_field': active_field, + }) + test_result = test_model._default_image() + + self.assertIsNone(test_result) + + def test_action_save_record_count_in_self(self): + """Should raise correct error if not called on recordset of 1""" + test_wizard = self.env['darkroom.modal'].with_context({ + 'active_model': 'res.users', + 'active_record_id': 1, + 'active_field': 'name', + }).create({}) + test_wizard_set = test_wizard + test_wizard.copy() + + with self.assertRaises(ValueError): + self.env['darkroom.modal'].action_save() + with self.assertRaises(ValueError): + test_wizard_set.action_save() + + def test_action_save_update_source(self): + """Should update source record correctly""" + active_model = 'res.users' + active_record_id = 1 + test_wizard = self.env['darkroom.modal'].with_context({ + 'active_model': active_model, + 'active_record_id': active_record_id, + 'active_field': 'name', + }).create({}) + test_name = 'Test Name' + test_wizard.image = test_name + test_wizard.action_save() + + result = self.env[active_model].browse(active_record_id).name + self.assertEqual(result, test_name) + + def test_action_save_return_action(self): + """Should return correct action""" + test_wizard = self.env['darkroom.modal'].with_context({ + 'active_model': 'res.users', + 'active_record_id': 1, + 'active_field': 'name', + }).create({}) + test_value = test_wizard.action_save() + + self.assertEqual(test_value, {'type': 'ir.actions.act_window_close'}) diff --git a/web_widget_darkroom/views/assets.xml b/web_widget_darkroom/views/assets.xml index 7b57c7a1..87e6a18a 100644 --- a/web_widget_darkroom/views/assets.xml +++ b/web_widget_darkroom/views/assets.xml @@ -1,31 +1,28 @@