web_widget_darkroom: Modal, fixes, cleanup * Fix bugs involving the crop and pan functionality by modifying crop and zoom plugins and Darkroom widget * Add Darkroom modal to normal image widget, using darkroom.modal wizard model to provide backend support for modal view * Remove res.users view changes introduced for demo purposes (not needed due to modal functionality) * Clean up existing code, removing many unnecessary DarkroomJS files
|
@ -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
|
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
|
||||||
:alt: License: LGPL-3
|
: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
|
.. _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
|
* Zoom and pan
|
||||||
* Rotate
|
* Rotate
|
||||||
* Crop
|
* Crop
|
||||||
* Step back in history client-side (before save)
|
* Step back in history client-side (before save)
|
||||||
|
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
=====
|
=====
|
||||||
|
|
||||||
To use this module, you need to:
|
After installing the module, you can use it in the following ways:
|
||||||
|
|
||||||
* Install web_widget_darkroom
|
* Specify the ``darkroom`` widget when adding an image field to a view.
|
||||||
* Add the to any One2many image relation by using the `darkroom` widget. Options can be passed through to Darkroom using the `options` key::
|
Configuration values can be provided using the ``options`` attribute::
|
||||||
|
|
||||||
<field name="image_id" widget="darkroom"
|
<field name="image" widget="darkroom" options="{'minWidth': 100}"/>
|
||||||
options="{'minWidth': 100}" />
|
|
||||||
|
|
||||||
The Odoo DarkroomJS widget passes options directly through to Darkroom, which are copied from the source below::
|
The widget passes options directly through to DarkroomJS, which supports the
|
||||||
|
following:
|
||||||
|
|
||||||
// Default options
|
* minWidth
|
||||||
defaults: {
|
* minHeight
|
||||||
// Canvas properties (dimension, ratio, color)
|
* maxWidth
|
||||||
minWidth: null,
|
* maxHeight
|
||||||
minHeight: null,
|
* ratio (aspect ratio)
|
||||||
maxWidth: null,
|
* backgroundColor
|
||||||
maxHeight: null,
|
|
||||||
ratio: null,
|
|
||||||
backgroundColor: '#fff',
|
|
||||||
|
|
||||||
// Plugins options
|
* Open a form view that contains an image in edit mode and hover over the
|
||||||
plugins: {},
|
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.
|
||||||
|
|
||||||
// Post-initialisation callback
|
.. image:: /web_widget_darkroom/static/description/modal_screenshot_1.png
|
||||||
initialize: function() { /* noop */ }
|
: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
|
||||||
|
======================
|
||||||
|
|
||||||
Known Issues/Roadmap
|
* Darkroom modals are currently not supported during record creation
|
||||||
====================
|
|
||||||
|
|
||||||
* 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.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Bug Tracker
|
Bug Tracker
|
||||||
===========
|
===========
|
||||||
|
|
||||||
Bugs are tracked on `GitHub Issues
|
Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/issues>`_. In
|
||||||
<https://github.com/OCA/{project_repo}/issues>`_. In case of trouble, please
|
case of trouble, please check there if your issue has already been reported.
|
||||||
check there if your issue has already been reported. If you spotted it first,
|
If you spotted it first, help us smash it by providing detailed and welcome
|
||||||
help us smashing it by providing a detailed and welcomed feedback.
|
feedback.
|
||||||
|
|
||||||
Credits
|
Credits
|
||||||
=======
|
=======
|
||||||
|
@ -72,12 +74,14 @@ Credits
|
||||||
Images
|
Images
|
||||||
------
|
------
|
||||||
|
|
||||||
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
|
* Odoo Community Association:
|
||||||
|
`Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
|
||||||
|
|
||||||
Contributors
|
Contributors
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* Dave Lasley <dave@laslabs.com>
|
* Dave Lasley <dave@laslabs.com>
|
||||||
|
* Oleg Bulkin <obulkin@laslabs.com>
|
||||||
|
|
||||||
Maintainer
|
Maintainer
|
||||||
----------
|
----------
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- 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).
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
|
||||||
|
|
||||||
|
from . import wizards
|
||||||
|
|
|
@ -1,28 +1,26 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- 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).
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "Web Darkroom Image Widget",
|
'name': 'Web DarkroomJS Image Editing',
|
||||||
"summary": "Widget provides a dynamic, editable canvas for use on any"
|
'summary': 'Provides web widget for image editing and adds it to standard'
|
||||||
" One2many image field in backend form views.",
|
' image widget as modal',
|
||||||
"version": "9.0.1.0.1",
|
'version': '9.0.1.0.1',
|
||||||
"category": "Web",
|
'category': 'Web',
|
||||||
"website": "https://laslabs.com/",
|
'website': 'https://laslabs.com/',
|
||||||
"author": "LasLabs, Odoo Community Association (OCA)",
|
'author': 'LasLabs, Odoo Community Association (OCA)',
|
||||||
"license": "LGPL-3",
|
'license': 'LGPL-3',
|
||||||
"application": False,
|
'application': False,
|
||||||
"installable": True,
|
'installable': True,
|
||||||
"depends": [
|
'depends': [
|
||||||
"web",
|
'web',
|
||||||
],
|
],
|
||||||
"data": [
|
'data': [
|
||||||
'views/assets.xml',
|
'views/assets.xml',
|
||||||
|
'wizards/darkroom_modal.xml',
|
||||||
],
|
],
|
||||||
'qweb': [
|
'qweb': [
|
||||||
"static/src/xml/field_templates.xml",
|
'static/src/xml/field_templates.xml',
|
||||||
],
|
],
|
||||||
'demo': [
|
|
||||||
'demo/res_users.xml',
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Copyright 2016 LasLabs Inc.
|
|
||||||
License LGPL-3 or later (http://www.gnu.org/licenses/lgpl.html).
|
|
||||||
-->
|
|
||||||
|
|
||||||
<openerp>
|
|
||||||
<data>
|
|
||||||
<record id="view_users_form" model="ir.ui.view">
|
|
||||||
<field name="name">res.users.form.darkroom</field>
|
|
||||||
<field name="model">res.users</field>
|
|
||||||
<field name="inherit_id" ref="base.view_users_form" />
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<xpath expr="//notebook" position="inside">
|
|
||||||
<page string="Darkroom">
|
|
||||||
<field name="image" widget="darkroom" />
|
|
||||||
</page>
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
</data>
|
|
||||||
</openerp>
|
|
||||||
|
|
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 56 KiB |
|
@ -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
|
|
|
@ -1,5 +0,0 @@
|
||||||
/.sass-cache/
|
|
||||||
/bower_components/
|
|
||||||
/build/
|
|
||||||
/node_modules/
|
|
||||||
/docs/
|
|
|
@ -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
|
|
|
@ -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.
|
|
|
@ -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
|
|
||||||
<img src="some-image.jpg" id="target">
|
|
||||||
<script>
|
|
||||||
new Darkroom('#target');
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
|
@ -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 <matthieu@moquet.net>"
|
|
||||||
],
|
|
||||||
"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"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})();
|
|
@ -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;
|
||||||
|
};
|
||||||
|
})();
|
|
@ -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;
|
||||||
|
};
|
||||||
|
})();
|
|
@ -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))),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})();
|
|
@ -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 -
|
|
|
@ -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))
|
|
||||||
})
|
|
|
@ -1,12 +0,0 @@
|
||||||
.darkroom-container {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.darkroom-image-container {
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.darkroom-image-container img {
|
|
||||||
// display: none;
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
@import 'layout';
|
|
||||||
@import 'toolbar';
|
|
|
@ -1,4 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
|
||||||
<path d="M19 6.41l-1.41-1.41-5.59 5.59-5.59-5.59-1.41 1.41 5.59 5.59-5.59 5.59 1.41 1.41 5.59-5.59 5.59 5.59 1.41-1.41-5.59-5.59z"/>
|
|
||||||
<path d="M0 0h24v24h-24z" fill="none"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 272 B |
|
@ -1,4 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
|
||||||
<path d="M0 0h24v24h-24z" fill="none"/>
|
|
||||||
<path d="M17 15h2v-8c0-1.1-.9-2-2-2h-8v2h8v8zm-10 2v-16h-2v4h-4v2h4v10c0 1.1.9 2 2 2h10v4h2v-4h4v-2h-16z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 247 B |
|
@ -1,4 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
|
||||||
<path d="M0 0h24v24h-24z" fill="none"/>
|
|
||||||
<path d="M9 16.17l-4.17-4.17-1.42 1.41 5.59 5.59 12-12-1.41-1.41z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 208 B |
|
@ -1,4 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
|
||||||
<path d="M0 0h24v24h-24z" fill="none"/>
|
|
||||||
<path d="M18.4 10.6c-1.85-1.61-4.25-2.6-6.9-2.6-4.65 0-8.58 3.03-9.96 7.22l2.36.78c1.05-3.19 4.05-5.5 7.6-5.5 1.95 0 3.73.72 5.12 1.88l-3.62 3.62h9v-9l-3.6 3.6z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 303 B |
|
@ -1,4 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
|
||||||
<path d="M0 0h24v24h-24z" fill="none"/>
|
|
||||||
<path d="M7.11 8.53l-1.41-1.42c-.9 1.16-1.46 2.5-1.63 3.89h2.02c.14-.87.49-1.72 1.02-2.47zm-1.02 4.47h-2.02c.17 1.39.72 2.73 1.62 3.89l1.41-1.42c-.52-.75-.87-1.59-1.01-2.47zm1.01 5.32c1.16.9 2.51 1.44 3.9 1.61v-2.03c-.87-.15-1.71-.49-2.46-1.03l-1.44 1.45zm5.9-14.25v-3.07l-4.55 4.55 4.55 4.45v-3.91c2.84.48 5 2.94 5 5.91s-2.16 5.43-5 5.91v2.02c3.95-.49 7-3.85 7-7.93s-3.05-7.44-7-7.93z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 528 B |
|
@ -1,4 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
|
||||||
<path d="M0 0h24v24h-24z" fill="none"/>
|
|
||||||
<path d="M15.55 5.55l-4.55-4.55v3.07c-3.94.49-7 3.85-7 7.93s3.05 7.44 7 7.93v-2.02c-2.84-.48-5-2.94-5-5.91s2.16-5.43 5-5.91v3.91l4.55-4.45zm4.38 5.45c-.17-1.39-.72-2.73-1.62-3.89l-1.42 1.42c.54.75.88 1.6 1.02 2.47h2.02zm-6.93 6.9v2.02c1.39-.17 2.74-.71 3.9-1.61l-1.44-1.44c-.75.54-1.59.89-2.46 1.03zm3.89-2.42l1.42 1.41c.9-1.16 1.45-2.5 1.62-3.89h-2.02c-.14.87-.48 1.72-1.02 2.48z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 523 B |
|
@ -1,4 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
|
||||||
<path d="M0 0h24v24h-24z" fill="none"/>
|
|
||||||
<path d="M17 3h-12c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-12l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10h-10v-4h10v4z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 301 B |
|
@ -1,4 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
|
||||||
<path d="M0 0h24v24h-24z" fill="none"/>
|
|
||||||
<path d="M12.5 8c-2.65 0-5.05.99-6.9 2.6l-3.6-3.6v9h9l-3.62-3.62c1.39-1.16 3.16-1.88 5.12-1.88 3.54 0 6.55 2.31 7.6 5.5l2.37-.78c-1.39-4.19-5.32-7.22-9.97-7.22z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 303 B |
|
@ -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 = '<!-- inject:svg --><!-- endinject -->';
|
|
||||||
document.body.appendChild(element);
|
|
||||||
|
|
||||||
})();
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
})();
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
})();
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
})();
|
|
|
@ -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 = '<svg class="darkroom-icon"><use xlink:href="#' + options.image + '" /></svg>';
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
|
@ -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))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
})();
|
|
|
@ -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');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
|
@ -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);
|
|
|
@ -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})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
|
@ -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));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
|
@ -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 <matthieu@moquet.net> (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"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
/*.darkroom-container{
|
|
||||||
padding-top: 50px;
|
|
||||||
}
|
|
||||||
.darkroom-toolbar{
|
|
||||||
top: 5px;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
.darkroom-button-group{
|
|
||||||
display: inline;
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
|
|
||||||
});
|
|
|
@ -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
|
* Copyright 2013 Matthieu Moquet
|
||||||
*/
|
* Copyright 2016-2017 LasLabs Inc.
|
||||||
|
* License MIT (https://opensource.org/licenses/MIT)
|
||||||
|
**/
|
||||||
|
|
||||||
odoo.define('web_widget_darkroom.darkroom_history', function(require){
|
odoo.define('web_widget_darkroom.darkroom_history', function() {
|
||||||
|
'use strict';
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var DarkroomPluginHistory = function() {
|
var DarkroomPluginHistory = function() {
|
||||||
|
Darkroom.plugins.history = Darkroom.Plugin.extend({
|
||||||
Darkroom.plugins['history'] = Darkroom.Plugin.extend({
|
undoTransformations: [],
|
||||||
undoTransformations: [],
|
|
||||||
|
initialize: function InitDarkroomHistoryPlugin() {
|
||||||
initialize: function InitDarkroomHistoryPlugin() {
|
this._initButtons();
|
||||||
this._initButtons();
|
this.darkroom.addEventListener('core:transformation', this._onTranformationApplied.bind(this));
|
||||||
|
},
|
||||||
this.darkroom.addEventListener('core:transformation', this._onTranformationApplied.bind(this));
|
|
||||||
},
|
undo: function() {
|
||||||
|
if (this.darkroom.transformations.length === 0) {
|
||||||
undo: function() {
|
return;
|
||||||
if (this.darkroom.transformations.length === 0) {
|
}
|
||||||
return;
|
|
||||||
}
|
var lastTransformation = this.darkroom.transformations.pop();
|
||||||
|
this.undoTransformations.unshift(lastTransformation);
|
||||||
var lastTransformation = this.darkroom.transformations.pop();
|
|
||||||
this.undoTransformations.unshift(lastTransformation);
|
this.darkroom.reinitializeImage();
|
||||||
|
this._updateButtons();
|
||||||
this.darkroom.reinitializeImage();
|
},
|
||||||
this._updateButtons();
|
|
||||||
},
|
redo: function() {
|
||||||
|
if (this.undoTransformations.length === 0) {
|
||||||
redo: function() {
|
return;
|
||||||
if (this.undoTransformations.length === 0) {
|
}
|
||||||
return;
|
|
||||||
}
|
var cancelTransformation = this.undoTransformations.shift();
|
||||||
|
this.darkroom.transformations.push(cancelTransformation);
|
||||||
var cancelTransformation = this.undoTransformations.shift();
|
|
||||||
this.darkroom.transformations.push(cancelTransformation);
|
this.darkroom.reinitializeImage();
|
||||||
|
this._updateButtons();
|
||||||
this.darkroom.reinitializeImage();
|
},
|
||||||
this._updateButtons();
|
|
||||||
},
|
_initButtons: function() {
|
||||||
|
var buttonGroup = this.darkroom.toolbar.createButtonGroup();
|
||||||
_initButtons: function() {
|
|
||||||
var buttonGroup = this.darkroom.toolbar.createButtonGroup();
|
this.backButton = buttonGroup.createButton({
|
||||||
|
image: 'fa fa-step-backward',
|
||||||
this.backButton = buttonGroup.createButton({
|
disabled: true,
|
||||||
image: 'fa fa-step-backward',
|
editOnly: true,
|
||||||
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.forwardButton = buttonGroup.createButton({
|
|
||||||
image: 'fa fa-step-forward',
|
return {DarkroomPluginHistory: DarkroomPluginHistory};
|
||||||
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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
return {DarkroomPluginHistory: DarkroomPluginHistory};
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -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){
|
odoo.define('web_widget_darkroom.darkroom_rotate', function() {
|
||||||
|
'use strict';
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var DarkroomPluginRotate = function() {
|
|
||||||
|
|
||||||
var Rotation = Darkroom.Transformation.extend({
|
var DarkroomPluginRotate = function() {
|
||||||
applyTransformation: function(canvas, image, next) {
|
var Rotation = Darkroom.Transformation.extend({
|
||||||
var angle = (image.getAngle() + this.options.angle) % 360;
|
applyTransformation: function(canvas, image, next) {
|
||||||
image.rotate(angle);
|
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)));
|
var 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)));
|
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.setWidth(width);
|
||||||
canvas.setHeight(height);
|
canvas.setHeight(height);
|
||||||
|
|
||||||
canvas.centerObject(image);
|
canvas.centerObject(image);
|
||||||
image.setCoords();
|
image.setCoords();
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
|
|
||||||
next();
|
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 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})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return {DarkroomPluginRotate: DarkroomPluginRotate};
|
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})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {DarkroomPluginRotate: DarkroomPluginRotate};
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,198 +1,148 @@
|
||||||
/* Copyright 2016 LasLabs Inc.
|
/**
|
||||||
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
|
* Copyright 2013 Matthieu Moquet
|
||||||
*/
|
* Copyright 2016-2017 LasLabs Inc.
|
||||||
|
* License MIT (https://opensource.org/licenses/MIT)
|
||||||
|
**/
|
||||||
|
|
||||||
odoo.define('web_widget_darkroom.darkroom_zoom', function(require){
|
odoo.define('web_widget_darkroom.darkroom_zoom', function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
'use strict';
|
var DarkroomPluginZoom = function() {
|
||||||
|
Darkroom.plugins.zoom = Darkroom.Plugin.extend({
|
||||||
|
inZoom: false,
|
||||||
|
zoomLevel: 0,
|
||||||
|
zoomFactor: 0.1,
|
||||||
|
|
||||||
var DarkroomPluginZoom = function(){
|
initialize: function() {
|
||||||
|
var self = this;
|
||||||
Darkroom.plugins['zoom'] = Darkroom.Plugin.extend({
|
var buttonGroup = this.darkroom.toolbar.createButtonGroup();
|
||||||
|
|
||||||
inZoom: false,
|
this.zoomButton = buttonGroup.createButton({
|
||||||
zoomLevel: 0,
|
image: 'fa fa-search',
|
||||||
zoomFactor: .1,
|
});
|
||||||
|
this.zoomInButton = buttonGroup.createButton({
|
||||||
initialize: function() {
|
image: 'fa fa-plus',
|
||||||
var self = this;
|
});
|
||||||
var buttonGroup = this.darkroom.toolbar.createButtonGroup();
|
this.zoomOutButton = buttonGroup.createButton({
|
||||||
|
image: 'fa fa-minus',
|
||||||
this.zoomButton = buttonGroup.createButton({
|
});
|
||||||
image: 'fa fa-search',
|
this.cancelButton = buttonGroup.createButton({
|
||||||
})
|
image: 'fa fa-times',
|
||||||
this.zoomInButton = buttonGroup.createButton({
|
type: 'danger',
|
||||||
image: 'fa fa-plus',
|
hide: true
|
||||||
})
|
});
|
||||||
this.zoomOutButton = buttonGroup.createButton({
|
|
||||||
image: 'fa fa-minus',
|
// Button click events
|
||||||
})
|
this.zoomButton.addEventListener('click', this.toggleZoom.bind(this));
|
||||||
this.okButton = buttonGroup.createButton({
|
this.zoomInButton.addEventListener('click', this.zoomIn.bind(this));
|
||||||
image: 'fa fa-check',
|
this.zoomOutButton.addEventListener('click', this.zoomOut.bind(this));
|
||||||
type: 'success',
|
this.cancelButton.addEventListener('click', this.releaseFocus.bind(this));
|
||||||
hide: true,
|
|
||||||
editOnly: true,
|
// 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);
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
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);
|
|
||||||
});
|
|
||||||
|
|
||||||
//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};
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,238 +1,224 @@
|
||||||
/* Copyright 2016 LasLabs Inc.
|
/**
|
||||||
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
|
* 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){
|
odoo.define('web_widget_darkroom.darkroom_widget', function(require) {
|
||||||
"use strict";
|
'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
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// 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 = '<!-- inject:svg --><!-- endinject -->';
|
|
||||||
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() {
|
var core = require('web.core');
|
||||||
if (!this.darkroom) {
|
var common = require('web.form_common');
|
||||||
this._init_darkroom_icons();
|
var session = require('web.session');
|
||||||
this._init_darkroom_ui();
|
var utils = require('web.utils');
|
||||||
this._init_darkroom_plugins();
|
var _ = require('_');
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_init_darkroom_ui: function() {
|
|
||||||
|
|
||||||
Darkroom.UI = {
|
var QWeb = core.qweb;
|
||||||
Toolbar: Toolbar,
|
|
||||||
ButtonGroup: ButtonGroup,
|
var FieldDarkroomImage = common.AbstractField.extend(common.ReinitializeFieldMixin, {
|
||||||
Button: Button,
|
className: 'darkroom-widget',
|
||||||
};
|
template: 'FieldDarkroomImage',
|
||||||
|
placeholder: "/web/static/src/img/placeholder.png",
|
||||||
// Toolbar object.
|
darkroom: null,
|
||||||
function Toolbar(element) {
|
no_rerender: false,
|
||||||
this.element = element;
|
|
||||||
}
|
defaults: {
|
||||||
|
// Canvas initialization size
|
||||||
Toolbar.prototype = {
|
minWidth: 100,
|
||||||
createButtonGroup: function(options) {
|
minHeight: 100,
|
||||||
var buttonGroup = document.createElement('div');
|
maxWidth: 700,
|
||||||
buttonGroup.className = 'darkroom-button-group';
|
maxHeight: 500,
|
||||||
this.element.appendChild(buttonGroup);
|
|
||||||
|
// Plugin options
|
||||||
return new ButtonGroup(buttonGroup);
|
plugins: {
|
||||||
}
|
crop: {
|
||||||
};
|
minHeight: 50,
|
||||||
|
minWidth: 50,
|
||||||
// ButtonGroup object.
|
ratio: 1
|
||||||
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 = '<i class="' + options.image + ' fa-2x"></i>';
|
|
||||||
if (options.editOnly) {
|
|
||||||
buttonElement.classList.add('oe_edit_only');
|
|
||||||
}
|
|
||||||
if (options.addClass) {
|
|
||||||
buttonElement.classList.add(options.addClass);
|
|
||||||
}
|
|
||||||
// buttonElement.innerHTML = '<svg class="darkroom-icon"><use xlink:href="#' + options.image + '" /></svg>';
|
|
||||||
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){
|
init: function(field_manager, node) {
|
||||||
this.element.removeEventListener(eventName, listener);
|
this._super(field_manager, node);
|
||||||
}
|
this.options = _.defaults(this.options, this.defaults);
|
||||||
},
|
},
|
||||||
active: function(value) {
|
|
||||||
if (value){
|
_init_darkroom: function() {
|
||||||
this.element.classList.add('darkroom-button-active');
|
if (!this.darkroom) {
|
||||||
this.element.disabled = false;
|
this._init_darkroom_icons();
|
||||||
} else {
|
this._init_darkroom_ui();
|
||||||
this.element.classList.remove('darkroom-button-active');
|
this._init_darkroom_plugins();
|
||||||
this.element.disabled = true;
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
hide: function(value) {
|
|
||||||
if (value)
|
_init_darkroom_icons: function() {
|
||||||
this.element.classList.add('hidden');
|
var element = document.createElement('div');
|
||||||
else
|
element.id = 'darkroom-icons';
|
||||||
this.element.classList.remove('hidden');
|
element.style.height = 0;
|
||||||
|
element.style.width = 0;
|
||||||
|
element.style.position = 'absolute';
|
||||||
|
element.style.visibility = 'hidden';
|
||||||
|
element.innerHTML = '<!-- inject:svg --><!-- endinject -->';
|
||||||
|
this.el.appendChild(element);
|
||||||
},
|
},
|
||||||
disable: function(value) {
|
|
||||||
this.element.disabled = (value) ? true : false;
|
_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();
|
||||||
|
},
|
||||||
destroy_content: function() {
|
|
||||||
if (this.darkroom && this.darkroom.containerElement) {
|
_init_darkroom_ui: function() {
|
||||||
this.darkroom.containerElement.remove();
|
// Button object
|
||||||
this.darkroom = null;
|
function Button(element) {
|
||||||
}
|
this.element = element;
|
||||||
},
|
}
|
||||||
|
|
||||||
set_value: function(value){
|
Button.prototype = {
|
||||||
this.destroy_content();
|
addEventListener: function(eventName, listener) {
|
||||||
return this._super(value);
|
if (this.element.addEventListener) {
|
||||||
},
|
this.element.addEventListener(eventName, listener);
|
||||||
|
} else if (this.element.attachEvent) {
|
||||||
render_value: function() {
|
this.element.attachEvent('on' + eventName, listener);
|
||||||
this._init_darkroom();
|
}
|
||||||
var url;
|
},
|
||||||
if (this.get('value') && !utils.is_bin_size(this.get('value'))) {
|
removeEventListener: function(eventName, listener) {
|
||||||
url = 'data:image/png;base64,' + this.get('value');
|
if (this.element.removeEventListener) {
|
||||||
} else if (this.get('value')) {
|
this.element.removeEventListener(eventName, listener);
|
||||||
var id = JSON.stringify(this.view.datarecord.id || null);
|
} else if (this.element.detachEvent) {
|
||||||
var field = this.name;
|
this.element.detachEvent('on' + eventName, listener);
|
||||||
if (this.options.preview_image)
|
}
|
||||||
field = this.options.preview_image;
|
},
|
||||||
url = session.url('/web/image', {
|
active: function(bool) {
|
||||||
model: this.view.dataset.model,
|
if (bool) {
|
||||||
id: id,
|
this.element.classList.add('darkroom-button-active');
|
||||||
field: field,
|
} else {
|
||||||
unique: (this.view.datarecord.__last_update || '').replace(/[^0-9]/g, ''),
|
this.element.classList.remove('darkroom-button-active');
|
||||||
});
|
}
|
||||||
} else {
|
},
|
||||||
url = this.placeholder;
|
hide: function(bool) {
|
||||||
}
|
if (bool) {
|
||||||
|
this.element.classList.add('hidden');
|
||||||
var $img = $(QWeb.render("FieldBinaryImage-img", { widget: this, url: url }));
|
} else {
|
||||||
this.$el.find('> img').remove();
|
this.element.classList.remove('hidden');
|
||||||
this.$el.append($img);
|
}
|
||||||
this.darkroom = new Darkroom($img.get(0), this.options);
|
},
|
||||||
this.darkroom.widget = this;
|
disable: function(bool) {
|
||||||
},
|
this.element.disabled = bool;
|
||||||
|
},
|
||||||
commit_value: function(callback) {
|
};
|
||||||
this.set_value(
|
|
||||||
this.darkroom.sourceImage.toDataURL().split(',')[1]
|
// ButtonGroup object
|
||||||
);
|
function ButtonGroup(element) {
|
||||||
},
|
this.element = element;
|
||||||
|
}
|
||||||
});
|
|
||||||
|
ButtonGroup.prototype = {
|
||||||
core.form_widget_registry.add("darkroom", FieldDarkroomImage);
|
createButton: function(options) {
|
||||||
|
var defaults = {
|
||||||
return {
|
image: 'fa fa-question-circle',
|
||||||
FieldDarkroomImage: FieldDarkroomImage,
|
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 = '<i class="' + optionsMerged.image + ' fa-2x"></i>';
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy_content: function() {
|
||||||
|
if (this.darkroom && this.darkroom.containerElement) {
|
||||||
|
this.darkroom.containerElement.remove();
|
||||||
|
this.darkroom = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
set_value: function(value) {
|
||||||
|
return this._super(value);
|
||||||
|
},
|
||||||
|
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
|
||||||
|
commit_value: function() {
|
||||||
|
if (this.darkroom.sourceImage) {
|
||||||
|
this.set_value(this.darkroom.sourceImage.toDataURL().split(',')[1]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
core.form_widget_registry.add("darkroom", FieldDarkroomImage);
|
||||||
|
|
||||||
|
return {FieldDarkroomImage: FieldDarkroomImage};
|
||||||
});
|
});
|
||||||
|
|
|
@ -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 = '<!-- inject:svg --><!-- endinject -->';
|
|
||||||
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 = '<i class="' + options.image + ' fa-2x"></i>';
|
|
||||||
if (options.editOnly) {
|
|
||||||
buttonElement.classList.add('oe_edit_only');
|
|
||||||
}
|
|
||||||
<<<<<<< Updated upstream
|
|
||||||
if (options.addClass) {
|
|
||||||
buttonElement.classList.add(options.addClass);
|
|
||||||
}
|
|
||||||
// buttonElement.innerHTML = '<svg class="darkroom-icon"><use xlink:href="#' + options.image + '" /></svg>';
|
|
||||||
=======
|
|
||||||
>>>>>>> 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,
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
|
@ -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%;
|
||||||
|
}
|
|
@ -1,17 +1,30 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Copyright 2016 LasLabs Inc.
|
Copyright 2016-2017 LasLabs Inc.
|
||||||
License LGPL-3 or later (http://www.gnu.org/licenses/lgpl.html).
|
License LGPL-3 or later (http://www.gnu.org/licenses/lgpl.html).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<templates id="field_templates" xml:space="preserve">
|
<templates id="field_templates" xml:space="preserve">
|
||||||
<t t-name="FieldDarkroomImage">
|
<t t-name="FieldDarkroomImage">
|
||||||
<span class="oe_form_field o_form_field_darkroom"
|
<span class="oe_form_field o_form_field_darkroom" t-att-style="widget.node.attrs.style">
|
||||||
t-att-style="widget.node.attrs.style">
|
|
||||||
<t t-if="!widget.get('effective_readonly')">
|
<t t-if="!widget.get('effective_readonly')">
|
||||||
<div class="darkroom-toolbar" />
|
<div class="darkroom-toolbar"/>
|
||||||
</t>
|
</t>
|
||||||
</span>
|
</span>
|
||||||
</t>
|
</t>
|
||||||
|
|
||||||
|
<t t-extend="FieldBinaryImage">
|
||||||
|
<t t-jquery=".oe_form_binary_file_edit" t-operation="after">
|
||||||
|
<t t-if="widget.darkroom_supported()">
|
||||||
|
<i class="fa fa-picture-o fa-lg oe_form_binary_image_darkroom_modal" title="Darkroom"></i>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
<t t-jquery=".oe_form_binary_file_edit" t-operation="replace">
|
||||||
|
<i class="fa fa-pencil fa-lg oe_form_binary_file_edit" title="Edit"></i>
|
||||||
|
</t>
|
||||||
|
<t t-jquery=".oe_form_binary_file_clear" t-operation="replace">
|
||||||
|
<i class="fa fa-trash-o fa-lg oe_form_binary_file_clear" title="Clear"></i>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
</templates>
|
</templates>
|
||||||
|
|
|
@ -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
|
|
@ -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'})
|
|
@ -1,31 +1,28 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Copyright 2016 LasLabs Inc.
|
Copyright 2016-2017 LasLabs Inc.
|
||||||
License LGPL-3 or later (http://www.gnu.org/licenses/lgpl.html).
|
License LGPL-3 or later (http://www.gnu.org/licenses/lgpl.html).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<odoo>
|
<odoo>
|
||||||
<template id="assets_darkroom" name="web_widget_darkroom Assets" inherit_id="web.assets_backend">
|
<template id="assets_darkroom" name="web_widget_darkroom Assets" inherit_id="web.assets_backend">
|
||||||
<xpath expr="//script[last()]" position="after">
|
<xpath expr="//script[last()]" position="after">
|
||||||
<!--<link href="/web_widget_darkroom/static/lib/darkroomjs/build/darkroom.css" rel="stylesheet" type="text/css" />-->
|
<link href="/web_widget_darkroom/static/src/less/darkroom.less" rel="stylesheet" type="text/less"/>
|
||||||
<link href="/web_widget_darkroom/static/src/css/darkroom.css" rel="stylesheet" type="text/css" />
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.require.min.js"/>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.require.min.js" />
|
|
||||||
|
|
||||||
<script src="/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/darkroom.js" />
|
<script src="/web_widget_darkroom/static/lib/darkroomjs/core/darkroom.js"/>
|
||||||
<script src="/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/plugin.js" />
|
<script src="/web_widget_darkroom/static/lib/darkroomjs/core/plugin.js"/>
|
||||||
<script src="/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/transformation.js" />
|
<script src="/web_widget_darkroom/static/lib/darkroomjs/core/transformation.js"/>
|
||||||
<!--<script src="/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/ui.js" />-->
|
<script src="/web_widget_darkroom/static/lib/darkroomjs/core/utils.js"/>
|
||||||
<script src="/web_widget_darkroom/static/lib/darkroomjs/lib/js/core/utils.js" />
|
|
||||||
|
|
||||||
<script src="/web_widget_darkroom/static/src/js/plugins/darkroom.crop.js" />
|
<script src="/web_widget_darkroom/static/src/js/plugins/darkroom.crop.js"/>
|
||||||
<script src="/web_widget_darkroom/static/src/js/plugins/darkroom.history.js" />
|
<script src="/web_widget_darkroom/static/src/js/plugins/darkroom.history.js"/>
|
||||||
<script src="/web_widget_darkroom/static/src/js/plugins/darkroom.rotate.js" />
|
<script src="/web_widget_darkroom/static/src/js/plugins/darkroom.rotate.js"/>
|
||||||
<script src="/web_widget_darkroom/static/src/js/plugins/darkroom.save.js" />
|
<script src="/web_widget_darkroom/static/src/js/plugins/darkroom.zoom.js"/>
|
||||||
<script src="/web_widget_darkroom/static/src/js/plugins/darkroom.zoom.js" />
|
|
||||||
|
|
||||||
<script src="/web_widget_darkroom/static/src/js/widget_darkroom.js" />
|
|
||||||
|
|
||||||
|
<script src="/web_widget_darkroom/static/src/js/widget_darkroom.js"/>
|
||||||
|
<script src="/web_widget_darkroom/static/src/js/widget_darkroom_modal.js"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
</template>
|
</template>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -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 darkroom_modal
|
|
@ -0,0 +1,82 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2017 LasLabs Inc.
|
||||||
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
|
||||||
|
|
||||||
|
from openerp import api, fields, models
|
||||||
|
from openerp.exceptions import MissingError
|
||||||
|
|
||||||
|
|
||||||
|
class DarkroomModal(models.TransientModel):
|
||||||
|
_name = 'darkroom.modal'
|
||||||
|
_description = 'Darkroom Modal - Wizard Model'
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _default_res_model_id(self):
|
||||||
|
res_model_name = self.env.context.get('active_model')
|
||||||
|
return self.env['ir.model'].search([('model', '=', res_model_name)])
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _default_res_record_id(self):
|
||||||
|
return self.env.context.get('active_record_id', 0)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _default_res_record(self):
|
||||||
|
res_model_name = self._default_res_model_id().model
|
||||||
|
try:
|
||||||
|
res_model_model = self.env[res_model_name]
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return res_model_model.browse(self._default_res_record_id())
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _default_res_field_id(self):
|
||||||
|
res_model_id = self._default_res_model_id()
|
||||||
|
res_field_name = self.env.context.get('active_field')
|
||||||
|
return self.env['ir.model.fields'].search([
|
||||||
|
('model_id', '=', res_model_id.id),
|
||||||
|
('name', '=', res_field_name),
|
||||||
|
])
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _default_image(self):
|
||||||
|
res_record = self._default_res_record()
|
||||||
|
res_field_name = self._default_res_field_id().name
|
||||||
|
|
||||||
|
try:
|
||||||
|
return getattr(res_record, res_field_name, None)
|
||||||
|
except (TypeError, MissingError):
|
||||||
|
return None
|
||||||
|
|
||||||
|
res_model_id = fields.Many2one(
|
||||||
|
comodel_name='ir.model',
|
||||||
|
string='Source Model',
|
||||||
|
required=True,
|
||||||
|
default=lambda s: s._default_res_model_id(),
|
||||||
|
)
|
||||||
|
res_record_id = fields.Integer(
|
||||||
|
string='Source Record ID',
|
||||||
|
required=True,
|
||||||
|
default=lambda s: s._default_res_record_id(),
|
||||||
|
)
|
||||||
|
res_field_id = fields.Many2one(
|
||||||
|
comodel_name='ir.model.fields',
|
||||||
|
string='Source Field',
|
||||||
|
required=True,
|
||||||
|
default=lambda s: s._default_res_field_id(),
|
||||||
|
)
|
||||||
|
image = fields.Binary(
|
||||||
|
string='Darkroom Image',
|
||||||
|
required=True,
|
||||||
|
default=lambda s: s._default_image(),
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def action_save(self):
|
||||||
|
self.ensure_one()
|
||||||
|
|
||||||
|
res_record = self._default_res_record()
|
||||||
|
res_field_name = self._default_res_field_id().name
|
||||||
|
setattr(res_record, res_field_name, self.image)
|
||||||
|
|
||||||
|
return {'type': 'ir.actions.act_window_close'}
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Copyright 2017 LasLabs Inc.
|
||||||
|
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
<record id="darkroom_modal_view_form" model="ir.ui.view">
|
||||||
|
<field name="name">Darkroom Modal Wizard</field>
|
||||||
|
<field name="model">darkroom.modal</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Darkroom Modal">
|
||||||
|
<header />
|
||||||
|
<sheet>
|
||||||
|
<group name="data">
|
||||||
|
<field name="image" widget="darkroom" nolabel="1"/>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
<footer>
|
||||||
|
<button special="cancel" string="Cancel" class="pull-left"/>
|
||||||
|
<button name="action_save" type="object" string="Save" class="oe_highlight pull-right"/>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|