From 7f8730c3add146f1ba107e6fc22d1f5a8348ed8b Mon Sep 17 00:00:00 2001 From: Armen Kesablyan Date: Tue, 7 Feb 2012 16:43:22 -0800 Subject: Refactored rich text editor location --- .../rich-text-editor.reel/rich-text-resizer.js | 349 +++++++++++++++++++++ 1 file changed, 349 insertions(+) create mode 100644 node_modules/labs/rich-text-editor.reel/rich-text-resizer.js (limited to 'node_modules/labs/rich-text-editor.reel/rich-text-resizer.js') diff --git a/node_modules/labs/rich-text-editor.reel/rich-text-resizer.js b/node_modules/labs/rich-text-editor.reel/rich-text-resizer.js new file mode 100644 index 00000000..5da834f4 --- /dev/null +++ b/node_modules/labs/rich-text-editor.reel/rich-text-resizer.js @@ -0,0 +1,349 @@ +/* + 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); + } + } + } + } + +}); \ No newline at end of file -- cgit v1.2.3