OSDN Git Service

modified stylesheet.
[feedblog/feedgenerator.git] / openwysiwyg / scripts / wysiwyg.js
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.
5  *
6  * $Id: wysiwyg.js,v 1.22 2007/09/08 21:45:57 xhaggi Exp $
7  * $Revision: 1.22 $
8  *
9  * An open source WYSIWYG editor for use in web based applications.
10  * For full source code and docs, visit http://www.openwebware.com
11  *
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.
16  *
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.
21  *
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  ********************************************************************/
26 var WYSIWYG = {
27
28     /**
29      * Settings class, holds all customizeable properties
30      */
31     Settings: function(){
32     
33         // Images Directory
34         this.ImagesDir = "./openwysiwyg/images/";
35         
36         // Popups Directory
37         this.PopupsDir = "./openwysiwyg/popups/";
38         
39         // CSS Directory File
40         this.CSSFile = "./openwysiwyg/styles/wysiwyg.css";
41         
42         // Default WYSIWYG width and height (use px or %)
43         this.Width = "500px";
44         this.Height = "300px";
45         
46         // Default stylesheet of the WYSIWYG editor window
47         this.DefaultStyle = "font-family: Arial; font-size: 12px; background-color: #FFFFFF";
48         
49         // Stylesheet if editor is disabled
50         this.DisabledStyle = "font-family: Arial; font-size: 12px; background-color: #EEEEEE";
51         
52         // Width + Height of the preview window
53         this.PreviewWidth = 500;
54         this.PreviewHeight = 400;
55         
56         // Confirmation message if you strip any HTML added by word
57         this.RemoveFormatConfMessage = "Clean HTML inserted by MS Word ?";
58         
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.";
61         
62         // Anchor path to strip, leave it blank to ignore
63         // or define auto to strip the path where the editor is placed 
64         // (only IE)
65         this.AnchorPathToStrip = "auto";
66         
67         // Image path to strip, leave it blank to ignore
68         // or define auto to strip the path where the editor is placed 
69         // (only IE)
70         this.ImagePathToStrip = "auto";
71         
72         // Enable / Disable the custom context menu
73         this.ContextMenu = true;
74         
75         // Enabled the status bar update. Within the status bar 
76         // node tree of the actually selected element will build
77         this.StatusBarEnabled = true;
78         
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;
83         
84         // Replace line breaks with <br> tags
85         this.ReplaceLineBreaks = false;
86         
87         // Page that opened the WYSIWYG (Used for the return command)
88         this.Opener = "admin.asp";
89         
90         // Insert image implementation
91         this.ImagePopupFile = "";
92         this.ImagePopupWidth = 0;
93         this.ImagePopupHeight = 0;
94         
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");
101         
102         // DropDowns
103         this.DropDowns = new Array();
104         // Fonts
105         this.DropDowns['font'] = {
106             id: "fonts",
107             command: "FontName",
108             label: "<font style=\"font-family:{value};font-size:12px;\">{value}</font>",
109             width: "90px",
110             elements: new Array("Arial", "Sans Serif", "Tahoma", "Verdana", "Courier New", "Georgia", "Times New Roman", "Impact", "Comic Sans MS")
111         };
112         // Font sizes
113         this.DropDowns['fontsize'] = {
114             id: "fontsizes",
115             command: "FontSize",
116             label: "<font size=\"{value}\">Size {value}</font>",
117             width: "54px",
118             elements: new Array("1", "2", "3", "4", "5", "6", "7")
119         };
120         // Headings
121         this.DropDowns['headings'] = {
122             id: "headings",
123             command: "FormatBlock",
124             label: "<{value} style=\"margin:0px;text-decoration:none;font-family:Arial\">{value}</{value}>",
125             width: "74px",
126             elements: new Array("H1", "H2", "H3", "H4", "H5", "H6")
127         };
128         
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);
134             }
135             if (this.Toolbar[toolbar - 1] == null) {
136                 this.Toolbar[toolbar - 1] = new Array();
137             }
138             this.Toolbar[toolbar - 1].splice(position + 1, 1, element);
139         };
140         
141         // Remove an element from the toolbar
142         this.removeToolbarElement = function(element){
143             if (element == "seperator") {
144                 return;
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);
152                         }
153                     }
154                 }
155             }
156         };
157         
158         // clear all or a given toolbar
159         this.clearToolbar = function(toolbar){
160             if (typeof toolbar == "undefined") {
161                 this.Toolbar = new Array();
162             }
163             else {
164                 this.Toolbar[toolbar + 1] = new Array();
165             }
166         };
167         
168     },
169     
170     
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"),
176     
177     // List of available actions and their respective ID and images
178     ToolbarList: {
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']
218     },
219     
220     // stores the different settings for each textarea
221     // the textarea identifier is used to store the settings object
222     config: new Array(),
223     // Create viewTextMode global variable and set to 0
224     // enabling all toolbar commands while in HTML mode
225     viewTextMode: new Array(),
226     // maximized
227     maximized: new Array(),
228     
229     /**
230      * Get the range of the given selection
231      *
232      * @param {Selection} sel Selection object
233      * @return {Range} Range object
234      */
235     getRange: function(sel){
236         return sel.createRange ? sel.createRange() : sel.getRangeAt(0);
237     },
238     
239     /**
240      * Return the editor div element
241      *
242      * @param {String} n Editor identifier
243      * @return {HtmlDivElement} Iframe object
244      */
245     getEditorDiv: function(n){
246         return $("wysiwyg_div_" + n);
247     },
248     
249     /**
250      * Return the editor table element
251      *
252      * @param {String} n Editor identifier
253      * @return {HtmlTableElement} Iframe object
254      */
255     getEditorTable: function(n){
256         return $("wysiwyg_table_" + n);
257     },
258     
259     /**
260      * Get the iframe object of the WYSIWYG editor
261      *
262      * @param {String} n Editor identifier
263      * @return {HtmlIframeElement} Iframe object
264      */
265     getEditor: function(n){
266         return $("wysiwyg" + n);
267     },
268     
269     /**
270      * Get editors window element
271      *
272      * @param {String} n Editor identifier
273      * @return {HtmlWindowElement} Html window object
274      */
275     getEditorWindow: function(n){
276         return this.getEditor(n).contentWindow;
277     },
278     
279     /**
280      * Attach the WYSIWYG editor to the given textarea element
281      *
282      * @param {String} id Textarea identifier (all = all textareas)
283      * @param {Settings} settings the settings which will be applied to the textarea
284      */
285     attach: function(id, settings){
286         if (id != "all") {
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);
291             });
292         }
293         else {
294             WYSIWYG_Core.addEvent(window, "load", function generateEditor(){
295                 WYSIWYG.attachAll(settings);
296             });
297         }
298     },
299     
300     /**
301      * Attach the WYSIWYG editor to all textarea elements
302      *
303      * @param {Settings} settings Settings to customize the look and feel
304      */
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 == "") 
310                 continue;
311             this.setSettings(id, settings);
312             WYSIWYG_Core.includeCSS(this.config[id].CSSFile);
313             WYSIWYG._generate(id, settings);
314         }
315     },
316     
317     /**
318      * Display an iframe instead of the textarea.
319      * It's used as textarea replacement to display HTML.
320      *
321      * @param id Textarea identifier (all = all textareas)
322      * @param settings the settings which will be applied to the textarea
323      */
324     display: function(id, settings){
325         if (id != "all") {
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);
330             });
331         }
332         else {
333             WYSIWYG_Core.addEvent(window, "load", function displayIframe(){
334                 WYSIWYG.displayAll(settings);
335             });
336         }
337     },
338     
339     /**
340      * Display an iframe instead of the textarea.
341      * It's apply the iframe to all textareas found in the current document.
342      *
343      * @param settings Settings to customize the look and feel
344      */
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 == "") 
350                 continue;
351             this.setSettings(id, settings);
352             WYSIWYG_Core.includeCSS(this.config[id].CSSFile);
353             WYSIWYG._display(id, settings);
354         }
355     },
356     
357     /**
358      * Set settings in config array, use the textarea id as identifier
359      *
360      * @param n Textarea identifier (all = all textareas)
361      * @param settings the settings which will be applied to the textarea
362      */
363     setSettings: function(n, settings){
364         if (typeof(settings) != "object") {
365             this.config[n] = new this.Settings();
366         }
367         else {
368             this.config[n] = settings;
369         }
370     },
371     
372     /**
373      * Insert or modify an image
374      *
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)
384      */
385     insertImage: function(src, width, height, align, border, alt, hspace, vspace, n){
386     
387         // get editor
388         var doc = this.getEditorWindow(n).document;
389         // get selection and range
390         var sel = this.getSelection(n);
391         var range = this.getRange(sel);
392         
393         // the current tag of range
394         var img = this.findParent("img", range);
395         
396         // element is not a link
397         var update = (img == null) ? false : true;
398         if (!update) {
399             img = doc.createElement("img");
400         }
401         
402         // set the attributes
403         WYSIWYG_Core.setAttribute(img, "src", src);
404         WYSIWYG_Core.setAttribute(img, "style", "width:" + width + ";height:" + height);
405         if (align != "") {
406             WYSIWYG_Core.setAttribute(img, "align", align);
407         }
408         else {
409             img.removeAttribute("align");
410         }
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");
417         
418         // on update exit here
419         if (update) {
420             return;
421         }
422         
423         // Check if IE or Mozilla (other)
424         if (WYSIWYG_Core.isMSIE) {
425             range.pasteHTML(img.outerHTML);
426         }
427         else {
428             this.insertNodeAtSelection(img, n);
429         }
430     },
431     
432     /**
433      * Insert or modify a link
434      *
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)
441      */
442     insertLink: function(href, target, style, styleClass, name, n){
443     
444         // get editor
445         var doc = this.getEditorWindow(n).document;
446         // get selection and range
447         var sel = this.getSelection(n);
448         var range = this.getRange(sel);
449         var lin = null;
450         
451         // get element from selection
452         if (WYSIWYG_Core.isMSIE) {
453             if (sel.type == "Control" && range.length == 1) {
454                 range = this.getTextRange(range(0));
455                 range.select();
456             }
457         }
458         
459         // find a as parent element
460         lin = this.findParent("a", range);
461         
462         // check if parent is found
463         var update = (lin == null) ? false : true;
464         if (!update) {
465             lin = doc.createElement("a");
466         }
467         
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);
475         
476         // on update exit here
477         if (update) {
478             return;
479         }
480         
481         // Check if IE or Mozilla (other)
482         if (WYSIWYG_Core.isMSIE) {
483             range.select();
484             lin.innerHTML = range.htmlText;
485             range.pasteHTML(lin.outerHTML);
486         }
487         else {
488             var node = range.startContainer;
489             var pos = range.startOffset;
490             if (node.nodeType != 3) {
491                 node = node.childNodes[pos];
492             }
493             if (node.tagName) 
494                 lin.appendChild(node);
495             else 
496                 lin.innerHTML = sel;
497             this.insertNodeAtSelection(lin, n);
498         }
499     },
500     
501     /**
502      * Strips any HTML added by word
503      *
504      * @param {String} n The editor identifier (the textarea's ID)
505      */
506     removeFormat: function(n){
507     
508         if (!confirm(this.config[n].RemoveFormatConfMessage)) {
509             return;
510         }
511         var doc = this.getEditorWindow(n).document;
512         var str = doc.body.innerHTML;
513         
514         str = str.replace(/<span([^>])*>(&nbsp;)*\s*<\/span>/gi, '');
515         str = str.replace(/<span[^>]*>/gi, '');
516         str = str.replace(/<\/span[^>]*>/gi, '');
517         str = str.replace(/<p([^>])*>(&nbsp;)*\s*<\/p>/gi, '');
518         str = str.replace(/<p[^>]*>/gi, '');
519         str = str.replace(/<\/p[^>]*>/gi, '');
520         str = str.replace(/<h([^>])[0-9]>(&nbsp;)*\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>');
524         
525         // var repl_i1 = /<I[^>]*>/ig;
526         // str = str.replace (repl_i1, '<i>');
527         
528         str = str.replace(/<DIV[^>]*>/ig, '');
529         str = str.replace(/<\/DIV>/gi, '');
530         str = str.replace(/<[\/\w?]+:[^>]*>/ig, '');
531         str = str.replace(/(&nbsp;){2,}/ig, '&nbsp;');
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, '');
549         
550         str = str.replace(/<!--([^>])*>(&nbsp;)*\s*<\/-->/gi, '');
551         str = str.replace(/<!--[^>]*>/gi, '');
552         str = str.replace(/<\/--[^>]*>/gi, '');
553         
554         doc.body.innerHTML = str;
555     },
556     
557     /**
558      * Display an iframe instead of the textarea.
559      *
560      * @private
561      * @param {String} n The editor identifier (the textarea's ID)
562      * @param {Object} settings Object which holds the settings
563      */
564     _display: function(n, settings){
565     
566         // Get the textarea element
567         var textarea = $(n);
568         
569         // Validate if textarea exists
570         if (textarea == null) {
571             alert("No textarea found with the given identifier (ID: " + n + ").");
572             return;
573         }
574         
575         // Validate browser compatiblity
576         if (!WYSIWYG_Core.isBrowserCompatible()) {
577             if (this.config[n].NoValidBrowserMessage != "") {
578                 alert(this.config[n].NoValidBrowserMessage);
579             }
580             return;
581         }
582         
583         // Load settings in config array, use the textarea id as identifier
584         if (typeof(settings) != "object") {
585             this.config[n] = new this.Settings();
586         }
587         else {
588             this.config[n] = settings;
589         }
590         
591         // Hide the textarea 
592         textarea.style.display = "none";
593         
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;
598         }
599         if (textarea.style.height) {
600             this.config[n].Height = textarea.style.height
601         }
602         
603         // determine the width + height
604         var currentWidth = this.config[n].Width;
605         var currentHeight = this.config[n].Height;
606         
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;
613         }
614         
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' +
618         n +
619         '" class="iframeText" style="width:' +
620         ifrmWidth +
621         ';height:' +
622         ifrmHeight +
623         ';"></iframe>\n' +
624         '</td></tr></table>\n';
625         
626         // Insert after the textArea both toolbar one and two
627         textarea.insertAdjacentHTML("afterEnd", iframe);
628         
629         // Pass the textarea's existing text over to the content variable
630         var content = textarea.value;
631         var doc = this.getEditorWindow(n).document;
632         
633         // Replace all \n with <br> 
634         if (this.config[n].ReplaceLineBreaks) {
635             content = content.replace(/(\r\n)|(\n)/ig, "<br>");
636         }
637         
638         // Write the textarea's content into the iframe
639         doc.open();
640         doc.write(content);
641         doc.close();
642         
643         // Set default style of the editor window
644         WYSIWYG_Core.setAttribute(doc.body, "style", this.config[n].DefaultStyle);
645     },
646     
647     /**
648      * Replace the given textarea with wysiwyg editor
649      *
650      * @private
651      * @param {String} n The editor identifier (the textarea's ID)
652      * @param {Object} settings Object which holds the settings
653      */
654     _generate: function(n, settings){
655     
656         // Get the textarea element
657         var textarea = $(n);
658         // Validate if textarea exists
659         if (textarea == null) {
660             alert("No textarea found with the given identifier (ID: " + n + ").");
661             return;
662         }
663         
664         // Validate browser compatiblity
665         if (!WYSIWYG_Core.isBrowserCompatible()) {
666             if (this.config[n].NoValidBrowserMessage != "") {
667                 alert(this.config[n].NoValidBrowserMessage);
668             }
669             return;
670         }
671         
672         // Hide the textarea 
673         textarea.style.display = 'none';
674         
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;
679         }
680         if (textarea.style.height) {
681             this.config[n].Height = textarea.style.height
682         }
683         
684         // determine the width + height
685         var currentWidth = this.config[n].Width;
686         var currentHeight = this.config[n].Height;
687         
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;
697         }
698         
699         // Generate the WYSIWYG Table
700         // This table holds the toolbars and the iframe as the editor
701         var 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;">';
705         
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];
710                 
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>';
714                 
715                 // Interate over the toolbar element
716                 for (var i = 0; i < toolbar.length; i++) {
717                     var id = toolbar[i];
718                     if (toolbar[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);
724                             editor += '</td>';
725                         }
726                         else {
727                         
728                             // Get the values of the Button from the global ToolbarList object
729                             var buttonObj = this.ToolbarList[toolbar[i]];
730                             if (buttonObj) {
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];
735                                 
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">';
739                                     editor += '</td>';
740                                 }
741                                 // View Source button
742                                 else 
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>';
747                                         editor += '</td>';
748                                     }
749                                     else {
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">';
752                                         editor += '</td>';
753                                     }
754                             }
755                         }
756                     }
757                 }
758                 editor += '<td>&nbsp;</td></tr></table>';
759             }
760         }
761         
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' +
765         '</td></tr>';
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 + '">&nbsp;</td></tr>';
769         }
770         editor += '</table>';
771         editor += '</div>';
772         
773         // Insert the editor after the textarea     
774         textarea.insertAdjacentHTML("afterEnd", editor);
775         
776         // Hide the "Text Mode" button
777         // Validate if textMode Elements are prensent
778         if ($("textMode" + n)) {
779             $("textMode" + n).style.display = 'none';
780         }
781         
782         // Pass the textarea's existing text over to the content variable
783         var content = textarea.value;
784         var doc = this.getEditorWindow(n).document;
785         
786         
787         // Replace all \n with <br> 
788         if (this.config[n].ReplaceLineBreaks) {
789             content = content.replace(/\n\r|\n/ig, "<br>");
790         }
791         
792         // Write the textarea's content into the iframe
793         doc.open();
794         doc.write(content);
795         doc.close();
796         
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;
801         }
802         else {
803             doc.designMode = "on";
804         }
805         
806         // Set default font style
807         WYSIWYG_Core.setAttribute(doc.body, "style", this.config[n].DefaultStyle);
808         
809         // Enable table highlighting
810         WYSIWYG_Table.refreshHighlighting(n);
811         
812         // Event Handling
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);
817             });
818         }
819         
820         // close font selection if mouse moves over the editor window
821         WYSIWYG_Core.addEvent(doc, "mouseover", function xxx_bb(){
822             WYSIWYG.closeDropDowns(n);
823         });
824         
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);
829             });
830         }
831         
832         // status bar update
833         if (this.config[n].StatusBarEnabled) {
834             WYSIWYG_Core.addEvent(doc, "mouseup", function xxx_dd(){
835                 WYSIWYG.updateStatusBar(n);
836             });
837         }
838         
839         // custom context menu
840         if (this.config[n].ContextMenu) {
841             WYSIWYG_ContextMenu.init(n);
842         }
843         
844         // init viewTextMode var
845         this.viewTextMode[n] = false;
846     },
847     
848     /**
849      * Disable the given WYSIWYG Editor Box
850      *
851      * @param {String} n The editor identifier (the textarea's ID)
852      */
853     disable: function(n){
854     
855         // get the editor window
856         var editor = this.getEditorWindow(n);
857         
858         // Validate if editor exists
859         if (editor == null) {
860             alert("No editor found with the given identifier (ID: " + n + ").");
861             return;
862         }
863         
864         if (editor) {
865             // disable design mode or content editable feature
866             if (editor.document.body.contentEditable) {
867                 editor.document.body.contentEditable = false;
868             }
869             else {
870                 editor.document.designMode = "Off";
871             }
872             
873             // change the style of the body
874             WYSIWYG_Core.setAttribute(editor.document.body, "style", this.config[n].DisabledStyle);
875             
876             // hide the status bar
877             this.hideStatusBar(n);
878             
879             // hide all toolbars
880             this.hideToolbars(n);
881         }
882     },
883     
884     /**
885      * Enables the given WYSIWYG Editor Box
886      *
887      * @param {String} n The editor identifier (the textarea's ID)
888      */
889     enable: function(n){
890     
891         // get the editor window
892         var editor = this.getEditorWindow(n);
893         
894         // Validate if editor exists
895         if (editor == null) {
896             alert("No editor found with the given identifier (ID: " + n + ").");
897             return;
898         }
899         
900         if (editor) {
901             // disable design mode or content editable feature
902             if (editor.document.body.contentEditable) {
903                 editor.document.body.contentEditable = true;
904             }
905             else {
906                 editor.document.designMode = "On";
907             }
908             
909             // change the style of the body
910             WYSIWYG_Core.setAttribute(editor.document.body, "style", this.config[n].DefaultStyle);
911             
912             // hide the status bar
913             this.showStatusBar(n);
914             
915             // hide all toolbars
916             this.showToolbars(n);
917         }
918     },
919     
920     /**
921      * Returns the node structure of the current selection as array
922      *
923      * @param {String} n The editor identifier (the textarea's ID)
924      */
925     getNodeTree: function(n){
926     
927         var sel = this.getSelection(n);
928         var range = this.getRange(sel);
929         
930         // get element of range
931         var tag = this.getTag(range);
932         if (tag == null) {
933             return;
934         }
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
940         var ii = 1;
941         
942         while (node != null && node.nodeName != "#document") {
943             nodeTree[ii] = node;
944             node = this.getParent(node);
945             ii++;
946         }
947         
948         return nodeTree;
949     },
950     
951     /**
952      * Removes the current node of the selection
953      *
954      * @param {String} n The editor identifier (the textarea's ID)
955      */
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) {
964             return;
965         }
966         if (tag.nodeName == "HTML" || tag.nodeName == "BODY") {
967             return;
968         }
969         
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);
976         
977         // remove node
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);
984             }
985         }
986         // update the status bar
987         this.updateStatusBar(n);
988     },
989     
990     /**
991      * Get the selection of the given editor
992      *
993      * @param {String} n The editor identifier (the textarea's ID)
994      */
995     getSelection: function(n){
996         var ifrm = this.getEditorWindow(n);
997         var doc = ifrm.document;
998         var sel = null;
999         if (ifrm.getSelection) {
1000             sel = ifrm.getSelection();
1001         }
1002         else 
1003             if (doc.getSelection) {
1004                 sel = doc.getSelection();
1005             }
1006             else 
1007                 if (doc.selection) {
1008                     sel = doc.selection;
1009                 }
1010         return sel;
1011     },
1012     
1013     /**
1014      * Updates the status bar with the current node tree
1015      *
1016      * @param {String} n The editor identifier (the textarea's ID)
1017      */
1018     updateStatusBar: function(n){
1019     
1020         // get the node structure
1021         var nodeTree = this.getNodeTree(n);
1022         if (nodeTree == null) {
1023             return;
1024         }
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>';
1031             }
1032             else {
1033                 outputTree += nodeTree[i].nodeName;
1034             }
1035             if (i > 0) {
1036                 outputTree += " > ";
1037             }
1038         }
1039         
1040         // update the status bar        
1041         var statusbar = $("wysiwyg_statusbar_" + n);
1042         if (statusbar) {
1043             statusbar.innerHTML = outputTree;
1044         }
1045     },
1046     
1047     /**
1048      * Execute a command on the editor document
1049      *
1050      * @param {String} command The execCommand (e.g. Bold)
1051      * @param {String} n The editor identifier
1052      * @param {String} value The value when applicable
1053      */
1054     execCommand: function(n, cmd, value){
1055     
1056         // When user clicks toolbar button make sure it always targets its respective WYSIWYG
1057         this.getEditorWindow(n).focus();
1058         
1059         // When in Text Mode these execCommands are enabled
1060         var textModeCommands = new Array("ViewText", "Print");
1061         
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) {
1066                 cmdValid = true;
1067             }
1068         }
1069         if (this.viewTextMode[n] && !cmdValid) {
1070             alert("You are in TEXT Mode. This feature has been disabled.");
1071             return;
1072         }
1073         
1074         // rbg to hex convertion implementation dependents on browser
1075         var toHexColor = WYSIWYG_Core.isMSIE ? WYSIWYG_Core._dec_to_rgb : WYSIWYG_Core.toHexColor;
1076         
1077         // popup screen positions
1078         var popupPosition = {
1079             left: parseInt(window.screen.availWidth / 3),
1080             top: parseInt(window.screen.availHeight / 3)
1081         };
1082         
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;
1089         }
1090         if (typeof this.config[n].ImagePopupWidth && this.config[n].ImagePopupWidth > 0) {
1091             imagePopupWidth = this.config[n].ImagePopupWidth;
1092         }
1093         if (typeof this.config[n].ImagePopupHeight && this.config[n].ImagePopupHeight > 0) {
1094             imagePopupHeight = this.config[n].ImagePopupHeight;
1095         }
1096         
1097         // switch which action have to do
1098         switch (cmd) {
1099             case "Maximize":
1100                 this.maximize(n);
1101                 break;
1102             case "FormatBlock":
1103                 WYSIWYG_Core.execCommand(n, cmd, "<" + value + ">");
1104                 break;
1105             // ForeColor and 
1106             case "ForeColor":
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();
1110                 break;
1111                 
1112             // BackColor
1113             case "BackColor":
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();
1116                 break;
1117                 
1118             // InsertImage
1119             case "InsertImage":
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();
1121                 break;
1122                 
1123             // Remove Image
1124             case "RemoveImage":
1125                 this.removeImage(n);
1126                 break;
1127                 
1128             // Remove Link
1129             case "RemoveLink":
1130                 this.removeLink(n);
1131                 break;
1132                 
1133             // Remove a Node
1134             case "RemoveNode":
1135                 this.removeNode(n);
1136                 break;
1137                 
1138             // Create Link
1139             case "CreateLink":
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();
1141                 break;
1142                 
1143             // InsertTable
1144             case "InsertTable":
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();
1146                 break;
1147                 
1148             // ViewSource
1149             case "ViewSource":
1150                 this.viewSource(n);
1151                 break;
1152                 
1153             // ViewText
1154             case "ViewText":
1155                 this.viewText(n);
1156                 break;
1157                 
1158             // Help
1159             case "Help":
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();
1161                 break;
1162                 
1163             // Strip any HTML added by word
1164             case "RemoveFormat":
1165                 this.removeFormat(n);
1166                 break;
1167                 
1168             // Preview thx to Korvo
1169             case "Preview":
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();
1171                 break;
1172                 
1173             // Print
1174             case "Print":
1175                 this.print(n);
1176                 break;
1177                 
1178             // Save
1179             case "Save":
1180                 WYSIWYG.updateTextArea(n);
1181                 var form = WYSIWYG_Core.findParentNode("FORM", this.getEditor(n));
1182                 if (form == null) {
1183                     alert("Can not submit the content, because no form element found.");
1184                     return;
1185                 }
1186                 form.submit();
1187                 break;
1188                 
1189             // Return
1190             case "Return":
1191                 location.replace(this.config[n].Opener);
1192                 break;
1193                 
1194             default:
1195                 WYSIWYG_Core.execCommand(n, cmd, value);
1196                 
1197         }
1198         
1199         // hide node the font + font size selection
1200         this.closeDropDowns(n);
1201     },
1202     
1203     /**
1204      * Maximize the editor instance
1205      *
1206      * @param {String} n The editor identifier
1207      */
1208     maximize: function(n){
1209     
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();
1215         size.width -= 5;
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;
1221         }
1222         else {
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;
1227         }
1228         
1229     },
1230     
1231     /**
1232      * Insert HTML into WYSIWYG in rich text
1233      *
1234      * @param {String} html The HTML being inserted (e.g. <b>hello</b>)
1235      * @param {String} n The editor identifier
1236      */
1237     insertHTML: function(html, n){
1238         if (WYSIWYG_Core.isMSIE) {
1239             this.getEditorWindow(n).document.selection.createRange().pasteHTML(html);
1240         }
1241         else {
1242             var span = this.getEditorWindow(n).document.createElement("span");
1243             span.innerHTML = html;
1244             this.insertNodeAtSelection(span, n);
1245         }
1246     },
1247     
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){
1256     
1257         // get editor document
1258         var doc = this.getEditorWindow(n).document;
1259         // get current selection
1260         var sel = this.getSelection(n);
1261         
1262         // get the first range of the selection
1263         // (there's almost always only one range)
1264         var range = sel.getRangeAt(0);
1265         
1266         // deselect everything
1267         sel.removeAllRanges();
1268         
1269         // remove content of current selection from document
1270         range.deleteContents();
1271         
1272         // get location of current selection
1273         var container = range.startContainer;
1274         var pos = range.startOffset;
1275         
1276         // make a new range for the new selection
1277         range = doc.createRange();
1278         
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);
1285         }
1286         else {
1287         
1288             var afterNode;
1289             var beforeNode;
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;
1297                 
1298                 // text before the split
1299                 var textBefore = text.substr(0, pos);
1300                 // text after the split
1301                 var textAfter = text.substr(pos);
1302                 
1303                 beforeNode = document.createTextNode(textBefore);
1304                 afterNode = document.createTextNode(textAfter);
1305                 
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);
1310                 
1311                 // remove the old node
1312                 container.removeChild(textNode);
1313             }
1314             else {
1315                 // else simply insert the node
1316                 afterNode = container.childNodes[pos];
1317                 container.insertBefore(insertNode, afterNode);
1318             }
1319             
1320             try {
1321                 range.setEnd(afterNode, 0);
1322                 range.setStart(afterNode, 0);
1323             } 
1324             catch (e) {
1325                 alert(e);
1326             }
1327         }
1328         
1329         sel.addRange(range);
1330     },
1331     
1332     /**
1333      * Prints the content of the WYSIWYG editor area
1334      *
1335      * @param {String} n The editor identifier (textarea ID)
1336      */
1337     print: function(n){
1338         if (document.all && navigator.appVersion.substring(22, 23) == 4) {
1339             var doc = this.getEditorWindow(n).document;
1340             doc.focus();
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 = '';
1348         }
1349         else {
1350             this.getEditorWindow(n).print();
1351         }
1352     },
1353     
1354     /**
1355      * Writes the content of an drop down
1356      *
1357      * @param {String} n The editor identifier (textarea ID)
1358      * @param {String} id Drop down identifier
1359      * @return {String} Drop down HTML
1360      */
1361     writeDropDown: function(n, id){
1362     
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();
1368         
1369         var output = "";
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);
1378                 // output
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>';
1383             }
1384         }
1385         output += '</span></td></tr></table>';
1386         
1387         return output;
1388     },
1389     
1390     /**
1391      * Close all drop downs. You can define a exclude dropdown id
1392      *
1393      * @param {String} n The editor identifier (textarea ID)
1394      * @param {String} exid Excluded drop down identifier
1395      */
1396     closeDropDowns: function(n, exid){
1397         if (typeof(exid) == "undefined") 
1398             exid = "";
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;
1404                 if ($(divId)) 
1405                     $(divId).style.display = 'none';
1406             }
1407         }
1408     },
1409     
1410     /**
1411      * Open a defined drop down
1412      *
1413      * @param {String} n The editor identifier (textarea ID)
1414      * @param {String} id Drop down identifier
1415      */
1416     openDropDown: function(n, id){
1417         var divId = "elm_" + id + "_" + n;
1418         if ($(divId).style.display == "none") {
1419             $(divId).style.display = "block";
1420         }
1421         else {
1422             $(divId).style.display = "none";
1423         }
1424         $(divId).style.position = "absolute";
1425         this.closeDropDowns(n, id);
1426     },
1427     
1428     /**
1429      * Shows the HTML source code generated by the WYSIWYG editor
1430      *
1431      * @param {String} n The editor identifier (textarea ID)
1432      */
1433     viewSource: function(n){
1434     
1435         // document
1436         var doc = this.getEditorWindow(n).document;
1437         
1438         // Enable table highlighting
1439         WYSIWYG_Table.disableHighlighting(n);
1440         
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;
1449         }
1450         // View Source for Mozilla/Netscape
1451         else {
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);
1457         }
1458         
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';
1463         }
1464         if ($('textMode' + n)) {
1465             $('textMode' + n).style.display = 'block';
1466         }
1467         
1468         // set the font values for displaying HTML source
1469         doc.body.style.fontSize = "12px";
1470         doc.body.style.fontFamily = "Courier New";
1471         
1472         this.viewTextMode[n] = true;
1473     },
1474     
1475     /**
1476      * Shows the HTML source code generated by the WYSIWYG editor
1477      *
1478      * @param {String} n The editor identifier (textarea ID)
1479      */
1480     viewText: function(n){
1481     
1482         // get document
1483         var doc = this.getEditorWindow(n).document;
1484         
1485         // View Text for IE              
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;
1493         }
1494         
1495         // View Text for Mozilla/Netscape
1496         else {
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;
1502         }
1503         
1504         // Enable table highlighting
1505         WYSIWYG_Table.refreshHighlighting(n);
1506         
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';
1511         }
1512         if ($('HTMLMode' + n)) {
1513             $('HTMLMode' + n).style.display = 'block';
1514         }
1515         
1516         // reset the font values (changed)
1517         WYSIWYG_Core.setAttribute(doc.body, "style", this.config[n].DefaultStyle);
1518         
1519         this.viewTextMode[n] = false;
1520     },
1521     
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){
1530     
1531         // parameter exact is optional
1532         if (typeof exact == "undefined") {
1533             exact = true;
1534         }
1535         
1536         var stripImgageUrl = null;
1537         var stripAnchorUrl = null;
1538         
1539         // add url to strip of anchors to array
1540         if (this.config[n].AnchorPathToStrip == "auto") {
1541             stripAnchorUrl = WYSIWYG_Core.getDocumentUrl(document);
1542         }
1543         else 
1544             if (this.config[n].AnchorPathToStrip != "") {
1545                 stripAnchorUrl = this.config[n].AnchorPathToStrip;
1546             }
1547         
1548         // add strip url of images to array
1549         if (this.config[n].ImagePathToStrip == "auto") {
1550             stripImgageUrl = WYSIWYG_Core.getDocumentUrl(document);
1551         }
1552         else 
1553             if (this.config[n].ImagePathToStrip != "") {
1554                 stripImgageUrl = this.config[n].ImagePathToStrip;
1555             }
1556         
1557         var url;
1558         var regex;
1559         var result;
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));
1564             
1565             // exact replacing of url. regex: src="<url>"
1566             if (exact) {
1567                 regex = eval("/(src=\")(" + url + ")([^\"]*)/gi");
1568                 content = content.replace(regex, "$1$3");
1569             }
1570             // not exect replacing of url. regex: <url>
1571             else {
1572                 regex = eval("/(" + url + ")(.+)/gi");
1573                 content = content.replace(regex, "$2");
1574             }
1575             
1576             // strip absolute urls without a heading slash ("images/print.gif") 
1577             result = WYSIWYG_Core.getDocumentPathOfUrl(stripImgageUrl).match(/.+[\/]{2,3}[^\/]*/, "");
1578             if (result) {
1579                 url = WYSIWYG_Core.stringToRegex(result[0]);
1580                 
1581                 // exact replacing of url. regex: src="<url>"
1582                 if (exact) {
1583                     regex = eval("/(src=\")(" + url + ")([^\"]*)/gi");
1584                     content = content.replace(regex, "$1$3");
1585                 }
1586                 // not exect replacing of url. regex: <url>
1587                 else {
1588                     regex = eval("/(" + url + ")(.+)/gi");
1589                     content = content.replace(regex, "$2");
1590                 }
1591             }
1592         }
1593         
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));
1598             
1599             // strip absolute urls with a heading slash ("/product/index.html")
1600             // exact replacing of url. regex: src="<url>"
1601             if (exact) {
1602                 regex = eval("/(href=\")(" + url + ")([^\"]*)/gi");
1603                 content = content.replace(regex, "$1$3");
1604             }
1605             // not exect replacing of url. regex: <url>
1606             else {
1607                 regex = eval("/(" + url + ")(.+)/gi");
1608                 content = content.replace(regex, "$2");
1609             }
1610             
1611             // strip absolute urls without a heading slash ("product/index.html")       
1612             result = WYSIWYG_Core.getDocumentPathOfUrl(stripAnchorUrl).match(/.+[\/]{2,3}[^\/]*/, "");
1613             if (result) {
1614                 url = WYSIWYG_Core.stringToRegex(result[0]);
1615                 // exact replacing of url. regex: src="<url>"
1616                 if (exact) {
1617                     regex = eval("/(href=\")(" + url + ")([^\"]*)/gi");
1618                     content = content.replace(regex, "$1$3");
1619                 }
1620                 // not exect replacing of url. regex: <url>
1621                 else {
1622                     regex = eval("/(" + url + ")(.+)/gi");
1623                     content = content.replace(regex, "$2");
1624                 }
1625                 
1626             }
1627             
1628             // stip off anchor links with #name                 
1629             url = WYSIWYG_Core.stringToRegex(stripAnchorUrl);
1630             // exact replacing of url. regex: src="<url>"
1631             if (exact) {
1632                 regex = eval("/(href=\")(" + url + ")(#[^\"]*)/gi");
1633                 content = content.replace(regex, "$1$3");
1634             }
1635             // not exect replacing of url. regex: <url>
1636             else {
1637                 regex = eval("/(" + url + ")(.+)/gi");
1638                 content = content.replace(regex, "$2");
1639             }
1640             
1641             
1642             // stip off anchor links with #name (only for local system)
1643             url = WYSIWYG_Core.getDocumentUrl(document);
1644             var pos = url.lastIndexOf("/");
1645             if (pos != -1) {
1646                 url = url.substring(pos + 1, url.length);
1647                 url = WYSIWYG_Core.stringToRegex(url);
1648                 // exact replacing of url. regex: src="<url>"
1649                 if (exact) {
1650                     regex = eval("/(href=\")(" + url + ")(#[^\"]*)/gi");
1651                     content = content.replace(regex, "$1$3");
1652                 }
1653                 // not exect replacing of url. regex: <url>
1654                 else {
1655                     regex = eval("/(" + url + ")(.+)/gi");
1656                     content = content.replace(regex, "$2");
1657                 }
1658             }
1659         }
1660         
1661         return content;
1662     },
1663     
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]) {
1672             this.viewText(n);
1673         }
1674         // get inner HTML
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, "");
1683         }
1684         // set content back in textarea
1685         $(n).value = content;
1686     },
1687     
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);
1697             if (toolbar) {
1698                 toolbar.style.display = "none";
1699             }
1700         }
1701     },
1702     
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);
1712             if (toolbar) {
1713                 toolbar.style.display = "";
1714             }
1715         }
1716     },
1717     
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);
1726         if (statusbar) {
1727             statusbar.style.display = "none";
1728         }
1729     },
1730     
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);
1739         if (statusbar) {
1740             statusbar.style.display = "";
1741         }
1742     },
1743     
1744     /**
1745      * Finds the node with the given tag name in the given range
1746      *
1747      * @param {String} tagName Parent tag to find
1748      * @param {Range} range Current range
1749      */
1750     findParent: function(parentTagName, range){
1751         parentTagName = parentTagName.toUpperCase();
1752         var rangeWorking;
1753         var elmWorking = null;
1754         try {
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];
1760                 }
1761                 return WYSIWYG_Core.findParentNode(parentTagName, node);
1762             }
1763             else {
1764                 elmWorking = (range.length > 0) ? range.item(0) : range.parentElement();
1765                 elmWorking = WYSIWYG_Core.findParentNode(parentTagName, elmWorking);
1766                 if (elmWorking != null) 
1767                     return elmWorking;
1768                 
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);
1777                         }
1778                     }
1779                 }
1780                 return null;
1781             }
1782         } 
1783         catch (e) {
1784             return null;
1785         }
1786     },
1787     
1788     /**
1789      * Get the acutally tag of the given range
1790      *
1791      * @param {Range} range Current range
1792      */
1793     getTag: function(range){
1794         try {
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];
1800                 }
1801                 
1802                 if (node.nodeName && node.nodeName.search(/#/) != -1) {
1803                     return node.parentNode;
1804                 }
1805                 return node;
1806             }
1807             else {
1808                 if (range.length > 0) {
1809                     return range.item(0);
1810                 }
1811                 else 
1812                     if (range.parentElement()) {
1813                         return range.parentElement();
1814                     }
1815             }
1816             return null;
1817         } 
1818         catch (e) {
1819             return null;
1820         }
1821     },
1822     
1823     /**
1824      * Get the parent node of the given node
1825      *
1826      * @param {DOMElement} element - Element which parent will be returned
1827      */
1828     getParent: function(element){
1829         if (element.parentNode) {
1830             return element.parentNode;
1831         }
1832         return null;
1833     },
1834     
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);
1844         return range;
1845     },
1846     
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){
1856     
1857         var editor = this.getEditorWindow(n);
1858         var sel;
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;
1866                 sel.select();
1867                 sel.moveEnd("character", 1);
1868                 sel.moveStart("character", 1);
1869                 sel.collapse(false);
1870                 return false;
1871             }
1872             else {
1873                 sel = this.getRange(this.getSelection(n));
1874                 sel.pasteHTML("<p>");
1875                 editor.event.cancelBubble = true;
1876                 editor.event.returnValue = false;
1877                 sel.select();
1878                 sel.moveEnd("character", 1);
1879                 sel.moveStart("character", 1);
1880                 sel.collapse(false);
1881                 return false;
1882             }
1883         }
1884     },
1885     
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){
1894     
1895         var sel = this.getSelection(n);
1896         var range = this.getRange(sel);
1897         var parentnode = this.getTag(range);
1898         var i = 0;
1899         
1900         for (var node = parentnode; (node && (node.nodeType == 1)); node = node.parentNode) {
1901             if (i == level) {
1902                 this.nodeSelection(n, node);
1903             }
1904             i++;
1905         }
1906         
1907         this.updateStatusBar(n);
1908     },
1909     
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){
1918     
1919         var doc = this.getEditorWindow(n).document;
1920         var sel = this.getSelection(n);
1921         var range = this.getRange(sel);
1922         
1923         if (!WYSIWYG_Core.isMSIE) {
1924             if (node.nodeName == "BODY") {
1925                 range.selectNodeContents(node);
1926             }
1927             else {
1928                 range.selectNode(node);
1929             }
1930             
1931             /*
1932              if (endNode) {
1933              try {
1934              range.setStart(node, startOffset);
1935              range.setEnd(endNode, endOffset);
1936              } catch(e) {
1937              }
1938              }
1939              */
1940             if (sel) {
1941                 sel.removeAllRanges();
1942             }
1943             if (sel) {
1944                 sel.addRange(range);
1945             }
1946         }
1947         else {
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")) {
1952                 try {
1953                     range = doc.body.createControlRange();
1954                     range.addElement(node);
1955                     range.select();
1956                 } 
1957                 catch (e) {
1958                 }
1959             }
1960             else {
1961                 range = doc.body.createTextRange();
1962                 if (range) {
1963                     range.collapse();
1964                     if (range.moveToElementText) {
1965                         try {
1966                             range.moveToElementText(node);
1967                             range.select();
1968                         } 
1969                         catch (e) {
1970                             try {
1971                                 range = doc.body.createTextRange();
1972                                 range.moveToElementText(node);
1973                                 range.select();
1974                             } 
1975                             catch (e) {
1976                             }
1977                         }
1978                     }
1979                     else {
1980                         try {
1981                             range = doc.body.createTextRange();
1982                             range.moveToElementText(node);
1983                             range.select();
1984                         } 
1985                         catch (e) {
1986                         }
1987                     }
1988                 }
1989             }
1990         }
1991     }
1992 }
1993
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.
1998  *
1999  * $Id: wysiwyg.js,v 1.22 2007/09/08 21:45:57 xhaggi Exp $
2000  ********************************************************************/
2001 var WYSIWYG_Core = {
2002
2003     /**
2004      * Holds true if browser is MSIE, otherwise false
2005      */
2006     isMSIE: navigator.appName == "Microsoft Internet Explorer" ? true : false,
2007     
2008     /**
2009      * Holds true if browser is Firefox (Mozilla)
2010      */
2011     isFF: !document.all && document.getElementById && !this.isOpera,
2012     
2013     /**
2014      * Holds true if browser is Opera, otherwise false
2015      */
2016     isOpera: navigator.appName == "Opera" ? true : false,
2017     
2018     /**
2019      * Trims whitespaces of the given string
2020      *
2021      * @param str String
2022      * @return Trimmed string
2023      */
2024     trim: function(str){
2025         return str.replace(/^\s*|\s*$/g, "");
2026     },
2027     
2028     /**
2029      * Determine if the given parameter is defined
2030      *
2031      * @param p Parameter
2032      * @return true/false dependents on definition of the parameter
2033      */
2034     defined: function(p){
2035         return typeof p == "undefined" ? false : true;
2036     },
2037     
2038     /**
2039      * Determine if the browser version is compatible
2040      *
2041      * @return true/false depending on compatiblity of the browser
2042      */
2043     isBrowserCompatible: function(){
2044         //Validate browser and compatibility
2045         if (!document.getElementById || !document.designMode || (!document.selection && typeof document.createRange == 'undefined')) {
2046             return false;
2047         }
2048         return true;
2049     },
2050     
2051     /**
2052      * Set the style attribute of the given element.
2053      * Private method to solve the IE bug while setting the style attribute.
2054      *
2055      * @param {DOMElement} node The element on which the style attribute will affect
2056      * @param {String} style Stylesheet which will be set
2057      */
2058     _setStyleAttribute: function(node, style){
2059         if (style == null) 
2060             return;
2061         var styles = style.split(";");
2062         var pos;
2063         for (var i = 0; i < styles.length; i++) {
2064             var attributes = styles[i].split(":");
2065             if (attributes.length == 2) {
2066                 try {
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;
2073                     }
2074                     var value = WYSIWYG_Core.trim(attributes[1]).toLowerCase();
2075                     node.style[attr] = value;
2076                 } 
2077                 catch (e) {
2078                     alert(e);
2079                 }
2080             }
2081         }
2082     },
2083     
2084     /**
2085      * Fix's the issue while getting the attribute style on IE
2086      * It's return an object but we need the style string
2087      *
2088      * @private
2089      * @param {DOMElement} node Node element
2090      * @return {String} Stylesheet
2091      */
2092     _getStyleAttribute: function(node){
2093         if (this.isMSIE) {
2094             return node.style['cssText'].toLowerCase();
2095         }
2096         else {
2097             return node.getAttribute("style");
2098         }
2099     },
2100     
2101     /**
2102      * Set an attribute's value on the given node element.
2103      *
2104      * @param {DOMElement} node Node element
2105      * @param {String} attr Attribute which is set
2106      * @param {String} value Value of the attribute
2107      */
2108     setAttribute: function(node, attr, value){
2109         if (value == null || node == null || attr == null) 
2110             return;
2111         if (attr.toLowerCase() == "style") {
2112             this._setStyleAttribute(node, value);
2113         }
2114         else {
2115             node.setAttribute(attr, value);
2116         }
2117     },
2118     
2119     /**
2120      * Removes an attribute on the given node
2121      *
2122      * @param {DOMElement} node Node element
2123      * @param {String} attr Attribute which will be removed
2124      */
2125     removeAttribute: function(node, attr){
2126         node.removeAttribute(attr, false);
2127     },
2128     
2129     /**
2130      * Get the vale of the attribute on the given node
2131      *
2132      * @param {DOMElement} node Node element
2133      * @param {String} attr Attribute which value will be returned
2134      */
2135     getAttribute: function(node, attr){
2136         if (node == null || attr == null) 
2137             return;
2138         if (attr.toLowerCase() == "style") {
2139             return this._getStyleAttribute(node);
2140         }
2141         else {
2142             return node.getAttribute(attr);
2143         }
2144     },
2145     
2146     /**
2147      * Get the path out of an given url
2148      *
2149      * @param {String} url The url with is used to get the path
2150      */
2151     getDocumentPathOfUrl: function(url){
2152         var path = null;
2153         
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("/");
2158         if (pos != -1) {
2159             path = url.substring(0, pos + 1);
2160         }
2161         return path;
2162     },
2163     
2164     /**
2165      * Get the documents url, convert local urls to web urls
2166      *
2167      * @param {DOMElement} doc Document which is used to get the url
2168      */
2169     getDocumentUrl: function(doc){
2170         // if local file system, convert local url into web url
2171         var url = doc.URL;
2172         url = url.replace(/file:\/\//gi, "file:///");
2173         url = url.replace(/\\/gi, "\/");
2174         return url;
2175     },
2176     
2177     /**
2178      * Find a parent node with the given name, of the given start node
2179      *
2180      * @param {String} tagName - Tag name of the node to find
2181      * @param {DOMElement} node - Node element
2182      */
2183     findParentNode: function(tagName, node){
2184         while (node.tagName != "HTML") {
2185             if (node.tagName == tagName) {
2186                 return node;
2187             }
2188             node = node.parentNode;
2189         }
2190         return null;
2191     },
2192     
2193     /**
2194      * Cancel the given event.
2195      *
2196      * @param e Event which will be canceled
2197      */
2198     cancelEvent: function(e){
2199         if (!e) 
2200             return false;
2201         if (this.isMSIE) {
2202             e.returnValue = false;
2203             e.cancelBubble = true;
2204         }
2205         else {
2206             e.preventDefault();
2207             e.stopPropagation && e.stopPropagation();
2208         }
2209         return false;
2210     },
2211     
2212     /**
2213      * Converts a RGB color string to hex color string.
2214      *
2215      * @param color RGB color string
2216      * @param Hex color string
2217      */
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();
2227         if (r.length < 2) {
2228             r = '0' + r;
2229         }
2230         if (g.length < 2) {
2231             g = '0' + g;
2232         }
2233         if (b.length < 2) {
2234             b = '0' + b;
2235         }
2236         return r + g + b;
2237     },
2238     
2239     /**
2240      * Converts a decimal color to hex color string.
2241      *
2242      * @param Decimal color
2243      * @param Hex color string
2244      */
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
2254         }
2255         return hex_string.toUpperCase();
2256     },
2257     
2258     /**
2259      * Replace RGB color strings with hex color strings within a string.
2260      *
2261      * @param {String} str RGB String
2262      * @param {String} Hex color string
2263      */
2264     replaceRGBWithHexColor: function(str){
2265         if (str == null) 
2266             return "";
2267         // find all decimal color strings
2268         var matcher = str.match(/rgb\([0-9 ]+,[0-9 ]+,[0-9 ]+\)/gi);
2269         if (matcher) {
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]));
2274             }
2275         }
2276         return str;
2277     },
2278     
2279     /**
2280      * Execute the given command on the given editor
2281      *
2282      * @param n The editor's identifier
2283      * @param cmd Command which is execute
2284      */
2285     execCommand: function(n, cmd, value){
2286         if (typeof(value) == "undefined") 
2287             value = null;
2288         
2289         // firefox BackColor problem fixed
2290         if (cmd == 'BackColor' && WYSIWYG_Core.isFF) 
2291             cmd = 'HiliteColor';
2292         
2293         // firefox cut, paste and copy
2294         if (WYSIWYG_Core.isFF && (cmd == "Cut" || cmd == "Paste" || cmd == "Copy")) {
2295             try {
2296                 WYSIWYG.getEditorWindow(n).document.execCommand(cmd, false, value);
2297             } 
2298             catch (e) {
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');
2301                 }
2302             }
2303         }
2304         
2305         else {
2306             WYSIWYG.getEditorWindow(n).document.execCommand(cmd, false, value);
2307         }
2308     },
2309     
2310     /**
2311      * Parse a given string to a valid regular expression
2312      *
2313      * @param {String} string String to be parsed
2314      * @return {RegEx} Valid regular expression
2315      */
2316     stringToRegex: function(string){
2317     
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");
2342         
2343         return string;
2344     },
2345     
2346     /**
2347      * Add an event listener
2348      *
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
2352      */
2353     addEvent: function(obj, ev, fu){
2354         if (obj.attachEvent) 
2355             obj.attachEvent("on" + ev, fu);
2356         else 
2357             obj.addEventListener(ev, fu, false);
2358     },
2359     
2360     /**
2361      * Remove an event listener
2362      *
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
2366      */
2367     removeEvent: function(obj, ev, fu){
2368         if (obj.attachEvent) 
2369             obj.detachEvent("on" + ev, fu);
2370         else 
2371             obj.removeEventListener(ev, fu, false);
2372     },
2373     
2374     /**
2375      * Includes a javascript file
2376      *
2377      * @param file Javascript file path and name
2378      */
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);
2386         }
2387     },
2388     
2389     /**
2390      * Includes a stylesheet file
2391      *
2392      * @param file Stylesheet file path and name
2393      */
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);
2402         }
2403     },
2404     
2405     /**
2406      * Get the screen position of the given element.
2407      *
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
2411      */
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;
2418         }
2419         return {
2420             left: left,
2421             top: top
2422         };
2423     },
2424     
2425     /**
2426      * Get the window size
2427      * @private
2428      */
2429     windowSize: function(){
2430         if (window.innerWidth) {
2431             return {
2432                 width: window.innerWidth,
2433                 height: window.innerHeight
2434             };
2435         }
2436         else 
2437             if (document.body && document.body.offsetWidth) {
2438                 return {
2439                     width: document.body.offsetWidth,
2440                     height: document.body.offsetHeight
2441                 };
2442             }
2443             else {
2444                 return {
2445                     width: 0,
2446                     height: 0
2447                 };
2448             }
2449     }
2450 }
2451
2452 /**
2453  * Context menu object
2454  */
2455 var WYSIWYG_ContextMenu = {
2456
2457     html: "",
2458     contextMenuDiv: null,
2459     
2460     /**
2461      * Init function
2462      *
2463      * @param {String} n Editor identifier
2464      */
2465     init: function(n){
2466         var doc = WYSIWYG.getEditorWindow(n).document;
2467         
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);
2479         
2480         // bind event listeners
2481         WYSIWYG_Core.addEvent(doc, "contextmenu", function context(e){
2482             WYSIWYG_ContextMenu.show(e, n);
2483         });
2484         WYSIWYG_Core.addEvent(doc, "click", function context(e){
2485             WYSIWYG_ContextMenu.close();
2486         });
2487         WYSIWYG_Core.addEvent(doc, "keydown", function context(e){
2488             WYSIWYG_ContextMenu.close();
2489         });
2490         WYSIWYG_Core.addEvent(document, "click", function context(e){
2491             WYSIWYG_ContextMenu.close();
2492         });
2493     },
2494     
2495     /**
2496      * Show the context menu
2497      *
2498      * @param e Event
2499      * @param n Editor identifier
2500      */
2501     show: function(e, n){
2502         if (this.contextMenuDiv == null) 
2503             return false;
2504         
2505         var ifrm = WYSIWYG.getEditor(n);
2506         var doc = WYSIWYG.getEditorWindow(n).document;
2507         
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);
2512         
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";
2517         
2518         // call the context menu, mozilla needs some time
2519         window.setTimeout("WYSIWYG_ContextMenu.output('" + n + "')", 10);
2520         
2521         WYSIWYG_Core.cancelEvent(e);
2522         return false;
2523     },
2524     
2525     /**
2526      * Output the context menu items
2527      *
2528      * @param n Editor identifier
2529      */
2530     output: function(n){
2531     
2532         // get selection
2533         var sel = WYSIWYG.getSelection(n);
2534         var range = WYSIWYG.getRange(sel);
2535         
2536         // get current selected node                                    
2537         var tag = WYSIWYG.getTag(range);
2538         if (tag == null) {
2539             return;
2540         }
2541         
2542         // clear context menu
2543         this.clear();
2544         
2545         // Determine kind of nodes
2546         var isImg = (tag.nodeName == "IMG") ? true : false;
2547         var isLink = (tag.nodeName == "A") ? true : false;
2548         
2549         // Selection is an image or selection is a text with length greater 0
2550         var len = 0;
2551         if (WYSIWYG_Core.isMSIE) 
2552             len = (document.selection && range.text) ? range.text.length : 0;
2553         else 
2554             len = range.toString().length;
2555         var sel = len != 0 || isImg;
2556         
2557         // Icons
2558         var iconLink = {
2559             enabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["createlink"][3],
2560             disabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["createlink"][2]
2561         };
2562         var iconImage = {
2563             enabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["insertimage"][3],
2564             disabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["insertimage"][2]
2565         };
2566         var iconDelete = {
2567             enabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["delete"][3],
2568             disabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["delete"][2]
2569         };
2570         var iconCopy = {
2571             enabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["copy"][3],
2572             disabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["copy"][2]
2573         };
2574         var iconCut = {
2575             enabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["cut"][3],
2576             disabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["cut"][2]
2577         };
2578         var iconPaste = {
2579             enabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["paste"][3],
2580             disabled: WYSIWYG.config[n].ImagesDir + WYSIWYG.ToolbarList["paste"][2]
2581         };
2582         
2583         // Create context menu html
2584         this.html += '<table class="wysiwyg-context-menu" border="0" cellpadding="0" cellspacing="0">';
2585         
2586         // Add items
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);
2594         
2595         this.html += '</table>';
2596         this.contextMenuDiv.innerHTML = this.html;
2597     },
2598     
2599     /**
2600      * Close the context menu
2601      */
2602     close: function(){
2603         this.contextMenuDiv.style.visibility = "hidden";
2604         this.contextMenuDiv.style.display = "none";
2605     },
2606     
2607     /**
2608      * Clear context menu
2609      */
2610     clear: function(){
2611         this.contextMenuDiv.innerHTML = "";
2612         this.html = "";
2613     },
2614     
2615     /**
2616      * Add context menu item
2617      *
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
2623      */
2624     addItem: function(n, cmd, icon, title, disabled){
2625         var item = '';
2626         
2627         if (disabled) {
2628             item += '<tr>';
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>';
2631             item += '</tr>';
2632         }
2633         else {
2634             item += '<tr>';
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>';
2637             item += '</tr>';
2638         }
2639         
2640         this.html += item;
2641     },
2642     
2643     /**
2644      * Add seperator to context menu
2645      */
2646     addSeperator: function(){
2647         var output = '';
2648         output += '<tr>';
2649         output += '<td colspan="2" style="text-align:center;"><hr size="1" color="#C9C9C9" width="95%"></td>';
2650         output += '</tr>';
2651         this.html += output;
2652     }
2653 }
2654
2655 /**
2656  * Table object
2657  */
2658 var WYSIWYG_Table = {
2659
2660     /**
2661      *
2662      */
2663     create: function(n, tbl){
2664     
2665         // get editor
2666         var doc = WYSIWYG.getEditorWindow(n).document;
2667         // get selection and range
2668         var sel = WYSIWYG.getSelection(n);
2669         var range = WYSIWYG.getRange(sel);
2670         var table = null;
2671         
2672         // get element from selection
2673         if (WYSIWYG_Core.isMSIE) {
2674             if (sel.type == "Control" && range.length == 1) {
2675                 range = WYSIWYG.getTextRange(range(0));
2676                 range.select();
2677             }
2678         }
2679         
2680         // find a parent TABLE element
2681         //table = WYSIWYG.findParent("table", range);
2682         
2683         // check if parent is found
2684         //var update = (table == null) ? false : true;
2685         //if(!update) table = tbl;
2686         table = tbl;
2687         
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();
2697                 tr.appendChild(td);
2698             }
2699             table.appendChild(tr);
2700         }
2701         
2702         // on update exit here
2703         //if(update) { return; }
2704         
2705         // Check if IE or Mozilla (other)
2706         if (WYSIWYG_Core.isMSIE) {
2707             range.pasteHTML(table.outerHTML);
2708         }
2709         else {
2710             WYSIWYG.insertNodeAtSelection(table, n);
2711         }
2712         
2713         // refresh table highlighting
2714         this.refreshHighlighting(n);
2715         
2716         
2717         
2718         
2719         // functions
2720         function createTD(){
2721             var td = doc.createElement("td");
2722             td.innerHTML = "&nbsp;";
2723             return td;
2724         }
2725         
2726     },
2727     
2728     /**
2729      * Enables the table highlighting
2730      *
2731      * @param {String} n The editor identifier (the textarea's ID)
2732      */
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]);
2738         }
2739         var tds = doc.getElementsByTagName("td");
2740         for (var i = 0; i < tds.length; i++) {
2741             this._enableHighlighting(tds[i]);
2742         }
2743     },
2744     
2745     /**
2746      * Enables the table highlighting
2747      *
2748      * @param {String} n The editor identifier (the textarea's ID)
2749      */
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]);
2755         }
2756         var tds = doc.getElementsByTagName("td");
2757         for (var i = 0; i < tds.length; i++) {
2758             this._disableHighlighting(tds[i]);
2759         }
2760         
2761     },
2762     
2763     /**
2764      * @private
2765      */
2766     _enableHighlighting: function(node){
2767         var style = WYSIWYG_Core.getAttribute(node, "style");
2768         if (style == null) 
2769             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;");
2774     },
2775     
2776     /**
2777      * @private
2778      */
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);
2785             return;
2786         }
2787         WYSIWYG_Core.removeAttribute(node, "prevstyle");
2788         WYSIWYG_Core.removeAttribute(node, "style");
2789         WYSIWYG_Core.setAttribute(node, "style", style);
2790     }
2791 }
2792
2793
2794 /**
2795  * Get an element by it's identifier
2796  *
2797  * @param id Element identifier
2798  */
2799 function $(id){
2800     return document.getElementById(id);
2801 }
2802
2803 /**
2804  * Emulates insertAdjacentHTML(), insertAdjacentText() and
2805  * insertAdjacentElement() three functions so they work with Netscape 6/Mozilla
2806  * by Thor Larholm me@jscript.dk
2807  */
2808 if (typeof HTMLElement != "undefined" && !HTMLElement.prototype.insertAdjacentElement) {
2809     HTMLElement.prototype.insertAdjacentElement = function(where, parsedNode){
2810         switch (where) {
2811             case 'beforeBegin':
2812                 this.parentNode.insertBefore(parsedNode, this);
2813                 break;
2814             case 'afterBegin':
2815                 this.insertBefore(parsedNode, this.firstChild);
2816                 break;
2817             case 'beforeEnd':
2818                 this.appendChild(parsedNode);
2819                 break;
2820             case 'afterEnd':
2821                 if (this.nextSibling) {
2822                     this.parentNode.insertBefore(parsedNode, this.nextSibling);
2823                 }
2824                 else {
2825                     this.parentNode.appendChild(parsedNode);
2826                 }
2827                 break;
2828         }
2829     };
2830     
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);
2836     };
2837     
2838     
2839     HTMLElement.prototype.insertAdjacentText = function(where, txtStr){
2840         var parsedText = document.createTextNode(txtStr);
2841         this.insertAdjacentElement(where, parsedText);
2842     };
2843 }
2844
2845 /**
2846  * Emulates insertAdjacentHTML(), insertAdjacentText() and
2847  * insertAdjacentElement() three functions so they work with Netscape 6/Mozilla/Safari
2848  * by Thor Larholm me@jscript.dk
2849  *
2850  * Modified:06112008 By CJBoCo www.cjboco.com
2851  * -Instead of testing for browser type,we test if the object is undefined
2852  */
2853 if (typeof HTMLElement != 'undefined') {
2854     if (typeof HTMLElement.insertAdjacentHTML == 'undefined') {
2855         HTMLElement.prototype.insertAdjacentElement = function(where, parsedNode){
2856             switch (where) {
2857                 case 'beforeBegin':
2858                     this.parentNode.insertBefore(parsedNode, this);
2859                     break;
2860                 case 'afterBegin':
2861                     this.insertBefore(parsedNode, this.firstChild);
2862                     break;
2863                 case 'beforeEnd':
2864                     this.appendChild(parsedNode);
2865                     break;
2866                 case 'afterEnd':
2867                     if (this.nextSibling) {
2868                         this.parentNode.insertBefore(parsedNode, this.nextSibling);
2869                     }
2870                     else {
2871                         this.parentNode.appendChild(parsedNode);
2872                     }
2873                     break;
2874             }
2875         };
2876     }
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);
2883         };
2884     }
2885     if (typeof HTMLElement.insertAdjacentText == 'undefined') {
2886         HTMLElement.prototype.insertAdjacentText = function(where, txtStr){
2887             var parsedText = document.createTextNode(txtStr);
2888             this.insertAdjacentElement(where, parsedText);
2889         };
2890     }
2891 }
2892