/**
* CropRect.js
*
* Released under LGPL License.
* Copyright (c) 1999-2017 Ephox Corp. All rights reserved
*
* License: http://www.tinymce.com/license
* Contributing: http://www.tinymce.com/contributing
*/
import DomQuery from 'tinymce/core/api/dom/DomQuery';
import Rect from 'tinymce/core/api/geom/Rect';
import Factory from 'tinymce/core/api/ui/Factory';
import Observable from 'tinymce/core/api/util/Observable';
import Tools from 'tinymce/core/api/util/Tools';
import VK from 'tinymce/core/api/util/VK';
let count = 0;
export default function (currentRect, viewPortRect, clampRect, containerElm, action) {
let instance;
let handles;
let dragHelpers;
let blockers;
const prefix = 'mce-';
const id = prefix + 'crid-' + count++;
handles = [
{ name: 'move', xMul: 0, yMul: 0, deltaX: 1, deltaY: 1, deltaW: 0, deltaH: 0, label: 'Crop Mask' },
{ name: 'nw', xMul: 0, yMul: 0, deltaX: 1, deltaY: 1, deltaW: -1, deltaH: -1, label: 'Top Left Crop Handle' },
{ name: 'ne', xMul: 1, yMul: 0, deltaX: 0, deltaY: 1, deltaW: 1, deltaH: -1, label: 'Top Right Crop Handle' },
{ name: 'sw', xMul: 0, yMul: 1, deltaX: 1, deltaY: 0, deltaW: -1, deltaH: 1, label: 'Bottom Left Crop Handle' },
{ name: 'se', xMul: 1, yMul: 1, deltaX: 0, deltaY: 0, deltaW: 1, deltaH: 1, label: 'Bottom Right Crop Handle' }
];
blockers = ['top', 'right', 'bottom', 'left'];
function getAbsoluteRect(outerRect, relativeRect) {
return {
x: relativeRect.x + outerRect.x,
y: relativeRect.y + outerRect.y,
w: relativeRect.w,
h: relativeRect.h
};
}
function getRelativeRect(outerRect, innerRect) {
return {
x: innerRect.x - outerRect.x,
y: innerRect.y - outerRect.y,
w: innerRect.w,
h: innerRect.h
};
}
function getInnerRect() {
return getRelativeRect(clampRect, currentRect);
}
function moveRect(handle, startRect, deltaX, deltaY) {
let x, y, w, h, rect;
x = startRect.x;
y = startRect.y;
w = startRect.w;
h = startRect.h;
x += deltaX * handle.deltaX;
y += deltaY * handle.deltaY;
w += deltaX * handle.deltaW;
h += deltaY * handle.deltaH;
if (w < 20) {
w = 20;
}
if (h < 20) {
h = 20;
}
rect = currentRect = Rect.clamp({ x, y, w, h }, clampRect, handle.name === 'move');
rect = getRelativeRect(clampRect, rect);
instance.fire('updateRect', { rect });
setInnerRect(rect);
}
function render() {
function createDragHelper(handle) {
let startRect;
const DragHelper = Factory.get('DragHelper');
return new DragHelper(id, {
document: containerElm.ownerDocument,
handle: id + '-' + handle.name,
start () {
startRect = currentRect;
},
drag (e) {
moveRect(handle, startRect, e.deltaX, e.deltaY);
}
});
}
DomQuery(
'
'
).appendTo(containerElm);
Tools.each(blockers, function (blocker) {
DomQuery('#' + id, containerElm).append(
'
'
);
});
Tools.each(handles, function (handle) {
DomQuery('#' + id, containerElm).append(
'
'
);
});
dragHelpers = Tools.map(handles, createDragHelper);
repaint(currentRect);
DomQuery(containerElm).on('focusin focusout', function (e) {
DomQuery(e.target).attr('aria-grabbed', e.type === 'focus');
});
DomQuery(containerElm).on('keydown', function (e) {
let activeHandle;
Tools.each(handles, function (handle) {
if (e.target.id === id + '-' + handle.name) {
activeHandle = handle;
return false;
}
});
function moveAndBlock(evt, handle, startRect, deltaX, deltaY) {
evt.stopPropagation();
evt.preventDefault();
moveRect(activeHandle, startRect, deltaX, deltaY);
}
switch (e.keyCode) {
case VK.LEFT:
moveAndBlock(e, activeHandle, currentRect, -10, 0);
break;
case VK.RIGHT:
moveAndBlock(e, activeHandle, currentRect, 10, 0);
break;
case VK.UP:
moveAndBlock(e, activeHandle, currentRect, 0, -10);
break;
case VK.DOWN:
moveAndBlock(e, activeHandle, currentRect, 0, 10);
break;
case VK.ENTER:
case VK.SPACEBAR:
e.preventDefault();
action();
break;
}
});
}
function toggleVisibility(state) {
let selectors;
selectors = Tools.map(handles, function (handle) {
return '#' + id + '-' + handle.name;
}).concat(Tools.map(blockers, function (blocker) {
return '#' + id + '-' + blocker;
})).join(',');
if (state) {
DomQuery(selectors, containerElm).show();
} else {
DomQuery(selectors, containerElm).hide();
}
}
function repaint(rect) {
function updateElementRect(name, rect) {
if (rect.h < 0) {
rect.h = 0;
}
if (rect.w < 0) {
rect.w = 0;
}
DomQuery('#' + id + '-' + name, containerElm).css({
left: rect.x,
top: rect.y,
width: rect.w,
height: rect.h
});
}
Tools.each(handles, function (handle) {
DomQuery('#' + id + '-' + handle.name, containerElm).css({
left: rect.w * handle.xMul + rect.x,
top: rect.h * handle.yMul + rect.y
});
});
updateElementRect('top', { x: viewPortRect.x, y: viewPortRect.y, w: viewPortRect.w, h: rect.y - viewPortRect.y });
updateElementRect('right', { x: rect.x + rect.w, y: rect.y, w: viewPortRect.w - rect.x - rect.w + viewPortRect.x, h: rect.h });
updateElementRect('bottom', {
x: viewPortRect.x,
y: rect.y + rect.h,
w: viewPortRect.w,
h: viewPortRect.h - rect.y - rect.h + viewPortRect.y
});
updateElementRect('left', { x: viewPortRect.x, y: rect.y, w: rect.x - viewPortRect.x, h: rect.h });
updateElementRect('move', rect);
}
function setRect(rect) {
currentRect = rect;
repaint(currentRect);
}
function setViewPortRect(rect) {
viewPortRect = rect;
repaint(currentRect);
}
function setInnerRect(rect) {
setRect(getAbsoluteRect(clampRect, rect));
}
function setClampRect(rect) {
clampRect = rect;
repaint(currentRect);
}
function destroy() {
Tools.each(dragHelpers, function (helper) {
helper.destroy();
});
dragHelpers = [];
}
render();
instance = Tools.extend({
toggleVisibility,
setClampRect,
setRect,
getInnerRect,
setInnerRect,
setViewPortRect,
destroy
}, Observable);
return instance;
}