| /******************************************************************************* |
| * 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 = " "; |
| |
| 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> </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, "'"); |
| 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 + "]"; |
| } |