diff --git a/lib/editor/htmlarea.php b/lib/editor/htmlarea.php index a25421c4206..7840ed5e4cc 100644 --- a/lib/editor/htmlarea.php +++ b/lib/editor/htmlarea.php @@ -3,7 +3,7 @@ $lastmodified = filemtime("htmlarea.php"); $lifetime = 1800; - + header("Content-type: application/x-javascript"); // Correct MIME type header("Last-Modified: " . gmdate("D, d M Y H:i:s", lastmodified) . " GMT"); header("Expires: " . gmdate("D, d M Y H:i:s", time() + $lifetime) . " GMT"); @@ -12,9 +12,9 @@ $lang = current_language(); - if (empty($lang)) { - $lang = "en"; - } + if (empty($lang)) { + $lang = "en"; + } $strheading = get_string("heading", "editor"); $strnormal = get_string("normal", "editor"); @@ -36,60 +36,60 @@ // $Id$ if (typeof _editor_url == "string") { - // Leave exactly one backslash at the end of _editor_url - _editor_url = _editor_url.replace(/\/*$/, '/'); + // Leave exactly one backslash at the end of _editor_url + _editor_url = _editor_url.replace(/\/*$/, '/'); } else { - //alert("WARNING: _editor_url is not set! You should set this variable to the editor files path; it should preferably be an absolute path, like in '/htmlarea', but it can be relative if you prefer. Further we will try to load the editor files correctly but we'll probably fail."); - _editor_url = 'wwwroot); ?>/lib/editor/'; + //alert("WARNING: _editor_url is not set! You should set this variable to the editor files path; it should preferably be an absolute path, like in '/htmlarea', but it can be relative if you prefer. Further we will try to load the editor files correctly but we'll probably fail."); + _editor_url = 'wwwroot); ?>/lib/editor/'; } // make sure we have a language if (typeof _editor_lang == "string") { - _editor_lang = _editor_lang.toLowerCase(); + _editor_lang = _editor_lang.toLowerCase(); } else { - _editor_lang = "en"; + _editor_lang = "en"; } // Creates a new HTMLArea object. Tries to replace the textarea with the given // ID with it. function HTMLArea(textarea, config) { - if (HTMLArea.checkSupportedBrowser()) { - if (typeof config == "undefined") { - this.config = new HTMLArea.Config(); - } else { - this.config = config; - } - this._htmlArea = null; - this._textArea = textarea; - this._editMode = "wysiwyg"; - this.plugins = {}; - this._timerToolbar = null; - this._timerUndo = null; - this._undoQueue = new Array(this.config.undoSteps); - this._undoPos = -1; - this._customUndo = false; - this._mdoc = document; // cache the document, we need it in plugins - this.doctype = ''; - } - // Hide cut copy paste from gecko browsers. - if (HTMLArea.is_gecko) { - this.config.hideSomeButtons(" cut copy paste "); - } + if (HTMLArea.checkSupportedBrowser()) { + if (typeof config == "undefined") { + this.config = new HTMLArea.Config(); + } else { + this.config = config; + } + this._htmlArea = null; + this._textArea = textarea; + this._editMode = "wysiwyg"; + this.plugins = {}; + this._timerToolbar = null; + this._timerUndo = null; + this._undoQueue = new Array(this.config.undoSteps); + this._undoPos = -1; + this._customUndo = false; + this._mdoc = document; // cache the document, we need it in plugins + this.doctype = ''; + } + // Hide cut copy paste from gecko browsers. + //if (HTMLArea.is_gecko) { + // this.config.hideSomeButtons(" cut copy paste "); + //} }; // load some scripts (function() { - var scripts = HTMLArea._scripts = [ _editor_url + "htmlarea.js", - _editor_url + "dialog.js", - _editor_url + "popupwin.js", - _editor_url + "lang/" + _editor_lang + ".js" ]; - var head = document.getElementsByTagName("head")[0]; - // start from 1, htmlarea.js is already loaded - for (var i = 1; i < scripts.length; ++i) { - var script = document.createElement("script"); - script.src = scripts[i]; - head.appendChild(script); - } + var scripts = HTMLArea._scripts = [ _editor_url + "htmlarea.js", + _editor_url + "dialog.js", + _editor_url + "popupwin.js", + _editor_url + "lang/" + _editor_lang + ".js" ]; + var head = document.getElementsByTagName("head")[0]; + // start from 1, htmlarea.js is already loaded + for (var i = 1; i < scripts.length; ++i) { + var script = document.createElement("script"); + script.src = scripts[i]; + head.appendChild(script); + } })(); // cache some regexps @@ -99,187 +99,188 @@ HTMLArea.RE_head = /
((.|\n)*?)<\/head>/i; HTMLArea.RE_body = /((.|\n)*?)<\/body>/i; HTMLArea.Config = function () { - this.version = "3.0"; + this.version = "3.0"; - this.width = "auto"; - this.height = "auto"; + this.width = "auto"; + this.height = "auto"; - // enable creation of a status bar? - this.statusBar = true; + // enable creation of a status bar? + this.statusBar = true; - // maximum size of the undo queue - this.undoSteps = 20; + // maximum size of the undo queue + this.undoSteps = 20; - // the time interval at which undo samples are taken - this.undoTimeout = 500; // 1/2 sec. + // the time interval at which undo samples are taken + this.undoTimeout = 500; // 1/2 sec. - // the next parameter specifies whether the toolbar should be included - // in the size or not. - this.sizeIncludesToolbar = true; + // the next parameter specifies whether the toolbar should be included + // in the size or not. + this.sizeIncludesToolbar = true; - // if true then HTMLArea will retrieve the full HTML, starting with the - // tag. - this.fullPage = false; + // if true then HTMLArea will retrieve the full HTML, starting with the + // tag. + this.fullPage = false; - // style included in the iframe document - this.pageStyle = "body { background-color: #fff; font-family: 'Times New Roman', Times; }"; + // style included in the iframe document + this.pageStyle = "body { background-color: #fff; font-family: 'Times New Roman', Times; }"; - // set to true if you want Word code to be cleaned upon Paste - this.killWordOnPaste = false; + // set to true if you want Word code to be cleaned upon Paste + this.killWordOnPaste = true; - // BaseURL included in the iframe document - this.baseURL = document.baseURI || document.URL; - if (this.baseURL && this.baseURL.match(/(.*)\/([^\/]+)/)) - this.baseURL = RegExp.$1 + "/"; + // BaseURL included in the iframe document + this.baseURL = document.baseURI || document.URL; + if (this.baseURL && this.baseURL.match(/(.*)\/([^\/]+)/)) + this.baseURL = RegExp.$1 + "/"; - // URL-s - this.imgURL = "images/"; - this.popupURL = "popups/"; + // URL-s + this.imgURL = "images/"; + this.popupURL = "popups/"; - /** CUSTOMIZING THE TOOLBAR - * ------------------------- - * - * It is recommended that you customize the toolbar contents in an - * external file (i.e. the one calling HTMLArea) and leave this one - * unchanged. That's because when we (InteractiveTools.com) release a - * new official version, it's less likely that you will have problems - * upgrading HTMLArea. - */ - this.toolbar = [ - [ "fontname", "space", - "fontsize", "space", - "formatblock", "space", - "bold", "italic", "underline", "strikethrough", "separator", - "subscript", "superscript", "separator", - "copy", "cut", "paste", "space", "undo", "redo" ], + /** CUSTOMIZING THE TOOLBAR + * ------------------------- + * + * It is recommended that you customize the toolbar contents in an + * external file (i.e. the one calling HTMLArea) and leave this one + * unchanged. That's because when we (InteractiveTools.com) release a + * new official version, it's less likely that you will have problems + * upgrading HTMLArea. + */ + this.toolbar = [ + [ "fontname", "space", + "fontsize", "space", + "formatblock", "space", + "bold", "italic", "underline", "strikethrough", "separator", + "subscript", "superscript", "separator", + "copy", "cut", "paste","clean", "separator", "undo", "redo" ], - [ "justifyleft", "justifycenter", "justifyright", "justifyfull", "separator", - "lefttoright", "righttoleft", "separator", - "insertorderedlist", "insertunorderedlist", "outdent", "indent", "separator", - "forecolor", "hilitecolor", "separator", - "inserthorizontalrule", "createlink", "insertimage", "inserttable", - "insertsmile", "insertchar", "separator", "htmlmode", "separator", "popupeditor" ] - ]; + [ "justifyleft", "justifycenter", "justifyright", "justifyfull", "separator", + "lefttoright", "righttoleft", "separator", + "insertorderedlist", "insertunorderedlist", "outdent", "indent", "separator", + "forecolor", "hilitecolor", "separator", + "inserthorizontalrule", "createlink", "insertimage", "inserttable", + "insertsmile", "insertchar", "separator", "htmlmode", "separator", "popupeditor" ] + ]; - this.fontname = { - "Arial": 'arial,helvetica,sans-serif', - "Courier New": 'courier new,courier,monospace', - "Georgia": 'georgia,times new roman,times,serif', - "Tahoma": 'tahoma,arial,helvetica,sans-serif', - "Times New Roman": 'times new roman,times,serif', - "Verdana": 'verdana,arial,helvetica,sans-serif', - "impact": 'impact', - "WingDings": 'wingdings' - }; + this.fontname = { + "Arial": 'arial,helvetica,sans-serif', + "Courier New": 'courier new,courier,monospace', + "Georgia": 'georgia,times new roman,times,serif', + "Tahoma": 'tahoma,arial,helvetica,sans-serif', + "Times New Roman": 'times new roman,times,serif', + "Verdana": 'verdana,arial,helvetica,sans-serif', + "impact": 'impact', + "WingDings": 'wingdings' + }; - this.fontsize = { - "1 (8 pt)": "1", - "2 (10 pt)": "2", - "3 (12 pt)": "3", - "4 (14 pt)": "4", - "5 (18 pt)": "5", - "6 (24 pt)": "6", - "7 (36 pt)": "7" - }; + this.fontsize = { + "1 (8 pt)": "1", + "2 (10 pt)": "2", + "3 (12 pt)": "3", + "4 (14 pt)": "4", + "5 (18 pt)": "5", + "6 (24 pt)": "6", + "7 (36 pt)": "7" + }; - this.formatblock = { - " 1": "h1", - " 2": "h2", - " 3": "h3", - " 4": "h4", - " 5": "h5", - " 6": "h6", - "": "p", - "": "address", - "": "pre" - }; + this.formatblock = { + " 1": "h1", + " 2": "h2", + " 3": "h3", + " 4": "h4", + " 5": "h5", + " 6": "h6", + "": "p", + "": "address", + "": "pre" + }; - this.customSelects = {}; + this.customSelects = {}; - function cut_copy_paste(e, cmd, obj) { - e.execCommand(cmd); - }; + function cut_copy_paste(e, cmd, obj) { + e.execCommand(cmd); + }; - // ADDING CUSTOM BUTTONS: please read below! - // format of the btnList elements is "ID: [ ToolTip, Icon, Enabled in text mode?, ACTION ]" - // - ID: unique ID for the button. If the button calls document.execCommand - // it's wise to give it the same name as the called command. - // - ACTION: function that gets called when the button is clicked. - // it has the following prototype: - // function(editor, buttonName) - // - editor is the HTMLArea object that triggered the call - // - buttonName is the ID of the clicked button - // These 2 parameters makes it possible for you to use the same - // handler for more HTMLArea objects or for more different buttons. - // - ToolTip: default tooltip, for cases when it is not defined in the -lang- file (HTMLArea.I18N) - // - Icon: path to an icon image file for the button (TODO: use one image for all buttons!) - // - Enabled in text mode: if false the button gets disabled for text-only mode; otherwise enabled all the time. - this.btnList = { - bold: [ "Bold", "ed_format_bold.gif", false, function(e) {e.execCommand("bold");} ], - italic: [ "Italic", "ed_format_italic.gif", false, function(e) {e.execCommand("italic");} ], - underline: [ "Underline", "ed_format_underline.gif", false, function(e) {e.execCommand("underline");} ], - strikethrough: [ "Strikethrough", "ed_format_strike.gif", false, function(e) {e.execCommand("strikethrough");} ], - subscript: [ "Subscript", "ed_format_sub.gif", false, function(e) {e.execCommand("subscript");} ], - superscript: [ "Superscript", "ed_format_sup.gif", false, function(e) {e.execCommand("superscript");} ], - justifyleft: [ "Justify Left", "ed_align_left.gif", false, function(e) {e.execCommand("justifyleft");} ], - justifycenter: [ "Justify Center", "ed_align_center.gif", false, function(e) {e.execCommand("justifycenter");} ], - justifyright: [ "Justify Right", "ed_align_right.gif", false, function(e) {e.execCommand("justifyright");} ], - justifyfull: [ "Justify Full", "ed_align_justify.gif", false, function(e) {e.execCommand("justifyfull");} ], - insertorderedlist: [ "Ordered List", "ed_list_num.gif", false, function(e) {e.execCommand("insertorderedlist");} ], - insertunorderedlist: [ "Bulleted List", "ed_list_bullet.gif", false, function(e) {e.execCommand("insertunorderedlist");} ], - outdent: [ "Decrease Indent", "ed_indent_less.gif", false, function(e) {e.execCommand("outdent");} ], - indent: [ "Increase Indent", "ed_indent_more.gif", false, function(e) {e.execCommand("indent");} ], - forecolor: [ "Font Color", "ed_color_fg.gif", false, function(e) {e.execCommand("forecolor");} ], - hilitecolor: [ "Background Color", "ed_color_bg.gif", false, function(e) {e.execCommand("hilitecolor");} ], - inserthorizontalrule: [ "Horizontal Rule", "ed_hr.gif", false, function(e) {e.execCommand("inserthorizontalrule");} ], - createlink: [ "Insert Web Link", "ed_link.gif", false, function(e) {e.execCommand("createlink", true);} ], - insertimage: [ "Insert/Modify Image", "ed_image.gif", false, function(e) {e.execCommand("insertimage");} ], - inserttable: [ "Insert Table", "insert_table.gif", false, function(e) {e.execCommand("inserttable");} ], - htmlmode: [ "Toggle HTML Source", "ed_html.gif", true, function(e) {e.execCommand("htmlmode");} ], - popupeditor: [ "Enlarge Editor", "fullscreen_maximize.gif", true, function(e) {e.execCommand("popupeditor");} ], - about: [ "About this editor", "ed_about.gif", true, function(e) {e.execCommand("about");} ], - showhelp: [ "Help using editor", "ed_help.gif", true, function(e) {e.execCommand("showhelp");} ], - undo: [ "Undoes your last action", "ed_undo.gif", false, function(e) {e.execCommand("undo");} ], - redo: [ "Redoes your last action", "ed_redo.gif", false, function(e) {e.execCommand("redo");} ], - cut: [ "Cut selection", "ed_cut.gif", false, cut_copy_paste ], - copy: [ "Copy selection", "ed_copy.gif", false, cut_copy_paste ], - paste: [ "Paste from clipboard", "ed_paste.gif", false, cut_copy_paste ], - lefttoright: [ "Direction left to right", "ed_left_to_right.gif", false, function(e) {e.execCommand("lefttoright");} ], - righttoleft: [ "Direction right to left", "ed_right_to_left.gif", false, function(e) {e.execCommand("righttoleft");} ], - insertsmile: ["Insert Smiley", "em.icon.smile.gif", false, function(e) {e.execCommand("insertsmile");} ], - insertchar: [ "Insert Char", "icon_ins_char.gif", false, function(e) {e.execCommand("insertchar");} ] - }; - /* ADDING CUSTOM BUTTONS - * --------------------- - * - * It is recommended that you add the custom buttons in an external - * file and leave this one unchanged. That's because when we - * (InteractiveTools.com) release a new official version, it's less - * likely that you will have problems upgrading HTMLArea. - * - * Example on how to add a custom button when you construct the HTMLArea: - * - * var editor = new HTMLArea("your_text_area_id"); - * var cfg = editor.config; // this is the default configuration - * cfg.btnList["my-hilite"] = - * [ function(editor) { editor.surroundHTML('', ''); }, // action - * "Highlight selection", // tooltip - * "my_hilite.gif", // image - * false // disabled in text mode - * ]; - * cfg.toolbar.push(["linebreak", "my-hilite"]); // add the new button to the toolbar - * - * An alternate (also more convenient and recommended) way to - * accomplish this is to use the registerButton function below. - */ - // initialize tooltips from the I18N module and generate correct image path - for (var i in this.btnList) { - var btn = this.btnList[i]; - btn[1] = _editor_url + this.imgURL + btn[1]; - if (typeof HTMLArea.I18N.tooltips[i] != "undefined") { - btn[0] = HTMLArea.I18N.tooltips[i]; - } - } + // ADDING CUSTOM BUTTONS: please read below! + // format of the btnList elements is "ID: [ ToolTip, Icon, Enabled in text mode?, ACTION ]" + // - ID: unique ID for the button. If the button calls document.execCommand + // it's wise to give it the same name as the called command. + // - ACTION: function that gets called when the button is clicked. + // it has the following prototype: + // function(editor, buttonName) + // - editor is the HTMLArea object that triggered the call + // - buttonName is the ID of the clicked button + // These 2 parameters makes it possible for you to use the same + // handler for more HTMLArea objects or for more different buttons. + // - ToolTip: default tooltip, for cases when it is not defined in the -lang- file (HTMLArea.I18N) + // - Icon: path to an icon image file for the button (TODO: use one image for all buttons!) + // - Enabled in text mode: if false the button gets disabled for text-only mode; otherwise enabled all the time. + this.btnList = { + bold: [ "Bold", "ed_format_bold.gif", false, function(e) {e.execCommand("bold");} ], + italic: [ "Italic", "ed_format_italic.gif", false, function(e) {e.execCommand("italic");} ], + underline: [ "Underline", "ed_format_underline.gif", false, function(e) {e.execCommand("underline");} ], + strikethrough: [ "Strikethrough", "ed_format_strike.gif", false, function(e) {e.execCommand("strikethrough");} ], + subscript: [ "Subscript", "ed_format_sub.gif", false, function(e) {e.execCommand("subscript");} ], + superscript: [ "Superscript", "ed_format_sup.gif", false, function(e) {e.execCommand("superscript");} ], + justifyleft: [ "Justify Left", "ed_align_left.gif", false, function(e) {e.execCommand("justifyleft");} ], + justifycenter: [ "Justify Center", "ed_align_center.gif", false, function(e) {e.execCommand("justifycenter");} ], + justifyright: [ "Justify Right", "ed_align_right.gif", false, function(e) {e.execCommand("justifyright");} ], + justifyfull: [ "Justify Full", "ed_align_justify.gif", false, function(e) {e.execCommand("justifyfull");} ], + insertorderedlist: [ "Ordered List", "ed_list_num.gif", false, function(e) {e.execCommand("insertorderedlist");} ], + insertunorderedlist: [ "Bulleted List", "ed_list_bullet.gif", false, function(e) {e.execCommand("insertunorderedlist");} ], + outdent: [ "Decrease Indent", "ed_indent_less.gif", false, function(e) {e.execCommand("outdent");} ], + indent: [ "Increase Indent", "ed_indent_more.gif", false, function(e) {e.execCommand("indent");} ], + forecolor: [ "Font Color", "ed_color_fg.gif", false, function(e) {e.execCommand("forecolor");} ], + hilitecolor: [ "Background Color", "ed_color_bg.gif", false, function(e) {e.execCommand("hilitecolor");} ], + inserthorizontalrule: [ "Horizontal Rule", "ed_hr.gif", false, function(e) {e.execCommand("inserthorizontalrule");} ], + createlink: [ "Insert Web Link", "ed_link.gif", false, function(e) {e.execCommand("createlink", true);} ], + insertimage: [ "Insert/Modify Image", "ed_image.gif", false, function(e) {e.execCommand("insertimage");} ], + inserttable: [ "Insert Table", "insert_table.gif", false, function(e) {e.execCommand("inserttable");} ], + htmlmode: [ "Toggle HTML Source", "ed_html.gif", true, function(e) {e.execCommand("htmlmode");} ], + popupeditor: [ "Enlarge Editor", "fullscreen_maximize.gif", true, function(e) {e.execCommand("popupeditor");} ], + about: [ "About this editor", "ed_about.gif", true, function(e) {e.execCommand("about");} ], + showhelp: [ "Help using editor", "ed_help.gif", true, function(e) {e.execCommand("showhelp");} ], + undo: [ "Undoes your last action", "ed_undo.gif", false, function(e) {e.execCommand("undo");} ], + redo: [ "Redoes your last action", "ed_redo.gif", false, function(e) {e.execCommand("redo");} ], + cut: [ "Cut selection", "ed_cut.gif", false, cut_copy_paste ], + copy: [ "Copy selection", "ed_copy.gif", false, cut_copy_paste ], + paste: [ "Paste from clipboard", "ed_paste.gif", false, cut_copy_paste ], + clean: [ "Clean Word HTML", "ed_wordclean.gif", false, function(e) {e.execCommand("killword"); }], + lefttoright: [ "Direction left to right", "ed_left_to_right.gif", false, function(e) {e.execCommand("lefttoright");} ], + righttoleft: [ "Direction right to left", "ed_right_to_left.gif", false, function(e) {e.execCommand("righttoleft");} ], + insertsmile: ["Insert Smiley", "em.icon.smile.gif", false, function(e) {e.execCommand("insertsmile");} ], + insertchar: [ "Insert Char", "icon_ins_char.gif", false, function(e) {e.execCommand("insertchar");} ] + }; + /* ADDING CUSTOM BUTTONS + * --------------------- + * + * It is recommended that you add the custom buttons in an external + * file and leave this one unchanged. That's because when we + * (InteractiveTools.com) release a new official version, it's less + * likely that you will have problems upgrading HTMLArea. + * + * Example on how to add a custom button when you construct the HTMLArea: + * + * var editor = new HTMLArea("your_text_area_id"); + * var cfg = editor.config; // this is the default configuration + * cfg.btnList["my-hilite"] = + * [ function(editor) { editor.surroundHTML('', ''); }, // action + * "Highlight selection", // tooltip + * "my_hilite.gif", // image + * false // disabled in text mode + * ]; + * cfg.toolbar.push(["linebreak", "my-hilite"]); // add the new button to the toolbar + * + * An alternate (also more convenient and recommended) way to + * accomplish this is to use the registerButton function below. + */ + // initialize tooltips from the I18N module and generate correct image path + for (var i in this.btnList) { + var btn = this.btnList[i]; + btn[1] = _editor_url + this.imgURL + btn[1]; + if (typeof HTMLArea.I18N.tooltips[i] != "undefined") { + btn[0] = HTMLArea.I18N.tooltips[i]; + } + } }; /** Helper function: register a new button with the configuration. It can be @@ -300,26 +301,26 @@ HTMLArea.Config = function () { * }); */ HTMLArea.Config.prototype.registerButton = function(id, tooltip, image, textMode, action, context) { - var the_id; - if (typeof id == "string") { - the_id = id; - } else if (typeof id == "object") { - the_id = id.id; - } else { - alert("ERROR [HTMLArea.Config::registerButton]:\ninvalid arguments"); - return false; - } - // check for existing id - if (typeof this.customSelects[the_id] != "undefined") { - // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists."); - } - if (typeof this.btnList[the_id] != "undefined") { - // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists."); - } - switch (typeof id) { - case "string": this.btnList[id] = [ tooltip, image, textMode, action, context ]; break; - case "object": this.btnList[id.id] = [ id.tooltip, id.image, id.textMode, id.action, id.context ]; break; - } + var the_id; + if (typeof id == "string") { + the_id = id; + } else if (typeof id == "object") { + the_id = id.id; + } else { + alert("ERROR [HTMLArea.Config::registerButton]:\ninvalid arguments"); + return false; + } + // check for existing id + if (typeof this.customSelects[the_id] != "undefined") { + // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists."); + } + if (typeof this.btnList[the_id] != "undefined") { + // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists."); + } + switch (typeof id) { + case "string": this.btnList[id] = [ tooltip, image, textMode, action, context ]; break; + case "object": this.btnList[id.id] = [ id.tooltip, id.image, id.textMode, id.action, id.context ]; break; + } }; /** The following helper function registers a dropdown box with the editor @@ -329,14 +330,14 @@ HTMLArea.Config.prototype.registerButton = function(id, tooltip, image, textMode * FIXME: add example */ HTMLArea.Config.prototype.registerDropdown = function(object) { - // check for existing id - if (typeof this.customSelects[object.id] != "undefined") { - // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists."); - } - if (typeof this.btnList[object.id] != "undefined") { - // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists."); - } - this.customSelects[object.id] = object; + // check for existing id + if (typeof this.customSelects[object.id] != "undefined") { + // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists."); + } + if (typeof this.btnList[object.id] != "undefined") { + // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists."); + } + this.customSelects[object.id] = object; }; /** Call this function to remove some buttons/drop-down boxes from the toolbar. @@ -350,549 +351,549 @@ HTMLArea.Config.prototype.registerDropdown = function(object) { * create a brand new toolbar ;-) */ HTMLArea.Config.prototype.hideSomeButtons = function(remove) { - var toolbar = this.toolbar; - for (var i in toolbar) { - var line = toolbar[i]; - for (var j = line.length; --j >= 0; ) { - if (remove.indexOf(" " + line[j] + " ") >= 0) { - var len = 1; - if (/separator|space/.test(line[j + 1])) { - len = 2; - } - line.splice(j, len); - } - } - } + var toolbar = this.toolbar; + for (var i in toolbar) { + var line = toolbar[i]; + for (var j = line.length; --j >= 0; ) { + if (remove.indexOf(" " + line[j] + " ") >= 0) { + var len = 1; + if (/separator|space/.test(line[j + 1])) { + len = 2; + } + line.splice(j, len); + } + } + } }; /** Helper function: replace all TEXTAREA-s in the document with HTMLArea-s. */ HTMLArea.replaceAll = function(config) { - var tas = document.getElementsByTagName("textarea"); - for (var i = tas.length; i > 0; (new HTMLArea(tas[--i], config)).generate()); + var tas = document.getElementsByTagName("textarea"); + for (var i = tas.length; i > 0; (new HTMLArea(tas[--i], config)).generate()); }; /** Helper function: replaces the TEXTAREA with the given ID with HTMLArea. */ HTMLArea.replace = function(id, config) { - var ta = document.getElementById(id); - return ta ? (new HTMLArea(ta, config)).generate() : null; + var ta = document.getElementById(id); + return ta ? (new HTMLArea(ta, config)).generate() : null; }; // Creates the toolbar and appends it to the _htmlarea HTMLArea.prototype._createToolbar = function () { - var editor = this; // to access this in nested functions + var editor = this; // to access this in nested functions - var toolbar = document.createElement("div"); - this._toolbar = toolbar; - toolbar.className = "toolbar"; - toolbar.unselectable = "1"; - var tb_row = null; - var tb_objects = new Object(); - this._toolbarObjects = tb_objects; + var toolbar = document.createElement("div"); + this._toolbar = toolbar; + toolbar.className = "toolbar"; + toolbar.unselectable = "1"; + var tb_row = null; + var tb_objects = new Object(); + this._toolbarObjects = tb_objects; - // creates a new line in the toolbar - function newLine() { - var table = document.createElement("table"); - table.border = "0px"; - table.cellSpacing = "0px"; - table.cellPadding = "0px"; - toolbar.appendChild(table); - // TBODY is required for IE, otherwise you don't see anything - // in the TABLE. - var tb_body = document.createElement("tbody"); - table.appendChild(tb_body); - tb_row = document.createElement("tr"); - tb_body.appendChild(tb_row); - }; // END of function: newLine - // init first line - newLine(); + // creates a new line in the toolbar + function newLine() { + var table = document.createElement("table"); + table.border = "0px"; + table.cellSpacing = "0px"; + table.cellPadding = "0px"; + toolbar.appendChild(table); + // TBODY is required for IE, otherwise you don't see anything + // in the TABLE. + var tb_body = document.createElement("tbody"); + table.appendChild(tb_body); + tb_row = document.createElement("tr"); + tb_body.appendChild(tb_row); + }; // END of function: newLine + // init first line + newLine(); - // updates the state of a toolbar element. This function is member of - // a toolbar element object (unnamed objects created by createButton or - // createSelect functions below). - function setButtonStatus(id, newval) { - var oldval = this[id]; - var el = this.element; - if (oldval != newval) { - switch (id) { - case "enabled": - if (newval) { - HTMLArea._removeClass(el, "buttonDisabled"); - el.disabled = false; - } else { - HTMLArea._addClass(el, "buttonDisabled"); - el.disabled = true; - } - break; - case "active": - if (newval) { - HTMLArea._addClass(el, "buttonPressed"); - } else { - HTMLArea._removeClass(el, "buttonPressed"); - } - break; - } - this[id] = newval; - } - }; // END of function: setButtonStatus + // updates the state of a toolbar element. This function is member of + // a toolbar element object (unnamed objects created by createButton or + // createSelect functions below). + function setButtonStatus(id, newval) { + var oldval = this[id]; + var el = this.element; + if (oldval != newval) { + switch (id) { + case "enabled": + if (newval) { + HTMLArea._removeClass(el, "buttonDisabled"); + el.disabled = false; + } else { + HTMLArea._addClass(el, "buttonDisabled"); + el.disabled = true; + } + break; + case "active": + if (newval) { + HTMLArea._addClass(el, "buttonPressed"); + } else { + HTMLArea._removeClass(el, "buttonPressed"); + } + break; + } + this[id] = newval; + } + }; // END of function: setButtonStatus - // this function will handle creation of combo boxes. Receives as - // parameter the name of a button as defined in the toolBar config. - // This function is called from createButton, above, if the given "txt" - // doesn't match a button. - function createSelect(txt) { - var options = null; - var el = null; - var cmd = null; - var customSelects = editor.config.customSelects; - var context = null; - switch (txt) { - case "fontsize": - case "fontname": - case "formatblock": - // the following line retrieves the correct - // configuration option because the variable name - // inside the Config object is named the same as the - // button/select in the toolbar. For instance, if txt - // == "formatblock" we retrieve config.formatblock (or - // a different way to write it in JS is - // config["formatblock"]. - options = editor.config[txt]; - cmd = txt; - break; - default: - // try to fetch it from the list of registered selects - cmd = txt; - var dropdown = customSelects[cmd]; - if (typeof dropdown != "undefined") { - options = dropdown.options; - context = dropdown.context; - } else { - alert("ERROR [createSelect]:\nCan't find the requested dropdown definition"); - } - break; - } - if (options) { - el = document.createElement("select"); - var obj = { - name : txt, // field name - element : el, // the UI element (SELECT) - enabled : true, // is it enabled? - text : false, // enabled in text mode? - cmd : cmd, // command ID - state : setButtonStatus, // for changing state - context : context - }; - tb_objects[txt] = obj; - for (var i in options) { - var op = document.createElement("option"); - op.appendChild(document.createTextNode(i)); - op.value = options[i]; - el.appendChild(op); - } - HTMLArea._addEvent(el, "change", function () { - editor._comboSelected(el, txt); - }); - } - return el; - }; // END of function: createSelect + // this function will handle creation of combo boxes. Receives as + // parameter the name of a button as defined in the toolBar config. + // This function is called from createButton, above, if the given "txt" + // doesn't match a button. + function createSelect(txt) { + var options = null; + var el = null; + var cmd = null; + var customSelects = editor.config.customSelects; + var context = null; + switch (txt) { + case "fontsize": + case "fontname": + case "formatblock": + // the following line retrieves the correct + // configuration option because the variable name + // inside the Config object is named the same as the + // button/select in the toolbar. For instance, if txt + // == "formatblock" we retrieve config.formatblock (or + // a different way to write it in JS is + // config["formatblock"]. + options = editor.config[txt]; + cmd = txt; + break; + default: + // try to fetch it from the list of registered selects + cmd = txt; + var dropdown = customSelects[cmd]; + if (typeof dropdown != "undefined") { + options = dropdown.options; + context = dropdown.context; + } else { + alert("ERROR [createSelect]:\nCan't find the requested dropdown definition"); + } + break; + } + if (options) { + el = document.createElement("select"); + var obj = { + name : txt, // field name + element : el, // the UI element (SELECT) + enabled : true, // is it enabled? + text : false, // enabled in text mode? + cmd : cmd, // command ID + state : setButtonStatus, // for changing state + context : context + }; + tb_objects[txt] = obj; + for (var i in options) { + var op = document.createElement("option"); + op.appendChild(document.createTextNode(i)); + op.value = options[i]; + el.appendChild(op); + } + HTMLArea._addEvent(el, "change", function () { + editor._comboSelected(el, txt); + }); + } + return el; + }; // END of function: createSelect - // appends a new button to toolbar - function createButton(txt) { - // the element that will be created - var el = null; - var btn = null; - switch (txt) { - case "separator": - el = document.createElement("div"); - el.className = "separator"; - break; - case "space": - el = document.createElement("div"); - el.className = "space"; - break; - case "linebreak": - newLine(); - return false; - case "textindicator": - el = document.createElement("div"); - el.appendChild(document.createTextNode("A")); - el.className = "indicator"; - el.title = HTMLArea.I18N.tooltips.textindicator; - var obj = { - name : txt, // the button name (i.e. 'bold') - element : el, // the UI element (DIV) - enabled : true, // is it enabled? - active : false, // is it pressed? - text : false, // enabled in text mode? - cmd : "textindicator", // the command ID - state : setButtonStatus // for changing state - }; - tb_objects[txt] = obj; - break; - default: - btn = editor.config.btnList[txt]; - } - if (!el && btn) { - el = document.createElement("div"); - el.title = btn[0]; - el.className = "button"; - // let's just pretend we have a button object, and - // assign all the needed information to it. - var obj = { - name : txt, // the button name (i.e. 'bold') - element : el, // the UI element (DIV) - enabled : true, // is it enabled? - active : false, // is it pressed? - text : btn[2], // enabled in text mode? - cmd : btn[3], // the command ID - state : setButtonStatus, // for changing state - context : btn[4] || null // enabled in a certain context? - }; - tb_objects[txt] = obj; - // handlers to emulate nice flat toolbar buttons - HTMLArea._addEvent(el, "mouseover", function () { - if (obj.enabled) { - HTMLArea._addClass(el, "buttonHover"); - } - }); - HTMLArea._addEvent(el, "mouseout", function () { - if (obj.enabled) with (HTMLArea) { - _removeClass(el, "buttonHover"); - _removeClass(el, "buttonActive"); - (obj.active) && _addClass(el, "buttonPressed"); - } - }); - HTMLArea._addEvent(el, "mousedown", function (ev) { - if (obj.enabled) with (HTMLArea) { - _addClass(el, "buttonActive"); - _removeClass(el, "buttonPressed"); - _stopEvent(is_ie ? window.event : ev); - } - }); - // when clicked, do the following: - HTMLArea._addEvent(el, "click", function (ev) { - if (obj.enabled) with (HTMLArea) { - _removeClass(el, "buttonActive"); - _removeClass(el, "buttonHover"); - obj.cmd(editor, obj.name, obj); - _stopEvent(is_ie ? window.event : ev); - } - }); - var img = document.createElement("img"); - img.src = btn[1]; - img.style.width = "18px"; - img.style.height = "18px"; - el.appendChild(img); - } else if (!el) { - el = createSelect(txt); - } - if (el) { - var tb_cell = document.createElement("td"); - tb_row.appendChild(tb_cell); - tb_cell.appendChild(el); - } else { - alert("FIXME: Unknown toolbar item: " + txt); - } - return el; - }; + // appends a new button to toolbar + function createButton(txt) { + // the element that will be created + var el = null; + var btn = null; + switch (txt) { + case "separator": + el = document.createElement("div"); + el.className = "separator"; + break; + case "space": + el = document.createElement("div"); + el.className = "space"; + break; + case "linebreak": + newLine(); + return false; + case "textindicator": + el = document.createElement("div"); + el.appendChild(document.createTextNode("A")); + el.className = "indicator"; + el.title = HTMLArea.I18N.tooltips.textindicator; + var obj = { + name : txt, // the button name (i.e. 'bold') + element : el, // the UI element (DIV) + enabled : true, // is it enabled? + active : false, // is it pressed? + text : false, // enabled in text mode? + cmd : "textindicator", // the command ID + state : setButtonStatus // for changing state + }; + tb_objects[txt] = obj; + break; + default: + btn = editor.config.btnList[txt]; + } + if (!el && btn) { + el = document.createElement("div"); + el.title = btn[0]; + el.className = "button"; + // let's just pretend we have a button object, and + // assign all the needed information to it. + var obj = { + name : txt, // the button name (i.e. 'bold') + element : el, // the UI element (DIV) + enabled : true, // is it enabled? + active : false, // is it pressed? + text : btn[2], // enabled in text mode? + cmd : btn[3], // the command ID + state : setButtonStatus, // for changing state + context : btn[4] || null // enabled in a certain context? + }; + tb_objects[txt] = obj; + // handlers to emulate nice flat toolbar buttons + HTMLArea._addEvent(el, "mouseover", function () { + if (obj.enabled) { + HTMLArea._addClass(el, "buttonHover"); + } + }); + HTMLArea._addEvent(el, "mouseout", function () { + if (obj.enabled) with (HTMLArea) { + _removeClass(el, "buttonHover"); + _removeClass(el, "buttonActive"); + (obj.active) && _addClass(el, "buttonPressed"); + } + }); + HTMLArea._addEvent(el, "mousedown", function (ev) { + if (obj.enabled) with (HTMLArea) { + _addClass(el, "buttonActive"); + _removeClass(el, "buttonPressed"); + _stopEvent(is_ie ? window.event : ev); + } + }); + // when clicked, do the following: + HTMLArea._addEvent(el, "click", function (ev) { + if (obj.enabled) with (HTMLArea) { + _removeClass(el, "buttonActive"); + _removeClass(el, "buttonHover"); + obj.cmd(editor, obj.name, obj); + _stopEvent(is_ie ? window.event : ev); + } + }); + var img = document.createElement("img"); + img.src = btn[1]; + img.style.width = "18px"; + img.style.height = "18px"; + el.appendChild(img); + } else if (!el) { + el = createSelect(txt); + } + if (el) { + var tb_cell = document.createElement("td"); + tb_row.appendChild(tb_cell); + tb_cell.appendChild(el); + } else { + alert("FIXME: Unknown toolbar item: " + txt); + } + return el; + }; - var first = true; - for (var i in this.config.toolbar) { - if (!first) { - createButton("linebreak"); - } else { - first = false; - } - var group = this.config.toolbar[i]; - for (var j in group) { - var code = group[j]; - if (/^([IT])\[(.*?)\]/.test(code)) { - // special case, create text label - var l7ed = RegExp.$1 == "I"; // localized? - var label = RegExp.$2; - if (l7ed) { - label = HTMLArea.I18N.custom[label]; - } - var tb_cell = document.createElement("td"); - tb_row.appendChild(tb_cell); - tb_cell.className = "label"; - tb_cell.innerHTML = label; - } else { - createButton(code); - } - } - } + var first = true; + for (var i in this.config.toolbar) { + if (!first) { + createButton("linebreak"); + } else { + first = false; + } + var group = this.config.toolbar[i]; + for (var j in group) { + var code = group[j]; + if (/^([IT])\[(.*?)\]/.test(code)) { + // special case, create text label + var l7ed = RegExp.$1 == "I"; // localized? + var label = RegExp.$2; + if (l7ed) { + label = HTMLArea.I18N.custom[label]; + } + var tb_cell = document.createElement("td"); + tb_row.appendChild(tb_cell); + tb_cell.className = "label"; + tb_cell.innerHTML = label; + } else { + createButton(code); + } + } + } - this._htmlArea.appendChild(toolbar); + this._htmlArea.appendChild(toolbar); }; HTMLArea.prototype._createStatusBar = function() { - var statusbar = document.createElement("div"); - statusbar.className = "statusBar"; - this._htmlArea.appendChild(statusbar); - this._statusBar = statusbar; - // statusbar.appendChild(document.createTextNode(HTMLArea.I18N.msg["Path"] + ": ")); - // creates a holder for the path view - div = document.createElement("span"); - div.className = "statusBarTree"; - div.innerHTML = HTMLArea.I18N.msg["Path"] + ": "; - this._statusBarTree = div; - this._statusBar.appendChild(div); - if (!this.config.statusBar) { - // disable it... - statusbar.style.display = "none"; - } + var statusbar = document.createElement("div"); + statusbar.className = "statusBar"; + this._htmlArea.appendChild(statusbar); + this._statusBar = statusbar; + // statusbar.appendChild(document.createTextNode(HTMLArea.I18N.msg["Path"] + ": ")); + // creates a holder for the path view + div = document.createElement("span"); + div.className = "statusBarTree"; + div.innerHTML = HTMLArea.I18N.msg["Path"] + ": "; + this._statusBarTree = div; + this._statusBar.appendChild(div); + if (!this.config.statusBar) { + // disable it... + statusbar.style.display = "none"; + } }; // Creates the HTMLArea object and replaces the textarea with it. HTMLArea.prototype.generate = function () { - var editor = this; // we'll need "this" in some nested functions - // get the textarea - var textarea = this._textArea; - if (typeof textarea == "string") { - // it's not element but ID - this._textArea = textarea = document.getElementById(textarea); - } - this._ta_size = { - w: textarea.offsetWidth, - h: textarea.offsetHeight - }; - textarea.style.display = "none"; + var editor = this; // we'll need "this" in some nested functions + // get the textarea + var textarea = this._textArea; + if (typeof textarea == "string") { + // it's not element but ID + this._textArea = textarea = document.getElementById(textarea); + } + this._ta_size = { + w: textarea.offsetWidth, + h: textarea.offsetHeight + }; + textarea.style.display = "none"; - // create the editor framework - var htmlarea = document.createElement("div"); - htmlarea.className = "htmlarea"; - this._htmlArea = htmlarea; + // create the editor framework + var htmlarea = document.createElement("div"); + htmlarea.className = "htmlarea"; + this._htmlArea = htmlarea; - // insert the editor before the textarea. - textarea.parentNode.insertBefore(htmlarea, textarea); + // insert the editor before the textarea. + textarea.parentNode.insertBefore(htmlarea, textarea); - if (textarea.form) { - // we have a form, on submit get the HTMLArea content and - // update original textarea. - var f = textarea.form; - if (typeof f.onsubmit == "function") { - var funcref = f.onsubmit; - if (typeof f.__msh_prevOnSubmit == "undefined") { - f.__msh_prevOnSubmit = []; - } - f.__msh_prevOnSubmit.push(funcref); - } - f.onsubmit = function() { - editor._textArea.value = editor.getHTML(); - var a = this.__msh_prevOnSubmit; - // call previous submit methods if they were there. - if (typeof a != "undefined") { - for (var i in a) { - a[i](); - } - } - }; - } + if (textarea.form) { + // we have a form, on submit get the HTMLArea content and + // update original textarea. + var f = textarea.form; + if (typeof f.onsubmit == "function") { + var funcref = f.onsubmit; + if (typeof f.__msh_prevOnSubmit == "undefined") { + f.__msh_prevOnSubmit = []; + } + f.__msh_prevOnSubmit.push(funcref); + } + f.onsubmit = function() { + editor._textArea.value = editor.getHTML(); + var a = this.__msh_prevOnSubmit; + // call previous submit methods if they were there. + if (typeof a != "undefined") { + for (var i in a) { + a[i](); + } + } + }; + } - // add a handler for the "back/forward" case -- on body.unload we save - // the HTML content into the original textarea. - window.onunload = function() { - editor._textArea.value = editor.getHTML(); - }; + // add a handler for the "back/forward" case -- on body.unload we save + // the HTML content into the original textarea. + window.onunload = function() { + editor._textArea.value = editor.getHTML(); + }; - // creates & appends the toolbar - this._createToolbar(); + // creates & appends the toolbar + this._createToolbar(); - // create the IFRAME - var iframe = document.createElement("iframe"); - htmlarea.appendChild(iframe); + // create the IFRAME + var iframe = document.createElement("iframe"); + htmlarea.appendChild(iframe); - this._iframe = iframe; + this._iframe = iframe; - // creates & appends the status bar, if the case - this._createStatusBar(); + // creates & appends the status bar, if the case + this._createStatusBar(); - // remove the default border as it keeps us from computing correctly - // the sizes. (somebody tell me why doesn't this work in IE) + // remove the default border as it keeps us from computing correctly + // the sizes. (somebody tell me why doesn't this work in IE) - if (!HTMLArea.is_ie) { - iframe.style.borderWidth = "1px"; - // iframe.frameBorder = "1"; - // iframe.marginHeight = "0"; - // iframe.marginWidth = "0"; - } + if (!HTMLArea.is_ie) { + iframe.style.borderWidth = "1px"; + // iframe.frameBorder = "1"; + // iframe.marginHeight = "0"; + // iframe.marginWidth = "0"; + } - // size the IFRAME according to user's prefs or initial textarea - var height = (this.config.height == "auto" ? (this._ta_size.h + "px") : this.config.height); - height = parseInt(height); - var width = (this.config.width == "auto" ? (this._ta_size.w + 50 + "px") : this.config.width); - width = parseInt(width); + // size the IFRAME according to user's prefs or initial textarea + var height = (this.config.height == "auto" ? (this._ta_size.h + "px") : this.config.height); + height = parseInt(height); + var width = (this.config.width == "auto" ? (this._ta_size.w + 50 + "px") : this.config.width); + width = parseInt(width); - if (!HTMLArea.is_ie) { - height -= 2; - width -= 2; - } + if (!HTMLArea.is_ie) { + height -= 2; + width -= 2; + } - iframe.style.width = width + "px"; - if (this.config.sizeIncludesToolbar) { - // substract toolbar height - height -= this._toolbar.offsetHeight; - height -= this._statusBar.offsetHeight; - } - if (height < 0) { - height = 0; - } - iframe.style.height = height + "px"; + iframe.style.width = width + "px"; + if (this.config.sizeIncludesToolbar) { + // substract toolbar height + height -= this._toolbar.offsetHeight; + height -= this._statusBar.offsetHeight; + } + if (height < 0) { + height = 0; + } + iframe.style.height = height + "px"; - // the editor including the toolbar now have the same size as the - // original textarea.. which means that we need to reduce that a bit. - textarea.style.width = iframe.style.width; - textarea.style.height = iframe.style.height; + // the editor including the toolbar now have the same size as the + // original textarea.. which means that we need to reduce that a bit. + textarea.style.width = iframe.style.width; + textarea.style.height = iframe.style.height; - // IMPORTANT: we have to allow Mozilla a short time to recognize the - // new frame. Otherwise we get a stupid exception. - function initIframe() { - var doc = editor._iframe.contentWindow.document; - if (!doc) { - // Try again.. - // FIXME: don't know what else to do here. Normally - // we'll never reach this point. - if (HTMLArea.is_gecko) { - setTimeout(initIframe, 100); - return false; - } else { - alert("ERROR: IFRAME can't be initialized."); - } - } - if (HTMLArea.is_gecko) { - // enable editable mode for Mozilla - doc.designMode = "on"; - } - editor._doc = doc; - if (!editor.config.fullPage) { - doc.open(); - var html = "\n"; - html += "\n"; - if (editor.config.baseURL) - html += ']*TEXT-ALIGN: justify[^>]*>/gi,'
'). - replace(/ style=\"[^>]*\"/gi,''). - replace(/ align=[^\s|>]*/gi,''); + // keep tags, strip attributes + D = D.replace(/ class=[^\s|>]*/gi,''). + //replace(/
]*TEXT-ALIGN: justify[^>]*>/gi,'
').
+ replace(/ style=\"[^>]*\"/gi,'').
+ replace(/ align=[^\s|>]*/gi,'');
- //clean up tags
- D = D.replace(/]*>/gi,'').
- replace(/]*>/gi,'').
- replace(/ ]*><\/P>/gi,'');
+ //remove empty tags
+ //D = D.replace(/<\/strong>/gi,'').
+ //replace(/<\/i>/gi,'').
+ //replace(/ ]*><\/P>/gi,'');
- // nuke double tags
- oldlen = D.length + 1;
- while(oldlen > D.length) {
- oldlen = D.length;
- // join us now and free the tags, we'll be free hackers, we'll be free... ;-)
- D = D.replace(/<([a-z][a-z]*)> *<\/\1>/gi,' ').
- replace(/<([a-z][a-z]*)> *<([a-z][^>]*)> *<\/\1>/gi,'<$2>');
- }
- D = D.replace(/<([a-z][a-z]*)><\1>/gi,'<$1>').
- replace(/<\/([a-z][a-z]*)><\/\1>/gi,'<\/$1>');
+ // nuke double tags
+ oldlen = D.length + 1;
+ while(oldlen > D.length) {
+ oldlen = D.length;
+ // join us now and free the tags, we'll be free hackers, we'll be free... ;-)
+ D = D.replace(/<([a-z][a-z]*)> *<\/\1>/gi,' ').
+ replace(/<([a-z][a-z]*)> *<([a-z][^>]*)> *<\/\1>/gi,'<$2>');
+ }
+ D = D.replace(/<([a-z][a-z]*)><\1>/gi,'<$1>').
+ replace(/<\/([a-z][a-z]*)><\/\1>/gi,'<\/$1>');
- // nuke double spaces
- D = D.replace(/ */gi,' ');
+ // nuke double spaces
+ D = D.replace(/ */gi,' ');
- this.setHTML(D);
- this.updateToolbar();
- }
+ this.setHTML(D);
+ this.updateToolbar();
+ }
};
HTMLArea.prototype.forceRedraw = function() {
- this._doc.body.style.visibility = "hidden";
- this._doc.body.style.visibility = "visible";
- // this._doc.body.innerHTML = this.getInnerHTML();
+ this._doc.body.style.visibility = "hidden";
+ this._doc.body.style.visibility = "visible";
+ // this._doc.body.innerHTML = this.getInnerHTML();
};
// focuses the iframe window. returns a reference to the editor document.
HTMLArea.prototype.focusEditor = function() {
- switch (this._editMode) {
- case "wysiwyg" : this._iframe.contentWindow.focus(); break;
- case "textmode": this._textArea.focus(); break;
- default : alert("ERROR: mode " + this._editMode + " is not defined");
- }
- return this._doc;
+ switch (this._editMode) {
+ case "wysiwyg" : this._iframe.contentWindow.focus(); break;
+ case "textmode": this._textArea.focus(); break;
+ default : alert("ERROR: mode " + this._editMode + " is not defined");
+ }
+ return this._doc;
};
// takes a snapshot of the current text (for undo)
HTMLArea.prototype._undoTakeSnapshot = function() {
- ++this._undoPos;
- if (this._undoPos >= this.config.undoSteps) {
- // remove the first element
- this._undoQueue.shift();
- --this._undoPos;
- }
- // use the fasted method (getInnerHTML);
- var take = true;
- var txt = this.getInnerHTML();
- if (this._undoPos > 0)
- take = (this._undoQueue[this._undoPos - 1] != txt);
- if (take) {
- this._undoQueue[this._undoPos] = txt;
- } else {
- this._undoPos--;
- }
+ ++this._undoPos;
+ if (this._undoPos >= this.config.undoSteps) {
+ // remove the first element
+ this._undoQueue.shift();
+ --this._undoPos;
+ }
+ // use the fasted method (getInnerHTML);
+ var take = true;
+ var txt = this.getInnerHTML();
+ if (this._undoPos > 0)
+ take = (this._undoQueue[this._undoPos - 1] != txt);
+ if (take) {
+ this._undoQueue[this._undoPos] = txt;
+ } else {
+ this._undoPos--;
+ }
};
HTMLArea.prototype.undo = function() {
- if (this._undoPos > 0) {
- var txt = this._undoQueue[--this._undoPos];
- if (txt) this.setHTML(txt);
- else ++this._undoPos;
- }
+ if (this._undoPos > 0) {
+ var txt = this._undoQueue[--this._undoPos];
+ if (txt) this.setHTML(txt);
+ else ++this._undoPos;
+ }
};
HTMLArea.prototype.redo = function() {
- if (this._undoPos < this._undoQueue.length - 1) {
- var txt = this._undoQueue[++this._undoPos];
- if (txt) this.setHTML(txt);
- else --this._undoPos;
- }
+ if (this._undoPos < this._undoQueue.length - 1) {
+ var txt = this._undoQueue[++this._undoPos];
+ if (txt) this.setHTML(txt);
+ else --this._undoPos;
+ }
};
// updates enabled/disable/active state of the toolbar elements
HTMLArea.prototype.updateToolbar = function(noStatus) {
- var doc = this._doc;
- var text = (this._editMode == "textmode");
- var ancestors = null;
- if (!text) {
- ancestors = this.getAllAncestors();
- if (this.config.statusBar && !noStatus) {
- this._statusBarTree.innerHTML = HTMLArea.I18N.msg["Path"] + ": "; // clear
- for (var i = ancestors.length; --i >= 0;) {
- var el = ancestors[i];
- if (!el) {
- // hell knows why we get here; this
- // could be a classic example of why
- // it's good to check for conditions
- // that are impossible to happen ;-)
- continue;
- }
- var a = document.createElement("a");
- a.href = "#";
- a.el = el;
- a.editor = this;
- a.onclick = function() {
- this.blur();
- this.editor.selectNodeContents(this.el);
- this.editor.updateToolbar(true);
- return false;
- };
- a.oncontextmenu = function() {
- // TODO: add context menu here
- this.blur();
- var info = "Inline style:\n\n";
- info += this.el.style.cssText.split(/;\s*/).join(";\n");
- alert(info);
- return false;
- };
- var txt = el.tagName.toLowerCase();
- a.title = el.style.cssText;
- if (el.id) {
- txt += "#" + el.id;
- }
- if (el.className) {
- txt += "." + el.className;
- }
- a.appendChild(document.createTextNode(txt));
- this._statusBarTree.appendChild(a);
- if (i != 0) {
- this._statusBarTree.appendChild(document.createTextNode(String.fromCharCode(0xbb)));
- }
- }
- }
- }
- for (var i in this._toolbarObjects) {
- var btn = this._toolbarObjects[i];
- var cmd = i;
- var inContext = true;
- if (btn.context && !text) {
- inContext = false;
- var context = btn.context;
- var attrs = [];
- if (/(.*)\[(.*?)\]/.test(context)) {
- context = RegExp.$1;
- attrs = RegExp.$2.split(",");
- }
- context = context.toLowerCase();
- var match = (context == "*");
- for (var k in ancestors) {
- if (!ancestors[k]) {
- // the impossible really happens.
- continue;
- }
- if (match || (ancestors[k].tagName.toLowerCase() == context)) {
- inContext = true;
- for (var ka in attrs) {
- if (!eval("ancestors[k]." + attrs[ka])) {
- inContext = false;
- break;
- }
- }
- if (inContext) {
- break;
- }
- }
- }
- }
- btn.state("enabled", (!text || btn.text) && inContext);
- if (typeof cmd == "function") {
- continue;
- }
- // look-it-up in the custom dropdown boxes
- var dropdown = this.config.customSelects[cmd];
- if ((!text || btn.text) && (typeof dropdown != "undefined")) {
- dropdown.refresh(this);
- continue;
- }
- switch (cmd) {
- case "fontname":
- case "fontsize":
- case "formatblock":
- if (!text) try {
- var value = ("" + doc.queryCommandValue(cmd)).toLowerCase();
- if (!value) {
- // FIXME: what do we do here?
- break;
- }
- // HACK -- retrieve the config option for this
- // combo box. We rely on the fact that the
- // variable in config has the same name as
- // button name in the toolbar.
- var options = this.config[cmd];
- var k = 0;
- // btn.element.selectedIndex = 0;
- for (var j in options) {
- // FIXME: the following line is scary.
- if ((j.toLowerCase() == value) ||
- (options[j].substr(0, value.length).toLowerCase() == value)) {
- btn.element.selectedIndex = k;
- break;
- }
- ++k;
- }
- } catch(e) {};
- break;
- case "textindicator":
- if (!text) {
- try {with (btn.element.style) {
- backgroundColor = HTMLArea._makeColor(
- doc.queryCommandValue(HTMLArea.is_ie ? "backcolor" : "hilitecolor"));
- if (/transparent/i.test(backgroundColor)) {
- // Mozilla
- backgroundColor = HTMLArea._makeColor(doc.queryCommandValue("backcolor"));
- }
- color = HTMLArea._makeColor(doc.queryCommandValue("forecolor"));
- fontFamily = doc.queryCommandValue("fontname");
- fontWeight = doc.queryCommandState("bold") ? "bold" : "normal";
- fontStyle = doc.queryCommandState("italic") ? "italic" : "normal";
- }} catch (e) {
- // alert(e + "\n\n" + cmd);
- }
- }
- break;
- case "htmlmode": btn.state("active", text); break;
- case "lefttoright":
- case "righttoleft":
- var el = this.getParentElement();
- while (el && !HTMLArea.isBlockElement(el))
- el = el.parentNode;
- if (el)
- btn.state("active", (el.style.direction == ((cmd == "righttoleft") ? "rtl" : "ltr")));
- break;
- default:
- try {
- btn.state("active", (!text && doc.queryCommandState(cmd)));
- } catch (e) {}
- }
- }
- // take undo snapshots
- if (this._customUndo && !this._timerUndo) {
- this._undoTakeSnapshot();
- var editor = this;
- this._timerUndo = setTimeout(function() {
- editor._timerUndo = null;
- }, this.config.undoTimeout);
- }
- // check if any plugins have registered refresh handlers
- for (var i in this.plugins) {
- var plugin = this.plugins[i].instance;
- if (typeof plugin.onUpdateToolbar == "function")
- plugin.onUpdateToolbar();
- }
+ var doc = this._doc;
+ var text = (this._editMode == "textmode");
+ var ancestors = null;
+ if (!text) {
+ ancestors = this.getAllAncestors();
+ if (this.config.statusBar && !noStatus) {
+ this._statusBarTree.innerHTML = HTMLArea.I18N.msg["Path"] + ": "; // clear
+ for (var i = ancestors.length; --i >= 0;) {
+ var el = ancestors[i];
+ if (!el) {
+ // hell knows why we get here; this
+ // could be a classic example of why
+ // it's good to check for conditions
+ // that are impossible to happen ;-)
+ continue;
+ }
+ var a = document.createElement("a");
+ a.href = "#";
+ a.el = el;
+ a.editor = this;
+ a.onclick = function() {
+ this.blur();
+ this.editor.selectNodeContents(this.el);
+ this.editor.updateToolbar(true);
+ return false;
+ };
+ a.oncontextmenu = function() {
+ // TODO: add context menu here
+ this.blur();
+ var info = "Inline style:\n\n";
+ info += this.el.style.cssText.split(/;\s*/).join(";\n");
+ alert(info);
+ return false;
+ };
+ var txt = el.tagName.toLowerCase();
+ a.title = el.style.cssText;
+ if (el.id) {
+ txt += "#" + el.id;
+ }
+ if (el.className) {
+ txt += "." + el.className;
+ }
+ a.appendChild(document.createTextNode(txt));
+ this._statusBarTree.appendChild(a);
+ if (i != 0) {
+ this._statusBarTree.appendChild(document.createTextNode(String.fromCharCode(0xbb)));
+ }
+ }
+ }
+ }
+ for (var i in this._toolbarObjects) {
+ var btn = this._toolbarObjects[i];
+ var cmd = i;
+ var inContext = true;
+ if (btn.context && !text) {
+ inContext = false;
+ var context = btn.context;
+ var attrs = [];
+ if (/(.*)\[(.*?)\]/.test(context)) {
+ context = RegExp.$1;
+ attrs = RegExp.$2.split(",");
+ }
+ context = context.toLowerCase();
+ var match = (context == "*");
+ for (var k in ancestors) {
+ if (!ancestors[k]) {
+ // the impossible really happens.
+ continue;
+ }
+ if (match || (ancestors[k].tagName.toLowerCase() == context)) {
+ inContext = true;
+ for (var ka in attrs) {
+ if (!eval("ancestors[k]." + attrs[ka])) {
+ inContext = false;
+ break;
+ }
+ }
+ if (inContext) {
+ break;
+ }
+ }
+ }
+ }
+ btn.state("enabled", (!text || btn.text) && inContext);
+ if (typeof cmd == "function") {
+ continue;
+ }
+ // look-it-up in the custom dropdown boxes
+ var dropdown = this.config.customSelects[cmd];
+ if ((!text || btn.text) && (typeof dropdown != "undefined")) {
+ dropdown.refresh(this);
+ continue;
+ }
+ switch (cmd) {
+ case "fontname":
+ case "fontsize":
+ case "formatblock":
+ if (!text) try {
+ var value = ("" + doc.queryCommandValue(cmd)).toLowerCase();
+ if (!value) {
+ // FIXME: what do we do here?
+ break;
+ }
+ // HACK -- retrieve the config option for this
+ // combo box. We rely on the fact that the
+ // variable in config has the same name as
+ // button name in the toolbar.
+ var options = this.config[cmd];
+ var k = 0;
+ // btn.element.selectedIndex = 0;
+ for (var j in options) {
+ // FIXME: the following line is scary.
+ if ((j.toLowerCase() == value) ||
+ (options[j].substr(0, value.length).toLowerCase() == value)) {
+ btn.element.selectedIndex = k;
+ break;
+ }
+ ++k;
+ }
+ } catch(e) {};
+ break;
+ case "textindicator":
+ if (!text) {
+ try {with (btn.element.style) {
+ backgroundColor = HTMLArea._makeColor(
+ doc.queryCommandValue(HTMLArea.is_ie ? "backcolor" : "hilitecolor"));
+ if (/transparent/i.test(backgroundColor)) {
+ // Mozilla
+ backgroundColor = HTMLArea._makeColor(doc.queryCommandValue("backcolor"));
+ }
+ color = HTMLArea._makeColor(doc.queryCommandValue("forecolor"));
+ fontFamily = doc.queryCommandValue("fontname");
+ fontWeight = doc.queryCommandState("bold") ? "bold" : "normal";
+ fontStyle = doc.queryCommandState("italic") ? "italic" : "normal";
+ }} catch (e) {
+ // alert(e + "\n\n" + cmd);
+ }
+ }
+ break;
+ case "htmlmode": btn.state("active", text); break;
+ case "lefttoright":
+ case "righttoleft":
+ var el = this.getParentElement();
+ while (el && !HTMLArea.isBlockElement(el))
+ el = el.parentNode;
+ if (el)
+ btn.state("active", (el.style.direction == ((cmd == "righttoleft") ? "rtl" : "ltr")));
+ break;
+ default:
+ try {
+ btn.state("active", (!text && doc.queryCommandState(cmd)));
+ } catch (e) {}
+ }
+ }
+ // take undo snapshots
+ if (this._customUndo && !this._timerUndo) {
+ this._undoTakeSnapshot();
+ var editor = this;
+ this._timerUndo = setTimeout(function() {
+ editor._timerUndo = null;
+ }, this.config.undoTimeout);
+ }
+ // check if any plugins have registered refresh handlers
+ for (var i in this.plugins) {
+ var plugin = this.plugins[i].instance;
+ if (typeof plugin.onUpdateToolbar == "function")
+ plugin.onUpdateToolbar();
+ }
};
/** Returns a node after which we can insert other nodes, in the current
* selection. The selection is removed. It splits a text node, if needed.
*/
HTMLArea.prototype.insertNodeAtSelection = function(toBeInserted) {
- if (!HTMLArea.is_ie) {
- var sel = this._getSelection();
- var range = this._createRange(sel);
- // remove the current selection
- sel.removeAllRanges();
- range.deleteContents();
- var node = range.startContainer;
- var pos = range.startOffset;
- switch (node.nodeType) {
- case 3: // Node.TEXT_NODE
- // we have to split it at the caret position.
- if (toBeInserted.nodeType == 3) {
- // do optimized insertion
- node.insertData(pos, toBeInserted.data);
- range = this._createRange();
- range.setEnd(node, pos + toBeInserted.length);
- range.setStart(node, pos + toBeInserted.length);
- sel.addRange(range);
- } else {
- node = node.splitText(pos);
- var selnode = toBeInserted;
- if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */) {
- selnode = selnode.firstChild;
- }
- node.parentNode.insertBefore(toBeInserted, node);
- this.selectNodeContents(selnode);
- this.updateToolbar();
- }
- break;
- case 1: // Node.ELEMENT_NODE
- var selnode = toBeInserted;
- if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */) {
- selnode = selnode.firstChild;
- }
- node.insertBefore(toBeInserted, node.childNodes[pos]);
- this.selectNodeContents(selnode);
- this.updateToolbar();
- break;
- }
- } else {
- return null; // this function not yet used for IE ]*>/gi,'
');
+ //clean up tags
+ D = D.replace(/]*>/gi,'').
+ replace(/]*>/gi,'').
+ replace(/
]*>/gi,'
');
- // replace outdated tags
- D = D.replace(//gi,'').
- replace(/<\/b>/gi,'');
+ // replace outdated tags
+ D = D.replace(//gi,'').
+ replace(/<\/b>/gi,'');
- // mozilla doesn't like tags
- D = D.replace(//gi,'').
- replace(/<\/em>/gi,'');
+ // mozilla doesn't like tags
+ D = D.replace(//gi,'').
+ replace(/<\/em>/gi,'');
- // kill unwanted tags
- D = D.replace(/<\?xml:[^>]*>/g, ''). // Word xml
- replace(/<\/?st1:[^>]*>/g,''). // Word SmartTags
- replace(/<\/?[a-z]\:[^>]*>/g,''). // All other funny Word non-HTML stuff
- replace(/<\/?font[^>]*>/gi,''). // Disable if you want to keep font formatting
- replace(/<\/?span[^>]*>/gi,' ').
- replace(/<\/?div[^>]*>/gi,' ').
- replace(/<\/?pre[^>]*>/gi,' ').
- replace(/<\/?h[1-6][^>]*>/gi,' ');
+ // kill unwanted tags
+ D = D.replace(/<\?xml:[^>]*>/g, ''). // Word xml
+ replace(/<\/?st1:[^>]*>/g,''). // Word SmartTags
+ replace(/<\/?[a-z]\:[^>]*>/g,''). // All other funny Word non-HTML stuff
+ replace(/<\/?font[^>]*>/gi,''). // Disable if you want to keep font formatting
+ replace(/<\/?span[^>]*>/gi,' ').
+ replace(/<\/?div[^>]*>/gi,' ').
+ replace(/<\/?pre[^>]*>/gi,' ').
+ replace(/<\/?h[1-6][^>]*>/gi,' ');
- //remove empty tags
- //D = D.replace(/<\/strong>/gi,'').
- //replace(/<\/i>/gi,'').
- //replace(/
");
- HTMLArea._stopEvent(ev);
- // }
- break;
- }
- }
- */
- // update the toolbar state after some time
- if (editor._timerToolbar) {
- clearTimeout(editor._timerToolbar);
- }
- editor._timerToolbar = setTimeout(function() {
- editor.updateToolbar();
- editor._timerToolbar = null;
- }, 50);
+ // headings
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ cmd = "formatblock";
+ value = "h" + key;
+ if (HTMLArea.is_ie) {
+ value = "<" + value + ">";
+ }
+ break;
+ }
+ if (cmd) {
+ // execute simple command
+ this.execCommand(cmd, false, value);
+ HTMLArea._stopEvent(ev);
+ }
+ }
+ /*
+ else if (keyEvent) {
+ // other keys here
+ switch (ev.keyCode) {
+ case 13: // KEY enter
+ // if (HTMLArea.is_ie) {
+ this.insertHTML("
");
+ HTMLArea._stopEvent(ev);
+ // }
+ break;
+ }
+ }
+ */
+ // update the toolbar state after some time
+ if (editor._timerToolbar) {
+ clearTimeout(editor._timerToolbar);
+ }
+ editor._timerToolbar = setTimeout(function() {
+ editor.updateToolbar();
+ editor._timerToolbar = null;
+ }, 50);
};
// retrieve the HTML
HTMLArea.prototype.getHTML = function() {
- switch (this._editMode) {
- case "wysiwyg" :
- if (!this.config.fullPage) {
- return HTMLArea.getHTML(this._doc.body, false, this);
- } else
- return this.doctype + "\n" + HTMLArea.getHTML(this._doc.documentElement, true, this);
- case "textmode" : return this._textArea.value;
- default : alert("Mode <" + mode + "> not defined!");
- }
- return false;
+ switch (this._editMode) {
+ case "wysiwyg" :
+ if (!this.config.fullPage) {
+ return HTMLArea.getHTML(this._doc.body, false, this);
+ } else
+ return this.doctype + "\n" + HTMLArea.getHTML(this._doc.documentElement, true, this);
+ case "textmode" : return this._textArea.value;
+ default : alert("Mode <" + mode + "> not defined!");
+ }
+ return false;
};
// retrieve the HTML (fastest version, but uses innerHTML)
HTMLArea.prototype.getInnerHTML = function() {
- switch (this._editMode) {
- case "wysiwyg" :
- if (!this.config.fullPage)
- return this._doc.body.innerHTML;
- else
- return this.doctype + "\n" + this._doc.documentElement.innerHTML;
- case "textmode" : return this._textArea.value;
- default : alert("Mode <" + mode + "> not defined!");
- }
- return false;
+ switch (this._editMode) {
+ case "wysiwyg" :
+ if (!this.config.fullPage)
+ return this._doc.body.innerHTML;
+ else
+ return this.doctype + "\n" + this._doc.documentElement.innerHTML;
+ case "textmode" : return this._textArea.value;
+ default : alert("Mode <" + mode + "> not defined!");
+ }
+ return false;
};
// completely change the HTML inside
HTMLArea.prototype.setHTML = function(html) {
- switch (this._editMode) {
- case "wysiwyg" :
- if (!this.config.fullPage)
- this._doc.body.innerHTML = html;
- else
- // this._doc.documentElement.innerHTML = html;
- this._doc.body.innerHTML = html;
- break;
- case "textmode" : this._textArea.value = html; break;
- default : alert("Mode <" + mode + "> not defined!");
- }
- return false;
+ switch (this._editMode) {
+ case "wysiwyg" :
+ if (!this.config.fullPage)
+ this._doc.body.innerHTML = html;
+ else
+ // this._doc.documentElement.innerHTML = html;
+ this._doc.body.innerHTML = html;
+ break;
+ case "textmode" : this._textArea.value = html; break;
+ default : alert("Mode <" + mode + "> not defined!");
+ }
+ return false;
};
// sets the given doctype (useful when config.fullPage is true)
HTMLArea.prototype.setDoctype = function(doctype) {
- this.doctype = doctype;
+ this.doctype = doctype;
};
/***************************************************
@@ -1884,9 +1887,9 @@ HTMLArea.prototype.setDoctype = function(doctype) {
// browser identification
HTMLArea.agt = navigator.userAgent.toLowerCase();
-HTMLArea.is_ie = ((HTMLArea.agt.indexOf("msie") != -1) && (HTMLArea.agt.indexOf("opera") == -1));
+HTMLArea.is_ie = ((HTMLArea.agt.indexOf("msie") != -1) && (HTMLArea.agt.indexOf("opera") == -1));
HTMLArea.is_opera = (HTMLArea.agt.indexOf("opera") != -1);
-HTMLArea.is_mac = (HTMLArea.agt.indexOf("mac") != -1);
+HTMLArea.is_mac = (HTMLArea.agt.indexOf("mac") != -1);
HTMLArea.is_mac_ie = (HTMLArea.is_ie && HTMLArea.is_mac);
HTMLArea.is_win_ie = (HTMLArea.is_ie && !HTMLArea.is_mac);
HTMLArea.is_gecko = (navigator.product == "Gecko");
@@ -1896,330 +1899,330 @@ HTMLArea._object = null;
// function that returns a clone of the given object
HTMLArea.cloneObject = function(obj) {
- var newObj = new Object;
+ var newObj = new Object;
- // check for array objects
- if (obj.constructor.toString().indexOf("function Array(") == 1) {
- newObj = obj.constructor();
- }
+ // check for array objects
+ if (obj.constructor.toString().indexOf("function Array(") == 1) {
+ newObj = obj.constructor();
+ }
- // check for function objects (as usual, IE is fucked up)
- if (obj.constructor.toString().indexOf("function Function(") == 1) {
- newObj = obj; // just copy reference to it
- } else for (var n in obj) {
- var node = obj[n];
- if (typeof node == 'object') { newObj[n] = HTMLArea.cloneObject(node); }
- else { newObj[n] = node; }
- }
+ // check for function objects (as usual, IE is fucked up)
+ if (obj.constructor.toString().indexOf("function Function(") == 1) {
+ newObj = obj; // just copy reference to it
+ } else for (var n in obj) {
+ var node = obj[n];
+ if (typeof node == 'object') { newObj[n] = HTMLArea.cloneObject(node); }
+ else { newObj[n] = node; }
+ }
- return newObj;
+ return newObj;
};
// FIXME!!! this should return false for IE < 5.5
HTMLArea.checkSupportedBrowser = function() {
- if (HTMLArea.is_gecko) {
- if (navigator.productSub < 20021201) {
- alert("You need at least Mozilla-1.3 Alpha.\n" +
- "Sorry, your Gecko is not supported.");
- return false;
- }
- if (navigator.productSub < 20030210) {
- alert("Mozilla < 1.3 Beta is not supported!\n" +
- "I'll try, though, but it might not work.");
- }
- }
- return HTMLArea.is_gecko || HTMLArea.is_ie;
+ if (HTMLArea.is_gecko) {
+ if (navigator.productSub < 20021201) {
+ alert("You need at least Mozilla-1.3 Alpha.\n" +
+ "Sorry, your Gecko is not supported.");
+ return false;
+ }
+ if (navigator.productSub < 20030210) {
+ alert("Mozilla < 1.3 Beta is not supported!\n" +
+ "I'll try, though, but it might not work.");
+ }
+ }
+ return HTMLArea.is_gecko || HTMLArea.is_ie;
};
// selection & ranges
// returns the current selection object
HTMLArea.prototype._getSelection = function() {
- if (HTMLArea.is_ie) {
- return this._doc.selection;
- } else {
- return this._iframe.contentWindow.getSelection();
- }
+ if (HTMLArea.is_ie) {
+ return this._doc.selection;
+ } else {
+ return this._iframe.contentWindow.getSelection();
+ }
};
// returns a range for the current selection
HTMLArea.prototype._createRange = function(sel) {
- if (HTMLArea.is_ie) {
- return sel.createRange();
- } else {
- this.focusEditor();
- if (typeof sel != "undefined") {
- try {
- return sel.getRangeAt(0);
- } catch(e) {
- return this._doc.createRange();
- }
- } else {
- return this._doc.createRange();
- }
- }
+ if (HTMLArea.is_ie) {
+ return sel.createRange();
+ } else {
+ this.focusEditor();
+ if (typeof sel != "undefined") {
+ try {
+ return sel.getRangeAt(0);
+ } catch(e) {
+ return this._doc.createRange();
+ }
+ } else {
+ return this._doc.createRange();
+ }
+ }
};
// event handling
HTMLArea._addEvent = function(el, evname, func) {
- if (HTMLArea.is_ie) {
- el.attachEvent("on" + evname, func);
- } else {
- el.addEventListener(evname, func, true);
- }
+ if (HTMLArea.is_ie) {
+ el.attachEvent("on" + evname, func);
+ } else {
+ el.addEventListener(evname, func, true);
+ }
};
HTMLArea._addEvents = function(el, evs, func) {
- for (var i in evs) {
- HTMLArea._addEvent(el, evs[i], func);
- }
+ for (var i in evs) {
+ HTMLArea._addEvent(el, evs[i], func);
+ }
};
HTMLArea._removeEvent = function(el, evname, func) {
- if (HTMLArea.is_ie) {
- el.detachEvent("on" + evname, func);
- } else {
- el.removeEventListener(evname, func, true);
- }
+ if (HTMLArea.is_ie) {
+ el.detachEvent("on" + evname, func);
+ } else {
+ el.removeEventListener(evname, func, true);
+ }
};
HTMLArea._removeEvents = function(el, evs, func) {
- for (var i in evs) {
- HTMLArea._removeEvent(el, evs[i], func);
- }
+ for (var i in evs) {
+ HTMLArea._removeEvent(el, evs[i], func);
+ }
};
HTMLArea._stopEvent = function(ev) {
- if (HTMLArea.is_ie) {
- ev.cancelBubble = true;
- ev.returnValue = false;
- } else {
- ev.preventDefault();
- ev.stopPropagation();
- }
+ if (HTMLArea.is_ie) {
+ ev.cancelBubble = true;
+ ev.returnValue = false;
+ } else {
+ ev.preventDefault();
+ ev.stopPropagation();
+ }
};
HTMLArea._removeClass = function(el, className) {
- if (!(el && el.className)) {
- return;
- }
- var cls = el.className.split(" ");
- var ar = new Array();
- for (var i = cls.length; i > 0;) {
- if (cls[--i] != className) {
- ar[ar.length] = cls[i];
- }
- }
- el.className = ar.join(" ");
+ if (!(el && el.className)) {
+ return;
+ }
+ var cls = el.className.split(" ");
+ var ar = new Array();
+ for (var i = cls.length; i > 0;) {
+ if (cls[--i] != className) {
+ ar[ar.length] = cls[i];
+ }
+ }
+ el.className = ar.join(" ");
};
HTMLArea._addClass = function(el, className) {
- // remove the class first, if already there
- HTMLArea._removeClass(el, className);
- el.className += " " + className;
+ // remove the class first, if already there
+ HTMLArea._removeClass(el, className);
+ el.className += " " + className;
};
HTMLArea._hasClass = function(el, className) {
- if (!(el && el.className)) {
- return false;
- }
- var cls = el.className.split(" ");
- for (var i = cls.length; i > 0;) {
- if (cls[--i] == className) {
- return true;
- }
- }
- return false;
+ if (!(el && el.className)) {
+ return false;
+ }
+ var cls = el.className.split(" ");
+ for (var i = cls.length; i > 0;) {
+ if (cls[--i] == className) {
+ return true;
+ }
+ }
+ return false;
};
HTMLArea.isBlockElement = function(el) {
- var blockTags = " body form textarea fieldset ul ol dl li div " +
- "p h1 h2 h3 h4 h5 h6 quote pre table thead " +
- "tbody tfoot tr td iframe address ";
- return (blockTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
+ var blockTags = " body form textarea fieldset ul ol dl li div " +
+ "p h1 h2 h3 h4 h5 h6 quote pre table thead " +
+ "tbody tfoot tr td iframe address ";
+ return (blockTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
};
HTMLArea.needsClosingTag = function(el) {
- var closingTags = " head script style div span tr td tbody table em strong font a title ";
- return (closingTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
+ var closingTags = " head script style div span tr td tbody table em strong font a title ";
+ return (closingTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
};
// performs HTML encoding of some given string
HTMLArea.htmlEncode = function(str) {
- // we don't need regexp for that, but.. so be it for now.
- str = str.replace(/&/ig, "&");
- str = str.replace(//ig, ">");
- str = str.replace(/\x22/ig, """);
- // \x22 means '"' -- we use hex reprezentation so that we don't disturb
- // JS compressors (well, at least mine fails.. ;)
- return str;
+ // we don't need regexp for that, but.. so be it for now.
+ str = str.replace(/&/ig, "&");
+ str = str.replace(//ig, ">");
+ str = str.replace(/\x22/ig, """);
+ // \x22 means '"' -- we use hex reprezentation so that we don't disturb
+ // JS compressors (well, at least mine fails.. ;)
+ return str;
};
-// Retrieves the HTML code from the given node. This is a replacement for
+// Retrieves the HTML code from the given node. This is a replacement for
// getting innerHTML, using standard DOM calls.
HTMLArea.getHTML = function(root, outputRoot, editor) {
- var html = "";
- switch (root.nodeType) {
- case 1: // Node.ELEMENT_NODE
- case 11: // Node.DOCUMENT_FRAGMENT_NODE
- var closed;
- var i;
- var root_tag = (root.nodeType == 1) ? root.tagName.toLowerCase() : '';
- if (HTMLArea.is_ie && root_tag == "head") {
- if (outputRoot)
- html += "";
- // lowercasize
- var save_multiline = RegExp.multiline;
- RegExp.multiline = true;
- var txt = root.innerHTML.replace(HTMLArea.RE_tagName, function(str, p1, p2) {
- return p1 + p2.toLowerCase();
- });
- RegExp.multiline = save_multiline;
- html += txt;
- if (outputRoot)
- html += "";
- break;
- } else if (outputRoot) {
- closed = (!(root.hasChildNodes() || HTMLArea.needsClosingTag(root)));
- html = "<" + root.tagName.toLowerCase();
- var attrs = root.attributes;
- for (i = 0; i < attrs.length; ++i) {
- var a = attrs.item(i);
- if (!a.specified) {
- continue;
- }
- var name = a.nodeName.toLowerCase();
- if (/_moz|contenteditable/.test(name)) {
- // avoid certain attributes
- continue;
- }
- var value;
- if (name != "style") {
- // IE5.5 reports 25 when cellSpacing is
- // 1; other values might be doomed too.
- // For this reason we extract the
- // values directly from the root node.
- // I'm starting to HATE JavaScript
- // development. Browser differences
- // suck.
- //
- // Using Gecko the values of href and src are converted to absolute links
- // unless we get them using nodeValue()
- if (typeof root[a.nodeName] != "undefined" && name != "href" && name != "src") {
- value = root[a.nodeName];
- } else {
- value = a.nodeValue;
- // IE seems not willing to return the original values - it converts to absolute
- // links using a.nodeValue, a.value, a.stringValue, root.getAttribute("href")
- // So we have to strip the baseurl manually -/
- if (HTMLArea.is_ie && (name == "href" || name == "src")) {
- value = editor.stripBaseURL(value);
- }
- }
- } else { // IE fails to put style in attributes list
- // FIXME: cssText reported by IE is UPPERCASE
- value = root.style.cssText;
- }
- if (/(_moz|^$)/.test(value)) {
- // Mozilla reports some special tags
- // here; we don't need them.
- continue;
- }
- html += " " + name + '="' + value + '"';
- }
- html += closed ? " />" : ">";
- }
- for (i = root.firstChild; i; i = i.nextSibling) {
- html += HTMLArea.getHTML(i, true, editor);
- }
- if (outputRoot && !closed) {
- html += "" + root.tagName.toLowerCase() + ">";
- }
- break;
- case 3: // Node.TEXT_NODE
- html = HTMLArea.htmlEncode(root.data);
- break;
- case 8: // Node.COMMENT_NODE
- html = "";
- break; // skip comments, for now.
- }
- return html;
+ var html = "";
+ switch (root.nodeType) {
+ case 1: // Node.ELEMENT_NODE
+ case 11: // Node.DOCUMENT_FRAGMENT_NODE
+ var closed;
+ var i;
+ var root_tag = (root.nodeType == 1) ? root.tagName.toLowerCase() : '';
+ if (HTMLArea.is_ie && root_tag == "head") {
+ if (outputRoot)
+ html += "";
+ // lowercasize
+ var save_multiline = RegExp.multiline;
+ RegExp.multiline = true;
+ var txt = root.innerHTML.replace(HTMLArea.RE_tagName, function(str, p1, p2) {
+ return p1 + p2.toLowerCase();
+ });
+ RegExp.multiline = save_multiline;
+ html += txt;
+ if (outputRoot)
+ html += "";
+ break;
+ } else if (outputRoot) {
+ closed = (!(root.hasChildNodes() || HTMLArea.needsClosingTag(root)));
+ html = "<" + root.tagName.toLowerCase();
+ var attrs = root.attributes;
+ for (i = 0; i < attrs.length; ++i) {
+ var a = attrs.item(i);
+ if (!a.specified) {
+ continue;
+ }
+ var name = a.nodeName.toLowerCase();
+ if (/_moz|contenteditable/.test(name)) {
+ // avoid certain attributes
+ continue;
+ }
+ var value;
+ if (name != "style") {
+ // IE5.5 reports 25 when cellSpacing is
+ // 1; other values might be doomed too.
+ // For this reason we extract the
+ // values directly from the root node.
+ // I'm starting to HATE JavaScript
+ // development. Browser differences
+ // suck.
+ //
+ // Using Gecko the values of href and src are converted to absolute links
+ // unless we get them using nodeValue()
+ if (typeof root[a.nodeName] != "undefined" && name != "href" && name != "src") {
+ value = root[a.nodeName];
+ } else {
+ value = a.nodeValue;
+ // IE seems not willing to return the original values - it converts to absolute
+ // links using a.nodeValue, a.value, a.stringValue, root.getAttribute("href")
+ // So we have to strip the baseurl manually -/
+ if (HTMLArea.is_ie && (name == "href" || name == "src")) {
+ value = editor.stripBaseURL(value);
+ }
+ }
+ } else { // IE fails to put style in attributes list
+ // FIXME: cssText reported by IE is UPPERCASE
+ value = root.style.cssText;
+ }
+ if (/(_moz|^$)/.test(value)) {
+ // Mozilla reports some special tags
+ // here; we don't need them.
+ continue;
+ }
+ html += " " + name + '="' + value + '"';
+ }
+ html += closed ? " />" : ">";
+ }
+ for (i = root.firstChild; i; i = i.nextSibling) {
+ html += HTMLArea.getHTML(i, true, editor);
+ }
+ if (outputRoot && !closed) {
+ html += "" + root.tagName.toLowerCase() + ">";
+ }
+ break;
+ case 3: // Node.TEXT_NODE
+ html = HTMLArea.htmlEncode(root.data);
+ break;
+ case 8: // Node.COMMENT_NODE
+ html = "";
+ break; // skip comments, for now.
+ }
+ return html;
};
HTMLArea.prototype.stripBaseURL = function(string) {
- var baseurl = this.config.baseURL;
+ var baseurl = this.config.baseURL;
- // strip to last directory in case baseurl points to a file
- baseurl = baseurl.replace(/[^\/]+$/, '');
- var basere = new RegExp(baseurl);
- string = string.replace(basere, "");
+ // strip to last directory in case baseurl points to a file
+ baseurl = baseurl.replace(/[^\/]+$/, '');
+ var basere = new RegExp(baseurl);
+ string = string.replace(basere, "");
- // strip host-part of URL which is added by MSIE to links relative to server root
- // commented out for moodle
- //baseurl = baseurl.replace(/^(https?:\/\/[^\/]+)(.*)$/, '$1');
- basere = new RegExp(baseurl);
- return string.replace(basere, "");
+ // strip host-part of URL which is added by MSIE to links relative to server root
+ // commented out for moodle
+ //baseurl = baseurl.replace(/^(https?:\/\/[^\/]+)(.*)$/, '$1');
+ basere = new RegExp(baseurl);
+ return string.replace(basere, "");
};
String.prototype.trim = function() {
- a = this.replace(/^\s+/, '');
- return a.replace(/\s+$/, '');
+ a = this.replace(/^\s+/, '');
+ return a.replace(/\s+$/, '');
};
// creates a rgb-style color from a number
HTMLArea._makeColor = function(v) {
- if (typeof v != "number") {
- // already in rgb (hopefully); IE doesn't get here.
- return v;
- }
- // IE sends number; convert to rgb.
- var r = v & 0xFF;
- var g = (v >> 8) & 0xFF;
- var b = (v >> 16) & 0xFF;
- return "rgb(" + r + "," + g + "," + b + ")";
+ if (typeof v != "number") {
+ // already in rgb (hopefully); IE doesn't get here.
+ return v;
+ }
+ // IE sends number; convert to rgb.
+ var r = v & 0xFF;
+ var g = (v >> 8) & 0xFF;
+ var b = (v >> 16) & 0xFF;
+ return "rgb(" + r + "," + g + "," + b + ")";
};
// returns hexadecimal color representation from a number or a rgb-style color.
HTMLArea._colorToRgb = function(v) {
- if (!v)
- return '';
+ if (!v)
+ return '';
- // returns the hex representation of one byte (2 digits)
- function hex(d) {
- return (d < 16) ? ("0" + d.toString(16)) : d.toString(16);
- };
+ // returns the hex representation of one byte (2 digits)
+ function hex(d) {
+ return (d < 16) ? ("0" + d.toString(16)) : d.toString(16);
+ };
- if (typeof v == "number") {
- // we're talking to IE here
- var r = v & 0xFF;
- var g = (v >> 8) & 0xFF;
- var b = (v >> 16) & 0xFF;
- return "#" + hex(r) + hex(g) + hex(b);
- }
+ if (typeof v == "number") {
+ // we're talking to IE here
+ var r = v & 0xFF;
+ var g = (v >> 8) & 0xFF;
+ var b = (v >> 16) & 0xFF;
+ return "#" + hex(r) + hex(g) + hex(b);
+ }
- if (v.substr(0, 3) == "rgb") {
- // in rgb(...) form -- Mozilla
- var re = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/;
- if (v.match(re)) {
- var r = parseInt(RegExp.$1);
- var g = parseInt(RegExp.$2);
- var b = parseInt(RegExp.$3);
- return "#" + hex(r) + hex(g) + hex(b);
- }
- // doesn't match RE?! maybe uses percentages or float numbers
- // -- FIXME: not yet implemented.
- return null;
- }
+ if (v.substr(0, 3) == "rgb") {
+ // in rgb(...) form -- Mozilla
+ var re = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/;
+ if (v.match(re)) {
+ var r = parseInt(RegExp.$1);
+ var g = parseInt(RegExp.$2);
+ var b = parseInt(RegExp.$3);
+ return "#" + hex(r) + hex(g) + hex(b);
+ }
+ // doesn't match RE?! maybe uses percentages or float numbers
+ // -- FIXME: not yet implemented.
+ return null;
+ }
- if (v.substr(0, 1) == "#") {
- // already hex rgb (hopefully :D )
- return v;
- }
+ if (v.substr(0, 1) == "#") {
+ // already hex rgb (hopefully :D )
+ return v;
+ }
- // if everything else fails ;)
- return null;
+ // if everything else fails ;)
+ return null;
};
// modal dialogs for Mozilla (for IE we're using the showModalDialog() call).
@@ -2228,29 +2231,29 @@ HTMLArea._colorToRgb = function(v) {
// this function will get called after the dialog is closed, with the return
// value of the dialog.
HTMLArea.prototype._popupDialog = function(url, action, init) {
- Dialog(this.popupURL(url), action, init);
+ Dialog(this.popupURL(url), action, init);
};
// paths
HTMLArea.prototype.imgURL = function(file, plugin) {
- if (typeof plugin == "undefined")
- return _editor_url + file;
- else
- return _editor_url + "plugins/" + plugin + "/img/" + file;
+ if (typeof plugin == "undefined")
+ return _editor_url + file;
+ else
+ return _editor_url + "plugins/" + plugin + "/img/" + file;
};
HTMLArea.prototype.popupURL = function(file) {
- var url = "";
- if (file.match(/^plugin:\/\/(.*?)\/(.*)/)) {
- var plugin = RegExp.$1;
- var popup = RegExp.$2;
- if (!/\.html$/.test(popup))
- popup += ".html";
- url = _editor_url + "plugins/" + plugin + "/popups/" + popup;
- } else
- url = _editor_url + this.config.popupURL + file;
- return url;
+ var url = "";
+ if (file.match(/^plugin:\/\/(.*?)\/(.*)/)) {
+ var plugin = RegExp.$1;
+ var popup = RegExp.$2;
+ if (!/\.html$/.test(popup))
+ popup += ".html";
+ url = _editor_url + "plugins/" + plugin + "/popups/" + popup;
+ } else
+ url = _editor_url + this.config.popupURL + file;
+ return url;
};
// EOF