web_widget_darkroom: Add o2m DarkroomJS widget

This commit is contained in:
Dave Lasley
2016-08-23 16:29:12 -07:00
parent 97b6b48a69
commit 112eba3e12
45 changed files with 3621 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
/*.darkroom-container{
padding-top: 50px;
}
.darkroom-toolbar{
top: 5px;
}
*/
.darkroom-button-group{
display: inline;
}

View File

@@ -0,0 +1,20 @@
/* 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
});

View File

@@ -0,0 +1,683 @@
/* Adapted from https://github.com/MattKetmo/darkroomjs/tree/master/lib/js/plugins
* License https://github.com/MattKetmo/darkroomjs/blob/master/LICENSE
*/
odoo.define('web_widget_darkroom.darkroom_crop', function(require){
'use strict';
var DarkroomPluginCrop = function(){
var Crop = Darkroom.Transformation.extend({
applyTransformation: function(canvas, image, next) {
// Snapshot the image delimited by the crop zone
var snapshot = new Image();
snapshot.onload = function() {
// Validate image
if (height < 1 || width < 1)
return;
var imgInstance = new fabric.Image(this, {
// options to make the image static
selectable: false,
evented: false,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
lockUniScaling: true,
hasControls: false,
hasBorders: false
});
var width = this.width;
var height = this.height;
// Update canvas size
canvas.setWidth(width);
canvas.setHeight(height);
// Add image
image.remove();
canvas.add(imgInstance);
next(imgInstance);
};
var viewport = Darkroom.Utils.computeImageViewPort(image);
var imageWidth = viewport.width;
var imageHeight = viewport.height;
var left = this.options.left * imageWidth;
var top = this.options.top * imageHeight;
var width = Math.min(this.options.width * imageWidth, imageWidth - left);
var height = Math.min(this.options.height * imageHeight, imageHeight - top);
snapshot.src = canvas.toDataURL({
left: left,
top: top,
width: width,
height: height,
});
}
});
var CropZone = fabric.util.createClass(fabric.Rect, {
_render: function(ctx) {
this.callSuper('_render', ctx);
var canvas = ctx.canvas;
var dashWidth = 7;
// Set original scale
var flipX = this.flipX ? -1 : 1;
var flipY = this.flipY ? -1 : 1;
var scaleX = flipX / this.scaleX;
var scaleY = flipY / this.scaleY;
ctx.scale(scaleX, scaleY);
// Overlay rendering
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
this._renderOverlay(ctx);
// Set dashed borders
if (ctx.setLineDash !== undefined)
ctx.setLineDash([dashWidth, dashWidth]);
else if (ctx.mozDash !== undefined)
ctx.mozDash = [dashWidth, dashWidth];
// First lines rendering with black
ctx.strokeStyle = 'rgba(0, 0, 0, 0.2)';
this._renderBorders(ctx);
this._renderGrid(ctx);
// Re render lines in white
ctx.lineDashOffset = dashWidth;
ctx.strokeStyle = 'rgba(255, 255, 255, 0.4)';
this._renderBorders(ctx);
this._renderGrid(ctx);
// Reset scale
ctx.scale(1/scaleX, 1/scaleY);
},
_renderOverlay: function(ctx) {
var canvas = ctx.canvas;
var borderOffset = 0;
//
// x0 x1 x2 x3
// y0 +------------------------+
// |\\\\\\\\\\\\\\\\\\\\\\\\|
// |\\\\\\\\\\\\\\\\\\\\\\\\|
// y1 +------+---------+-------+
// |\\\\\\| |\\\\\\\|
// |\\\\\\| 0 |\\\\\\\|
// |\\\\\\| |\\\\\\\|
// y2 +------+---------+-------+
// |\\\\\\\\\\\\\\\\\\\\\\\\|
// |\\\\\\\\\\\\\\\\\\\\\\\\|
// y3 +------------------------+
//
var x0 = Math.ceil(-this.getWidth() / 2 - this.getLeft());
var x1 = Math.ceil(-this.getWidth() / 2);
var x2 = Math.ceil(this.getWidth() / 2);
var x3 = Math.ceil(this.getWidth() / 2 + (canvas.width - this.getWidth() - this.getLeft()));
var y0 = Math.ceil(-this.getHeight() / 2 - this.getTop());
var y1 = Math.ceil(-this.getHeight() / 2);
var y2 = Math.ceil(this.getHeight() / 2);
var y3 = Math.ceil(this.getHeight() / 2 + (canvas.height - this.getHeight() - this.getTop()));
// Upper rect
ctx.fillRect(x0, y0, x3 - x0, y1 - y0 + borderOffset);
// Left rect
ctx.fillRect(x0, y1, x1 - x0, y2 - y1 + borderOffset);
// Right rect
ctx.fillRect(x2, y1, x3 - x2, y2 - y1 + borderOffset);
// Down rect
ctx.fillRect(x0, y2, x3 - x0, y3 - y2);
},
_renderBorders: function(ctx) {
ctx.beginPath();
ctx.moveTo(-this.getWidth()/2, -this.getHeight()/2); // upper left
ctx.lineTo(this.getWidth()/2, -this.getHeight()/2); // upper right
ctx.lineTo(this.getWidth()/2, this.getHeight()/2); // down right
ctx.lineTo(-this.getWidth()/2, this.getHeight()/2); // down left
ctx.lineTo(-this.getWidth()/2, -this.getHeight()/2); // upper left
ctx.stroke();
},
_renderGrid: function(ctx) {
// Vertical lines
ctx.beginPath();
ctx.moveTo(-this.getWidth()/2 + 1/3 * this.getWidth(), -this.getHeight()/2);
ctx.lineTo(-this.getWidth()/2 + 1/3 * this.getWidth(), this.getHeight()/2);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(-this.getWidth()/2 + 2/3 * this.getWidth(), -this.getHeight()/2);
ctx.lineTo(-this.getWidth()/2 + 2/3 * this.getWidth(), this.getHeight()/2);
ctx.stroke();
// Horizontal lines
ctx.beginPath();
ctx.moveTo(-this.getWidth()/2, -this.getHeight()/2 + 1/3 * this.getHeight());
ctx.lineTo(this.getWidth()/2, -this.getHeight()/2 + 1/3 * this.getHeight());
ctx.stroke();
ctx.beginPath();
ctx.moveTo(-this.getWidth()/2, -this.getHeight()/2 + 2/3 * this.getHeight());
ctx.lineTo(this.getWidth()/2, -this.getHeight()/2 + 2/3 * this.getHeight());
ctx.stroke();
}
});
Darkroom.plugins['crop'] = Darkroom.Plugin.extend({
// Init point
startX: null,
startY: null,
// Keycrop
isKeyCroping: false,
isKeyLeft: false,
isKeyUp: false,
defaults: {
// min crop dimension
minHeight: 1,
minWidth: 1,
// ensure crop ratio
ratio: null,
// quick crop feature (set a key code to enable it)
quickCropKey: false
},
initialize: function InitDarkroomCropPlugin() {
var buttonGroup = this.darkroom.toolbar.createButtonGroup();
this.cropButton = buttonGroup.createButton({
image: 'fa fa-crop',
editOnly: true,
});
this.okButton = buttonGroup.createButton({
image: 'fa fa-check',
editOnly: true,
type: 'success',
hide: true
});
this.cancelButton = buttonGroup.createButton({
image: 'fa fa-times',
editOnly: true,
type: 'danger',
hide: true
});
// 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');
}
});
}
return {DarkroomPluginCrop: DarkroomPluginCrop};
});

