/* Copyright (c) 2011, Geert Bergman (geert@scrivo.nl)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of "Scrivo" nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* $Id: HTMLEditControl.js 743 2013-07-18 10:12:39Z geert $
*/
"use strict";
SUI.control.HTMLEditControl = SUI.defineClass(
/** @lends SUI.control.HTMLEditControl.prototype */{
/** @ignore */ baseClass: SUI.control.BaseHTMLEditControl,
/**
* @class
* SUI.control.HTMLEditControl implements a WYSIWIG/HTML edit control. It
* should provide a standardized interface to the different implementations
* of contenteditable in various browsers. Because of code size of the
* class it is splitted in two parts: This class is extended from
* BaseHTMLEditControl which implements all of the technical structure.
* This part implements the actions that can be performed on the content.
*
* @augments SUI.control.BaseHTMLEditControl
*
* @description
* SUI.control.HTMLEditControl constructor
*
* @constructs
*
* @param {inherit} arg
* @param {Function} arg.onContextMenu event handler, see onContextMenu()
* @param {Function} arg.onKeyDown event handler, see onKeyDown()
* @param {Function} arg.onLoad event handler, see onLoad()
* @param {Function} arg.onSelectionChange event handler, see
* onSelectionChange()
*/
initializer: function(arg) {
SUI.control.HTMLEditControl.initializeBase(this, arg);
},
/**
* Return an object containing the command that are enabled given the
* current cursor position/selection in the editor. the following command
* are supported: cut, copy, paste, undo, redo, link, image, anchor,
* pageBreak, removeFormatting, orderedList, unorderedList, indent,
* deIndent.
* @return {Object} an object with the enabled state of all supported
* commands
*/
commandsEnabled: function() {
var cut = false, copy = false, paste = false;
if (SUI.browser.isWebKit) {
// WebKit does not support these so this is an alternative
var sel = this._getSelection();
cut = copy = !sel.isCollapsed;
paste = true;
} else {
// normal method for other browsers
cut = this._editDoc.queryCommandEnabled("Cut");
copy = this._editDoc.queryCommandEnabled("Copy");
// This also triggers onbeforepaste event
this._ieDoBeforePaste = false;
paste = this._editDoc.queryCommandEnabled("Paste");
this._ieDoBeforePaste = true;
}
return {
cut: cut,
copy: copy,
paste: paste,
undo: this._editDoc.queryCommandEnabled("Undo"),
redo: this._editDoc.queryCommandEnabled("Redo"),
link: this._editDoc.queryCommandEnabled("createlink"),
image: this._editDoc.queryCommandEnabled("insertimage"),
anchor: this._editDoc.queryCommandEnabled("createlink"),
pageBreak:
this._editDoc.queryCommandEnabled("inserthorizontalrule"),
removeFormatting:
this._editDoc.queryCommandEnabled("removeformat"),
orderedList:
this._editDoc.queryCommandEnabled("insertorderedlist"),
unorderedList:
this._editDoc.queryCommandEnabled("insertunorderedlist"),
indent: this._editDoc.queryCommandEnabled("indent"),
deIndent: this._editDoc.queryCommandEnabled("outdent")
};
},
/**
* Return an object containing the command state of the commands
* applicable at the current cursor position/selection in the editor.
* the following commands are supported: bold, underline, italic,
* justifyleft, justifyCenter, justifyRight.
* @return {Object} an object with the state of all supported commands
*/
commandStates: function() {
return {
bold: this._editDoc.queryCommandState("Bold"),
underline: this._editDoc.queryCommandState("Underline"),
italic: this._editDoc.queryCommandState("Italic"),
alignLeft: this._editDoc.queryCommandState("justifyleft"),
alignCenter: this._editDoc.queryCommandState("justifyCenter"),
alignRight: this._editDoc.queryCommandState("justifyRight"),
insertUnorderedList:
this._editDoc.queryCommandState("insertUnorderedList"),
insertOrderedList:
this._editDoc.queryCommandState("insertOrderedList")
};
},
/**
* Set the background color of the current editor selection.
* This command transfers the focus to the editor.
* @param {String} col CSS color to set the background to
*/
doBackColor: function(col) {
if (SUI.browser.isIE) {
// In IE we do a setTimeout to settle the focus (r.select creates
// an application error)
var that = this;
setTimeout(function() {
that._execCommand("BackColor", col);
that._onCommandExecuted();
}, 10);
} else {
this._selectWord();
this._execCommand("styleWithCSS", true);
this._execCommand("HiliteColor", col);
this._execCommand("styleWithCSS", false);
this._onCommandExecuted();
}
},
/**
* Toggle the bold state of the current editor selection.
* This command transfers the focus to the editor.
*/
doBold: function() {
this._selectWord();
this._execCommand("Bold");
this._onCommandExecuted();
},
/**
* Find text in the editor. Gecko ignores the wholeWord parameter.
* @param {String} what text to search
* @param {boolean} caseSensitive case sensitive search
* @param {boolean} backward search directions
* @param {boolean} wholeWord search on whole words only (not Gecko)
* @return {boolean} true if the text was found
*/
doFind: function(what, caseSensitive, backward, wholeWord) {
this.focus();
// normalize parameter values (false i.o. null)
caseSensitive = caseSensitive ? true : false;
backward = backward ? true : false;
wholeWord = wholeWord ? true : false;
// find the text in the editor
var strFound = SUI.browser.isIE
? this._ieFind(what, caseSensitive, backward, wholeWord)
: this._editWin.find(what, caseSensitive, backward, false,
wholeWord, false, false);
this._onCommandExecuted();
// return if the search was successful
return strFound ? true : false;
},
/**
* Set the font of the current editor selection.
* This command transfers the focus to the editor.
* @param {String} val The font name (prefixed by "f_")
*/
doFontName: function(val) {
this._selectWord();
if (SUI.browser.isIE) {
// Note: prefix the font name with "f_" (so "f_arial" instead of
// "arial"). This will cause the font tags to be replaced by span
// tags (IE). Browsers that support a span instead of a font tag
// will use that.
this._execCommand("FontName", "f_" + val);
this._fontFix();
} else {
this._execCommand("styleWithCSS", true);
this._execCommand("FontName", val);
this._execCommand("styleWithCSS", false);
}
this._onCommandExecuted();
},
/**
* Set the font size of the current editor selection.
* This command transfers the focus to the editor.
* @param {String} val The font size (prefixed by "s_")
*/
doFontSize: function(val) {
this._selectWord();
// Prefix the font name with "s_" (so "s_200P" instead of "200%").
// This will cause the font tags to be replaced by span tags (IE).
// Browsers that support a span instead of a font tag will use that.
// Unfortunately execCommand fontsize can't be used: no browser
// supports it well.
val = "s_" + val.replace("%", "P");
if (SUI.browser.isIE) {
this._execCommand("FontName", val);
this._fontFix();
} else {
this._execCommand("styleWithCSS", true);
this._execCommand("FontName", val);
this._execCommand("styleWithCSS", false);
this._spanFix();
}
this._onCommandExecuted();
},
/**
* Set the foreground color of the current editor selection.
* This command transfers the focus to the editor.
* @param {String} col CSS color to set the background to
*/
doForeColor: function(col) {
this._selectWord();
if (SUI.browser.isIE) {
// In IE we do a setTimeout to settle the focus (r.select creates
// an application error)
var that = this;
setTimeout(function() {
that._execCommand("ForeColor", col);
that._fontFix();
that._onCommandExecuted();
}, 10);
} else {
this._execCommand("styleWithCSS", true);
this._execCommand("ForeColor", col);
this._execCommand("styleWithCSS", false);
this._onCommandExecuted();
}
},
/**
* Format the block that is currently selected (ie. P, H1, H2 etc)
* This command transfers the focus to the editor.
* @param {String} val the block format tag name
*/
doFormatBlock: function(val) {
this._execCommand("FormatBlock", "<"+val+">");
this._onCommandExecuted();
},
/**
* Get the acronym node that is parent to the current editor selection.
* @return {HTMLElementNode} the parent acronym node or null if there is
* none
*/
doGetAbbr: function() {
var e = this.getSelectedElement();
// traverse up the dom tree until the root only checking element nodes
while (e != null && e.nodeType != 9) {
if (e.nodeType == 1 && e.tagName=="ACRONYM") {
return e;
} else {
e = e.parentNode;
}
}
return null;
},
/**
* Get the span:lang node that is parent to the current editor selection.
* @return {HTMLElementNode} the parent span:lang node or null if there is
* none
*/
doGetLang: function() {
var e = this.getSelectedElement();
// traverse up the dom tree until the root only checking element nodes
while (e != null && e.nodeType != 9) {
if (e.nodeType == 1 && e.tagName=="SPAN") {
if (e.lang != "" && e.lang != null) {
return e;
}
}
e = e.parentNode;
}
return null;
},
/**
* Get the a node that is parent to the current editor selection.
* @return {HTMLElementNode} the parent a node or null if there is none
*/
doGetLink: function() {
var e = this.getSelectedElement();
// traverse up the dom tree until the root only checking element nodes
while (e!=null && e.nodeType != 9) {
if (e.nodeType == 1 && e.tagName=="A") {
return e;
} else {
e=e.parentNode;
}
}
return null;
},
/**
* Get the selected image is selected in the editor. Note: for IE we
* return a reference to the img node, for other browsers we create
* a copy.
* @return {HTMLElementNode} an img node or null if there is none
*/
doGetImg: function() {
var img = null;
if (SUI.browser.isIE) {
var s = this._getSelection();
// it is proven code but I'm not sure about this (why not
// s.type == "Control" ?)
if (s.type != "None" && s.type != "Text") {
img = s.createRange().item(0);
// and then this loop up the dom tree ???
while (img != null && img.tagName != "IMG"){
img = img.parentNode;
}
}
} else {
var r = this._getCurrentRange();
// create a copy: overwrite later
var fr = r.cloneContents();
// find the first image in the selected range
for (var i=0; i<fr.childNodes.length; i++) {
var n = fr.childNodes[i];
if (n.nodeType==1 && n.tagName=="IMG") {
img = n;
break;
}
}
}
return img;
},
/**
* Indent the current paragraph or create a deeper level in a list.
* This command transfers the focus to the editor.
*/
doIndent: function() {
this._execCommand("Indent");
this._onCommandExecuted();
},
/**
* Insert a acronym node into the editor content. Unfortunately no abbr
* because IE does not support this. The acronym gets the 'sys_abbr'
* system style.
* This command transfers the focus to the editor.
* @param {String} fullText the full text for the abbreviation
*/
doInsertAbbr: function(fullText) {
this.doInsertInlineElement("ACRONYM",
{"class": "sys_abbr", title: fullText});
},
/**
* Insert an anchor node into the editor content. The anchor gets the
* 'sys_anchor' system style.
* This command transfers the focus to the editor.
* @param {String} name the name for the anchor
*/
doInsertAnchor: function(name) {
name=name.replace(/[\s]+/g,"_");
this.doInsertInlineElement("A", {name: name, "class": "sys_anchor"});
},
/**
* Insert a horizontal ruler into the editor content.
* This command transfers the focus to the editor.
*/
doInsertHorizontalRule: function() {
this._execCommand("InsertHorizontalRule");
this._onCommandExecuted();
},
/**
* Insert any html you like into the editor content.
* This command transfers the focus to the editor.
* @param {String} html the HTML code to insert
*/
doInsertHTML: function(html) {
if (SUI.browser.isIE) {
var that = this;
setTimeout(function() {
that.focus();
var r = that._getCurrentRange();
r.collapse(true);
r.select();
r.pasteHTML(html);
that._onCommandExecuted();
}, 10);
} else {
this._execCommand("inserthtml", html);
this._onCommandExecuted();
}
},
/**
* Insert an image into the editor content.
* This command transfers the focus to the editor.
* @param {HTMLElementNode} img an HTML image node to insert
*/
doInsertImg: function(img) {
if (SUI.browser.isIE) {
// In IE we do a setTimeout to settle the focus (r.select creates
// an application error)
var that = this;
setTimeout(function() {
that.focus();
// why not doInsertHTML ??
var r = that._getCurrentRange();
r.pasteHTML(img.outerHTML);
r.select();
that._onCommandExecuted();
}, 10);
} else {
this.focus();
// doUpdateImg does an insert
this.doUpdateImg(img);
// make it possible to select the image in WebKit
this._webKitImgFix();
this._onCommandExecuted();
}
},
/**
* Insert an inline element into the editor content.
* This command transfers the focus to the editor.
* @param {String} tag The html tag name of the element
* @param {Object} attr an object with name-values pairs
*/
doInsertInlineElement: function(tag, attr) {
if (SUI.browser.isIE) {
// In IE we do a setTimeout to settle the focus (r.select creates
// an application error)
var that = this;
setTimeout(function() {
that.focus();
var elem = that._editDoc.createElement(tag);
SUI.browser.setAttributes(elem, attr);
var s = that._getSelection();
var r = that._getCurrentRange(s);
// if necessary expand a collapsed range to single word selection
if (s.type == "None") {
// if necessary expand collapsed range to single word selection
that._rangeSelectWord(r);
r.select();
}
// if the previous step failed and range is still collapsed ...
if (r.text == "" || s.type == "None") {
// ... then there is nothing to do
return;
}
if (s.type == "Text") {
// if there is a text selection overwrite it with the html
elem.innerHTML = r.htmlText;
r.pasteHTML(elem.outerHTML);
} else {
// if the selection was element then replace that element
// with ours and then append it to ours
//el = r.commonParentElement();
//el.parentElement.replaceChild(elem, el);
//elem.appendChild(el);
// TODO: No create a childnode instead, but that is also not
// fully correct (common parent can be more that the selection)
el = SUI.browser.version < 9
? r.commonParentElement() : r.item(0);
elem.innerHTML = el.innerHTML;
r.pasteHTML(elem.outerHTML);
}
that._onCommandExecuted();
}, 10);
} else {
this.focus();
var elem = this._editDoc.createElement(tag);
SUI.browser.setAttributes(elem, attr);
var s = this._getSelection();
var r = this._getCurrentRange(s);
if (r.collapsed) {
// if necessary expand collapsed range to single word selection
this._rangeSelectWord(r);
}
// if the previous step failed and range is still collapsed ...
if (r.collapsed) {
// ... then there is nothing to do
return;
}
// set the innerHTML of our element to the selected html fragment
elem.innerHTML = this._outerHTML(r.cloneContents());
var html = this._outerHTML(elem);
// add the range to the selection
s.removeAllRanges();
s.addRange(r);
// and paste the new html over it
this._execCommand("inserthtml", html);
this._onCommandExecuted();
}
},
/**
* Insert a language mark into the editor content. The language mark gets
* the 'sys_anchor' system style.
* This command transfers the focus to the editor.
* @param {String} language a language code
*/
doInsertLang: function(language) {
this.doInsertInlineElement("SPAN",
{"class": "sys_language", lang: language});
},
/**
* Insert a hyperlink into the editor content.
* This command transfers the focus to the editor.
* @param {Object} attr the attributes of the link
*/
doInsertLink: function(attr) {
if (SUI.browser.isIE) {
// In IE we do a setTimeout to settle the focus (r.select creates
// an application error)
var that = this;
setTimeout(function() {
that.focus();
// don't use doInsertInlineElement because we can use execCommand
var s = that._editDoc.selection;
var r = that._getCurrentRange(s);
// if necessary expand a collapsed range to single word selection
if (s.type == "None") {
// if necessary expand collapsed range to single word selection
that._rangeSelectWord(r);
r.select();
}
// if the previous step failed and range is still collapsed ...
if (r.text == "" || s.type == "None") {
// ... then there is nothing to do
return;
}
if (s.type == "Text" || s.type == "None") {
// craete the link with execCommand
that._execCommand("CreateLink", attr.href);
// an try to find it in the editor content
var anA = r.parentElement();
if (anA.tagName != "A") {
anA = anA.firstChild;
}
// if we found it then set the other attributes
if (anA.tagName == "A") {
SUI.browser.setAttributes(anA, attr);
r.collapse(false);
}
} else {
var anAnchor = that._editDoc.createElement("A");
SUI.browser.setAttributes(anAnchor, attr);
// if the selection was element then replace this element
// with ours and then append it to ours
el = SUI.browser.version < 9
? r.commonParentElement() : r.item(0);
el.parentElement.replaceChild(anAnchor, el);
anAnchor.appendChild(el);
}
that._onCommandExecuted();
}, 10);
} else {
this.focus();
this.doInsertInlineElement("A", attr);
}
},
/**
* Insert an node into the editor content. Unlike doInsertInlineElement
* this command overwrites the current selection.
* This command transfers the focus to the editor.
* @param {HTMLElementNode} node The node to insert
*/
doInsertNode: function(node) {
this.doInsertHTML(this._outerHTML(node));
this._onCommandExecuted();
},
/**
* Change a paragraph into the first element of (or a couple of
* paragraphs into) an ordered list.
* This command transfers the focus to the editor.
*/
doInsertOrderedList: function() {
this._execCommand("InsertOrderedList");
this._onCommandExecuted();
},
/**
* Change a paragraph into the first element of (or a couple of
* paragraphs into) an unordered list.
* This command transfers the focus to the editor.
*/
doInsertUnorderedList: function() {
this._execCommand("InsertUnorderedList");
this._onCommandExecuted();
},
/**
* Toggle the italic state of the current editor selection.
* This command transfers the focus to the editor.
*/
doItalic: function() {
this._selectWord();
this._execCommand("Italic");
this._onCommandExecuted();
},
/**
* Justify current editor paragraph or paragraphs in a selection
* to the Center.
* This command transfers the focus to the editor.
*/
doJustifyCenter: function() {
this._execCommand("JustifyCenter");
this._onCommandExecuted();
},
/**
* Justify current editor paragraph or paragraphs in a selection
* to the Left.
* This command transfers the focus to the editor.
*/
doJustifyLeft: function() {
this._execCommand("JustifyLeft");
this._onCommandExecuted();
},
/**
* Justify current editor paragraph or paragraphs in a selection
* to the Right.
* This command transfers the focus to the editor.
*/
doJustifyRight: function() {
this._execCommand("JustifyRight");
this._onCommandExecuted();
},
/**
* De-indent the current paragraph or move a list item to a higher level
* in a list.
* This command transfers the focus to the editor.
*/
doOutdent: function() {
this._execCommand("Outdent");
this._onCommandExecuted();
},
/**
* Move up on the editor's undo stack.
* This command transfers the focus to the editor.
*/
doRedo: function() {
this._execCommand("Redo");
this._onCommandExecuted();
},
/**
* Remove an anchor from the editor content.
* This command transfers the focus to the editor.
* @param {HTMLElementNode} link reference to the anchor element to remove
*/
doRemoveAnchor: function(link) {
if (link.href) {
// if the anchor a href too only remove the name and class
this.doUpdateInlineElement(link, {name: null, "class": ""});
} else {
this.doRemoveNode(link);
}
},
/**
* Remove all formatting from the current editor selection.
* This command transfers the focus to the editor.
*/
doRemoveFormat: function() {
if (SUI.browser.isIE) {
this._execCommand("RemoveFormat");
// And do some more work to remove the style attributes in
// the current selection too
var sel = this._editDoc.selection.createRange();
var oRng1 = this._editDoc.body.createTextRange();
var l = this._editDoc.body.getElementsByTagName("*");
for (var i=l.length-1; i>=0; i--) {
oRng1.moveToElementText(l[i]);
if (1 == oRng1.compareEndPoints("StartToStart", sel)
&& -1 == oRng1.compareEndPoints("StartToEnd", sel)) {
l[i].removeAttribute("style");
}
}
} else {
this._execCommand("RemoveFormat");
}
this._onCommandExecuted();
},
/**
* Remove all formatting from the current editor selection.
* @param {HTMLElementNode} link reference to the anchor element to remove
*/
doRemoveLink: function(link) {
this.doRemoveNode(link);
// if the link also was an anchor reappend the anchor
if (link.name) {
this.doInsertAnchor(link.name);
}
},
/**
* Remove node from the editor but keep its contents.
* @param {HTMLElementNode} el reference to the node to remove
*/
doRemoveNode: function(el) {
if (SUI.browser.isIE) {
// can't use execCommand, this breaks the undo stack
el.removeNode(false);
} else {
// select node
var r = this._getCurrentRange();
r.selectNode(el);
var s = this._getSelection();
s.removeAllRanges();
s.addRange(r);
// remove it
this._execCommand("delete", false);
// and paste the contents
this._execCommand("inserthtml", el.innerHTML);
}
this._onCommandExecuted();
},
/**
* Replace selected text in the editor (think search and replace). There
* needs to be selected text, this is not a text insert function.
* @param {String} text text to replace the selected text
*/
doReplace: function(text) {
if (SUI.browser.isIE) {
this.focus();
// get the current range
var rng = this._getCurrentRange();
// check if there is data to replace, then replace
if (rng.text != "") {
rng.text = text;
}
} else {
// get the current range
var r = this._getCurrentRange();
// check if there is data to replace, then replace
if (r.toString() != "") {
this._execCommand("inserthtml", text);
}
}
this._onCommandExecuted();
},
/**
* Replace all occurances of a text in the editor. The search direction
* is ignored: replaceAll from current cursor position might be
* implemented in the future. Gecko ignores the wholeWord parameter.
* @param {String} what text to search and replace
* @param {String} text new text
* @param {boolean} caseSensitive case sensitive search
* @param {boolean} backward search direction, ignored: always foreward
* @param {boolean} wholeWord search on whole words only
* @return {boolean} the number of replacements made
*/
doReplaceAll: function(what, text, caseSensitive, backward, wholeWord) {
var cnt = 0;
this.focus();
// normalize parameter values (false i.o. null)
caseSensitive = caseSensitive ? true : false;
backward = false;
wholeWord = wholeWord ? true : false;
if (SUI.browser.isIE) {
// set the current range to the beginning of the document
var rng = this._getCurrentRange();
rng.expand("textedit");
rng.collapse(true);
rng.select();
// while found replace text
while (this._ieFind(what, caseSensitive, backward, wholeWord)) {
cnt++;
this.doReplace(text);
}
} else {
// set the current selection to the beginning of the document
var s = this._getSelection();
var rng = this._editDoc.createRange();
rng.selectNode(this._editDoc.body);
rng.collapse(true);
s.removeAllRanges();
s.addRange(rng);
// while found replace text
while (this._editWin.find(what, caseSensitive, backward,
false, wholeWord, false, false)) {
cnt++;
this.doReplace(text);
}
}
this._onCommandExecuted();
return cnt;
},
/**
* Select the whole content of the editor.
* This command transfers the focus to the editor.
*/
doSelectAll: function() {
this._execCommand("SelectAll");
this._onCommandExecuted();
},
/**
* Toggle the underline state of the current editor selection.
* This command transfers the focus to the editor.
*/
doUnderline: function() {
this._selectWord();
this._execCommand("Underline");
this._onCommandExecuted();
},
/**
* Move down on the editor's undo stack.
* This command transfers the focus to the editor.
*/
doUndo: function() {
this._execCommand("Undo");
this._onCommandExecuted();
},
/**
* Update an abbreviation in the editor content.
* This command transfers the focus to the editor.
* @param {HTMLElementNode} el a reference to the abbreviation element to
* update
* @param {String} fullText the new full text for the abbreviation
*/
doUpdateAbbr: function(el, fullText) {
this.doUpdateInlineElement(el, {title: fullText});
},
/**
* Update an anchor element in the editor content.
* This command transfers the focus to the editor.
* @param {HTMLElementNode} link a reference to the anchor element to
* update
* @param {String} name the new anchor name
*/
doUpdateAnchor: function(link, name) {
name=name.replace(/[\s]+/g,"_");
this.doUpdateInlineElement(link, {name: name, "class": "sys_anchor"});
},
/**
* Update an image element in the editor content.
* This command transfers the focus to the editor.
* @param {HTMLElementNode} img te new image node to replace the old one
*/
doUpdateImg: function(img) {
if (SUI.browser.isIE) {
this.focus();
// done by setting properties
} else {
// overwrite the current selection with new img-html
this._execCommand("inserthtml", this._outerHTML(img));
this._webKitImgFix();
}
this._onCommandExecuted();
},
/**
* Update an inline element in the editor content.
* This command transfers the focus to the editor.
* @param {HTMLElementNode} el a reference to element to update
* @param {Object} attr the new attributes for the element
*/
doUpdateInlineElement: function(el, attr) {
if (SUI.browser.isIE) {
this.focus();
SUI.browser.setAttributes(el, attr);
} else {
// place te node into a range
var r = this._editDoc.createRange();
r.selectNode(el);
// clone that range an add attributes
var c = r.cloneContents();
SUI.browser.setAttributes(c.childNodes[0], attr);
// select te range
var s = this._getSelection();
s.removeAllRanges();
r.selectNode(el);
s.addRange(r);
// and overwrite with new html
this._execCommand("inserthtml", this._outerHTML(c));
}
this._onCommandExecuted();
},
/**
* Update a language mark in the editor content.
* This command transfers the focus to the editor.
* @param {HTMLElementNode} el a reference to the language mark to update
* @param {String} language the new language code for the marker
*/
doUpdateLang: function(el, language) {
this.doUpdateInlineElement(el, {lang: language});
},
/**
* Update a link in the editor content.
* This command transfers the focus to the editor.
* @param {HTMLElementNode} link a reference to the link to update
* @param {String} attr the new attributes for the link
*/
doUpdateLink: function(link, attr) {
this.doUpdateInlineElement(link, attr);
},
_onCommandExecuted: function() {
// update the undo state
this._undoState = (this._editDoc.queryCommandEnabled("Undo") ? 1 : 0)
+ (this._editDoc.queryCommandEnabled("Redo") ? 2 : 0);
this.callListener("onCommandExecuted");
}
});