blob: c9bba989454003120f124a18162c831352188573 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
// Note: Mozilla/Firefox does not allow unprivileged scripts to invoke the cut,
// copy and paste commands. The Javascript must either be signed
// (see http://www.mozilla.org/projects/security/components/signed-scripts.html),
// or the users must change their preferences
// (see http://www.mozilla.org/editor/midasdemo/securityprefs.html).
// Alternatively, the users can use the ctrl-x, ctrl-c and ctrl-v keys.
//------------------------------------------------------------------------------
// Note: The SWT component in eclipse 3.4.X and above has a bug that
// keyReleased event can't be notified to listener. See (https://bugs.eclipse.org/bugs/show_bug.cgi?id=280146).
// This issue has blocked EPF upgrade to 1.5.0.5. As a workaround, capture keyReleased event in
// Javascript. Once this issue is resolved in SWT, we need to resume it
var STATUS_NOP = 0;
var STATUS_INITIALIZED = 1;
var STATUS_MODIFIED = 2;
var STATUS_GET_TEXT = 3;
var STATUS_KEY_DOWN = 4;
var STATUS_KEY_UP = 5;
var STATUS_SELECT_TEXT = 6;
var STATUS_SELECT_CONTROL = 7;
var STATUS_SELECT_NONE = 8;
var STATUS_EXEC_CMD = 9;
var STATUS_REFORMAT_LINKS = 10;
var KEY_ARROW_DOWN = 40;
var KEY_ARROW_LEFT = 37;
var KEY_ARROW_RIGHT = 39;
var KEY_ARROW_UP = 38;
var KEY_BACKSPACE = 8;
var KEY_END = 35;
var KEY_HOME = 36;
var KEY_PAGE_DOWN = 34;
var KEY_PAGE_UP = 33;
var KEY_TAB = 9;
var KEY_B = 66;
var KEY_U = 85;
var KEY_I = 73;
var KEY_C = 67;
var KEY_F = 70;
var KEY_S = 83;
var KEY_V = 86;
var KEY_X = 88;
var KEY_Z = 90;
var CMD_COPY = "copy";
var CMD_CUT = "cut";
var CMD_FIND_TEXT = "findText";
var CMD_PASTE = "paste";
var CMD_SAVE = "save";
var CMD_SAVE_ALL = "saveAll";
var TABLE_HEADERS_NONE = 0;
var TABLE_HEADERS_COLS = 1;
var TABLE_HEADERS_ROWS = 2;
var TABLE_HEADERS_BOTH = 3;
var BOLD = 1;
var ITALIC = BOLD << 1;
var UNDERLINE = ITALIC << 1;
var SUBSCRIPT = UNDERLINE << 1;
var SUPERSCRIPT = SUBSCRIPT << 1;
var editorId;
var editorCSS;
var baseHREF;
var supportRichTextEditing = true;
var editorDoc;
var selection;
var selectionRange;
var readOnly = false;
var initialized = false;
var modified = false;
var checkResizeElement;
var selectionInfo = null;
// Initializes the editor.
function initEditor(id, css, baseURL) {
editorId = id;
editorCSS = css;
baseHREF = baseURL;
try {
enableRichTextEditing('');
initialized = true;
setStatus(STATUS_INITIALIZED, null);
}
catch (e) {
supportRichTextEditing = false;
}
}
var excludeModify = new Array(16, KEY_ARROW_DOWN, KEY_ARROW_LEFT,
KEY_ARROW_RIGHT, KEY_ARROW_UP, KEY_END, KEY_HOME, KEY_PAGE_DOWN,
KEY_PAGE_UP, KEY_TAB);
function keyReleased(event) {
var keyCode = event.keyCode;
if (keyCode == 0 && !document.all) {
keyCode = event.charCode;
switch (keyCode) {
case 98:
keyCode = KEY_B;
break;
case 99:
keyCode = KEY_C;
break;
case 102:
keyCode = KEY_F;
break;
case 105:
keyCode = KEY_I;
break;
case 115:
keyCode = KEY_S;
break;
case 117:
keyCode = KEY_U;
break;
case 118:
keyCode = KEY_V;
break;
case 120:
keyCode = KEY_X;
break;
case 122:
keyCode = KEY_Z;
break;
}
}
var ctrlKey = event.ctrlKey;
var shiftKey = event.shiftKey;
var altKey = event.altKey;
if ( !ctrlKey && !altKey ) {
var modified = true;
for (var i = 0; i < excludeModify.length; i++ ) {
if ( keyCode == excludeModify[i] ) {
modified = false;
break;
}
}
if ( modified == true ) {
setTimeout("setStatus(STATUS_MODIFIED, null);", 10);
}
}
}
// Handles the key events.
function keyPressed(event) {
var keyCode = event.keyCode;
if (keyCode == 0 && !document.all) {
keyCode = event.charCode;
switch (keyCode) {
case 98:
keyCode = KEY_B;
break;
case 99:
keyCode = KEY_C;
break;
case 102:
keyCode = KEY_F;
break;
case 105:
keyCode = KEY_I;
break;
case 115:
keyCode = KEY_S;
break;
case 117:
keyCode = KEY_U;
break;
case 118:
keyCode = KEY_V;
break;
case 120:
keyCode = KEY_X;
break;
case 122:
keyCode = KEY_Z;
break;
}
}
var ctrlKey = event.ctrlKey;
var shiftKey = event.shiftKey;
switch(keyCode) {
case KEY_ARROW_DOWN:
case KEY_ARROW_LEFT:
case KEY_ARROW_RIGHT:
case KEY_ARROW_UP:
case KEY_END:
case KEY_HOME:
case KEY_PAGE_DOWN:
case KEY_PAGE_UP:
break;
case KEY_TAB:
if ( readOnly || !ctrlKey)
break;
var editorWindow = document.getElementById(editorId).contentWindow;
var editorDocument = editorWindow.document;
var tabText = "&nbsp;&nbsp;&nbsp;&nbsp;";
if(document.all ){
event.keyCode = -1;
event.returnValue=false; //IE: cancel the default behavior
var rng=editorDocument.selection.createRange();
rng.pasteHTML(tabText); //insert 4 spaces for one tab key
} else {
event.preventDefault();
editorDoc.execCommand("insertHTML","",tabText);
}
setTimeout("setStatus(STATUS_MODIFIED, null);", 10);
break;
case KEY_BACKSPACE:
if (!readOnly) {
setTimeout("setStatus(STATUS_MODIFIED, null);", 10);
}
break;
case KEY_B:
case KEY_U:
case KEY_I:
if (!readOnly && ctrlKey) {
setTimeout("setStatus(STATUS_MODIFIED, null);",10);
}
break;
case KEY_C:
if (ctrlKey) {
setStatus(STATUS_KEY_DOWN, CMD_COPY);
}
else if (!document.all && readOnly) {
event.preventDefault();
}
break;
case KEY_F:
if (ctrlKey) {
if (document.all) {
event.keyCode = -1;
event.returnValue = false;
}
else {
event.preventDefault();
}
setStatus(STATUS_KEY_DOWN, CMD_FIND_TEXT);
}
else if (!document.all && readOnly) {
event.preventDefault();
}
break;
case KEY_S:
if (!readOnly && ctrlKey) {
if (document.all) {
event.keyCode = -1;
event.returnValue = false;
}
else {
event.preventDefault();
}
if (shiftKey) {
setStatus(STATUS_KEY_DOWN, CMD_SAVE_ALL);
}
else {
setStatus(STATUS_KEY_DOWN, CMD_SAVE);
}
}
else if (!document.all && readOnly) {
event.preventDefault();
}
break;
case KEY_V:
if (ctrlKey) {
if (document.all) {
event.keyCode = -1;
event.returnValue = false;
if (!readOnly) {
setStatus(STATUS_KEY_DOWN, CMD_PASTE);
}
}
else {
if (!readOnly) {
// Workaround Mozilla/Firefox paste issues.
setTimeout("setStatus(STATUS_KEY_DOWN, CMD_PASTE);", 10);
}
else {
event.preventDefault();
}
}
}
else if (!document.all && readOnly) {
event.preventDefault();
}
break;
case KEY_X:
if (ctrlKey) {
setStatus(STATUS_KEY_DOWN, CMD_CUT);
}
else if (!document.all && readOnly) {
event.preventDefault();
}
break;
case KEY_Z:
if (!readOnly && ctrlKey) {
setTimeout("setStatus(STATUS_MODIFIED, null);", 10);
}
else if (!document.all && readOnly) {
event.preventDefault();
}
break;
default:
if (!document.all && readOnly) {
event.preventDefault();
}
}
}
function selChanged(event) {
updateSelection();
}
function enableRichTextEditing(html) {
var doc = document.getElementById(editorId).contentWindow.document;
doc.designMode = "on";
var htmlSrc = '<html><head><title></title>';
if (editorCSS != null && editorCSS != '') {
htmlSrc += '<link rel="StyleSheet" href="' + editorCSS + '" type="text/css"/>';
}
if (baseHREF != null && baseHREF != '') {
htmlSrc += '<base href="' + baseHREF + '"/>';
}
if (!document.all && html == '') {
// Mozilla/Firefox will only display the caret if <br/> is added to the HTML body.
// Adding <br/> also enables the backspace and delete key by default. Otherwise, the
// user need to enter some text before these 2 keys start to function.
html = "<br />";
}
htmlSrc += '</head><body>' + html + '</body></html>';
doc.open();
doc.write(htmlSrc);
doc.close();
modified = false;
if ("attachEvent" in doc) {
doc.attachEvent("onkeydown", keyPressed);
doc.attachEvent("onkeyup", keyReleased);
doc.attachEvent("onselectionchange", selChanged);
// for DnD (internal)
doc.body.attachEvent("ondrop", checkModified);
// for image/table resizing:
doc.body.attachEvent("onresizeend", checkModified);
}
if ("addEventListener" in doc) {
doc.addEventListener("keypress", keyPressed, true);
doc.addEventListener("keyrelease", keyReleased, true);
doc.addEventListener("keypress", selChanged, false);
doc.addEventListener("mouseup", selChanged, false);
doc.addEventListener("dragdrop", checkModified, false);
// check mouseup event for image/table resizing
doc.addEventListener("mouseup", checkModified, false);
}
setStatus(STATUS_EXEC_CMD, 1);
}
// this one is for modification check on drag n drop within the RTE
// checkModified listener
function checkModified(event) {
setTimeout("setStatus(STATUS_MODIFIED, null);", 10);
}
// Sets the height of the editor.
function setHeight(height) {
if (initialized) {
document.getElementById(editorId).height = height + "px";
}
}
// Sets the status.
// Note: By default, Firefox disables changes to the status bar. For this to work, the user
// must set the global preference "dom.disable_window_status_change" to false.
// For Firefox 1.0.x, this setting can be made in /usr/firefox-1.0.7/defaults/pref/firefox.js.
function setStatus(type, value) {
var status = '$$$' + type;
if (value != null && value != '') {
status += ('$' + value);
}
window.status = status;
window.status = '$$$' + STATUS_NOP;
}
// Returns the HTML source.
function getHTML() {
var html = document.getElementById(editorId).contentWindow.document.body.innerHTML;
if (html == "<P>&nbsp;</P>") {
html = "";
}
if (html != null && html != '') {
var regEx = new RegExp("\"file\:([^=]*)(/resources/)([^\"]+)\"", "g");
html = html.replace(regEx, "\"./resources/$3\"");
regEx = new RegExp("\"file\:([^=]*)/#([^\"]+)\"", "g");
html = html.replace(regEx, "\"#$2\"");
}
return html;
}
//Returns the HTML source to the Java layer
function getText() {
var html = getHTML();
setStatus(STATUS_GET_TEXT, html);
return html;
}
function setInnerHTML(html) {
if (document.all) {
// IE has problem setting complex HTML set via doc.body.innerHTML.
enableRichTextEditing(html);
}
else {
if (html == '') {
// Mozilla/Firefox will only display the caret if <br/> is added to the HTML body.
html = "<br/>";
}
var doc = document.getElementById(editorId).contentWindow.document;
if (doc.body != null) {
doc.body.innerHTML = html;
}
else {
// Mozilla/Firefox can take a while to initialize document.body
// after document.write().
try {
setTimeout("setInnerHTML('" + html + "');", 10);
}
catch (e) {
}
}
}
}
// Sets the HTML source.
function setText(html) {
if (supportRichTextEditing) {
html = decodeString(html);
selectionInfo = getSelectionInfo();
setInnerHTML(html);
if (selectionInfo != null) {
setTimeout("setSelection(selectionInfo);", 10);
}
modified = false;
setStatus(STATUS_EXEC_CMD, 1);
}
}
function setSelection(selectionInfo) {
if (!supportRichTextEditing) {
return;
}
contentWindow = document.getElementById(editorId).contentWindow;
editorDoc = contentWindow.document;
try {
if (document.all) {
var startOffset = selectionInfo.start;
var len = selectionInfo.len;
if (startOffset == 0 && len == 0) {
return;
}
var tempRange = editorDoc.body.createTextRange();
tempRange.moveStart('character', startOffset);
tempRange.collapse();
tempRange.moveEnd('character', len);
tempRange.select();
tempRange.scrollIntoView();
} else {
selection = this.window.getSelection();
var startContainer = selectionInfo.startContainer;
var start = selectionInfo.start;
var endContainer = selectionInfo.endContainer;
var end = selectionInfo.end;
var tempRange = document.createRange();
tempRange.setStart(startContainer, start);
tempRange.setEnd(endContainer, end);
selection.removeAllRanges();
selection.addRange(tempRange);
contentWindow.focus();
}
} catch (e) {
}
}
function getSelectionInfo() {
if (!supportRichTextEditing) {
return null;
}
contentWindow = document.getElementById(editorId).contentWindow;
editorDoc = contentWindow.document;
var tempSelRange;
try {
if (document.all) {
selection = editorDoc.selection;
if (selection != null) {
tempSelRange = selection.createRange();
}
// length of selection
var tempSelLen = tempSelRange.text.length;
// create new range
var tempRange = editorDoc.body.createTextRange();
// set end of new range to start of selection
// this will throw an exception if tempSelRange is not in editor.doc.body (ie, at the start of the RTE).
tempRange.setEndPoint("EndToStart", tempSelRange);
// length of new range is the start offset
var tempText = tempRange.text;
// IE counts newlines as 2 characters for length property, but they count as 1 when using moveStart so remove the \r to make the count the same
tempText = tempText.replace(/\r/g, "");
var startOffset = tempText.length;
return {start:startOffset, len:tempSelLen};
} else {
selection = contentWindow.getSelection();
if (selection != null) {
tempSelRange = selection.getRangeAt(selection.rangeCount - 1).cloneRange();
}
return {startContainer: tempSelRange.startContainer, start:tempSelRange.startOffset,
endContainer: tempSelRange.endContainer, end:tempSelRange.endOffset};
}
} catch (e) {
return null;
}
}
// Decodes the HTML passed from the Java layer.
function decodeString(str) {
if (str != null && str != '') {
if (document.all) {
str = str.replace(/%sq%/g, "'");
str = str.replace(/%EOL%/g, "\n");
}
else {
str = str.replace(/%sq%/g, "&apos;");
str = str.replace(/%EOL%/g, "");
str = str.replace(/\n/g, "");
}
}
return str;
}
// updates selection without notifying the Java layer of the selection state
function internalUpdateSelection() {
if (!supportRichTextEditing) {
return false;
}
contentWindow = document.getElementById(editorId).contentWindow;
editorDoc = contentWindow.document;
if (document.all) {
selection = editorDoc.selection;
if (selection != null) {
selectionRange = selection.createRange();
reformatElementLinks();
}
}
else {
selection = contentWindow.getSelection();
if (selection != null) {
selectionRange = selection.getRangeAt(selection.rangeCount - 1).cloneRange();
if (selectionRange.startContainer.nodeName == "HTML" &&
selectionRange.endContainer.nodeName == "HTML") {
// Mozilla selects the whole document when there's no RTE content, so select just the body
selectionRange = editorDoc.createRange();
selectionRange.setStart(editorDoc.body, 0);
selectionRange.setEnd(editorDoc.body, 0);
}
}
}
return true;
}
// Updates the current selection and selection range.
function updateSelection() {
if (!supportRichTextEditing) {
return false;
}
contentWindow = document.getElementById(editorId).contentWindow;
editorDoc = contentWindow.document;
var tempSelRange;
var selOffsetStart = 0;
var selectedText = "";
var fontName = "";
var fontSize = "";
var blockStyle = "";
var textFlags = 0;
if (document.all) {
selection = editorDoc.selection;
if (selection != null) {
selectionRange = selection.createRange();
if (selectionRange != null && selection.type != "Control") {
tempSelRange = selectionRange.duplicate();
}
reformatElementLinks();
}
}
else {
selection = contentWindow.getSelection();
if (selection != null) {
selectionRange = selection.getRangeAt(selection.rangeCount - 1).cloneRange();
tempSelRange = selectionRange.cloneRange();
}
}
if (tempSelRange != null) {
try {
if (document.all) {
if (selectionRange.text) {
selectedText = selectionRange.text;
}
/* for getting selection offset - commented because we can't select the
* proper location in the HTML source tab because JTidy's reformatting of the HTML
var html = getHTML();
var tempSelLen = tempSelRange.htmlText.length;
tempSelRange.moveStart('character', -html.length);
selOffsetStart = tempSelRange.htmlText.length - tempSelLen;
*/
var selParent = tempSelRange.parentElement();
fontName = tempSelRange.queryCommandValue('fontName');
fontSize = tempSelRange.queryCommandValue('fontSize');
blockStyle = tempSelRange.queryCommandValue('formatBlock');
if (blockStyle == "Normal") {
if (selParent.className == "quote") {
blockStyle = "<quote>";
} else if (selParent.className == "codeSample") {
blockStyle = "<code>";
} else {
blockStyle = "<p>";
}
} else if (blockStyle == "Heading 3") {
blockStyle = "<h3>";
} else if (blockStyle == "Heading 4") {
blockStyle = "<h4>";
} else if (blockStyle == "Heading 5") {
blockStyle = "<h5>";
} else if (blockStyle == "" || blockStyle == null) {
blockStyle = "<p>";
}
if (tempSelRange.queryCommandValue('bold') == true) {
textFlags |= BOLD;
}
if (tempSelRange.queryCommandValue('italic') == true) {
textFlags |= ITALIC;
}
if (tempSelRange.queryCommandValue('underline') == true) {
textFlags |= UNDERLINE;
}
if (tempSelRange.queryCommandValue('subscript') == true) {
textFlags |= SUBSCRIPT;
}
if (tempSelRange.queryCommandValue('superscript') == true) {
textFlags |= SUPERSCRIPT;
}
setStatus(STATUS_SELECT_TEXT, /* selOffsetStart + "$" + */
fontName + "$" + fontSize + "$" + blockStyle + "$" + textFlags + "$" + selectedText);
} else {
if (selectionRange != null) {
selectedText = selectionRange.toString();
}
var selParent = selection.focusNode;
fontName = editorDoc.queryCommandValue('fontName');
if (fontName == "") {
fontName = "default";
}
fontSize = editorDoc.queryCommandValue('fontSize');
if (fontSize == "") {
fontSize = "default";
}
blockStyle = editorDoc.queryCommandValue('formatBlock');
if (blockStyle == "p") {
if (selParent.parentNode.className == "quote") {
blockStyle = "<quote>";
} else if (selParent.parentNode.className == "codeSample") {
blockStyle = "<code>";
} else {
blockStyle = "<p>";
}
} else if (blockStyle == "h3") {
blockStyle = "<h3>";
} else if (blockStyle == "h4") {
blockStyle = "<h4>";
} else if (blockStyle == "h5") {
blockStyle = "<h5>";
} else if (blockStyle == "") {
blockStyle = "<p>";
}
if (editorDoc.queryCommandState('bold') == true) {
textFlags |= BOLD;
}
if (editorDoc.queryCommandState('italic') == true) {
textFlags |= ITALIC;
}
if (editorDoc.queryCommandState('underline') == true) {
textFlags |= UNDERLINE;
}
if (editorDoc.queryCommandState('subscript') == true) {
textFlags |= SUBSCRIPT;
}
if (editorDoc.queryCommandState('superscript') == true) {
textFlags |= SUPERSCRIPT;
}
setStatus(STATUS_SELECT_TEXT, /* selOffsetStart + "$" + */
fontName + "$" + fontSize + "$" + blockStyle + "$" + textFlags + "$" + selectedText);
}
} catch (e) { }
}
return true;
}
// Sets focus to this editor.
function setFocus() {
if (!supportRichTextEditing) {
return;
}
if (document.all) {
iframe = document.getElementById(editorId);
iframe.focus();
} else {
contentWindow = document.getElementById(editorId).contentWindow;
contentWindow.focus();
}
setStatus(STATUS_EXEC_CMD, 1);
}
// Reformats element links created via drag & drop.
function reformatElementLinks() {
var linksReformatted = 0;
var elements = editorDoc.getElementsByTagName('A');
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
if (element.className.toLowerCase() == 'elementlink' ||
element.className.toLowerCase() == 'elementlinkwithtype' ||
element.className.toLowerCase() == 'elementlinkwithusertext') {
if (element.firstChild != null && element.firstChild.firstChild != null &&
element.firstChild.firstChild.firstChild != null) {
var linkText = element.firstChild.firstChild.firstChild.nodeValue;
element.removeChild(element.firstChild);
element.appendChild(editorDoc.createTextNode(linkText));
linksReformatted++;
}
}
}
if (linksReformatted > 0) {
setStatus(STATUS_REFORMAT_LINKS, null);
}
}
// Formats the selected text.
function formatText(command, option) {
if (!readOnly && internalUpdateSelection()) {
if (editorDoc.execCommand(command, false, option)) {
setStatus(STATUS_EXEC_CMD, 1);
setStatus(STATUS_MODIFIED, null);
}
}
}
// Adds HTML.
function addHTML(html) {
if (!readOnly && html != "") {
html = decodeString(html);
if (internalUpdateSelection()) {
if (document.all) {
if (selectionRange.text != null) {
selectionRange.pasteHTML(html);
setStatus(STATUS_EXEC_CMD, 1);
setStatus(STATUS_MODIFIED, null);
}
}
else {
selectionRange.deleteContents();
var documentFragment = selectionRange.createContextualFragment(html);
selectionRange.insertNode(documentFragment);
setStatus(STATUS_EXEC_CMD, 1);
setStatus(STATUS_MODIFIED, null);
}
}
}
}
// Adds an image.
function addImage(url, height, width, alt) {
if (internalUpdateSelection()) {
if (document.all) {
if (url != null && url != '') {
formatText('insertimage', url);
}
if (selection != null && selection.type == 'Control' && selectionRange != null) {
if (height != null && height != '') selectionRange.item().height = height;
if (width != null && width != '') selectionRange.item().width = width;
if (alt != null) selectionRange.item().alt = alt;
}
} else {
var START_MARKER = "A_-_-_";
var END_MARKER = ":.:.:";
// mark img links with START_MARKER + id + END_MARKER in the id, for later recovery
var elements = editorDoc.getElementsByTagName('img');
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
element.id = START_MARKER + element.id + END_MARKER;
}
if (url != null && url != '') {
formatText('insertimage', url);
}
if (internalUpdateSelection()) {
var regExID = new RegExp(START_MARKER + "(.*?)" + END_MARKER);
var elements = editorDoc.getElementsByTagName('img');
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
var id = element.id;
if (id != null && id != '') {
RegExp.lastIndex=0;
var matchArray = id.match(regExID);
if (matchArray != null && matchArray.length > 0) {
var newId = matchArray[1];
if (newId.length > 0) {
element.id = newId;
} else {
element.removeAttribute('id');
}
}
} else {
// no id, must be the new img
if (height != null && height != '') element.height = height;
if (width != null && width != '') element.width = width;
if (alt != null) element.alt = alt;
}
}
}
}
setStatus(STATUS_MODIFIED, null);
}
}
// Adds a horizontal line.
function addLine() {
formatText('inserthorizontalrule', null);
}
// Adds a link.
function addLink(url) {
if (!readOnly && url != null && url != '' && internalUpdateSelection()) {
if (document.all) {
if (selectionRange.text == null || selectionRange.text == '') {
selectionRange.text = url;
setStatus(STATUS_EXEC_CMD, 1);
setStatus(STATUS_MODIFIED, null);
}
else if (selectionRange.execCommand('createlink', false, url)) {
setStatus(STATUS_EXEC_CMD, 1);
setStatus(STATUS_MODIFIED, null);
}
}
else {
if (selection == null || selection == "") {
var urlTextNode = editorDoc.createTextNode(url);
insertNodeAtSelection(document.getElementById(editorFrameId).contentWindow, urlTextNode);
}
if (editorDoc.execCommand('createlink', false, url)) {
setStatus(STATUS_EXEC_CMD, 1);
setStatus(STATUS_MODIFIED, null);
}
}
}
}
// Adds an ordered list.
function addOrderedList() {
formatText('insertorderedlist', null);
}
// Adds a table.
function addTable(rows, cols, width, summary, caption, tableheaders) {
if (readOnly) return;
if (rows == 0) rows = 2;
if (cols == 0) cols = 2;
if (width == 0) width = "85%";
if (internalUpdateSelection()) {
var table = editorDoc.createElement("table");
table.cellPadding = "2";
table.cellSpacing = "0";
table.border = "1";
table.width = width;
table.title = "";
if (summary != null && summary != '') {
table.summary = summary;
}
if (caption != null && caption != '') {
table.title = caption;
table.createCaption();
var captionNode = editorDoc.createTextNode(caption);
table.caption.appendChild(captionNode);
}
tbody = editorDoc.createElement("tbody");
for (var i = 0; i < rows; i++) {
tr = editorDoc.createElement("tr");
for (var j = 0; j < cols; j++) {
if (i == 0 && (tableheaders == TABLE_HEADERS_COLS || tableheaders == TABLE_HEADERS_BOTH)) {
th = editorDoc.createElement("th");
th.scope = "col";
th.id = "";
th.abbr = th.id;
var headerNode = editorDoc.createTextNode(th.id);
th.appendChild(headerNode);
if (!document.all) {
br = editorDoc.createElement("br");
th.appendChild(br);
}
tr.appendChild(th);
}
else if (j == 0 && (tableheaders == TABLE_HEADERS_ROWS || tableheaders == TABLE_HEADERS_BOTH)) {
th = editorDoc.createElement("th");
th.scope = "row";
th.id = "";
th.abbr = th.id;
var headerNode = editorDoc.createTextNode(th.id);
th.appendChild(headerNode);
if (!document.all) {
br = editorDoc.createElement("br");
th.appendChild(br);
}
tr.appendChild(th);
}
else {
td = editorDoc.createElement("td");
if (!document.all) {
br = editorDoc.createElement("br");
td.appendChild(br);
}
tr.appendChild(td);
}
}
tbody.appendChild(tr);
}
table.appendChild(tbody);
if (document.all) {
selectionRange.parentElement().appendChild(table);
}
else {
selectionRange.insertNode(table);
}
setStatus(STATUS_EXEC_CMD, 1);
setStatus(STATUS_MODIFIED, null);
}
}
// Adds an unordered list.
function addUnorderedList() {
formatText('insertunorderedlist', null);
}
// Sets the background color of the selected text.
function backColor(color) {
if (color != null && color != '') {
formatText('backcolor', color);
}
}
// Toggles the 'bold' attribute of the selected text.
function bold() {
formatText('bold', null);
}
// Copies the selected text to the clipboard.
function copy() {
if (internalUpdateSelection()) {
if (editorDoc.execCommand('copy', false, null)) {
setStatus(STATUS_EXEC_CMD, 1);
}
}
}
// Cuts the selected text to the clipboard.
function cut() {
formatText('cut', null);
}
// Deletes the selected text.
function deleteText() {
formatText('delete', null);
}
// Finds text.
function findText(text, dir, options) {
if (text == null || text == "") {
return;
}
else {
text = decodeString(text);
}
if (internalUpdateSelection()) {
if (document.all) {
selectionRange.collapse(dir < 0);
if (selectionRange.findText(text, dir, options)) {
selectionRange.scrollIntoView();
selectionRange.select();
selectionRange.collapse(dir < 0);
setStatus(STATUS_EXEC_CMD, 1);
}
}
else {
// find(text, caseSensitive, backwards, wrapAround, wholeWord, searchInFrames, showDialog)
var caseSensitive = true;
var backwards = false;
var wholeWord = true;
if ((options & 4) == 0) caseSensitive = false;
if (dir == -1) backwards = true;
if ((options & 2) == 0) wholeWord = false;
if ( wholeWord )
{
while ( contentWindow.find(text, caseSensitive, backwards, false, wholeWord, false, false) )
{
var ffSelection = contentWindow.getSelection();
selectionRange = ffSelection.getRangeAt(0).cloneRange();
if (selectionRange.startOffset > 0 )
selectionRange.setStart(selectionRange.startContainer, selectionRange.startOffset -1);
if (selectionRange.endOffset < selectionRange.endContainer.length )
{
selectionRange.setEnd(selectionRange.endContainer, selectionRange.endOffset+1);
}
var newText = selectionRange.toString();
var regex = new RegExp("\\b"+text+"\\b");
if ( newText.match(regex))
{
setStatus(STATUS_EXEC_CMD, 1);
break;
}
}
} else if ( contentWindow.find(text, caseSensitive, backwards, false, wholeWord, false, false) ) {
setStatus(STATUS_EXEC_CMD, 1);
}
}
}
}
// Sets the foreground color of the selected text.
function foreColor(color) {
if (color != null && color != '') {
formatText('forecolor', color);
}
}
// Formats the selected text using the given HTML heading tag.
function formatBlock(tag) {
if (tag != null && tag != '') {
formatText('formatblock', tag);
}
}
var INDENTED_LIST_BAD_HTML_IE = "</li>.*<li style=\"list-style: none\">";
var INDENTED_LIST_BAD_HTML_MOZ = "</li>.*<li style=\"list-style-type: none; list-style-image: none; list-style-position: outside;\">";
// Indents the selected text.
function indent() {
formatText('indent', null);
// fix for sub-lists
var html = document.getElementById(editorId).contentWindow.document.body.innerHTML;
if (document.all) {
html = html.replace(INDENTED_LIST_BAD_HTML_IE, "");
} else {
// firefox sometimes puts the same as IE, sometimes more junk
html = html.replace(INDENTED_LIST_BAD_HTML_IE, "");
html = html.replace(INDENTED_LIST_BAD_HTML_MOZ, "");
}
setText(html);
}
// Toggles the 'italic' attribute of the selected text.
function italic() {
formatText('italic', null);
}
// Center justifies the selected text.
function justifyCenter() {
formatText('justifycenter', null);
}
// Fully justifies the selected text.
function justifyFull() {
formatText('justifyfull', null);
}
// Left justifies the selected text.
function justifyLeft() {
formatText('justifyleft', null);
}
// Right justifies the selected text.
function justifyRight() {
formatText('justifyright', null);
}
// Outdents the selected text.
function outdent() {
formatText('outdent', null);
}
// Pastes text from the clipboard.
function paste(sourceURL) {
if (sourceURL == null) {
sourceURL = "";
}
else {
sourceURL = decodeString(sourceURL);
}
if (document.all) {
var START_MARKER = "A_-_-_";
var END_MARKER = ":.:.:";
// mark img and <a /> links with START_MARKER + src/href + END_MARKER in the id, for later recovery
var elements = editorDoc.getElementsByTagName('img');
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
var id = element.id;
element.id = START_MARKER + element.src + END_MARKER + id;
}
var elements = editorDoc.getElementsByTagName('a');
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
var id = element.id;
element.id = START_MARKER + element.href + END_MARKER + id;
}
// change the <base> of the document
var oldBaseHREF = editorDoc.getElementsByTagName('base')[0].href;
editorDoc.getElementsByTagName('base')[0].href = sourceURL;
formatText('paste', null);
// restore <base>
editorDoc.getElementsByTagName('base')[0].href = oldBaseHREF;
}
else {
setStatus(STATUS_EXEC_CMD, 1);
setStatus(STATUS_MODIFIED, null);
}
if (internalUpdateSelection()) {
try {
var regExRes = new RegExp("file\:([^=]+)(/resources/)(.+)", "g");
var regExRef = new RegExp("(.+)(#.+)");
var regEx = new RegExp("file\:([^=]+)/([^/]+)", "g");
var regExID = new RegExp(START_MARKER + "(.*?)" + END_MARKER + "(.*?)");
var elements = editorDoc.getElementsByTagName('img');
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
var id = element.id;
if (id != null && id != '') {
RegExp.lastIndex=0;
var matchArray = id.match(regExID);
if (matchArray != null && matchArray.length > 1) {
element.src = matchArray[1];
if (matchArray.length > 2 && matchArray[2].length > 0) {
element.id = matchArray[2];
}
else {
element.removeAttribute('id');
}
continue;
}
}
var src = element.src;
if (src != null && src != '') {
if (src.indexOf('about:./resources') != -1) {
// fix for IE 7 when pasting from another RTE
// IE7 resolves these as "about:./resources/<file>"
// so remove the "about:."
src = src.replace("about:", "");
}
if (src.indexOf('about:resources') != -1) {
// fix for IE 7 when pasting from another RTE
// IE7 sometimes resolves these as "about:resources/<file>"
// so remove the "about:" and put in "./"
src = src.replace("about:", "./");
}
if (src.indexOf('resources') != -1) {
element.src = src.replace(regExRes, "./resources/$3");
}
else {
element.src = src.replace(regEx, "./resources/$2");
}
}
}
var elements = editorDoc.getElementsByTagName('a');
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
var id = element.id;
if (id != null && id != '') {
RegExp.lastIndex=0;
var matchArray = id.match(regExID);
if (matchArray != null && matchArray.length > 1) {
element.href = matchArray[1];
if (matchArray.length > 2 && matchArray[2].length > 0) {
element.id = matchArray[2];
}
else {
element.removeAttribute('id');
}
continue;
}
}
var href = element.href;
if (href != null && href != '') {
// fix self-referencing hrefs
if (href.indexOf('#') != -1) {
RegExp.lastIndex=0;
var matchArray = href.match(regExRef);
if (matchArray != null && matchArray.length > 2) {
var hrefFile = matchArray[1];
var ref = matchArray[2];
if (hrefFile == sourceURL) {
element.href = ref;
continue;
}
}
}
// fix hrefs already in resources
if (href.indexOf('resources') != -1) {
element.href = href.replace(regExRes, "./resources/$3");
}
// fix hrefs not in resources
else {
element.href = href.replace(regEx, "./resources/$2");
}
}
}
}
catch (e) {
}
}
}
// Redo the previous command.
function redo() {
formatText('redo', null);
}
// Redo the previous command.
function removeFormat() {
formatText('removeformat', null);
}
function _replaceAllText(findText, replaceText, options) {
// this is IE only
if (document.all) {
var tempRange = document.getElementById(editorId).contentWindow.document.body.createTextRange();
tempRange.moveStart('character', -10000000000);
do {
tempRange.collapse();
if (tempRange.findText(findText, 10000000000, options)) {
tempRange.text = replaceText;
tempRange.select();
} else {
break;
}
} while (true);
}
}
// Replaces all text.
function replaceAllText(findText, replaceText, options) {
if (readOnly || findText == null || findText == "") {
return;
}
else {
findText = decodeString(findText);
}
if (replaceText == null) {
replaceText = "";
}
else {
replaceText = decodeString(replaceText);
}
if (document.all) {
// TODO: Move the insertion point to the start of the HTML
// and perform a search and replace in the forward direction.
_replaceAllText(findText, replaceText, options);
}
else {
// TODO: Emulate the IE implementation.
var html = document.getElementById(editorId).contentWindow.document.body.innerHTML;
var optionStr = "/g";
if ((options & 4) == 0) {
optionStr += "i";
}
var regExp = eval("/" + findText + optionStr);
html = html.replace(regExp, replaceText);
setText(html);
}
setStatus(STATUS_EXEC_CMD, 1);
setStatus(STATUS_MODIFIED, null);
}
// Replaces text.
function replaceText(replaceText, dir, options) {
if (readOnly || !internalUpdateSelection()) {
return;
}
if (replaceText == null) {
replaceText = "";
}
else {
replaceText = decodeString(replaceText);
}
if (document.all) {
selectionRange.text = replaceText;
if (replaceText != "") {
selectionRange.moveStart("word", -1);
selectionRange.select();
selectionRange.collapse(dir < 0);
}
}
else {
selectionRange.deleteContents();
selectionRange.insertNode(editorDoc.createTextNode(replaceText));
}
setStatus(STATUS_EXEC_CMD, 1);
setStatus(STATUS_MODIFIED, null);
}
// Selects all text.
function selectAll() {
if (internalUpdateSelection()) {
if (editorDoc.execCommand('selectall', false, null)) {
if ( !document.all )
{
updateSelection();
}
setStatus(STATUS_EXEC_CMD, 1);
}
}
}
// Sets the font name for the selected text.
function setFontName(name) {
if (internalUpdateSelection()) {
if (name != null) {
if (name == '') {
formatText('removeFormat');
} else {
formatText('fontname', name);
}
}
}
}
// Sets the font size for the selected text.
function setFontSize(size) {
if (internalUpdateSelection()) {
if (size != null) {
if (size == '') {
formatText('removeFormat');
} else {
formatText('fontsize', size);
}
}
}
}
// Sets the font style for the selected text.
function setFontStyle(style) {
if (!readOnly && style != null && style != '' && internalUpdateSelection()) {
try {
if (document.all) {
selectionRange.execCommand("removeformat");
selectionRange.parentElement().removeAttribute("className");
}
}
catch (e) {
}
if (style == "<quote>") {
formatText('formatblock', '<p>');
if (document.all) {
selectionRange.parentElement().className = "quote";
}
else {
selection.focusNode.parentNode.className = "quote";
}
}
else if (style == "<code>") {
formatText('formatblock', '<p>');
if (document.all) {
selectionRange.parentElement().className = "codeSample";
}
else {
selection.focusNode.parentNode.className = "codeSample";
}
}
else {
if (!document.all && style == "<p>") {
// A hack to get rid of the "className" attribute in Mozilla/Firefox.
formatText('formatblock', '<h4>');
}
formatText('formatblock', style);
}
}
}
// Sets whether the content can be edited.
function setEditable(editable) {
var doc = document.getElementById(editorId).contentWindow.document;
if (editable != null && editable == 'true') {
if (document.all) {
doc.body.contentEditable = "true";
}
else {
doc.designMode = "on";
}
readOnly = false;
}
else {
if (document.all) {
doc.body.contentEditable = "false";
}
else {
doc.designMode = "off";
}
readOnly = true;
}
setStatus(STATUS_EXEC_CMD, 1);
}
// Toggles the 'strike-through' attribute of the selected text.
function strikeThrough() {
formatText('strikethrough', size);
}
// Toggles the 'subscript' attribute of the selected text.
function subscript() {
formatText('subscript', null);
}
// Toggles the 'superscript' attribute of the selected text.
function superscript() {
formatText('superscript', null);
}
// Toggles the 'underline' attribute of the selected text.
function underline() {
formatText('underline', null);
}
// Converts a link to normal text.
function unlink() {
formatText('unlink', null);
}
function ObjToString(object) {
var ret = "Object " + object.name + " is [\n";
for (var prop in object) {
ret += " " + prop + " is " + object[prop] + ";\n";
}
return ret + "]";
}