View File

@@ -0,0 +1,80 @@
/* Adapted from https://github.com/MattKetmo/darkroomjs/tree/master/lib/js/plugins
* License https://github.com/MattKetmo/darkroomjs/blob/master/LICENSE
*/
odoo.define('web_widget_darkroom.darkroom_history', function(require){
'use strict';
var DarkroomPluginHistory = function() {
Darkroom.plugins['history'] = Darkroom.Plugin.extend({
undoTransformations: [],
initialize: function InitDarkroomHistoryPlugin() {
this._initButtons();
this.darkroom.addEventListener('core:transformation', this._onTranformationApplied.bind(this));
},
undo: function() {
if (this.darkroom.transformations.length === 0) {
return;
}
var lastTransformation = this.darkroom.transformations.pop();
this.undoTransformations.unshift(lastTransformation);
this.darkroom.reinitializeImage();
this._updateButtons();
},
redo: function() {
if (this.undoTransformations.length === 0) {
return;
}
var cancelTransformation = this.undoTransformations.shift();
this.darkroom.transformations.push(cancelTransformation);
this.darkroom.reinitializeImage();
this._updateButtons();
},
_initButtons: function() {
var buttonGroup = this.darkroom.toolbar.createButtonGroup();
this.backButton = buttonGroup.createButton({
image: 'fa fa-step-backward',
disabled: true,
editOnly: true,
});
this.forwardButton = buttonGroup.createButton({
image: 'fa fa-step-forward',
disabled: true,
editOnly: true,
});
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};
});

View File

@@ -0,0 +1,70 @@
/* Adapted from https://github.com/MattKetmo/darkroomjs/tree/master/lib/js/plugins
* License https://github.com/MattKetmo/darkroomjs/blob/master/LICENSE
*/
odoo.define('web_widget_darkroom.darkroom_rotate', function(require){
'use strict';
var DarkroomPluginRotate = function() {
var Rotation = Darkroom.Transformation.extend({
applyTransformation: function(canvas, image, next) {
var angle = (image.getAngle() + this.options.angle) % 360;
image.rotate(angle);
var width, height;
height = Math.abs(image.getWidth()*(Math.sin(angle*Math.PI/180)))+Math.abs(image.getHeight()*(Math.cos(angle*Math.PI/180)));
width = Math.abs(image.getHeight()*(Math.sin(angle*Math.PI/180)))+Math.abs(image.getWidth()*(Math.cos(angle*Math.PI/180)));
canvas.setWidth(width);
canvas.setHeight(height);
canvas.centerObject(image);
image.setCoords();
canvas.renderAll();
next();
}
});
Darkroom.plugins['rotate'] = Darkroom.Plugin.extend({
initialize: function InitDarkroomRotatePlugin() {
var buttonGroup = this.darkroom.toolbar.createButtonGroup();
var leftButton = buttonGroup.createButton({
image: 'fa fa-undo oe_edit_only',
editOnly: true,
});
var 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};
});

View File

@@ -0,0 +1,198 @@
/* Copyright 2016 LasLabs Inc.
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
*/
odoo.define('web_widget_darkroom.darkroom_zoom', function(require){
'use strict';
var DarkroomPluginZoom = function(){
Darkroom.plugins['zoom'] = Darkroom.Plugin.extend({
inZoom: false,
zoomLevel: 0,
zoomFactor: .1,
initialize: function() {
var self = this;
var buttonGroup = this.darkroom.toolbar.createButtonGroup();
this.zoomButton = buttonGroup.createButton({
image: 'fa fa-search',
})
this.zoomInButton = buttonGroup.createButton({
image: 'fa fa-plus',
})
this.zoomOutButton = buttonGroup.createButton({
image: 'fa fa-minus',
})
this.okButton = buttonGroup.createButton({
image: 'fa fa-check',
type: 'success',
hide: true,
editOnly: true,
});
this.cancelButton = buttonGroup.createButton({
image: 'fa fa-times',
type: 'danger',
hide: true
});
// Buttons click
this.zoomButton.addEventListener('click', this.toggleZoom.bind(this));
this.zoomInButton.addEventListener('click', this.zoomIn.bind(this));
this.zoomOutButton.addEventListener('click', this.zoomOut.bind(this));
//this.okButton.addEventListener('click', this.saveZoom.bind(this));
this.cancelButton.addEventListener('click', this.releaseFocus.bind(this));
// Canvas events
this.darkroom.canvas.on('mouse:down', this.onMouseDown.bind(this));
this.darkroom.canvas.on('mouse:move', this.onMouseMove.bind(this));
this.darkroom.canvas.on('mouse:up', this.onMouseUp.bind(this));
//this.darkroom.canvas.on('object:moving', this.onObjectMoving.bind(this));
//this.darkroom.canvas.on('object:scaling', this.onObjectScaling.bind(this));
$(this.darkroom.canvas.wrapperEl).on('mousewheel', function(event){
self.onMouseWheel(event);
});
//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};
});

View File

@@ -0,0 +1,238 @@
/* Copyright 2016 LasLabs Inc.
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
*/
odoo.define('web_widget_darkroom.darkroom_widget', function(require){
"use strict";
var core = require('web.core');
var common = require('web.form_common');
var session = require('web.session');
var utils = require('web.utils');
var framework = require('web.framework');
var crash_manager = require('web.crash_manager');
var QWeb = core.qweb;
var _t = core._t;
var FieldDarkroomImage = common.AbstractField.extend(common.ReinitializeFieldMixin, {
className: 'darkroom-widget',
template: 'FieldDarkroomImage',
placeholder: "/web/static/src/img/placeholder.png",
darkroom: null,
no_rerender: false,
defaults: {
// Canvas initialization size
minWidth: 100,
minHeight: 100,
maxWidth: 700,
maxHeight: 500,
// Plugins options
plugins: {
crop: {
minHeight: 50,
minWidth: 50,
ratio: 1
},
},
// 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() {
if (!this.darkroom) {
this._init_darkroom_icons();
this._init_darkroom_ui();
this._init_darkroom_plugins();
}
},
_init_darkroom_ui: function() {
Darkroom.UI = {
Toolbar: Toolbar,
ButtonGroup: ButtonGroup,
Button: Button,
};
// Toolbar object.
function Toolbar(element) {
this.element = element;
}
Toolbar.prototype = {
createButtonGroup: function(options) {
var buttonGroup = document.createElement('div');
buttonGroup.className = 'darkroom-button-group';
this.element.appendChild(buttonGroup);
return new ButtonGroup(buttonGroup);
}
};
// ButtonGroup object.
function ButtonGroup(element) {
this.element = element;
}
ButtonGroup.prototype = {
createButton: function(options) {
var defaults = {
image: 'fa fa-question-circle',
type: 'default',
group: 'default',
hide: false,
disabled: false,
editOnly: false,
addClass: '',
};
options = Darkroom.Utils.extend(options, defaults);
var buttonElement = document.createElement('button');
buttonElement.type = 'button';
buttonElement.className = 'darkroom-button darkroom-button-' + options.type;
buttonElement.innerHTML = '<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){
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() {
if (this.darkroom && this.darkroom.containerElement) {
this.darkroom.containerElement.remove();
this.darkroom = null;
}
},
set_value: function(value){
this.destroy_content();
return this._super(value);
},
render_value: function() {
this._init_darkroom();
var url;
if (this.get('value') && !utils.is_bin_size(this.get('value'))) {
url = 'data:image/png;base64,' + this.get('value');
} else if (this.get('value')) {
var id = JSON.stringify(this.view.datarecord.id || null);
var field = this.name;
if (this.options.preview_image)
field = this.options.preview_image;
url = session.url('/web/image', {
model: this.view.dataset.model,
id: id,
field: field,
unique: (this.view.datarecord.__last_update || '').replace(/[^0-9]/g, ''),
});
} else {
url = this.placeholder;
}
var $img = $(QWeb.render("FieldBinaryImage-img", { widget: this, url: url }));
this.$el.find('> img').remove();
this.$el.append($img);
this.darkroom = new Darkroom($img.get(0), this.options);
this.darkroom.widget = this;
},
commit_value: function(callback) {
this.set_value(
this.darkroom.sourceImage.toDataURL().split(',')[1]
);
},
});
core.form_widget_registry.add("darkroom", FieldDarkroomImage);
return {
FieldDarkroomImage: FieldDarkroomImage,
}
});

View File

@@ -0,0 +1,246 @@
/* © 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,
}
});

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2016 LasLabs Inc.
License LGPL-3 or later (http://www.gnu.org/licenses/lgpl.html).
-->
<templates id="field_templates" xml:space="preserve">
<t t-name="FieldDarkroomImage">
<span class="oe_form_field o_form_field_darkroom"
t-att-style="widget.node.attrs.style">
<t t-if="!widget.get('effective_readonly')">
<div class="darkroom-toolbar" />
</t>
</span>
</t>
</templates>