/* 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-editor.reel" @requires montage/core/core */ var Montage = require("montage").Montage, RichTextEditorBase = require("./rich-text-editor-base").RichTextEditorBase, Sanitizer = require("./rich-text-sanitizer").Sanitizer, MutableEvent = require("core/event/mutable-event").MutableEvent, defaultEventManager = require("core/event/event-manager").defaultEventManager; /** @class module:"montage/ui/rich-text-editor.reel".RichTextEditor @extends module:montage/ui/component.Component */ exports.RichTextEditor = Montage.create(RichTextEditorBase,/** @lends module:"montage/ui/rich-text-editor.reel".RichTextEditor# */ { /** Description TODO @type {Function} */ hasFocus: { enumerable: true, get: function() { return this._hasFocus; } }, /** Description TODO @type {Function} */ innerElement: { enumerable: true, get: function() { return this._innerElement; } }, /** Description TODO @type {Function} */ focus: { enumerable: true, value: function() { this._needsFocus = true; this.needsDraw = true; } }, /** Description TODO @type {Function} */ isActiveElement: { enumerable: true, get: function() { return this._isActiveElement; } }, /** Description TODO @type {Function} */ readOnly: { enumerable: true, get: function() { return this._readOnly; }, set: function(value) { if (this._readOnly !== value) { this._readOnly = value; if (value) { // Remove any overlay this.hideOverlay(); } this.needsDraw = true; } } }, /** Description TODO @type {Function} */ value: { enumerable: true, serializable: true, get: function() { var contentNode = this._innerElement, content = "", overlayElement = null, overlayParent, overlayNextSibling; if (this._dirtyValue) { if (contentNode) { // Temporary orphran the overlay slot while retrieving the content overlayElement = contentNode.querySelector(".montage-editor-overlay"); if (overlayElement) { overlayParent = overlayElement.parentNode; overlayNextSibling = overlayElement.nextSibling; overlayParent.removeChild(overlayElement); } content = contentNode.innerHTML; } if (content == "
") { // when the contentEditable div is emptied, Chrome add a
, let's filter it out content = ""; } if (this._sanitizer === undefined) { this._sanitizer = Sanitizer.create(); } if (this._sanitizer) { content = this._sanitizer.didGetValue(content, this._uniqueId); } // restore the overlay if (overlayElement) { overlayParent.insertBefore(overlayElement, overlayNextSibling); } this._value = content; this._dirtyValue = false; } return this._value; }, set: function(value) { if (this._value !== value || this._dirtyValue) { // Remove any overlay this.hideOverlay(); if (this._sanitizer === undefined) { this._sanitizer = Sanitizer.create(); } if (this._sanitizer) { value = this._sanitizer.willSetValue(value, this._uniqueId); } this._value = value; this._dirtyValue = false; this._dirtyTextValue = true; this._needsAssingValue = true; this.needsDraw = true; } this._needsOriginalContent = false; } }, /** Description TODO @type {Function} */ textValue: { enumerable: true, get: function() { var contentNode = this._innerElement, overlayElement = null, overlayParent, overlayNextSibling; if (this._dirtyTextValue) { if (contentNode) { // Temporary orphran the overlay slot in order to retrieve the content overlayElement = contentNode.querySelector(".montage-editor-overlay"); if (overlayElement) { overlayParent = overlayElement.parentNode; overlayNextSibling = overlayElement.nextSibling; overlayParent.removeChild(overlayElement); } this._textValue = this._innerText(contentNode); // restore the overlay if (overlayElement) { overlayParent.insertBefore(overlayElement, overlayNextSibling); } } else { this._textValue = ""; } this._dirtyTextValue = false; } return this._textValue; }, set: function (value) { if (this._textValue !== value || this._dirtyTextValue) { // Remove any overlay this.hideOverlay(); this._textValue = value; this._dirtyTextValue = false; this._dirtyValue = true; this._needsAssingValue = true; this.needsDraw = true; } this._needsOriginalContent = false; } }, /** Description TODO @type {} */ delegate: { enumerable: true, value: null }, /** Description TODO @type {Function} */ sanitizer: { enumerable: false, get: function() { return this._sanitizer; }, set: function(value) { this._sanitizer = value; } }, /** Description TODO @type {Function} */ overlays: { enumerable: false, get: function() { return this._overlays; }, set: function(value) { this.hideOverlay(); if (value instanceof Array) { this._overlays = value; this._callOverlays("initWithEditor", this, true); } else { this._overlays = null; } } }, /** Description TODO @type {Function} */ activeOverlay: { get: function() { return this._activeOverlay; } }, /** Description TODO @type {Function} */ showOverlay: { value: function(overlay) { var slot = this._overlaySlot, slotElem = slot ? slot.element : null; if (slotElem) { this._activeOverlay = overlay; this._innerElement.appendChild(slotElem.parentNode ? slotElem.parentNode.removeChild(slotElem) : slotElem); slot.attachToParentComponent(); slot.content = overlay; } } }, /** Description TODO @type {Function} */ hideOverlay: { value: function(a) { var slot = this._overlaySlot, slotElem = slot ? slot.element : null; if (slotElem) { if (slotElem.parentNode) { slotElem.parentNode.removeChild(slotElem) } this._activeOverlay = null; slot.content = null; } } }, // Edit Actions & Properties /** Description TODO @type {Function} */ bold: { enumerable: true, get: function() { return this._genericCommandGetter("bold", "bold"); }, set: function(value) { this._genericCommandSetter("bold", "bold", value); } }, /** Description TODO @type {Function} */ underline: { enumerable: true, get: function() { return this._genericCommandGetter("underline", "underline"); }, set: function(value) { this._genericCommandSetter("underline", "underline", value); } }, /** Description TODO @type {Function} */ italic: { enumerable: true, get: function() { return this._genericCommandGetter("italic", "italic"); }, set: function(value) { this._genericCommandSetter("italic", "italic", value); } }, /** Description TODO @type {Function} */ strikeThrough: { enumerable: false, get: function() { return this._genericCommandGetter("strikeThrough", "strikethrough"); }, set: function(value) { this._genericCommandSetter("strikeThrough", "strikethrough", value); } }, /** Description TODO @type {Function} */ baselineShift: { enumerable: true, get: function() { this._baselineShift = this._baselineShiftGetState(); return this._baselineShift; }, set: function(value) { var state = this._baselineShiftGetState(); if (state != value) { if (value == "baseline") { if (state == "subscript") { this.doAction("subscript"); } else if (state == "superscript") { this.doAction("superscript"); } } else if (value == "subscript") { this.doAction("subscript"); } else if (value == "superscript") { this.doAction("superscript"); } } } }, /** Description TODO @type {Function} */ indent: { enumerable: true, value: function() { this.doAction("indent"); } }, /** Description TODO @type {Function} */ outdent: { enumerable: true, value: function() { this.doAction("outdent"); } }, /** Description TODO @type {Function} */ listStyle: { enumerable: true, get: function() { this._liststyle = this._listStyleGetState(); return this._liststyle; }, set: function(value) { var state = this._listStyleGetState(); if (state != value) { if (value == "none") { this.doAction(state == "ordered" ? "insertorderedlist" : "insertunorderedlist"); } else if (value == "ordered") { this.doAction("insertorderedlist"); } else if (value == "unordered") { this.doAction("insertunorderedlist"); } } } }, /** Description TODO @type {Function} */ justify: { enumerable: true, get: function() { this._justify = this._justifyGetState(); return this._justify; }, set: function(value) { var state = this._justifyGetState(); if (state != value && ["left", "center", "right", "full"].indexOf(value) !== -1) { this.doAction("justify" + value); } } }, /** Description TODO @type {Function} */ fontName: { enumerable: true, get: function() { this._fontName = this._fontNameGetState(); return this._fontName; }, set: function(value) { this._genericCommandSetter("fontName", "fontname", value); } }, /** Description TODO @type {Function} */ fontSize: { enumerable: true, get: function() { return this._genericCommandGetter("fontSize", "fontsize"); }, set: function(value) { this._genericCommandSetter("fontSize", "fontsize", value); } }, /** Description TODO @type {Function} */ backColor: { enumerable: true, get: function() { return this._genericCommandGetter("backColor", "backcolor"); }, set: function(value) { this._genericCommandSetter("backColor", "backcolor", value === null ? "inherit" : value); } }, /** Description TODO @type {Function} */ foreColor: { enumerable: true, get: function() { return this._genericCommandGetter("foreColor", "forecolor"); }, set: function(value) { this._genericCommandSetter("foreColor", "forecolor", value === null ? "inherit" : value); } }, /** Description TODO @type {Function} */ selectAll: { enumerable: true, value: function() { this.doAction("selectall"); } }, /** Description TODO @type {Function} */ selectElement: { enumerable: true, value: function(element) { var offset, range; offset = this._nodeOffset(element); if (offset !== -1) { range = document.createRange(); range.setStart(element.parentNode, offset); range.setEnd(element.parentNode, offset + 1); this._selectedRange = range; } } }, /** Description TODO @function */ undoManager: { enumerable: true, get: function() { return this._undoManager }, set: function(value) { this._undoManager = value } }, /** Description TODO @function */ undo: { enumerable: true, value: function() { if (this.undoManager) { this.undoManager.undo(); } else { this._undo(); } } }, /** Description TODO @function */ redo: { enumerable: true, value: function() { if (this.undoManager) { this.undoManager.redo(); } else { this._redo(); } } }, /** Description TODO @function */ execCommand: { enumerable: false, value: function(command, showUI, value, label) { label = label || this._execCommandLabel[command] || "Typing"; this._executingCommand = true; if (document.execCommand(command, showUI, value)) { this._executingCommand = false; this._stopTyping(); if (this.undoManager && ["selectall"].indexOf(command) == -1 ) { this.undoManager.add(label, this._undo, this, label, this._innerElement); } return true; } else { this._executingCommand = true; return false } } }, /** Description TODO @private @function */ markDirty: { enumerable: false, value: function() { var thisRef = this, prevValue; updateValues = function() { clearTimeout(thisRef._forceUpdateValuesTimeout); delete thisRef._forceUpdateValuesTimeout; clearTimeout(thisRef._updateValuesTimeout); delete thisRef._updateValuesTimeout; if (defaultEventManager.registeredEventListenersForEventType_onTarget_("change@value", this)) { prevValue = thisRef._value; if (thisRef.value !== prevValue) { thisRef.dispatchEvent(MutableEvent.changeEventForKeyAndValue("value" , prevValue).withPlusValue(thisRef.value)); } } if (defaultEventManager.registeredEventListenersForEventType_onTarget_("change@textValue", this)) { prevValue = thisRef._textValue; if (thisRef.textValue !== prevValue) { thisRef.dispatchEvent(MutableEvent.changeEventForKeyAndValue("textValue" , prevValue).withPlusValue(thisRef.textValue)); } } thisRef._dispatchEditorEvent("editorChange"); }; if (!this._needsAssingValue) { // Clear the cached value this._dirtyValue = true; this._dirtyTextValue = true; } if (!this._forceUpdateValuesTimeout) { this._forceUpdateValuesTimeout = setTimeout(updateValues, 1000); } if (this._updateValuesTimeout) { clearTimeout(this._updateValuesTimeout); } this._updateValuesTimeout = setTimeout(updateValues, 100); } } });