1 /********************************************************************
2 * openWYSIWYG v1.47 Copyright (c) 2006 openWebWare.com
3 * Contact us at devs@openwebware.com
4 * This copyright notice MUST stay intact for use.
6 * $Id: wysiwyg.js,v 1.22 2007/09/08 21:45:57 xhaggi Exp $
9 * An open source WYSIWYG editor for use in web based applications.
10 * For full source code and docs, visit http://www.openwebware.com
12 * This library is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License as published
14 * by the Free Software Foundation; either version 2.1 of the License, or
15 * (at your option) any later version.
17 * This library is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
20 * License for more details.
22 * You should have received a copy of the GNU Lesser General Public License along
23 * with this library; if not, write to the Free Software Foundation, Inc., 59
24 * Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 ********************************************************************/
29 * Settings class, holds all customizeable properties
34 this.ImagesDir = "./openwysiwyg/images/";
37 this.PopupsDir = "./openwysiwyg/popups/";
40 this.CSSFile = "./openwysiwyg/styles/wysiwyg.css";
42 // Default WYSIWYG width and height (use px or %)
44 this.Height = "300px";
46 // Default stylesheet of the WYSIWYG editor window
47 this.DefaultStyle = "font-family: Arial; font-size: 12px; background-color: #FFFFFF";
49 // Stylesheet if editor is disabled
50 this.DisabledStyle = "font-family: Arial; font-size: 12px; background-color: #EEEEEE";
52 // Width + Height of the preview window
53 this.PreviewWidth = 500;
54 this.PreviewHeight = 400;
56 // Confirmation message if you strip any HTML added by word
57 this.RemoveFormatConfMessage = "Clean HTML inserted by MS Word ?";
59 // Nofication if browser is not supported by openWYSIWYG, leave it blank for no message output.
60 this.NoValidBrowserMessage = "openWYSIWYG does not support your browser.";
62 // Anchor path to strip, leave it blank to ignore
63 // or define auto to strip the path where the editor is placed
65 this.AnchorPathToStrip = "auto";
67 // Image path to strip, leave it blank to ignore
68 // or define auto to strip the path where the editor is placed
70 this.ImagePathToStrip = "auto";
72 // Enable / Disable the custom context menu
73 this.ContextMenu = true;
75 // Enabled the status bar update. Within the status bar
76 // node tree of the actually selected element will build
77 this.StatusBarEnabled = true;
79 // If enabled than the capability of the IE inserting line breaks will be inverted.
80 // Normal: ENTER = <p> , SHIFT + ENTER = <br>
81 // Inverted: ENTER = <br>, SHIFT + ENTER = <p>
82 this.InvertIELineBreaks = false;
84 // Replace line breaks with <br> tags
85 this.ReplaceLineBreaks = false;
87 // Page that opened the WYSIWYG (Used for the return command)
88 this.Opener = "admin.asp";
90 // Insert image implementation
91 this.ImagePopupFile = "";
92 this.ImagePopupWidth = 0;
93 this.ImagePopupHeight = 0;
95 // Holds the available buttons displayed
96 // on the toolbar of the editor
97 this.Toolbar = new Array();
98 this.Toolbar[0] = new Array("font", "fontsize", "headings", "bold", "italic", "underline", "strikethrough", "seperator", "forecolor", "backcolor", "seperator", "justifyfull", "justifyleft", "justifycenter", "justifyright", "seperator", "unorderedlist", "orderedlist", "outdent", "indent");
99 this.Toolbar[1] = new Array("save", // "return", // return button disabled by default
100 "seperator", "subscript", "superscript", "seperator", "cut", "copy", "paste", "removeformat", "seperator", "undo", "redo", "seperator", "inserttable", "insertimage", "createlink", "seperator", "preview", "print", "seperator", "viewSource", "maximize", "seperator", "help");
103 this.DropDowns = new Array();
105 this.DropDowns['font'] = {
108 label: "<font style=\"font-family:{value};font-size:12px;\">{value}</font>",
110 elements: new Array("Arial", "Sans Serif", "Tahoma", "Verdana", "Courier New", "Georgia", "Times New Roman", "Impact", "Comic Sans MS")
113 this.DropDowns['fontsize'] = {
116 label: "<font size=\"{value}\">Size {value}</font>",
118 elements: new Array("1", "2", "3", "4", "5", "6", "7")
121 this.DropDowns['headings'] = {
123 command: "FormatBlock",
124 label: "<{value} style=\"margin:0px;text-decoration:none;font-family:Arial\">{value}</{value}>",
126 elements: new Array("H1", "H2", "H3", "H4", "H5", "H6")
129 // Add the given element to the defined toolbar
130 // on the defined position
131 this.addToolbarElement = function(element, toolbar, position){
132 if (element != "seperator") {
133 this.removeToolbarElement(element);
135 if (this.Toolbar[toolbar - 1] == null) {
136 this.Toolbar[toolbar - 1] = new Array();
138 this.Toolbar[toolbar - 1].splice(position + 1, 1, element);
141 // Remove an element from the toolbar
142 this.removeToolbarElement = function(element){
143 if (element == "seperator") {
145 } // do not remove seperators
146 for (var i = 0; i < this.Toolbar.length; i++) {
147 if (this.Toolbar[i]) {
148 var toolbar = this.Toolbar[i];
149 for (var j = 0; j < toolbar.length; j++) {
150 if (toolbar[j] != null && toolbar[j] == element) {
151 this.Toolbar[i].splice(j, 1);
158 // clear all or a given toolbar
159 this.clearToolbar = function(toolbar){
160 if (typeof toolbar == "undefined") {
161 this.Toolbar = new Array();
164 this.Toolbar[toolbar + 1] = new Array();
171 /* ---------------------------------------------------------------------- *\
172 !! Do not change something below or you know what you are doning !!
173 \* ---------------------------------------------------------------------- */
174 // List of available block formats (not in use)
175 //BlockFormats: new Array("Address", "Bulleted List", "Definition", "Definition Term", "Directory List", "Formatted", "Heading 1", "Heading 2", "Heading 3", "Heading 4", "Heading 5", "Heading 6", "Menu List", "Normal", "Numbered List"),
177 // List of available actions and their respective ID and images
179 //Name buttonID buttonTitle buttonImage buttonImageRollover
180 "bold": ['Bold', 'Bold', 'bold.gif', 'bold_on.gif'],
181 "italic": ['Italic', 'Italic', 'italics.gif', 'italics_on.gif'],
182 "underline": ['Underline', 'Underline', 'underline.gif', 'underline_on.gif'],
183 "strikethrough": ['Strikethrough', 'Strikethrough', 'strikethrough.gif', 'strikethrough_on.gif'],
184 "seperator": ['', '', 'seperator.gif', 'seperator.gif'],
185 "subscript": ['Subscript', 'Subscript', 'subscript.gif', 'subscript_on.gif'],
186 "superscript": ['Superscript', 'Superscript', 'superscript.gif', 'superscript_on.gif'],
187 "justifyleft": ['Justifyleft', 'Justifyleft', 'justify_left.gif', 'justify_left_on.gif'],
188 "justifycenter": ['Justifycenter', 'Justifycenter', 'justify_center.gif', 'justify_center_on.gif'],
189 "justifyright": ['Justifyright', 'Justifyright', 'justify_right.gif', 'justify_right_on.gif'],
190 "justifyfull": ['Justifyfull', 'Justifyfull', 'justify_justify.gif', 'justify_justify_on.gif'],
191 "unorderedlist": ['InsertUnorderedList', 'Insert Unordered List', 'list_unordered.gif', 'list_unordered_on.gif'],
192 "orderedlist": ['InsertOrderedList', 'Insert Ordered List', 'list_ordered.gif', 'list_ordered_on.gif'],
193 "outdent": ['Outdent', 'Outdent', 'indent_left.gif', 'indent_left_on.gif'],
194 "indent": ['Indent', 'Indent', 'indent_right.gif', 'indent_right_on.gif'],
195 "cut": ['Cut', 'Cut', 'cut.gif', 'cut_on.gif'],
196 "copy": ['Copy', 'Copy', 'copy.gif', 'copy_on.gif'],
197 "paste": ['Paste', 'Paste', 'paste.gif', 'paste_on.gif'],
198 "forecolor": ['ForeColor', 'Fore Color', 'forecolor.gif', 'forecolor_on.gif'],
199 "backcolor": ['BackColor', 'Back Color', 'backcolor.gif', 'backcolor_on.gif'],
200 "undo": ['Undo', 'Undo', 'undo.gif', 'undo_on.gif'],
201 "redo": ['Redo', 'Redo', 'redo.gif', 'redo_on.gif'],
202 "inserttable": ['InsertTable', 'Insert Table', 'insert_table.gif', 'insert_table_on.gif'],
203 "insertimage": ['InsertImage', 'Insert Image', 'insert_picture.gif', 'insert_picture_on.gif'],
204 "createlink": ['CreateLink', 'Create Link', 'insert_hyperlink.gif', 'insert_hyperlink_on.gif'],
205 "viewSource": ['ViewSource', 'View Source', 'view_source.gif', 'view_source_on.gif'],
206 "viewText": ['ViewText', 'View Text', 'view_text.gif', 'view_text_on.gif'],
207 "help": ['Help', 'Help', 'help.gif', 'help_on.gif'],
208 "fonts": ['Fonts', 'Select Font', 'select_font.gif', 'select_font_on.gif'],
209 "fontsizes": ['Fontsizes', 'Select Size', 'select_size.gif', 'select_size_on.gif'],
210 "headings": ['Headings', 'Select Size', 'select_heading.gif', 'select_heading_on.gif'],
211 "preview": ['Preview', 'Preview', 'preview.gif', 'preview_on.gif'],
212 "print": ['Print', 'Print', 'print.gif', 'print_on.gif'],
213 "removeformat": ['RemoveFormat', 'Strip Word HTML', 'remove_format.gif', 'remove_format_on.gif'],
214 "delete": ['Delete', 'Delete', 'delete.gif', 'delete_on.gif'],
215 "save": ['Save', 'Save document', 'save.gif', 'save_on.gif'],
216 "return": ['Return', 'Return without saving', 'return.gif', 'return_on.gif'],
217 "maximize": ['Maximize', 'Maximize the editor', 'maximize.gif', 'maximize_on.gif']
220 // stores the different settings for each textarea
221 // the textarea identifier is used to store the settings object
223 // Create viewTextMode global variable and set to 0
224 // enabling all toolbar commands while in HTML mode
225 viewTextMode: new Array(),
227 maximized: new Array(),
230 * Get the range of the given selection
232 * @param {Selection} sel Selection object
233 * @return {Range} Range object
235 getRange: function(sel){
236 return sel.createRange ? sel.createRange() : sel.getRangeAt(0);
240 * Return the editor div element
242 * @param {String} n Editor identifier
243 * @return {HtmlDivElement} Iframe object
245 getEditorDiv: function(n){
246 return $("wysiwyg_div_" + n);
250 * Return the editor table element
252 * @param {String} n Editor identifier
253 * @return {HtmlTableElement} Iframe object
255 getEditorTable: function(n){
256 return $("wysiwyg_table_" + n);
260 * Get the iframe object of the WYSIWYG editor
262 * @param {String} n Editor identifier
263 * @return {HtmlIframeElement} Iframe object
265 getEditor: function(n){
266 return $("wysiwyg" + n);
270 * Get editors window element
272 * @param {String} n Editor identifier
273 * @return {HtmlWindowElement} Html window object
275 getEditorWindow: function(n){
276 return this.getEditor(n).contentWindow;
280 * Attach the WYSIWYG editor to the given textarea element
282 * @param {String} id Textarea identifier (all = all textareas)
283 * @param {Settings} settings the settings which will be applied to the textarea
285 attach: function(id, settings){
287 this.setSettings(id, settings);
288 WYSIWYG_Core.includeCSS(this.config[id].CSSFile);
289 WYSIWYG_Core.addEvent(window, "load", function generateEditor(){
290 WYSIWYG._generate(id, settings);
294 WYSIWYG_Core.addEvent(window, "load", function generateEditor(){
295 WYSIWYG.attachAll(settings);
301 * Attach the WYSIWYG editor to all textarea elements
303 * @param {Settings} settings Settings to customize the look and feel
305 attachAll: function(settings){
306 var areas = document.getElementsByTagName("textarea");
307 for (var i = 0; i < areas.length; i++) {
308 var id = areas[i].getAttribute("id");
309 if (id == null || id == "")
311 this.setSettings(id, settings);
312 WYSIWYG_Core.includeCSS(this.config[id].CSSFile);
313 WYSIWYG._generate(id, settings);
318 * Display an iframe instead of the textarea.
319 * It's used as textarea replacement to display HTML.
321 * @param id Textarea identifier (all = all textareas)
322 * @param settings the settings which will be applied to the textarea
324 display: function(id, settings){
326 this.setSettings(id, settings);
327 WYSIWYG_Core.includeCSS(this.config[id].CSSFile);
328 WYSIWYG_Core.addEvent(window, "load", function displayIframe(){
329 WYSIWYG._display(id, settings);
333 WYSIWYG_Core.addEvent(window, "load", function displayIframe(){
334 WYSIWYG.displayAll(settings);
340 * Display an iframe instead of the textarea.
341 * It's apply the iframe to all textareas found in the current document.
343 * @param settings Settings to customize the look and feel
345 displayAll: function(settings){
346 var areas = document.getElementsByTagName("textarea");
347 for (var i = 0; i < areas.length; i++) {
348 var id = areas[i].getAttribute("id");
349 if (id == null || id == "")
351 this.setSettings(id, settings);
352 WYSIWYG_Core.includeCSS(this.config[id].CSSFile);
353 WYSIWYG._display(id, settings);
358 * Set settings in config array, use the textarea id as identifier
360 * @param n Textarea identifier (all = all textareas)
361 * @param settings the settings which will be applied to the textarea
363 setSettings: function(n, settings){
364 if (typeof(settings) != "object") {
365 this.config[n] = new this.Settings();
368 this.config[n] = settings;
373 * Insert or modify an image
375 * @param {String} src Source of the image
376 * @param {Integer} width Width
377 * @param {Integer} height Height
378 * @param {String} align Alignment of the image
379 * @param {String} border Border size
380 * @param {String} alt Alternativ Text
381 * @param {Integer} hspace Horizontal Space
382 * @param {Integer} vspace Vertical Space
383 * @param {String} n The editor identifier (the textarea's ID)
385 insertImage: function(src, width, height, align, border, alt, hspace, vspace, n){
388 var doc = this.getEditorWindow(n).document;
389 // get selection and range
390 var sel = this.getSelection(n);
391 var range = this.getRange(sel);
393 // the current tag of range
394 var img = this.findParent("img", range);
396 // element is not a link
397 var update = (img == null) ? false : true;
399 img = doc.createElement("img");
402 // set the attributes
403 WYSIWYG_Core.setAttribute(img, "src", src);
404 WYSIWYG_Core.setAttribute(img, "style", "width:" + width + ";height:" + height);
406 WYSIWYG_Core.setAttribute(img, "align", align);
409 img.removeAttribute("align");
411 WYSIWYG_Core.setAttribute(img, "border", border);
412 WYSIWYG_Core.setAttribute(img, "alt", alt);
413 WYSIWYG_Core.setAttribute(img, "hspace", hspace);
414 WYSIWYG_Core.setAttribute(img, "vspace", vspace);
415 img.removeAttribute("width");
416 img.removeAttribute("height");
418 // on update exit here
423 // Check if IE or Mozilla (other)
424 if (WYSIWYG_Core.isMSIE) {
425 range.pasteHTML(img.outerHTML);
428 this.insertNodeAtSelection(img, n);
433 * Insert or modify a link
435 * @param {String} href The url of the link
436 * @param {String} target Target of the link
437 * @param {String} style Stylesheet of the link
438 * @param {String} styleClass Stylesheet class of the link
439 * @param {String} name Name attribute of the link
440 * @param {String} n The editor identifier (the textarea's ID)
442 insertLink: function(href, target, style, styleClass, name, n){
445 var doc = this.getEditorWindow(n).document;
446 // get selection and range
447 var sel = this.getSelection(n);
448 var range = this.getRange(sel);
451 // get element from selection
452 if (WYSIWYG_Core.isMSIE) {
453 if (sel.type == "Control" && range.length == 1) {
454 range = this.getTextRange(range(0));
459 // find a as parent element
460 lin = this.findParent("a", range);
462 // check if parent is found
463 var update = (lin == null) ? false : true;
465 lin = doc.createElement("a");
468 // set the attributes
469 WYSIWYG_Core.setAttribute(lin, "href", href);
470 WYSIWYG_Core.setAttribute(lin, "class", styleClass);
471 WYSIWYG_Core.setAttribute(lin, "className", styleClass);
472 WYSIWYG_Core.setAttribute(lin, "target", target);
473 WYSIWYG_Core.setAttribute(lin, "name", name);
474 WYSIWYG_Core.setAttribute(lin, "style", style);
476 // on update exit here
481 // Check if IE or Mozilla (other)
482 if (WYSIWYG_Core.isMSIE) {
484 lin.innerHTML = range.htmlText;
485 range.pasteHTML(lin.outerHTML);
488 var node = range.startContainer;
489 var pos = range.startOffset;
490 if (node.nodeType != 3) {
491 node = node.childNodes[pos];
494 lin.appendChild(node);
497 this.insertNodeAtSelection(lin, n);
502 * Strips any HTML added by word
504 * @param {String} n The editor identifier (the textarea's ID)
506 removeFormat: function(n){
508 if (!confirm(this.config[n].RemoveFormatConfMessage)) {
511 var doc = this.getEditorWindow(n).document;
512 var str = doc.body.innerHTML;
514 str = str.replace(/<span([^>])*>( )*\s*<\/span>/gi, '');
515 str = str.replace(/<span[^>]*>/gi, '');
516 str = str.replace(/<\/span[^>]*>/gi, '');
517 str = str.replace(/<p([^>])*>( )*\s*<\/p>/gi, '');
518 str = str.replace(/<p[^>]*>/gi, '');
519 str = str.replace(/<\/p[^>]*>/gi, '');
520 str = str.replace(/<h([^>])[0-9]>( )*\s*<\/h>/gi, '');
521 str = str.replace(/<h[^>][0-9]>/gi, '');
522 str = str.replace(/<\/h[^>][0-9]>/gi, '');
523 str = str.replace(/<B [^>]*>/ig, '<b>');
525 // var repl_i1 = /<I[^>]*>/ig;
526 // str = str.replace (repl_i1, '<i>');
528 str = str.replace(/<DIV[^>]*>/ig, '');
529 str = str.replace(/<\/DIV>/gi, '');
530 str = str.replace(/<[\/\w?]+:[^>]*>/ig, '');
531 str = str.replace(/( ){2,}/ig, ' ');
532 str = str.replace(/<STRONG>/ig, '');
533 str = str.replace(/<\/STRONG>/ig, '');
534 str = str.replace(/<TT>/ig, '');
535 str = str.replace(/<\/TT>/ig, '');
536 str = str.replace(/<FONT [^>]*>/ig, '');
537 str = str.replace(/<\/FONT>/ig, '');
538 str = str.replace(/STYLE=\"[^\"]*\"/ig, '');
539 str = str.replace(/<([\w]+) class=([^ |>]*)([^>]*)/gi, '<$1$3');
540 str = str.replace(/<([\w]+) style="([^"]*)"([^>]*)/gi, '<$1$3');
541 str = str.replace(/width=([^ |>]*)([^>]*)/gi, '');
542 str = str.replace(/classname=([^ |>]*)([^>]*)/gi, '');
543 str = str.replace(/align=([^ |>]*)([^>]*)/gi, '');
544 str = str.replace(/valign=([^ |>]*)([^>]*)/gi, '');
545 str = str.replace(/<\\?\??xml[^>]>/gi, '');
546 str = str.replace(/<\/?\w+:[^>]*>/gi, '');
547 str = str.replace(/<st1:.*?>/gi, '');
548 str = str.replace(/o:/gi, '');
550 str = str.replace(/<!--([^>])*>( )*\s*<\/-->/gi, '');
551 str = str.replace(/<!--[^>]*>/gi, '');
552 str = str.replace(/<\/--[^>]*>/gi, '');
554 doc.body.innerHTML = str;
558 * Display an iframe instead of the textarea.
561 * @param {String} n The editor identifier (the textarea's ID)
562 * @param {Object} settings Object which holds the settings
564 _display: function(n, settings){
566 // Get the textarea element
569 // Validate if textarea exists
570 if (textarea == null) {
571 alert("No textarea found with the given identifier (ID: " + n + ").");
575 // Validate browser compatiblity
576 if (!WYSIWYG_Core.isBrowserCompatible()) {
577 if (this.config[n].NoValidBrowserMessage != "") {
578 alert(this.config[n].NoValidBrowserMessage);
583 // Load settings in config array, use the textarea id as identifier
584 if (typeof(settings) != "object") {
585 this.config[n] = new this.Settings();
588 this.config[n] = settings;
592 textarea.style.display = "none";
594 // Override the width and height of the editor with the
595 // size given by the style attributes width and height
596 if (textarea.style.width) {
597 this.config[n].Width = textarea.style.width;
599 if (textarea.style.height) {
600 this.config[n].Height = textarea.style.height
603 // determine the width + height
604 var currentWidth = this.config[n].Width;
605 var currentHeight = this.config[n].Height;
607 // Calculate the width + height of the editor
608 var ifrmWidth = "100%";
609 var ifrmHeight = "100%";
610 if (currentWidth.search(/%/) == -1) {
611 ifrmWidth = currentWidth;
612 ifrmHeight = currentHeight;
615 // Create iframe which will be used for rich text editing
616 var iframe = '<table cellpadding="0" cellspacing="0" border="0" style="width:' + currentWidth + '; height:' + currentHeight + ';" class="tableTextareaEditor"><tr><td valign="top">\n' +
617 '<iframe frameborder="0" id="wysiwyg' +
619 '" class="iframeText" style="width:' +
624 '</td></tr></table>\n';
626 // Insert after the textArea both toolbar one and two
627 textarea.insertAdjacentHTML("afterEnd", iframe);
629 // Pass the textarea's existing text over to the content variable
630 var content = textarea.value;
631 var doc = this.getEditorWindow(n).document;
633 // Replace all \n with <br>
634 if (this.config[n].ReplaceLineBreaks) {
635 content = content.replace(/(\r\n)|(\n)/ig, "<br>");
638 // Write the textarea's content into the iframe
643 // Set default style of the editor window
644 WYSIWYG_Core.setAttribute(doc.body, "style", this.config[n].DefaultStyle);
648 * Replace the given textarea with wysiwyg editor
651 * @param {String} n The editor identifier (the textarea's ID)
652 * @param {Object} settings Object which holds the settings
654 _generate: function(n, settings){
656 // Get the textarea element
658 // Validate if textarea exists
659 if (textarea == null) {
660 alert("No textarea found with the given identifier (ID: " + n + ").");
664 // Validate browser compatiblity
665 if (!WYSIWYG_Core.isBrowserCompatible()) {
666 if (this.config[n].NoValidBrowserMessage != "") {
667 alert(this.config[n].NoValidBrowserMessage);
673 textarea.style.display = 'none';
675 // Override the width and height of the editor with the
676 // size given by the style attributes width and height
677 if (textarea.style.width) {
678 this.config[n].Width = textarea.style.width;
680 if (textarea.style.height) {
681 this.config[n].Height = textarea.style.height
684 // determine the width + height
685 var currentWidth = this.config[n].Width;
686 var currentHeight = this.config[n].Height;
688 // Calculate the width + height of the editor
689 var toolbarWidth = currentWidth;
690 var ifrmWidth = "100%";
691 var ifrmHeight = "100%";
692 if (currentWidth.search(/%/) == -1) {
693 toolbarWidth = currentWidth.replace(/px/gi, "");
694 toolbarWidth = (parseFloat(toolbarWidth) + 2) + "px";
695 ifrmWidth = currentWidth;
696 ifrmHeight = currentHeight;
699 // Generate the WYSIWYG Table
700 // This table holds the toolbars and the iframe as the editor
702 editor += '<div id="wysiwyg_div_' + n + '" style="width:' + currentWidth + ';">';
703 editor += '<table border="0" cellpadding="0" cellspacing="0" class="tableTextareaEditor" id="wysiwyg_table_' + n + '" style="width:' + currentWidth + '; height:' + currentHeight + ';">';
704 editor += '<tr><td style="height:22px;vertical-align:top;">';
706 // Output all command buttons that belong to toolbar one
707 for (var j = 0; j < this.config[n].Toolbar.length; j++) {
708 if (this.config[n].Toolbar[j] && this.config[n].Toolbar[j].length > 0) {
709 var toolbar = this.config[n].Toolbar[j];
711 // Generate WYSIWYG toolbar one
712 editor += '<table border="0" cellpadding="0" cellspacing="0" class="toolbar1" style="width:100%;" id="toolbar' + j + '_' + n + '">';
713 editor += '<tr><td style="width:6px;"><img src="' + this.config[n].ImagesDir + 'seperator2.gif" alt="" hspace="3"></td>';
715 // Interate over the toolbar element
716 for (var i = 0; i < toolbar.length; i++) {
719 if (typeof(this.config[n].DropDowns[id]) != "undefined") {
720 var dropdown = this.config[n].DropDowns[id];
721 editor += '<td style="width: ' + dropdown.width + ';">';
722 // write the drop down content
723 editor += this.writeDropDown(n, id);
728 // Get the values of the Button from the global ToolbarList object
729 var buttonObj = this.ToolbarList[toolbar[i]];
731 var buttonID = buttonObj[0];
732 var buttonTitle = buttonObj[1];
733 var buttonImage = this.config[n].ImagesDir + buttonObj[2];
734 var buttonImageRollover = this.config[n].ImagesDir + buttonObj[3];
736 if (toolbar[i] == "seperator") {
737 editor += '<td style="width: 12px;" align="center">';
738 editor += '<img src="' + buttonImage + '" border=0 unselectable="on" width="2" height="18" hspace="2" unselectable="on">';
741 // View Source button
743 if (toolbar[i] == "viewSource") {
744 editor += '<td style="width: 22px;">';
745 editor += '<span id="HTMLMode' + n + '"><img src="' + buttonImage + '" border="0" unselectable="on" title="' + buttonTitle + '" id="' + buttonID + '" class="buttonEditor" onmouseover="this.className=\'buttonEditorOver\'; this.src=\'' + buttonImageRollover + '\';" onmouseout="this.className=\'buttonEditor\'; this.src=\'' + buttonImage + '\';" onclick="WYSIWYG.execCommand(\'' + n + '\', \'' + buttonID + '\');" unselectable="on" width="20" height="20"></span>';
746 editor += '<span id="textMode' + n + '"><img src="' + this.config[n].ImagesDir + 'view_text.gif" border="0" unselectable="on" title="viewText" id="ViewText" class="buttonEditor" onmouseover="this.className=\'buttonEditorOver\'; this.src=\'' + this.config[n].ImagesDir + 'view_text_on.gif\';" onmouseout="this.className=\'buttonEditor\'; this.src=\'' + this.config[n].ImagesDir + 'view_text.gif\';" onclick="WYSIWYG.execCommand(\'' + n + '\',\'ViewText\');" unselectable="on" width="20" height="20"></span>';
750 editor += '<td style="width: 22px;">';
751 editor += '<img src="' + buttonImage + '" border=0 unselectable="on" title="' + buttonTitle + '" id="' + buttonID + '" class="buttonEditor" onmouseover="this.className=\'buttonEditorOver\'; this.src=\'' + buttonImageRollover + '\';" onmouseout="this.className=\'buttonEditor\'; this.src=\'' + buttonImage + '\';" onclick="WYSIWYG.execCommand(\'' + n + '\', \'' + buttonID + '\');" unselectable="on" width="20" height="20">';
758 editor += '<td> </td></tr></table>';
762 editor += '</td></tr><tr><td valign="top">\n';
763 // Create iframe which will be used for rich text editing
764 editor += '<iframe frameborder="0" id="wysiwyg' + n + '" class="iframeText" style="width:100%;height:' + currentHeight + ';"></iframe>\n' +
766 // Status bar HTML code
767 if (this.config[n].StatusBarEnabled) {
768 editor += '<tr><td class="wysiwyg-statusbar" style="height:10px;" id="wysiwyg_statusbar_' + n + '"> </td></tr>';
770 editor += '</table>';
773 // Insert the editor after the textarea
774 textarea.insertAdjacentHTML("afterEnd", editor);
776 // Hide the "Text Mode" button
777 // Validate if textMode Elements are prensent
778 if ($("textMode" + n)) {
779 $("textMode" + n).style.display = 'none';
782 // Pass the textarea's existing text over to the content variable
783 var content = textarea.value;
784 var doc = this.getEditorWindow(n).document;
787 // Replace all \n with <br>
788 if (this.config[n].ReplaceLineBreaks) {
789 content = content.replace(/\n\r|\n/ig, "<br>");
792 // Write the textarea's content into the iframe
797 // Make the iframe editable in both Mozilla and IE
798 // Improve compatiblity for IE + Mozilla
799 if (doc.body.contentEditable) {
800 doc.body.contentEditable = true;
803 doc.designMode = "on";
806 // Set default font style
807 WYSIWYG_Core.setAttribute(doc.body, "style", this.config[n].DefaultStyle);
809 // Enable table highlighting
810 WYSIWYG_Table.refreshHighlighting(n);
813 // Update the textarea with content in WYSIWYG when user submits form
814 for (var idx = 0; idx < document.forms.length; idx++) {
815 WYSIWYG_Core.addEvent(document.forms[idx], "submit", function xxx_aa(){
816 WYSIWYG.updateTextArea(n);
820 // close font selection if mouse moves over the editor window
821 WYSIWYG_Core.addEvent(doc, "mouseover", function xxx_bb(){
822 WYSIWYG.closeDropDowns(n);
825 // If it's true invert the line break capability of IE
826 if (this.config[n].InvertIELineBreaks) {
827 WYSIWYG_Core.addEvent(doc, "keypress", function xxx_cc(){
828 WYSIWYG.invertIELineBreakCapability(n);
833 if (this.config[n].StatusBarEnabled) {
834 WYSIWYG_Core.addEvent(doc, "mouseup", function xxx_dd(){
835 WYSIWYG.updateStatusBar(n);
839 // custom context menu
840 if (this.config[n].ContextMenu) {
841 WYSIWYG_ContextMenu.init(n);
844 // init viewTextMode var
845 this.viewTextMode[n] = false;
849 * Disable the given WYSIWYG Editor Box
851 * @param {String} n The editor identifier (the textarea's ID)
853 disable: function(n){
855 // get the editor window
856 var editor = this.getEditorWindow(n);
858 // Validate if editor exists
859 if (editor == null) {
860 alert("No editor found with the given identifier (ID: " + n + ").");
865 // disable design mode or content editable feature
866 if (editor.document.body.contentEditable) {
867 editor.document.body.contentEditable = false;
870 editor.document.designMode = "Off";
873 // change the style of the body
874 WYSIWYG_Core.setAttribute(editor.document.body, "style", this.config[n].DisabledStyle);
876 // hide the status bar
877 this.hideStatusBar(n);
880 this.hideToolbars(n);
885 * Enables the given WYSIWYG Editor Box
887 * @param {String} n The editor identifier (the textarea's ID)
891 // get the editor window
892 var editor = this.getEditorWindow(n);
894 // Validate if editor exists
895 if (editor == null) {
896 alert("No editor found with the given identifier (ID: " + n + ").");
901 // disable design mode or content editable feature
902 if (editor.document.body.contentEditable) {
903 editor.document.body.contentEditable = true;
906 editor.document.designMode = "On";
909 // change the style of the body
910 WYSIWYG_Core.setAttribute(editor.document.body, "style", this.config[n].DefaultStyle);
912 // hide the status bar
913 this.showStatusBar(n);
916 this.showToolbars(n);
921 * Returns the node structure of the current selection as array
923 * @param {String} n The editor identifier (the textarea's ID)
925 getNodeTree: function(n){
927 var sel = this.getSelection(n);
928 var range = this.getRange(sel);
930 // get element of range
931 var tag = this.getTag(range);
935 // get parent of element
936 var node = this.getParent(tag);
937 // init the tree as array with the current selected element
938 var nodeTree = new Array(tag);
939 // get all parent nodes
942 while (node != null && node.nodeName != "#document") {
944 node = this.getParent(node);
952 * Removes the current node of the selection
954 * @param {String} n The editor identifier (the textarea's ID)
956 removeNode: function(n){
957 // get selection and range
958 var sel = this.getSelection(n);
959 var range = this.getRange(sel);
960 // the current tag of range
961 var tag = this.getTag(range);
962 var parent = tag.parentNode;
963 if (tag == null || parent == null) {
966 if (tag.nodeName == "HTML" || tag.nodeName == "BODY") {
970 // copy child elements of the node to the parent element before remove the node
971 var childNodes = new Array();
972 for (var i = 0; i < tag.childNodes.length; i++)
973 childNodes[i] = tag.childNodes[i];
974 for (var i = 0; i < childNodes.length; i++)
975 parent.insertBefore(childNodes[i], tag);
978 parent.removeChild(tag);
979 // validate if parent is a link and the node is only
980 // surrounded by the link, then remove the link too
981 if (parent.nodeName == "A" && !parent.hasChildNodes()) {
982 if (parent.parentNode) {
983 parent.parentNode.removeChild(parent);
986 // update the status bar
987 this.updateStatusBar(n);
991 * Get the selection of the given editor
993 * @param {String} n The editor identifier (the textarea's ID)
995 getSelection: function(n){
996 var ifrm = this.getEditorWindow(n);
997 var doc = ifrm.document;
999 if (ifrm.getSelection) {
1000 sel = ifrm.getSelection();
1003 if (doc.getSelection) {
1004 sel = doc.getSelection();
1007 if (doc.selection) {
1008 sel = doc.selection;
1014 * Updates the status bar with the current node tree
1016 * @param {String} n The editor identifier (the textarea's ID)
1018 updateStatusBar: function(n){
1020 // get the node structure
1021 var nodeTree = this.getNodeTree(n);
1022 if (nodeTree == null) {
1025 // format the output
1026 var outputTree = "";
1027 var max = nodeTree.length - 1;
1028 for (var i = max; i >= 0; i--) {
1029 if (nodeTree[i].nodeName != "HTML" && nodeTree[i].nodeName != "BODY") {
1030 outputTree += '<a class="wysiwyg-statusbar" href="javascript:WYSIWYG.selectNode(\'' + n + '\',' + i + ');">' + nodeTree[i].nodeName + '</a>';
1033 outputTree += nodeTree[i].nodeName;
1036 outputTree += " > ";
1040 // update the status bar
1041 var statusbar = $("wysiwyg_statusbar_" + n);
1043 statusbar.innerHTML = outputTree;
1048 * Execute a command on the editor document
1050 * @param {String} command The execCommand (e.g. Bold)
1051 * @param {String} n The editor identifier
1052 * @param {String} value The value when applicable
1054 execCommand: function(n, cmd, value){
1056 // When user clicks toolbar button make sure it always targets its respective WYSIWYG
1057 this.getEditorWindow(n).focus();
1059 // When in Text Mode these execCommands are enabled
1060 var textModeCommands = new Array("ViewText", "Print");
1062 // Check if in Text mode and a disabled command execute
1063 var cmdValid = false;
1064 for (var i = 0; i < textModeCommands.length; i++) {
1065 if (textModeCommands[i] == cmd) {
1069 if (this.viewTextMode[n] && !cmdValid) {
1070 alert("You are in TEXT Mode. This feature has been disabled.");
1074 // rbg to hex convertion implementation dependents on browser
1075 var toHexColor = WYSIWYG_Core.isMSIE ? WYSIWYG_Core._dec_to_rgb : WYSIWYG_Core.toHexColor;
1077 // popup screen positions
1078 var popupPosition = {
1079 left: parseInt(window.screen.availWidth / 3),
1080 top: parseInt(window.screen.availHeight / 3)
1083 // Check the insert image popup implementation
1084 var imagePopupFile = this.config[n].PopupsDir + 'insert_image.html';
1085 var imagePopupWidth = 400;
1086 var imagePopupHeight = 210;
1087 if (typeof this.config[n].ImagePopupFile != "undefined" && this.config[n].ImagePopupFile != "") {
1088 imagePopupFile = this.config[n].ImagePopupFile;
1090 if (typeof this.config[n].ImagePopupWidth && this.config[n].ImagePopupWidth > 0) {
1091 imagePopupWidth = this.config[n].ImagePopupWidth;
1093 if (typeof this.config[n].ImagePopupHeight && this.config[n].ImagePopupHeight > 0) {
1094 imagePopupHeight = this.config[n].ImagePopupHeight;
1097 // switch which action have to do
1103 WYSIWYG_Core.execCommand(n, cmd, "<" + value + ">");
1107 var rgb = this.getEditorWindow(n).document.queryCommandValue(cmd);
1108 var currentColor = rgb != '' ? toHexColor(this.getEditorWindow(n).document.queryCommandValue(cmd)) : "000000";
1109 window.open(this.config[n].PopupsDir + 'select_color.html?color=' + currentColor + '&command=' + cmd + '&wysiwyg=' + n, 'popup', 'location=0,status=0,scrollbars=0,width=210,height=165,top=' + popupPosition.top + ',left=' + popupPosition.left).focus();
1114 var currentColor = toHexColor(this.getEditorWindow(n).document.queryCommandValue(cmd));
1115 window.open(this.config[n].PopupsDir + 'select_color.html?color=' + currentColor + '&command=' + cmd + '&wysiwyg=' + n, 'popup', 'location=0,status=0,scrollbars=0,width=210,height=165,top=' + popupPosition.top + ',left=' + popupPosition.left).focus();
1120 window.open(imagePopupFile + '?wysiwyg=' + n, 'popup', 'location=0,status=0,scrollbars=0,resizable=0,width=' + imagePopupWidth + ',height=' + imagePopupHeight + ',top=' + popupPosition.top + ',left=' + popupPosition.left).focus();
1125 this.removeImage(n);
1140 window.open(this.config[n].PopupsDir + 'insert_hyperlink.html?wysiwyg=' + n, 'popup', 'location=0,status=0,scrollbars=0,resizable=0,width=350,height=160,top=' + popupPosition.top + ',left=' + popupPosition.left).focus();
1145 window.open(this.config[n].PopupsDir + 'create_table.html?wysiwyg=' + n, 'popup', 'location=0,status=0,scrollbars=0,resizable=0,width=500,height=260,top=' + popupPosition.top + ',left=' + popupPosition.left).focus();
1160 window.open(this.config[n].PopupsDir + 'about.html?wysiwyg=' + n, 'popup', 'location=0,status=0,scrollbars=0,resizable=0,width=400,height=350,top=' + popupPosition.top + ',left=' + popupPosition.left).focus();
1163 // Strip any HTML added by word
1164 case "RemoveFormat":
1165 this.removeFormat(n);
1168 // Preview thx to Korvo
1170 window.open(this.config[n].PopupsDir + 'preview.html?wysiwyg=' + n, 'popup', 'location=0,status=0,scrollbars=1,resizable=1,width=' + this.config[n].PreviewWidth + ',height=' + this.config[n].PreviewHeight + ',top=' + popupPosition.top + ',left=' + popupPosition.left).focus();
1180 WYSIWYG.updateTextArea(n);
1181 var form = WYSIWYG_Core.findParentNode("FORM", this.getEditor(n));
1183 alert("Can not submit the content, because no form element found.");
1191 location.replace(this.config[n].Opener);
1195 WYSIWYG_Core.execCommand(n, cmd, value);
1199 // hide node the font + font size selection
1200 this.closeDropDowns(n);
1204 * Maximize the editor instance
1206 * @param {String} n The editor identifier
1208 maximize: function(n){
1210 var divElm = this.getEditorDiv(n);
1211 var tableElm = this.getEditorTable(n);
1212 var editor = this.getEditor(n);
1213 var setting = this.config[n];
1214 var size = WYSIWYG_Core.windowSize();
1216 if (this.maximized[n]) {
1217 WYSIWYG_Core.setAttribute(divElm, "style", "position:static;z-index:9998;top:0px;left:0px;width:" + setting.Width + ";height:100%;");
1218 WYSIWYG_Core.setAttribute(tableElm, "style", "width:" + setting.Width + ";height:" + setting.Height + ";");
1219 WYSIWYG_Core.setAttribute(editor, "style", "width:100%;height:" + setting.Height + ";");
1220 this.maximized[n] = false;
1223 WYSIWYG_Core.setAttribute(divElm, "style", "position:absolute;z-index:9998;top:0px;left:0px;width:" + size.width + "px;height:" + size.height + "px;");
1224 WYSIWYG_Core.setAttribute(tableElm, "style", "width:100%;height:100%;");
1225 WYSIWYG_Core.setAttribute(editor, "style", "width:100%;height:100%;");
1226 this.maximized[n] = true;
1232 * Insert HTML into WYSIWYG in rich text
1234 * @param {String} html The HTML being inserted (e.g. <b>hello</b>)
1235 * @param {String} n The editor identifier
1237 insertHTML: function(html, n){
1238 if (WYSIWYG_Core.isMSIE) {
1239 this.getEditorWindow(n).document.selection.createRange().pasteHTML(html);
1242 var span = this.getEditorWindow(n).document.createElement("span");
1243 span.innerHTML = html;
1244 this.insertNodeAtSelection(span, n);
1248 /* ---------------------------------------------------------------------- *\
1249 Function : insertNodeAtSelection()
1250 Description : insert HTML into WYSIWYG in rich text (mozilla)
1251 Usage : WYSIWYG.insertNodeAtSelection(insertNode, n)
1252 Arguments : insertNode - The HTML being inserted (must be innerHTML inserted within a div element)
1253 n - The editor identifier that the HTML will be inserted into (the textarea's ID)
1254 \* ---------------------------------------------------------------------- */
1255 insertNodeAtSelection: function(insertNode, n){
1257 // get editor document
1258 var doc = this.getEditorWindow(n).document;
1259 // get current selection
1260 var sel = this.getSelection(n);
1262 // get the first range of the selection
1263 // (there's almost always only one range)
1264 var range = sel.getRangeAt(0);
1266 // deselect everything
1267 sel.removeAllRanges();
1269 // remove content of current selection from document
1270 range.deleteContents();
1272 // get location of current selection
1273 var container = range.startContainer;
1274 var pos = range.startOffset;
1276 // make a new range for the new selection
1277 range = doc.createRange();
1279 if (container.nodeType == 3 && insertNode.nodeType == 3) {
1280 // if we insert text in a textnode, do optimized insertion
1281 container.insertData(pos, insertNode.data);
1282 // put cursor after inserted text
1283 range.setEnd(container, pos + insertNode.length);
1284 range.setStart(container, pos + insertNode.length);
1290 if (container.nodeType == 3) {
1291 // when inserting into a textnode
1292 // we create 2 new textnodes
1293 // and put the insertNode in between
1294 var textNode = container;
1295 container = textNode.parentNode;
1296 var text = textNode.nodeValue;
1298 // text before the split
1299 var textBefore = text.substr(0, pos);
1300 // text after the split
1301 var textAfter = text.substr(pos);
1303 beforeNode = document.createTextNode(textBefore);
1304 afterNode = document.createTextNode(textAfter);
1306 // insert the 3 new nodes before the old one
1307 container.insertBefore(afterNode, textNode);
1308 container.insertBefore(insertNode, afterNode);
1309 container.insertBefore(beforeNode, insertNode);
1311 // remove the old node
1312 container.removeChild(textNode);
1315 // else simply insert the node
1316 afterNode = container.childNodes[pos];
1317 container.insertBefore(insertNode, afterNode);
1321 range.setEnd(afterNode, 0);
1322 range.setStart(afterNode, 0);
1329 sel.addRange(range);
1333 * Prints the content of the WYSIWYG editor area
1335 * @param {String} n The editor identifier (textarea ID)
1338 if (document.all && navigator.appVersion.substring(22, 23) == 4) {
1339 var doc = this.getEditorWindow(n).document;
1341 var OLECMDID_PRINT = 6;
1342 var OLECMDEXECOPT_DONTPROMPTUSER = 2;
1343 var OLECMDEXECOPT_PROMPTUSER = 1;
1344 var WebBrowser = '<object id="WebBrowser1" width="0" height="0" classid="CLSID:8856F961-340A-11D0-A96B-00C04FD705A2"></object>';
1345 doc.body.insertAdjacentHTML('beforeEnd', WebBrowser);
1346 WebBrowser.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER);
1347 WebBrowser.outerHTML = '';
1350 this.getEditorWindow(n).print();
1355 * Writes the content of an drop down
1357 * @param {String} n The editor identifier (textarea ID)
1358 * @param {String} id Drop down identifier
1359 * @return {String} Drop down HTML
1361 writeDropDown: function(n, id){
1363 var dropdown = this.config[n].DropDowns[id];
1364 var toolbarObj = this.ToolbarList[dropdown.id];
1365 var image = this.config[n].ImagesDir + toolbarObj[2];
1366 var imageOn = this.config[n].ImagesDir + toolbarObj[3];
1367 dropdown.elements.sort();
1370 output += '<table border="0" cellpadding="0" cellspacing="0"><tr>';
1371 output += '<td onMouseOver="$(\'img_' + dropdown.id + '_' + n + '\').src=\'' + imageOn + '\';" onMouseOut="$(\'img_' + dropdown.id + '_' + n + '\').src=\'' + image + '\';">';
1372 output += '<img src="' + image + '" id="img_' + dropdown.id + '_' + n + '" height="20" onClick="WYSIWYG.openDropDown(\'' + n + '\',\'' + dropdown.id + '\');" unselectable="on" border="0"><br>';
1373 output += '<span id="elm_' + dropdown.id + '_' + n + '" class="dropdown" style="width: 145px;display:none;">';
1374 for (var i = 0; i < dropdown.elements.length; i++) {
1375 if (dropdown.elements[i]) {
1376 var value = dropdown.elements[i];
1377 var label = dropdown.label.replace(/{value}/gi, value);
1379 output += '<button type="button" onClick="WYSIWYG.execCommand(\'' + n + '\',\'' + dropdown.command + '\',\'' + value + '\')\;" onMouseOver="this.className=\'mouseOver\'" onMouseOut="this.className=\'mouseOut\'" class="mouseOut" style="width: 120px;">';
1380 output += '<table cellpadding="0" cellspacing="0" border="0"><tr>';
1381 output += '<td align="left">' + label + '</td>';
1382 output += '</tr></table></button><br>';
1385 output += '</span></td></tr></table>';
1391 * Close all drop downs. You can define a exclude dropdown id
1393 * @param {String} n The editor identifier (textarea ID)
1394 * @param {String} exid Excluded drop down identifier
1396 closeDropDowns: function(n, exid){
1397 if (typeof(exid) == "undefined")
1399 var dropdowns = this.config[n].DropDowns;
1400 for (var id in dropdowns) {
1401 var dropdown = dropdowns[id];
1402 if (dropdown.id != exid) {
1403 var divId = "elm_" + dropdown.id + "_" + n;
1405 $(divId).style.display = 'none';
1411 * Open a defined drop down
1413 * @param {String} n The editor identifier (textarea ID)
1414 * @param {String} id Drop down identifier
1416 openDropDown: function(n, id){
1417 var divId = "elm_" + id + "_" + n;
1418 if ($(divId).style.display == "none") {
1419 $(divId).style.display = "block";
1422 $(divId).style.display = "none";
1424 $(divId).style.position = "absolute";
1425 this.closeDropDowns(n, id);
1429 * Shows the HTML source code generated by the WYSIWYG editor
1431 * @param {String} n The editor identifier (textarea ID)
1433 viewSource: function(n){
1436 var doc = this.getEditorWindow(n).document;
1438 // Enable table highlighting
1439 WYSIWYG_Table.disableHighlighting(n);
1441 // View Source for IE
1442 if (WYSIWYG_Core.isMSIE) {
1443 var iHTML = doc.body.innerHTML;
1444 // strip off the absolute urls
1445 iHTML = this.stripURLPath(n, iHTML);
1446 // replace all decimal color strings with hex decimal color strings
1447 iHTML = WYSIWYG_Core.replaceRGBWithHexColor(iHTML);
1448 doc.body.innerText = iHTML;
1450 // View Source for Mozilla/Netscape
1452 // replace all decimal color strings with hex decimal color strings
1453 var html = WYSIWYG_Core.replaceRGBWithHexColor(doc.body.innerHTML);
1454 html = document.createTextNode(html);
1455 doc.body.innerHTML = "";
1456 doc.body.appendChild(html);
1459 // Hide the HTML Mode button and show the Text Mode button
1460 // Validate if Elements are present
1461 if ($('HTMLMode' + n)) {
1462 $('HTMLMode' + n).style.display = 'none';
1464 if ($('textMode' + n)) {
1465 $('textMode' + n).style.display = 'block';
1468 // set the font values for displaying HTML source
1469 doc.body.style.fontSize = "12px";
1470 doc.body.style.fontFamily = "Courier New";
1472 this.viewTextMode[n] = true;
1476 * Shows the HTML source code generated by the WYSIWYG editor
1478 * @param {String} n The editor identifier (textarea ID)
1480 viewText: function(n){
1483 var doc = this.getEditorWindow(n).document;
1486 if (WYSIWYG_Core.isMSIE) {
1487 var iText = doc.body.innerText;
1488 // strip off the absolute urls
1489 iText = this.stripURLPath(n, iText);
1490 // replace all decimal color strings with hex decimal color strings
1491 iText = WYSIWYG_Core.replaceRGBWithHexColor(iText);
1492 doc.body.innerHTML = iText;
1495 // View Text for Mozilla/Netscape
1497 var html = doc.body.ownerDocument.createRange();
1498 html.selectNodeContents(doc.body);
1499 // replace all decimal color strings with hex decimal color strings
1500 html = WYSIWYG_Core.replaceRGBWithHexColor(html.toString());
1501 doc.body.innerHTML = html;
1504 // Enable table highlighting
1505 WYSIWYG_Table.refreshHighlighting(n);
1507 // Hide the Text Mode button and show the HTML Mode button
1508 // Validate if Elements are present
1509 if ($('textMode' + n)) {
1510 $('textMode' + n).style.display = 'none';
1512 if ($('HTMLMode' + n)) {
1513 $('HTMLMode' + n).style.display = 'block';
1516 // reset the font values (changed)
1517 WYSIWYG_Core.setAttribute(doc.body, "style", this.config[n].DefaultStyle);
1519 this.viewTextMode[n] = false;
1522 /* ---------------------------------------------------------------------- *\
1523 Function : stripURLPath()
1524 Description : Strips off the defined image and the anchor urls of the given content.
1525 It also can strip the document URL automatically if you define auto.
1526 Usage : WYSIWYG.stripURLPath(content)
1527 Arguments : content - Content on which the stripping applies
1528 \* ---------------------------------------------------------------------- */
1529 stripURLPath: function(n, content, exact){
1531 // parameter exact is optional
1532 if (typeof exact == "undefined") {
1536 var stripImgageUrl = null;
1537 var stripAnchorUrl = null;
1539 // add url to strip of anchors to array
1540 if (this.config[n].AnchorPathToStrip == "auto") {
1541 stripAnchorUrl = WYSIWYG_Core.getDocumentUrl(document);
1544 if (this.config[n].AnchorPathToStrip != "") {
1545 stripAnchorUrl = this.config[n].AnchorPathToStrip;
1548 // add strip url of images to array
1549 if (this.config[n].ImagePathToStrip == "auto") {
1550 stripImgageUrl = WYSIWYG_Core.getDocumentUrl(document);
1553 if (this.config[n].ImagePathToStrip != "") {
1554 stripImgageUrl = this.config[n].ImagePathToStrip;
1560 // strip url of image path
1561 if (stripImgageUrl) {
1562 // escape reserved characters to be a valid regex
1563 url = WYSIWYG_Core.stringToRegex(WYSIWYG_Core.getDocumentPathOfUrl(stripImgageUrl));
1565 // exact replacing of url. regex: src="<url>"
1567 regex = eval("/(src=\")(" + url + ")([^\"]*)/gi");
1568 content = content.replace(regex, "$1$3");
1570 // not exect replacing of url. regex: <url>
1572 regex = eval("/(" + url + ")(.+)/gi");
1573 content = content.replace(regex, "$2");
1576 // strip absolute urls without a heading slash ("images/print.gif")
1577 result = WYSIWYG_Core.getDocumentPathOfUrl(stripImgageUrl).match(/.+[\/]{2,3}[^\/]*/, "");
1579 url = WYSIWYG_Core.stringToRegex(result[0]);
1581 // exact replacing of url. regex: src="<url>"
1583 regex = eval("/(src=\")(" + url + ")([^\"]*)/gi");
1584 content = content.replace(regex, "$1$3");
1586 // not exect replacing of url. regex: <url>
1588 regex = eval("/(" + url + ")(.+)/gi");
1589 content = content.replace(regex, "$2");
1594 // strip url of image path
1595 if (stripAnchorUrl) {
1596 // escape reserved characters to be a valid regex
1597 url = WYSIWYG_Core.stringToRegex(WYSIWYG_Core.getDocumentPathOfUrl(stripAnchorUrl));
1599 // strip absolute urls with a heading slash ("/product/index.html")
1600 // exact replacing of url. regex: src="<url>"
1602 regex = eval("/(href=\")(" + url + ")([^\"]*)/gi");
1603 content = content.replace(regex, "$1$3");
1605 // not exect replacing of url. regex: <url>
1607 regex = eval("/(" + url + ")(.+)/gi");
1608 content = content.replace(regex, "$2");
1611 // strip absolute urls without a heading slash ("product/index.html")
1612 result = WYSIWYG_Core.getDocumentPathOfUrl(stripAnchorUrl).match(/.+[\/]{2,3}[^\/]*/, "");
1614 url = WYSIWYG_Core.stringToRegex(result[0]);
1615 // exact replacing of url. regex: src="<url>"
1617 regex = eval("/(href=\")(" + url + ")([^\"]*)/gi");
1618 content = content.replace(regex, "$1$3");
1620 // not exect replacing of url. regex: <url>
1622 regex = eval("/(" + url + ")(.+)/gi");
1623 content = content.replace(regex, "$2");
1628 // stip off anchor links with #name
1629 url = WYSIWYG_Core.stringToRegex(stripAnchorUrl);
1630 // exact replacing of url. regex: src="<url>"
1632 regex = eval("/(href=\")(" + url + ")(#[^\"]*)/gi");
1633 content = content.replace(regex, "$1$3");
1635 // not exect replacing of url. regex: <url>
1637 regex = eval("/(" + url + ")(.+)/gi");
1638 content = content.replace(regex, "$2");
1642 // stip off anchor links with #name (only for local system)
1643 url = WYSIWYG_Core.getDocumentUrl(document);
1644 var pos = url.lastIndexOf("/");
1646 url = url.substring(pos + 1, url.length);
1647 url = WYSIWYG_Core.stringToRegex(url);
1648 // exact replacing of url. regex: src="<url>"
1650 regex = eval("/(href=\")(" + url + ")(#[^\"]*)/gi");
1651 content = content.replace(regex, "$1$3");
1653 // not exect replacing of url. regex: <url>
1655 regex = eval("/(" + url + ")(.+)/gi");
1656 content = content.replace(regex, "$2");
1664 /* ---------------------------------------------------------------------- *\
1665 Function : updateTextArea()
1666 Description : Updates the text area value with the HTML source of the WYSIWYG
1667 Arguments : n - The editor identifier (the textarea's ID)
1668 \* ---------------------------------------------------------------------- */
1669 updateTextArea: function(n){
1670 // on update switch editor back to html mode
1671 if (this.viewTextMode[n]) {
1675 var content = this.getEditorWindow(n).document.body.innerHTML;
1676 // strip off defined URLs on IE
1677 content = this.stripURLPath(n, content);
1678 // replace all decimal color strings with hex color strings
1679 content = WYSIWYG_Core.replaceRGBWithHexColor(content);
1680 // remove line breaks before content will be updated
1681 if (this.config[n].ReplaceLineBreaks) {
1682 content = content.replace(/(\r\n)|(\n)/ig, "");
1684 // set content back in textarea
1685 $(n).value = content;
1688 /* ---------------------------------------------------------------------- *\
1689 Function : hideToolbars()
1690 Description : Hide all toolbars
1691 Usage : WYSIWYG.hideToolbars(n)
1692 Arguments : n - The editor identifier (the textarea's ID)
1693 \* ---------------------------------------------------------------------- */
1694 hideToolbars: function(n){
1695 for (var i = 0; i < this.config[n].Toolbar.length; i++) {
1696 var toolbar = $("toolbar" + i + "_" + n);
1698 toolbar.style.display = "none";
1703 /* ---------------------------------------------------------------------- *\
1704 Function : showToolbars()
1705 Description : Display all toolbars
1706 Usage : WYSIWYG.showToolbars(n)
1707 Arguments : n - The editor identifier (the textarea's ID)
1708 \* ---------------------------------------------------------------------- */
1709 showToolbars: function(n){
1710 for (var i = 0; i < this.config[n].Toolbar.length; i++) {
1711 var toolbar = $("toolbar" + i + "_" + n);
1713 toolbar.style.display = "";
1718 /* ---------------------------------------------------------------------- *\
1719 Function : hideStatusBar()
1720 Description : Hide the status bar
1721 Usage : WYSIWYG.hideStatusBar(n)
1722 Arguments : n - The editor identifier (the textarea's ID)
1723 \* ---------------------------------------------------------------------- */
1724 hideStatusBar: function(n){
1725 var statusbar = $('wysiwyg_statusbar_' + n);
1727 statusbar.style.display = "none";
1731 /* ---------------------------------------------------------------------- *\
1732 Function : showStatusBar()
1733 Description : Display the status bar
1734 Usage : WYSIWYG.showStatusBar(n)
1735 Arguments : n - The editor identifier (the textarea's ID)
1736 \* ---------------------------------------------------------------------- */
1737 showStatusBar: function(n){
1738 var statusbar = $('wysiwyg_statusbar_' + n);
1740 statusbar.style.display = "";
1745 * Finds the node with the given tag name in the given range
1747 * @param {String} tagName Parent tag to find
1748 * @param {Range} range Current range
1750 findParent: function(parentTagName, range){
1751 parentTagName = parentTagName.toUpperCase();
1753 var elmWorking = null;
1755 if (!WYSIWYG_Core.isMSIE) {
1756 var node = range.startContainer;
1757 var pos = range.startOffset;
1758 if (node.nodeType != 3) {
1759 node = node.childNodes[pos];
1761 return WYSIWYG_Core.findParentNode(parentTagName, node);
1764 elmWorking = (range.length > 0) ? range.item(0) : range.parentElement();
1765 elmWorking = WYSIWYG_Core.findParentNode(parentTagName, elmWorking);
1766 if (elmWorking != null)
1769 rangeWorking = range.duplicate();
1770 rangeWorking.collapse(true);
1771 rangeWorking.moveEnd("character", 1);
1772 if (rangeWorking.text.length > 0) {
1773 while (rangeWorking.compareEndPoints("EndToEnd", range) < 0) {
1774 rangeWorking.move("Character");
1775 if (null != this.findParentTag(parentTagName, rangeWorking)) {
1776 return this.findParentTag(parentTagName, rangeWorking);
1789 * Get the acutally tag of the given range
1791 * @param {Range} range Current range
1793 getTag: function(range){
1795 if (!WYSIWYG_Core.isMSIE) {
1796 var node = range.startContainer;
1797 var pos = range.startOffset;
1798 if (node.nodeType != 3) {
1799 node = node.childNodes[pos];
1802 if (node.nodeName && node.nodeName.search(/#/) != -1) {
1803 return node.parentNode;
1808 if (range.length > 0) {
1809 return range.item(0);
1812 if (range.parentElement()) {
1813 return range.parentElement();
1824 * Get the parent node of the given node
1826 * @param {DOMElement} element - Element which parent will be returned
1828 getParent: function(element){
1829 if (element.parentNode) {
1830 return element.parentNode;
1835 /* ---------------------------------------------------------------------- *\
1836 Function : getTextRange()
1837 Description : Get the text range object of the given element
1838 Usage : WYSIWYG.getTextRange(element)
1839 Arguments : element - An element of which you get the text range object
1840 \* ---------------------------------------------------------------------- */
1841 getTextRange: function(element){
1842 var range = element.parentTextEdit.createTextRange();
1843 range.moveToElementText(element);
1847 /* ---------------------------------------------------------------------- *\
1848 Function : invertIELineBreakCapability()
1849 Description : Inverts the line break capability of IE (Thx to richyrich)
1850 Normal: ENTER = <p> , SHIFT + ENTER = <br>
1851 Inverted: ENTER = <br>, SHIFT + ENTER = <p>
1852 Usage : WYSIWYG.invertIELineBreakCapability(n)
1853 Arguments : n - The editor identifier (the textarea's ID)
1854 \* ---------------------------------------------------------------------- */
1855 invertIELineBreakCapability: function(n){
1857 var editor = this.getEditorWindow(n);
1859 // validate if the press key is the carriage return key
1860 if (editor.event.keyCode == 13) {
1861 if (!editor.event.shiftKey) {
1862 sel = this.getRange(this.getSelection(n));
1863 sel.pasteHTML("<br>");
1864 editor.event.cancelBubble = true;
1865 editor.event.returnValue = false;
1867 sel.moveEnd("character", 1);
1868 sel.moveStart("character", 1);
1869 sel.collapse(false);
1873 sel = this.getRange(this.getSelection(n));
1874 sel.pasteHTML("<p>");
1875 editor.event.cancelBubble = true;
1876 editor.event.returnValue = false;
1878 sel.moveEnd("character", 1);
1879 sel.moveStart("character", 1);
1880 sel.collapse(false);
1886 /* ---------------------------------------------------------------------- *\
1887 Function : selectNode()
1888 Description : Select a node within the current editor
1889 Usage : WYSIWYG.selectNode(n, level)
1890 Arguments : n - The editor identifier (the textarea's ID)
1891 level - identifies the level of the element which will be selected
1892 \* ---------------------------------------------------------------------- */
1893 selectNode: function(n, level){
1895 var sel = this.getSelection(n);
1896 var range = this.getRange(sel);
1897 var parentnode = this.getTag(range);
1900 for (var node = parentnode; (node && (node.nodeType == 1)); node = node.parentNode) {
1902 this.nodeSelection(n, node);
1907 this.updateStatusBar(n);
1910 /* ---------------------------------------------------------------------- *\
1911 Function : nodeSelection()
1912 Description : Do the node selection
1913 Usage : WYSIWYG.nodeSelection(n, node)
1914 Arguments : n - The editor identifier (the textarea's ID)
1915 node - The node which will be selected
1916 \* ---------------------------------------------------------------------- */
1917 nodeSelection: function(n, node){
1919 var doc = this.getEditorWindow(n).document;
1920 var sel = this.getSelection(n);
1921 var range = this.getRange(sel);
1923 if (!WYSIWYG_Core.isMSIE) {
1924 if (node.nodeName == "BODY") {
1925 range.selectNodeContents(node);
1928 range.selectNode(node);
1934 range.setStart(node, startOffset);
1935 range.setEnd(endNode, endOffset);
1941 sel.removeAllRanges();
1944 sel.addRange(range);
1948 // MSIE may not select everything when BODY is selected -
1949 // start may be set to first text node instead of first non-text node -
1950 // no known workaround
1951 if ((node.nodeName == "TABLE") || (node.nodeName == "IMG") || (node.nodeName == "INPUT") || (node.nodeName == "SELECT") || (node.nodeName == "TEXTAREA")) {
1953 range = doc.body.createControlRange();
1954 range.addElement(node);
1961 range = doc.body.createTextRange();
1964 if (range.moveToElementText) {
1966 range.moveToElementText(node);
1971 range = doc.body.createTextRange();
1972 range.moveToElementText(node);
1981 range = doc.body.createTextRange();
1982 range.moveToElementText(node);
1994 /********************************************************************
1995 * openWYSIWYG core functions Copyright (c) 2006 openWebWare.com
1996 * Contact us at devs@openwebware.com
1997 * This copyright notice MUST stay intact for use.
1999 * $Id: wysiwyg.js,v 1.22 2007/09/08 21:45:57 xhaggi Exp $
2000 ********************************************************************/
2001 var WYSIWYG_Core = {
2004 * Holds true if browser is MSIE, otherwise false
2006 isMSIE: navigator.appName == "Microsoft Internet Explorer" ? true : false,
2009 * Holds true if browser is Firefox (Mozilla)
2011 isFF: !document.all && document.getElementById && !this.isOpera,
2014 * Holds true if browser is Opera, otherwise false
2016 isOpera: navigator.appName == "Opera" ? true : false,
2019 * Trims whitespaces of the given string
2022 * @return Trimmed string
2024 trim: function(str){
2025 return str.replace(/^\s*|\s*$/g, "");
2029 * Determine if the given parameter is defined
2031 * @param p Parameter
2032 * @return true/false dependents on definition of the parameter
2034 defined: function(p){
2035 return typeof p == "undefined" ? false : true;
2039 * Determine if the browser version is compatible
2041 * @return true/false depending on compatiblity of the browser
2043 isBrowserCompatible: function(){
2044 //Validate browser and compatibility
2045 if (!document.getElementById || !document.designMode || (!document.selection && typeof document.createRange == 'undefined')) {
2052 * Set the style attribute of the given element.
2053 * Private method to solve the IE bug while setting the style attribute.
2055 * @param {DOMElement} node The element on which the style attribute will affect
2056 * @param {String} style Stylesheet which will be set
2058 _setStyleAttribute: function(node, style){
2061 var styles = style.split(";");
2063 for (var i = 0; i < styles.length; i++) {
2064 var attributes = styles[i].split(":");
2065 if (attributes.length == 2) {
2067 var attr = WYSIWYG_Core.trim(attributes[0]);
2068 while ((pos = attr.search(/-/)) != -1) {
2069 var strBefore = attr.substring(0, pos);
2070 var strToUpperCase = attr.substring(pos + 1, pos + 2);
2071 var strAfter = attr.substring(pos + 2, attr.length);
2072 attr = strBefore + strToUpperCase.toUpperCase() + strAfter;
2074 var value = WYSIWYG_Core.trim(attributes[1]).toLowerCase();
2075 node.style[attr] = value;
2085 * Fix's the issue while getting the attribute style on IE
2086 * It's return an object but we need the style string
2089 * @param {DOMElement} node Node element
2090 * @return {String} Stylesheet
2092 _getStyleAttribute: function(node){
2094 return node.style['cssText'].toLowerCase();
2097 return node.getAttribute("style");
2102 * Set an attribute's value on the given node element.
2104 * @param {DOMElement} node Node element
2105 * @param {String} attr Attribute which is set
2106 * @param {String} value Value of the attribute
2108 setAttribute: function(node, attr, value){
2109 if (value == null || node == null || attr == null)
2111 if (attr.toLowerCase() == "style") {
2112 this._setStyleAttribute(node, value);
2115 node.setAttribute(attr, value);
2120 * Removes an attribute on the given node
2122 * @param {DOMElement} node Node element
2123 * @param {String} attr Attribute which will be removed
2125 removeAttribute: function(node, attr){
2126 node.removeAttribute(attr, false);
2130 * Get the vale of the attribute on the given node
2132 * @param {DOMElement} node Node element
2133 * @param {String} attr Attribute which value will be returned
2135 getAttribute: function(node, attr){
2136 if (node == null || attr == null)
2138 if (attr.toLowerCase() == "style") {
2139 return this._getStyleAttribute(node);
2142 return node.getAttribute(attr);
2147 * Get the path out of an given url
2149 * @param {String} url The url with is used to get the path
2151 getDocumentPathOfUrl: function(url){
2154 // if local file system, convert local url into web url
2155 url = url.replace(/file:\/\//gi, "file:///");
2156 url = url.replace(/\\/gi, "\/");
2157 var pos = url.lastIndexOf("/");
2159 path = url.substring(0, pos + 1);
2165 * Get the documents url, convert local urls to web urls
2167 * @param {DOMElement} doc Document which is used to get the url
2169 getDocumentUrl: function(doc){
2170 // if local file system, convert local url into web url
2172 url = url.replace(/file:\/\//gi, "file:///");
2173 url = url.replace(/\\/gi, "\/");
2178 * Find a parent node with the given name, of the given start node
2180 * @param {String} tagName - Tag name of the node to find
2181 * @param {DOMElement} node - Node element
2183 findParentNode: function(tagName, node){
2184 while (node.tagName != "HTML") {
2185 if (node.tagName == tagName) {
2188 node = node.parentNode;
2194 * Cancel the given event.
2196 * @param e Event which will be canceled
2198 cancelEvent: function(e){
2202 e.returnValue = false;
2203 e.cancelBubble = true;
2207 e.stopPropagation && e.stopPropagation();
2213 * Converts a RGB color string to hex color string.
2215 * @param color RGB color string
2216 * @param Hex color string
2218 toHexColor: function(color){
2219 color = color.replace(/^rgb/g, '');
2220 color = color.replace(/\(/g, '');
2221 color = color.replace(/\)/g, '');
2222 color = color.replace(/ /g, '');
2223 color = color.split(',');
2224 var r = parseFloat(color[0]).toString(16).toUpperCase();
2225 var g = parseFloat(color[1]).toString(16).toUpperCase();
2226 var b = parseFloat(color[2]).toString(16).toUpperCase();
2240 * Converts a decimal color to hex color string.
2242 * @param Decimal color
2243 * @param Hex color string
2245 _dec_to_rgb: function(value){
2246 var hex_string = "";
2247 for (var hexpair = 0; hexpair < 3; hexpair++) {
2248 var myByte = value & 0xFF; // get low byte
2249 value >>= 8; // drop low byte
2250 var nybble2 = myByte & 0x0F; // get low nybble (4 bits)
2251 var nybble1 = (myByte >> 4) & 0x0F; // get high nybble
2252 hex_string += nybble1.toString(16); // convert nybble to hex
2253 hex_string += nybble2.toString(16); // convert nybble to hex
2255 return hex_string.toUpperCase();
2259 * Replace RGB color strings with hex color strings within a string.
2261 * @param {String} str RGB String
2262 * @param {String} Hex color string
2264 replaceRGBWithHexColor: function(str){
2267 // find all decimal color strings
2268 var matcher = str.match(/rgb\([0-9 ]+,[0-9 ]+,[0-9 ]+\)/gi);
2270 for (var j = 0; j < matcher.length; j++) {
2271 var regex = eval("/" + WYSIWYG_Core.stringToRegex(matcher[j]) + "/gi");
2272 // replace the decimal color strings with hex color strings
2273 str = str.replace(regex, "#" + this.toHexColor(matcher[j]));
2280 * Execute the given command on the given editor
2282 * @param n The editor's identifier
2283 * @param cmd Command which is execute
2285 execCommand: function(n, cmd, value){
2286 if (typeof(value) == "undefined")
2289 // firefox BackColor problem fixed
2290 if (cmd == 'BackColor' && WYSIWYG_Core.isFF)
2291 cmd = 'HiliteColor';
2293 // firefox cut, paste and copy
2294 if (WYSIWYG_Core.isFF && (cmd == "Cut" || cmd == "Paste" || cmd == "Copy")) {
2296 WYSIWYG.getEditorWindow(n).document.execCommand(cmd, false, value);
2299 if (confirm("Copy/Cut/Paste is not available in Mozilla and Firefox\nDo you want more information about this issue?")) {
2300 window.open('http://www.mozilla.org/editor/midasdemo/securityprefs.html');
2306 WYSIWYG.getEditorWindow(n).document.execCommand(cmd, false, value);
2311 * Parse a given string to a valid regular expression
2313 * @param {String} string String to be parsed
2314 * @return {RegEx} Valid regular expression
2316 stringToRegex: function(string){
2318 string = string.replace(/\//gi, "\\/");
2319 string = string.replace(/\(/gi, "\\(");
2320 string = string.replace(/\)/gi, "\\)");
2321 string = string.replace(/\[/gi, "\\[");
2322 string = string.replace(/\]/gi, "\\]");
2323 string = string.replace(/\+/gi, "\\+");
2324 string = string.replace(/\$/gi, "\\$");
2325 string = string.replace(/\*/gi, "\\*");
2326 string = string.replace(/\?/gi, "\\?");
2327 string = string.replace(/\^/gi, "\\^");
2328 string = string.replace(/\\b/gi, "\\\\b");
2329 string = string.replace(/\\B/gi, "\\\\B");
2330 string = string.replace(/\\d/gi, "\\\\d");
2331 string = string.replace(/\\B/gi, "\\\\B");
2332 string = string.replace(/\\D/gi, "\\\\D");
2333 string = string.replace(/\\f/gi, "\\\\f");
2334 string = string.replace(/\\n/gi, "\\\\n");
2335 string = string.replace(/\\r/gi, "\\\\r");
2336 string = string.replace(/\\t/gi, "\\\\t");
2337 string = string.replace(/\\v/gi, "\\\\v");
2338 string = string.replace(/\\s/gi, "\\\\s");
2339 string = string.replace(/\\S/gi, "\\\\S");
2340 string = string.replace(/\\w/gi, "\\\\w");
2341 string = string.replace(/\\W/gi, "\\\\W");
2347 * Add an event listener
2349 * @param obj Object on which the event will be attached
2350 * @param ev Kind of event
2351 * @param fu Function which is execute on the event
2353 addEvent: function(obj, ev, fu){
2354 if (obj.attachEvent)
2355 obj.attachEvent("on" + ev, fu);
2357 obj.addEventListener(ev, fu, false);
2361 * Remove an event listener
2363 * @param obj Object on which the event will be attached
2364 * @param ev Kind of event
2365 * @param fu Function which is execute on the event
2367 removeEvent: function(obj, ev, fu){
2368 if (obj.attachEvent)
2369 obj.detachEvent("on" + ev, fu);
2371 obj.removeEventListener(ev, fu, false);
2375 * Includes a javascript file
2377 * @param file Javascript file path and name
2379 includeJS: function(file){
2380 var script = document.createElement("script");
2381 this.setAttribute(script, "type", "text/javascript");
2382 this.setAttribute(script, "src", file);
2383 var heads = document.getElementsByTagName("head");
2384 for (var i = 0; i < heads.length; i++) {
2385 heads[i].appendChild(script);
2390 * Includes a stylesheet file
2392 * @param file Stylesheet file path and name
2394 includeCSS: function(path){
2395 var link = document.createElement("link");
2396 this.setAttribute(link, "rel", "stylesheet");
2397 this.setAttribute(link, "type", "text/css");
2398 this.setAttribute(link, "href", path);
2399 var heads = document.getElementsByTagName("head");
2400 for (var i = 0; i < heads.length; i++) {
2401 heads[i].appendChild(link);
2406 * Get the screen position of the given element.
2408 * @param {HTMLObject} elm1 Element which position will be calculate
2409 * @param {HTMLObject} elm2 Element which is the last one before calculation stops
2410 * @param {Object} Left and top position of the given element
2412 getElementPosition: function(elm1, elm2){
2413 var top = 0, left = 0;
2414 while (elm1 && elm1 != elm2) {
2415 left += elm1.offsetLeft;
2416 top += elm1.offsetTop;
2417 elm1 = elm1.offsetParent;
2426 * Get the window size
2429 windowSize: function(){
2430 if (window.innerWidth) {
2432 width: window.innerWidth,
2433 height: window.innerHeight
2437 if (document.body && document.body.offsetWidth) {
2439 width: document.body.offsetWidth,
2440 height: document.body.offsetHeight
2453 * Context menu object
2455 var WYSIWYG_ContextMenu = {
2458 contextMenuDiv: null,
2463 * @param {String} n Editor identifier
2466 var doc = WYSIWYG.getEditorWindow(n).document;
2468 // create context menu div
2469 this.contextMenuDiv = document.createElement("div");
2470 this.contextMenuDiv.className = "wysiwyg-context-menu-div";
2471 this.contextMenuDiv.setAttribute("class", "wysiwyg-context-menu-div");
2472 this.contextMenuDiv.style.display = "none";
2473 this.contextMenuDiv.style.position = "absolute";
2474 this.contextMenuDiv.style.zIndex = 9999;
2475 this.contextMenuDiv.style.left = "0";
2476 this.contextMenuDiv.style.top = "0";
2477 this.contextMenuDiv.unselectable = "on";
2478 document.body.insertBefore(this.contextMenuDiv, document.body.firstChild);
2480 // bind event listeners
2481 WYSIWYG_Core.addEvent(doc, "contextmenu", function context(e){
2482 WYSIWYG_ContextMenu.show(e, n);
2484 WYSIWYG_Core.addEvent(doc, "click", function context(e){
2485 WYSIWYG_ContextMenu.close();
2487 WYSIWYG_Core.addEvent(doc, "keydown", function context(e){
2488 WYSIWYG_ContextMenu.close();
2490 WYSIWYG_Core.addEvent(document, "click", function context(e){
2491 WYSIWYG_ContextMenu.close();
2496 * Show the context menu
2499 * @param n Editor identifier
2501 show: function(e, n){
2502 if (this.contextMenuDiv == null)
2505 var ifrm = WYSIWYG.getEditor(n);
2506 var doc = WYSIWYG.getEditorWindow(n).document;
2508 // set the context menu position
2509 var pos = WYSIWYG_Core.getElementPosition(ifrm);
2510 var x = WYSIWYG_Core.isMSIE ? pos.left + e.clientX : pos.left + (e.pageX - doc.body.scrollLeft);
2511 var y = WYSIWYG_Core.isMSIE ? pos.top + e.clientY : pos.top + (e.pageY - doc.body.scrollTop);
2513 this.contextMenuDiv.style.left = x + "px";
2514 this.contextMenuDiv.style.top = y + "px";
2515 this.contextMenuDiv.style.visibility = "visible";
2516 this.contextMenuDiv.style.display = "block";
2518 // call the context menu, mozilla needs some time
2519 window.setTimeout("WYSIWYG_ContextMenu.output('" + n + "')", 10);
2521 WYSIWYG_Core.cancelEvent(e);
2526 * Output the context menu items
2528 * @param n Editor identifier
2530 output: function(n){
2533 var sel = WYSIWYG.getSelection(n);
2534 var range = WYSIWYG.getRange(sel);
2536 // get current selected node
2537 var tag = WYSIWYG.getTag(range);
2542 // clear context menu
2545 // Determine kind of nodes
2546 var isImg = (tag.nodeName == "IMG") ? true : false;
2547 var isLink = (tag.nodeName == "A") ? true : false;
2549 // Selection is an image or selection is a text with length greater 0
2551 if (WYSIWYG_Core.isMSIE)
2552 len = (document.selection && range.text) ? range.text.length : 0;
2554 len = range.toString().length;
2555 var sel = len != 0 || isImg;
2559 enabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["createlink"][3],
2560 disabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["createlink"][2]
2563 enabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["insertimage"][3],
2564 disabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["insertimage"][2]
2567 enabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["delete"][3],
2568 disabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["delete"][2]
2571 enabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["copy"][3],
2572 disabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["copy"][2]
2575 enabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["cut"][3],
2576 disabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["cut"][2]
2579 enabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["paste"][3],
2580 disabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["paste"][2]
2583 // Create context menu html
2584 this.html += '<table class="wysiwyg-context-menu" border="0" cellpadding="0" cellspacing="0">';
2587 this.addItem(n, 'Copy', iconCopy, 'Copy', sel);
2588 this.addItem(n, 'Cut', iconCut, 'Cut', sel);
2589 this.addItem(n, 'Paste', iconPaste, 'Paste', true);
2590 this.addSeperator();
2591 this.addItem(n, 'InsertImage', iconImage, 'Modify Image Properties...', isImg);
2592 this.addItem(n, 'CreateLink', iconLink, 'Create or Modify Link...', sel || isLink);
2593 this.addItem(n, 'RemoveNode', iconDelete, 'Remove', true);
2595 this.html += '</table>';
2596 this.contextMenuDiv.innerHTML = this.html;
2600 * Close the context menu
2603 this.contextMenuDiv.style.visibility = "hidden";
2604 this.contextMenuDiv.style.display = "none";
2608 * Clear context menu
2611 this.contextMenuDiv.innerHTML = "";
2616 * Add context menu item
2618 * @param n editor identifier
2619 * @param cmd Command
2620 * @param icon Icon which is diabled
2621 * @param title Title of the item
2622 * @param disabled If item is diabled
2624 addItem: function(n, cmd, icon, title, disabled){
2629 item += '<td class="icon"><a href="javascript:WYSIWYG.execCommand(\'' + n + '\',\'' + cmd + '\', null);"><img src="' + icon.enabled + '" border="0"></a></td>';
2630 item += '<td onmouseover="this.className=\'mouseover\'" onmouseout="this.className=\'\'" onclick="WYSIWYG.execCommand(\'' + n + '\', \'' + cmd + '\', null);WYSIWYG_ContextMenu.close();"><a href="javascript:void(0);">' + title + '</a></td>';
2635 item += '<td class="icon"><img src="' + icon.disabled + '" border="0"></td>';
2636 item += '<td onmouseover="this.className=\'mouseover\'" onmouseout="this.className=\'\'"><span class="disabled">' + title + '</span></td>';
2644 * Add seperator to context menu
2646 addSeperator: function(){
2649 output += '<td colspan="2" style="text-align:center;"><hr size="1" color="#C9C9C9" width="95%"></td>';
2651 this.html += output;
2658 var WYSIWYG_Table = {
2663 create: function(n, tbl){
2666 var doc = WYSIWYG.getEditorWindow(n).document;
2667 // get selection and range
2668 var sel = WYSIWYG.getSelection(n);
2669 var range = WYSIWYG.getRange(sel);
2672 // get element from selection
2673 if (WYSIWYG_Core.isMSIE) {
2674 if (sel.type == "Control" && range.length == 1) {
2675 range = WYSIWYG.getTextRange(range(0));
2680 // find a parent TABLE element
2681 //table = WYSIWYG.findParent("table", range);
2683 // check if parent is found
2684 //var update = (table == null) ? false : true;
2685 //if(!update) table = tbl;
2688 // add rows and cols
2689 var rows = WYSIWYG_Core.getAttribute(tbl, "tmprows");
2690 var cols = WYSIWYG_Core.getAttribute(tbl, "tmpcols");
2691 WYSIWYG_Core.removeAttribute(tbl, "tmprows");
2692 WYSIWYG_Core.removeAttribute(tbl, "tmpcols");
2693 for (var i = 0; i < rows; i++) {
2694 var tr = doc.createElement("tr");
2695 for (var j = 0; j < cols; j++) {
2696 var td = createTD();
2699 table.appendChild(tr);
2702 // on update exit here
2703 //if(update) { return; }
2705 // Check if IE or Mozilla (other)
2706 if (WYSIWYG_Core.isMSIE) {
2707 range.pasteHTML(table.outerHTML);
2710 WYSIWYG.insertNodeAtSelection(table, n);
2713 // refresh table highlighting
2714 this.refreshHighlighting(n);
2720 function createTD(){
2721 var td = doc.createElement("td");
2722 td.innerHTML = " ";
2729 * Enables the table highlighting
2731 * @param {String} n The editor identifier (the textarea's ID)
2733 refreshHighlighting: function(n){
2734 var doc = WYSIWYG.getEditorWindow(n).document;
2735 var tables = doc.getElementsByTagName("table");
2736 for (var i = 0; i < tables.length; i++) {
2737 this._enableHighlighting(tables[i]);
2739 var tds = doc.getElementsByTagName("td");
2740 for (var i = 0; i < tds.length; i++) {
2741 this._enableHighlighting(tds[i]);
2746 * Enables the table highlighting
2748 * @param {String} n The editor identifier (the textarea's ID)
2750 disableHighlighting: function(n){
2751 var doc = WYSIWYG.getEditorWindow(n).document;
2752 var tables = doc.getElementsByTagName("table");
2753 for (var i = 0; i < tables.length; i++) {
2754 this._disableHighlighting(tables[i]);
2756 var tds = doc.getElementsByTagName("td");
2757 for (var i = 0; i < tds.length; i++) {
2758 this._disableHighlighting(tds[i]);
2766 _enableHighlighting: function(node){
2767 var style = WYSIWYG_Core.getAttribute(node, "style");
2770 //alert("ENABLE: ELM = " + node.tagName + "; STYLE = " + style);
2771 WYSIWYG_Core.removeAttribute(node, "prevstyle");
2772 WYSIWYG_Core.setAttribute(node, "prevstyle", style);
2773 WYSIWYG_Core.setAttribute(node, "style", "border:1px dashed #AAAAAA;");
2779 _disableHighlighting: function(node){
2780 var style = WYSIWYG_Core.getAttribute(node, "prevstyle");
2781 //alert("DISABLE: ELM = " + node.tagName + "; STYLE = " + style);
2782 // if no prevstyle is defined, the table is not in highlighting mode
2783 if (style == null || style == "") {
2784 this._enableHighlighting(node);
2787 WYSIWYG_Core.removeAttribute(node, "prevstyle");
2788 WYSIWYG_Core.removeAttribute(node, "style");
2789 WYSIWYG_Core.setAttribute(node, "style", style);
2795 * Get an element by it's identifier
2797 * @param id Element identifier
2800 return document.getElementById(id);
2804 * Emulates insertAdjacentHTML(), insertAdjacentText() and
2805 * insertAdjacentElement() three functions so they work with Netscape 6/Mozilla
2806 * by Thor Larholm me@jscript.dk
2808 if (typeof HTMLElement != "undefined" && !HTMLElement.prototype.insertAdjacentElement) {
2809 HTMLElement.prototype.insertAdjacentElement = function(where, parsedNode){
2812 this.parentNode.insertBefore(parsedNode, this);
2815 this.insertBefore(parsedNode, this.firstChild);
2818 this.appendChild(parsedNode);
2821 if (this.nextSibling) {
2822 this.parentNode.insertBefore(parsedNode, this.nextSibling);
2825 this.parentNode.appendChild(parsedNode);
2831 HTMLElement.prototype.insertAdjacentHTML = function(where, htmlStr){
2832 var r = this.ownerDocument.createRange();
2833 r.setStartBefore(this);
2834 var parsedHTML = r.createContextualFragment(htmlStr);
2835 this.insertAdjacentElement(where, parsedHTML);
2839 HTMLElement.prototype.insertAdjacentText = function(where, txtStr){
2840 var parsedText = document.createTextNode(txtStr);
2841 this.insertAdjacentElement(where, parsedText);
2846 * Emulates insertAdjacentHTML(), insertAdjacentText() and
2847 * insertAdjacentElement() three functions so they work with Netscape 6/Mozilla/Safari
2848 * by Thor Larholm me@jscript.dk
2850 * Modified:06112008 By CJBoCo www.cjboco.com
2851 * -Instead of testing for browser type,we test if the object is undefined
2853 if (typeof HTMLElement != 'undefined') {
2854 if (typeof HTMLElement.insertAdjacentHTML == 'undefined') {
2855 HTMLElement.prototype.insertAdjacentElement = function(where, parsedNode){
2858 this.parentNode.insertBefore(parsedNode, this);
2861 this.insertBefore(parsedNode, this.firstChild);
2864 this.appendChild(parsedNode);
2867 if (this.nextSibling) {
2868 this.parentNode.insertBefore(parsedNode, this.nextSibling);
2871 this.parentNode.appendChild(parsedNode);
2877 if (typeof HTMLElement.insertAdjacentHTML == 'undefined') {
2878 HTMLElement.prototype.insertAdjacentHTML = function(where, htmlStr){
2879 var r = this.ownerDocument.createRange();
2880 r.setStartBefore(this);
2881 var parsedHTML = r.createContextualFragment(htmlStr);
2882 this.insertAdjacentElement(where, parsedHTML);
2885 if (typeof HTMLElement.insertAdjacentText == 'undefined') {
2886 HTMLElement.prototype.insertAdjacentText = function(where, txtStr){
2887 var parsedText = document.createTextNode(txtStr);
2888 this.insertAdjacentElement(where, parsedText);