/*
This file contains proprietary software owned by Motorola Mobility, Inc.
No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.
(c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved.
*/
/**
@module "montage/ui/rich-text-resizer.js"
@requires montage/core/core
*/
var Montage = require("montage/core/core").Montage,
dom = require("montage/ui/dom"),
Point = require("montage/core/geometry/point").Point;
/**
@class module:"montage/ui/rich-text-resizer.js".Resizer
@extends module:montage/core/core.Montage
*/
exports.Resizer = Montage.create(Montage,/** @lends module:"montage/ui/rich-text-resizer.js".Resizer# */ {
_editor: {
value: null
},
_element: {
value: null
},
element: {
get: function() {
return this._element;
}
},
initialize: {
value: function(editor) {
this._editor = editor;
}
},
show: {
value: function(element) {
// Remove the current resizer
if (this._element) {
this._removeResizer(element);
}
if (element) {
this._addResizer(element);
}
this._element = element;
}
},
hide: {
value: function() {
this._removeResizer(this._element);
this._element = null;
}
},
cleanup: {
value: function(contentNode) {
var cleanContentNode = contentNode,
resizers = contentNode.getElementsByClassName("montage-resizer"),
nbrResizers,
resizer,
i;
if (resizers) {
// We don't want to hide the resizer, just return a copy of the content without the resizer
cleanContentNode = contentNode.cloneNode(true);
resizers = cleanContentNode.getElementsByClassName("montage-resizer");
nbrResizers = resizers.length;
// Note: We should not have more than one resizer, this is just in case...
for (i = 0; i < nbrResizers; i ++) {
resizer = resizers[0];
resizer.parentNode.removeChild(resizer);
}
}
return cleanContentNode;
}
},
draw : {
value: function() {
var thisRef = this;
if (this._draggedElement) {
// Resize the resizer frame
var frame = this._draggedElement.parentNode.firstChild,
zero = Point.create().init(0, 0),
framePosition = dom.convertPointFromNodeToPage(frame, zero),
cursor = this._cursorPosition,
direction = this._draggedElement.id.substring("editor-resizer-".length),
info = this._resizerFrameInfo,
ratio = info.ratio,
height = frame.clientHeight,
width = frame.clientWidth,
top = parseFloat(frame.style.top, 10),
left = parseFloat(frame.style.left, 10),
minSize = 15;
element = this._draggedElement.parentNode.previousSibling;
if (direction == "n") {
height += framePosition.y - cursor.y;
top = info.top - (height - info.height);
} else if (direction == "ne") {
height += framePosition.y - cursor.y;
width = Math.round(height * ratio);
if (cursor.x > (framePosition.x + width)) {
width = cursor.x - framePosition.x;
height = Math.round(width / ratio);
}
top = info.top - (height - info.height);
} else if (direction == "e") {
width = cursor.x - framePosition.x;
} else if (direction == "se") {
height = cursor.y - framePosition.y;
width = Math.round(height * ratio);
if (cursor.x > (framePosition.x + width)) {
width = cursor.x - framePosition.x;
height = Math.round(width / ratio);
}
} else if (direction == "s") {
height = cursor.y - framePosition.y;
} else if (direction == "sw") {
height = cursor.y - framePosition.y;
width = Math.round(height * ratio);
if (cursor.x <= framePosition.x - width + frame.clientWidth) {
width = frame.clientWidth + framePosition.x - cursor.x;
height = Math.round(width / ratio);
}
left = info.left - (width - info.width);
} else if (direction == "w") {
width += framePosition.x - cursor.x;
left = info.left - (width - info.width);
} else if (direction == "nw") {
height += framePosition.y - cursor.y;
width = Math.round(height * ratio);
if (cursor.x <= framePosition.x - width + frame.clientWidth) {
width = frame.clientWidth + framePosition.x - cursor.x;
height = Math.round(width / ratio);
}
top = info.top - (height - info.height);
left = info.left - (width - info.width);
}
//set the frame's new height and width
if (height > minSize && width > minSize) {
frame.style.height = height + "px";
frame.style.width = width + "px";
frame.style.top = top + "px";
frame.style.left = left + "px";
}
if (this._finalizeDrag) {
this._draggedElement.parentNode.classList.remove("dragged");
delete this._finalizeDrag;
delete this._resizerFrameInfo;
delete this._draggedElement;
// Remove the resizer, we don't wont it in case of undo!
this._removeResizer(element);
// Prevent the editor to try to delete the resizer from now on due to a selection change
this._editor._selectingResizer = true;
// Take the element offline to modify it
var div = document.createElement("div"),
offlineElement,
savedID;
div.innerHTML = element ? element.outerHTML : "";
offlineElement = div.firstChild;
// Resize the element now that it's offline
offlineElement.width = (width + 1);
offlineElement.height = (height + 1);
offlineElement.style.removeProperty("width");
offlineElement.style.removeProperty("height");
savedID = offlineElement.id;
offlineElement.id = "montage-editor-resized-image";
// Inject the resized element into the contentEditable using execCommand in order to be in the browser undo queue
document.execCommand("inserthtml", false, div.innerHTML);
element = document.getElementById(offlineElement.id);
if (element && savedID !== undefined) {
element.id = savedID;
}
this._element = element;
// Add back the resizer
this._addResizer(element);
// Reset the selection (using the editor's internal
offset = this._editor._nodeOffset(element);
range = document.createRange();
range.setStart(element.parentNode, offset);
range.setEnd(element.parentNode, offset + 1);
this._editor._selectedRange = range;
// Note: Chrome (and maybe other browsers) will fire 2 selectionchange event asynchronously, to work around it let's use a timer
setTimeout(function() {delete thisRef._editor._selectingResizer;}, 0);
} else {
this._draggedElement.parentNode.classList.add("dragged");
}
}
}
},
startUserAction: {
value: function(event) {
var element = event.target,
frame;
if (element.classList.contains("montage-resizer-handle")) {
if (window.Touch) {
this._editor._observePointer(target.id);
document.addEventListener("touchmove", this);
} else {
this._editor._observePointer("mouse");
document.addEventListener("mousemove", this);
}
this._draggedElement = element;
frame = element.parentNode.firstChild;
this._resizerFrameInfo = {
width: frame.clientWidth,
height: frame.clientHeight,
left: parseInt(frame.style.left, 10),
top: parseInt(frame.style.top, 10),
ratio: frame.clientWidth / frame.clientHeight
};
this._cursorPosition = {x:event.pageX, y:event.pageY};
return true;
}
return false;
}
},
endUserAction: {
value: function(event) {
if (this._draggedElement && !this._finalizeDrag) {
// We are dragging the resizer
if (window.Touch) {
document.removeEventListener("touchmove", this, false);
} else {
this._cursorPosition = {x:event.pageX, y:event.pageY};
document.removeEventListener("mousemove", this, false);
}
this._editor._releaseInterest();
this._finalizeDrag = true;
this._editor.needsDraw = true;
event.preventDefault();
event.stopPropagation();
return true;
}
return false;
}
},
handleMousemove: {
value: function(event) {
if (this._draggedElement) {
// We are dragging the resizer
this._cursorPosition = {x:event.pageX, y:event.pageY};
this._editor.needsDraw = true;
event.preventDefault();
event.stopPropagation();
}
}
},
handleTouchmove: {
value: function(event) {
this.handleMousemove(event);
}
},
_addResizer: {
enumerable: true,
value: function(element) {
var parentNode = element.parentNode,
nextSibling = element.nextSibling,
frame,
w = element.offsetWidth -1,
h = element.offsetHeight -1,
l = element.offsetLeft,
t = element.offsetTop,
resizerFrameHtml = '
' +
'
' +
'
' +
'
' +
'
' +
'
' +
'
' +
'
' +
'
' +
'
' +
'
',
i;
// sanity check: make sure we don't already have a frame
if (!nextSibling || nextSibling.tagName !== "DIV" || !nextSibling.classList.contains("montage-resizer")) {
frame = document.createElement("DIV");
frame.innerHTML = resizerFrameHtml;
parentNode.insertBefore(frame.firstChild, nextSibling);
element.classList.add("montage-resizer-element");
}
}
},
_removeResizer: {
enumerable: true,
value: function(element) {
var resizer;
if (!element) {
return;
}
resizer = element.nextSibling;
if (resizer && resizer.tagName === "DIV" && resizer.classList.contains("montage-resizer")) {
element.parentNode.removeChild(resizer)
element.classList.remove("montage-resizer-element");
} else {
// Handle case where the element has been removed from the DOM or the resizer is not in sync with the
// element anymore (hapen after an undo)
resizer = document.getElementById("montage-resizer");
if (resizer && resizer.tagName === "DIV" && resizer.classList.contains("montage-resizer")) {
resizer.parentNode.removeChild(resizer);
}
}
}
}
});