/* * ================================================================= * HTML Editor - A WYSIWYG web based editor for IE5.5+ and Mozilla v1.4+ * * Website : http://gossamer-threads.com/ * Support : http://gossamer-threads.com/scripts/support/ * Revision : $Id: editor.js,v 1.12 2009/05/09 05:52:50 brewt Exp $ * * Copyright (c) 2005 Gossamer Threads Inc. All Rights Reserved. * Redistribution in part or in whole strictly prohibited. Please * see LICENSE file for full details. * ================================================================= */ var dialogWindow = {}; function advancedEditor() { this.config = { imageURL : 'images', imageDialogURL : '', defaultFont : 'Arial', defaultFontSize : '3', defaultForeground : '#000000', defaultBackground : '#FFFFFF', focusOnload : false }; this.id = { editorFrame : 'editor_iframe', toolbarArea : 'toolbar_area', editableFrame : 'editable_iframe', editableContent : 'editable_content', editableSource : 'editable_source', form : '', content : '' }; // command, image, title, updateType(0 = don't update, 1 = queryCommandState, 2 = queryCommandEnabled, 3 = customQueryCommandState, 4 = customQueryCommandEnabled), customCommand, customStateCommand, useCSS (mozilla) this.toolbar = [ { buttons : { copy : ['Copy', 'copy.gif', 'Copy', 2], cut : ['Cut', 'cut.gif', 'Cut', 2], paste : ['Paste', 'paste.gif', 'Paste', 2] }, num_buttons : 3, hidden : isIE ? 0 : 1 }, // The undo/redo buttons don't work too reliably and the buffer is lost after // doing things like resizing (changing any properties of the contenteditable // area will wipe it), or custom commands. /* { buttons : { undo : ['Undo', 'undo.gif', 'Undo', 2], redo : ['Redo', 'redo.gif', 'Redo', 2] }, num_buttons : 2 }, */ { buttons : { bold : ['Bold', 'bold.gif', 'Bold', 1], italic : ['Italic', 'italic.gif', 'Italic', 1], underline : ['Underline', 'underline.gif', 'Underline', 1] }, num_buttons : 3 }, { buttons : { quote : ['', 'quote.gif', 'Quote', 0, 'this.insertQuote()'] /* reply : ['', 'reply.gif', 'Reply', 0, 'this.insertReply()'], code : ['', 'code.gif', 'Code', 0, 'this.insertCode()'] */ }, num_buttons : 3 }, { buttons : { alignleft : ['JustifyLeft', 'alignleft.gif', 'Align Left', 1], center : ['JustifyCenter', 'center.gif', 'Center', 1], alignright : ['JustifyRight', 'alignright.gif', 'Align Right', 1] }, num_buttons : 3 }, { buttons : { numlist : ['InsertOrderedList', 'numlist.gif', 'Numbering', 1], bullist : ['InsertUnorderedList', 'bullist.gif', 'Bullets', 1], outdent : ['Outdent', 'outdent.gif', 'Decrease Indent', 0], indent : ['Indent', 'indent.gif', 'Increase Indent', 0] }, num_buttons : 4 }, { buttons : { font : ['FontName', 'font.gif', 'Font', 0, 'this.fontDialog()'], color : ['', 'color.gif', 'Color', 0, 'this.colorDialog("foreground")'] }, num_buttons : 2 }, { buttons : { hr : ['InsertHorizontalRule', 'hr.gif', 'Horizontal Rule', 0], image : ['', 'image.gif', 'Insert Image', 0, 'this.imageDialog()'], link : ['', 'link.gif', 'Create Link', 4, 'this.linkDialog()', 'this.selectionMade()'] }, num_buttons : 3 }, { buttons : { source : ['', 'source.gif', 'Toggle HTML Source', 3, 'this.toggleSource()', 'this.sourceMode'] }, num_buttons : 1 } ]; this.toolbarMapping = {}; this.toolbarQueryCommandState = {}; this.toolbarQueryCommandEnabled = {}; this.objects = {}; this.loadingInterval = 0; this.initCount = 0; this.toolbarInterval = 0; this.selection = null; this.loaded = false; this.sourceMode = false; // TODO remove this and get values from currentStyle // Dimensions of various objects // *Extra are padding/border/margin widths that need to be added to properly // calculate object total widths this.editorFrameExtra = 2; this.editableContentExtra = 10; this.toolbarExtra = 2; this.toolbarHeight = 26; this.buttonWidth = 25; this.separatorWidth = 8; } advancedEditor.prototype.load = function (form, content) { var self = this; if (form) this.id.form = form; if (content) this.id.content = content; this.loadingInterval = setInterval(function () { self.init() }, 100); } advancedEditor.prototype.init = function () { if (this.initCount++ > 100) { clearInterval(this.loadingInterval); return; } var self = this; this.objects.editorFrame = document.getElementById(this.id.editorFrame); if (!this.objects.editorFrame) return; this.objects.editorFrameDoc = this.objects.editorFrame.contentWindow.document; this.objects.editableSource = this.objects.editorFrameDoc.getElementById(this.id.editableSource); this.objects.editableFrame = this.objects.editorFrameDoc.getElementById(this.id.editableFrame); if (!this.objects.editableFrame) return; if (!this.objects.editableSource) this.deleteButton('source'); this.objects.editableFrameDoc = this.objects.editableFrame.contentWindow.document; this.objects.editableContent = this.objects.editableFrameDoc.getElementById(this.id.editableContent); if (!this.objects.editableContent) return; this.objects.toolbarArea = this.objects.editorFrameDoc.getElementById(this.id.toolbarArea); if (!this.objects.toolbarArea) return; clearInterval(this.loadingInterval); this.objects.form = document.getElementById(this.id.form); this.objects.content = document.getElementById(this.id.content); this.objects.editableContent.innerHTML = this.objects.content.value; registerEvent(this.objects.form, 'submit', function () { self.objects.content.value = self.getContent(); }); // Before leaving the page, make sure the content is put back into the input so that the content isn't lost if the user goes back to the page registerEvent(window, 'beforeunload', function () { self.objects.content.value = self.getContent(); }); // Tabbing from the subject takes you to the iframe in IE, but the document in Mozilla registerEvent(this.objects.editorFrame, 'focus', function () { self.focus() }); registerEvent(this.objects.editorFrameDoc, 'focus', function () { self.focus() }); var width = this.objects.editorFrame.offsetWidth - this.editorFrameExtra; var toolbar; for (var i = 0; i < this.toolbar.length; i++) { if (this.toolbar[i].hidden || this.toolbar[i].num_buttons <= 0) continue; var group = this.objects.editorFrameDoc.createElement('div'); group.aewidth = this.buttonWidth * this.toolbar[i].num_buttons; if (!toolbar || toolbar.aewidth + this.toolbarExtra + this.separatorWidth + group.aewidth > width) toolbar = this.addToolbar(); else this.addSeparator(toolbar); this.addButtonGroup(toolbar, group); for (var item in this.toolbar[i].buttons) { var button = this.objects.editorFrameDoc.createElement('div'); group.appendChild(button); button.id = item; button.enabled = true; button.className = 'button'; button.style.backgroundImage = 'url(' + this.config.imageURL + '/' + this.toolbar[i].buttons[item][1] + ')'; button.title = this.toolbar[i].buttons[item][2]; button.setAttribute('unselectable', 'on'); registerEvent(button, 'mouseover', function (e) { self.mouseHandler(e) }); registerEvent(button, 'mouseout', function (e) { self.mouseHandler(e) }); registerEvent(button, 'mousedown', function (e) { self.mouseHandler(e) }); registerEvent(button, 'click', function (e) { self.mouseHandler(e) }); this.toolbarMapping[item] = i; if (this.toolbar[i].buttons[item][3] == 1 || this.toolbar[i].buttons[item][3] == 3) this.toolbarQueryCommandState[item] = i; else if (this.toolbar[i].buttons[item][3] == 2 || this.toolbar[i].buttons[item][3] == 4) this.toolbarQueryCommandEnabled[item] = i; } } this.objects.editableContent.style.fontFamily = this.config.defaultFont; var fontsize = { 1 : 'xx-small', 2 : 'x-small', 3 : 'small', 4 : 'medium', 5 : 'large', 6 : 'x-large', 7 : 'xx-large' }; this.objects.editableContent.style.fontSize = fontsize[parseInt(this.config.defaultFontSize) + (isIE ? 0 : 1)]; this.objects.editableContent.style.backgroundColor = this.config.defaultBackground; this.objects.editableFrameDoc.body.contentEditable = true; if (!isIE) this.objects.editableFrameDoc.designMode = 'on'; // This actually turns *off* the usage of css if (isMozilla) this.execCommand("useCSS", true); this.toolbarInterval = setInterval(function () { self.updateButtonStatus(); }, 250); registerEvent(this.objects.editableContent, 'dblclick', function (e) { if (!e) e = self.objects.editableFrame.contentWindow.event; var target = e.target ? e.target : e.srcElement; if (target.tagName == 'IMG') self.imageDialog(); }); registerEvent(self.objects.editorFrame.contentWindow, 'resize', function () { self.resize(); }); if (this.config.focusOnload) this.focus(); this.loaded = true; } advancedEditor.prototype.getContent = function (bodyOnly) { var content = this.objects.editableContent.innerHTML; if (content) { // Perform some clean up on the editor generated HTML if (isIE) content = content.replace(/

/g, '').replace(/<\/P>/g, '
').replace(/\r?\n/g, ''); else content = content.replace(/
<(ol|ul)>/g, "<$1>"); if (content.match(/^(?: )?
\n*$/i)) content = ''; if (bodyOnly) return content; else { var bgcolor = advancedEditor.getHexColor(this.objects.editableContent.style.backgroundColor); var html = ''; return html; } } return ''; } advancedEditor.prototype.setContent = function (html) { this.objects.editableContent.innerHTML = html; } advancedEditor.prototype.enable = function () { this.changeButtonState(true); } advancedEditor.prototype.disable = function () { this.changeButtonState(false); } advancedEditor.prototype.deleteButton = function () { var args = advancedEditor.prototype.deleteButton.arguments; if (this.loaded || !args.length) { alert("Can't call deleteButton() after the editor has loaded!"); return; } var del = {}; for (var i = 0; i < args.length; i++) del[args[i]] = 1; for (var i = 0; i < this.toolbar.length; i++) { for (var button in this.toolbar[i].buttons) { if (del[button]) { delete this.toolbar[i].buttons[button]; delete del[button]; this.toolbar[i].num_buttons--; if (!del) break; } } } } advancedEditor.prototype.changeButtonState = function (enabled) { var args = advancedEditor.prototype.changeButtonState.arguments; var leave = {}; for (var i = 0; i < args.length; i++) leave[args[i]] = 1; for (var id in this.toolbarMapping) { if (leave[id]) continue; var button = this.objects.editorFrameDoc.getElementById(id); if (enabled) { button.enabled = button.previousState; button.className = button.previousClass; button.noUpdates = false; } else { button.noUpdates = true; button.previousState = button.enabled; button.enabled = false; button.previousClass = button.className; button.className = 'button button-disabled'; } } } advancedEditor.prototype.addToolbar = function () { var toolbar = this.objects.editorFrameDoc.createElement('div'); this.objects.toolbarArea.appendChild(toolbar); toolbar.aewidth = 0; toolbar.className = 'toolbar'; toolbar.setAttribute('unselectable', 'on'); this.objects.editableFrame.style.height = this.objects.editorFrame.offsetHeight - this.editorFrameExtra - this.toolbarHeight * this.objects.toolbarArea.childNodes.length + 'px'; return toolbar; } advancedEditor.prototype.addButtonGroup = function (toolbar, group, beforeElement) { if (beforeElement) toolbar.insertBefore(group, beforeElement); else toolbar.appendChild(group); toolbar.aewidth += group.aewidth; } advancedEditor.prototype.addSeparator = function (toolbar, beforeElement) { var separator = this.objects.editorFrameDoc.createElement('div'); separator.className = 'separator'; separator.setAttribute('unselectable', 'on'); if (beforeElement) toolbar.insertBefore(separator, beforeElement); else toolbar.appendChild(separator); toolbar.aewidth += this.separatorWidth; } advancedEditor.prototype.updateButtonStatus = function () { for (var item in this.toolbarQueryCommandState) { var buttonConfig = this.toolbar[this.toolbarQueryCommandState[item]].buttons[item]; var command = buttonConfig[0]; var button = this.objects.editorFrameDoc.getElementById(item); if (button.mousedown || button.noUpdates) continue; var previousState = button.pressed; if (buttonConfig[3] == 3) button.pressed = eval(buttonConfig[5]); else button.pressed = this.objects.editableFrameDoc.queryCommandState(command); if (previousState == button.pressed) continue; if (button.pressed) button.className = 'button button-pressed' + (button.mouseover ? ' button-pressed-mouse-over' : ''); else button.className = 'button' + (button.mouseover ? ' button-mouse-over' : ''); } for (var item in this.toolbarQueryCommandEnabled) { var buttonConfig = this.toolbar[this.toolbarQueryCommandEnabled[item]].buttons[item]; var command = buttonConfig[0]; var button = this.objects.editorFrameDoc.getElementById(item); if (button.noUpdates) continue; var enable; if (buttonConfig[3] == 4) enable = eval(buttonConfig[5]); else enable = this.objects.editableFrameDoc.queryCommandEnabled(command); if (button.enabled == enable) continue; if (enable) { button.enabled = true; // mozilla bug loses mouseout event after changing applying the alpha transparency button.className = 'button' + (button.mouseover && !isMozilla ? ' button-mouse-over' : ''); } else { button.enabled = false; button.className = 'button button-disabled'; } } } advancedEditor.prototype.mouseHandler = function (e) { if (!e) e = this.objects.editorFrame.contentWindow.event; var target = e.target ? e.target : e.srcElement; switch (e.type) { case 'mouseover': target.mouseover = true; break; case 'mouseout': target.mouseover = false; break; case 'mousedown': target.mousedown = true; break; case 'click': target.mousedown = false; break; } if (!target.enabled) return; switch (e.type) { case 'mouseover': if (target.pressed) target.className = 'button button-pressed button-pressed-mouse-over'; else target.className = 'button button-mouse-over'; break; case 'mouseout': if (target.pressed) target.className = 'button button-pressed'; else target.className = 'button'; break; case 'mousedown': if (!target.pressed) target.className = 'button button-pressed button-pressed-mouse-over'; break; case 'click': // Only do something on a left click if ((e.which && e.which != 1) || (e.button && e.button != 1)) return; var button = this.toolbar[this.toolbarMapping[target.id]].buttons[target.id]; if (button[4]) eval(button[4]); else this.execCommand(button[0], null, button[6]); if (button[3] == 1) target.pressed = this.objects.editableFrameDoc.queryCommandState(button[0]); else if (button[3] == 3) target.pressed = eval(button[5]); if (target.pressed) target.className = 'button button-pressed' + (target.mouseover ? ' button-pressed-mouse-over' : ''); else target.className = 'button' + (target.mouseover ? ' button-mouse-over' : ''); break; } } advancedEditor.prototype.resize = function () { var width = this.objects.editorFrame.offsetWidth - this.editorFrameExtra; var height = this.objects.editorFrame.offsetHeight - this.editorFrameExtra; var toolbarCount = this.objects.toolbarArea.childNodes.length; for (var i = 0; i < this.objects.toolbarArea.childNodes.length; i++) { var toolbar = this.objects.toolbarArea.childNodes[i]; // Window has shrunk, move overflowing buttons down to next toolbar while (toolbar.aewidth + this.toolbarExtra > width && toolbar.childNodes.length > 2) { var group = toolbar.removeChild(toolbar.lastChild); toolbar.removeChild(toolbar.lastChild); toolbar.aewidth -= group.aewidth + this.separatorWidth; var nextToolbar; if (nextToolbar = toolbar.nextSibling) { this.addSeparator(nextToolbar, nextToolbar.firstChild); this.addButtonGroup(nextToolbar, group, nextToolbar.firstChild); } else this.addButtonGroup(this.addToolbar(), group); } // Window has grown, fill up empty space var nextToolbar; if (nextToolbar = toolbar.nextSibling) { var group = nextToolbar.firstChild; while (nextToolbar && group && toolbar.aewidth + this.toolbarExtra + this.separatorWidth + group.aewidth <= width) { nextToolbar.removeChild(group); nextToolbar.aewidth -= group.aewidth; if (nextToolbar.hasChildNodes()) { nextToolbar.removeChild(nextToolbar.firstChild); nextToolbar.aewidth -= this.separatorWidth; } else { this.objects.toolbarArea.removeChild(nextToolbar); nextToolbar = toolbar.nextSibling; } this.addSeparator(toolbar); this.addButtonGroup(toolbar, group); group = nextToolbar ? nextToolbar.firstChild : null; } } } if (toolbarCount != this.objects.toolbarArea.childNodes.length) { var contentHeight = height - this.toolbarHeight * this.objects.toolbarArea.childNodes.length; if (this.sourceMode) { this.objects.editableSource.style.top = this.toolbarHeight * this.objects.toolbarArea.childNodes.length + 'px'; this.objects.editableSource.style.height = contentHeight + 'px'; // Reset height back to 0px, since addToolbar() sets it this.objects.editableFrame.style.height = '0px'; } else this.objects.editableFrame.style.height = contentHeight + 'px'; } } // Set focus on the editor content area advancedEditor.prototype.focus = function (focusWindow) { if (focusWindow) window.focus(); this.objects.editableFrame.contentWindow.focus(); } advancedEditor.prototype.execCommand = function (cmd, value, useCSS) { if (isMozilla && useCSS) this.objects.editableFrameDoc.execCommand("useCSS", false, false); // For some commands (eg. indent, lists), without focusing it ends up running the // command in the toolbar area. If you comment out the focus code, then in IE // click in the editor, click outside the editor, then click indent, you'll see // the problem. if (isIE || isWebkit) this.focus(); var res; try { res = this.objects.editableFrameDoc.execCommand(cmd, false, value); } catch (err) { // debug("execCommand failed (" + cmd + " => " + value + ")" + err); } // debug("execCommand(" + cmd + ", " + value + ") => " + res + (useCSS ? ' (css)' : '')); if (isMozilla && useCSS) this.objects.editableFrameDoc.execCommand("useCSS", false, true); } advancedEditor.prototype.queryCommandValue = function (cmd, useCSS) { if (isMozilla && useCSS) this.objects.editableFrameDoc.execCommand("useCSS", false, false); var value = this.objects.editableFrameDoc.queryCommandValue(cmd); if (isMozilla && useCSS) this.objects.editableFrameDoc.execCommand("useCSS", false, true); // debug("queryCommandValue(" + cmd + ") => " + value + (useCSS ? ' (css)' : '')); cmd = cmd.toLowerCase(); if (cmd == 'forecolor' || cmd == 'backcolor' || cmd == 'hilitecolor') return advancedEditor.getHexColor(value); return value; } advancedEditor.prototype.setColor = function () { this.focus(1); if (dialogWindow.values._type == 'foreground') this.execCommand('ForeColor', dialogWindow.values.hexcolor); else if (dialogWindow.values._type == 'background') this.objects.editableContent.style.backgroundColor = dialogWindow.values.hexcolor; else if (dialogWindow.values._type == 'highlight') this.execCommand(isIE ? 'BackColor' : 'HiliteColor', dialogWindow.values.hexcolor, true); } advancedEditor.prototype.setFont = function () { this.focus(1); if (dialogWindow.values.size) this.execCommand('FontSize', dialogWindow.values.size); if (dialogWindow.values.font) this.execCommand('FontName', dialogWindow.values.font); if (dialogWindow.values.hexcolor) this.execCommand('ForeColor', dialogWindow.values.hexcolor); if ((dialogWindow.values.style.indexOf('b') != -1) != dialogWindow.values._bold) this.execCommand('Bold'); if ((dialogWindow.values.style.indexOf('i') != -1) != dialogWindow.values._italic) this.execCommand('Italic'); if ((dialogWindow.values.underline == 'on') != dialogWindow.values._underline) this.execCommand('Underline'); } advancedEditor.prototype.insertImage = function () { this.focus(1); var img = this.objects.editableFrameDoc.createElement('img'); var values = dialogWindow.values; img.src = values['src-url-input']; if (values['src-inline']) img.setAttribute('gforuminline', values['src-inline-input']); var currentSelection = new advancedEditorSelection(isIE ? this.objects.editableFrameDoc : this.objects.editableFrame.contentWindow); currentSelection.replaceSelection(img); } advancedEditor.prototype.insertLink = function () { this.focus(1); if (dialogWindow.values.url) this.execCommand('CreateLink', dialogWindow.values.url); } advancedEditor.prototype.colorDialog = function (type) { var values = { hexcolor : null, _type : type, _foreground : null, _background : null, _highlight : null }; values._selection = new advancedEditorSelection(isIE ? this.objects.editableFrameDoc : this.objects.editableFrame.contentWindow); var selectionObjects = values._selection.getSelection(); values._foreground = this.queryCommandValue('ForeColor') || this.config.defaultForeground; values._background = advancedEditor.getHexColor(this.objects.editableContent.style.backgroundColor) || this.config.defaultBackground; values._highlight = this.queryCommandValue(isIE ? 'BackColor' : 'HiliteColor', true); if (type == 'foreground') values.hexcolor = values._foreground; else if (type == 'background') values.hexcolor = values._background; else if (type == 'highlight') values.hexcolor = values._highlight; this.showDialog('editor_color.html', isSafari ? 375 : 358, 200, values, 'opener.dialogWindow.editor.setColor()'); } advancedEditor.prototype.fontDialog = function () { var values = { font : null, style : null, size : null, underline : null, hexcolor : null, _type : 'foreground', _foreground : null, _background : null, _highlight : null }; values._selection = new advancedEditorSelection(isIE ? this.objects.editableFrameDoc : this.objects.editableFrame.contentWindow); var selectionObjects = values._selection.getSelection(); values.font = this.queryCommandValue('FontName') || this.config.defaultFont; values.size = this.queryCommandValue('FontSize') || this.config.defaultFontSize; values.underline = this.objects.editableFrameDoc.queryCommandState('Underline'); values._underline = values.underline; values._bold = this.objects.editableFrameDoc.queryCommandState('Bold'); values._italic = this.objects.editableFrameDoc.queryCommandState('Italic'); values.style = (values._bold && values._italic ? 'bi' : values._bold ? 'b' : values._italic ? 'i' : 'r'); values._foreground = this.queryCommandValue('ForeColor') || this.config.defaultForeground; values._background = advancedEditor.getHexColor(this.objects.editableContent.style.backgroundColor) || this.config.defaultBackground; values._highlight = this.queryCommandValue(isIE ? 'BackColor' : 'HiliteColor', true); values.hexcolor = values._foreground; this.showDialog('editor_font.html', 447, 250, values, 'opener.dialogWindow.editor.setFont()'); } advancedEditor.prototype.imageDialog = function () { var values = { 'src-inline' : null, 'src-inline-input' : null, 'src-url-input' : null }; var url = 'editor_image.html'; if (this.config.imageDialogURL && this.config.imageDialogURL.match(/^https?:/i)) url = this.config.imageDialogURL; else url = parent.location.protocol + '//' + parent.location.host + parent.location.pathname.substr(0, parent.location.pathname.lastIndexOf('/') + 1) + this.config.imageDialogURL; this.showDialog(url, 430, 500, values, 'opener.dialogWindow.editor.insertImage()'); } advancedEditor.prototype.linkDialog = function () { var values = { protocol : null, url : null, title : null }; values._selection = new advancedEditorSelection(isIE ? this.objects.editableFrameDoc : this.objects.editableFrame.contentWindow); var selectionObjects = values._selection.getSelection(); if (selectionObjects.length == 1 && selectionObjects[0].tagName == 'A') { values.url = selectionObjects[0].href; values.protocol = values.url.replace(/^(\w+:(?:\/\/)?).*$/, "$1"); values.title = selectionObjects[0].title; } this.showDialog('editor_link.html', 390, 100, values, 'opener.dialogWindow.editor.insertLink()'); } advancedEditor.prototype.showDialog = function (url, width, height, values, returnCall) { if (dialogWindow.win && !dialogWindow.win.closed) dialogWindow.win.close(); if (url.match(/^https?:/i)) dialogWindow.url = url; else dialogWindow.url = this.objects.editorFrame.contentWindow.location.href.replace(/editor_iframe\.html$/, '') + url; dialogWindow.width = width; dialogWindow.height = height; dialogWindow.name = Math.random().toString().replace(/\./, ""); dialogWindow.left = (screen.width - width) / 2; dialogWindow.top = (screen.height - height) / 2; dialogWindow.attribs = 'left=' + dialogWindow.left + ',top=' + dialogWindow.top + 'location=0,menubar=0,resizable=1,scrollbars=0,status=0,toolbar=0,width=' + dialogWindow.width + ',height=' + dialogWindow.height; dialogWindow.editor = this; dialogWindow.values = values; dialogWindow.returnCall = returnCall; try { dialogWindow.win = window.open(dialogWindow.url, dialogWindow.name, dialogWindow.attribs); dialogWindow.win.focus(); } catch (e) { // FIXME should we alert? } } advancedEditor.prototype.toggleSource = function () { if (!this.objects.editableSource) return; this.sourceMode = !this.sourceMode; if (this.sourceMode) { this.changeButtonState(false, 'source'); this.objects.editableSource.value = this.getContent(); this.objects.editableSource.style.top = this.toolbarHeight * this.objects.toolbarArea.childNodes.length + 'px'; this.objects.editableSource.style.height = this.objects.editableFrame.offsetHeight + 'px'; this.objects.editableSource.style.visibility = 'visible'; this.objects.editableFrame.style.visibility = 'hidden'; // Without setting this to 0px, mozilla loses the cursor in the source textarea this.objects.editableFrame.style.height = '0px'; this.objects.editableSource.focus(); } else { this.changeButtonState(true, 'source'); this.objects.editableContent.innerHTML = this.objects.editableSource.value; this.objects.editableSource.style.visibility = 'hidden'; this.objects.editableFrame.style.height = this.objects.editableSource.style.height; this.objects.editableFrame.style.visibility = 'visible'; this.focus(); } } advancedEditor.prototype.selectionMade = function () { if (this.objects.editableFrameDoc.selection) return this.objects.editableFrameDoc.selection.type != 'None'; // Don't use advancedEditorSelection because in Gecko, range.cloneContents() // tries to re-fetch broken images, and since this gets called on a timer, // floods the server with requests. if (this.objects.editableFrame.contentWindow.getSelection) return this.objects.editableFrame.contentWindow.getSelection().toString().length > 0; } advancedEditor.getHexColor = function (color) { function intToHex(i) { i = parseInt(i); return (i < 16 ? '0' : '') + i.toString(16).toUpperCase(); } if (typeof(color) == 'number') return '#' + intToHex(color & 0xff) + intToHex((color >> 8) & 0xff) + intToHex((color >> 16) & 0xff); else if (typeof(color) != 'string') return; if (color.match(/rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i)) return '#' + intToHex(RegExp.$1) + intToHex(RegExp.$2) + intToHex(RegExp.$3); if (color == 'transparent') return; return color.toUpperCase(); } function advancedEditorSelection(documentWindow) { if (documentWindow.selection) this.document = documentWindow; else this.window = documentWindow; } advancedEditorSelection.prototype.getSelection = function () { if (this.document) { this.document.parentWindow.focus(); this.selection = this.document.selection; this.range = this.selection.createRange(); this.nodes = []; if (this.selection.type == 'Control') { for (var i = 0; i < this.range.length; i++) this.nodes[i] = this.range(i); } else if (this.selection.type == 'Text') { this.nodesContainer = this.document.createElement('span'); this.nodesContainer.innerHTML = this.range.htmlText; this.nodes = this.nodesContainer.childNodes; } } else { this.selection = this.window.getSelection(); this.nodes = []; if (this.selection && this.selection.getRangeAt) { // Safari triggers an exception when there's nothing selected try { this.range = this.selection.getRangeAt(0); this.documentFragment = this.range.cloneContents(); this.nodes = this.documentFragment.childNodes; if (this.nodes.length == 1 && this.nodes[0].nodeType == 3 && this.nodes[0].nodeValue == '') this.nodes = []; } catch (e) {} } } return this.nodes; } advancedEditorSelection.prototype.getSelectionHTML = function () { if (!this.selection) this.getSelection(); if (this.document) return this.range.htmlText; else { var div = document.createElement('div'); div.appendChild(this.documentFragment); return div.innerHTML; } } advancedEditorSelection.prototype.replaceSelection = function (node) { if (!this.selection) this.getSelection(); if (this.document) { if (this.selection.type == 'Control') { this.selection.clear(); this.range = this.selection.createRange(); } this.range.pasteHTML(node.outerHTML); } else { this.range.deleteContents(); this.range.insertNode(node); } }