/* 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); } } } } });