thomascube
2006-09-24 3381d45ef674884897efddb1c87a0aa2b777214f
commit | author | age
a0109c 1
S 2 /* file:jscripts/tiny_mce/classes/TinyMCE_Engine.class.js */
3
4 function TinyMCE_Engine() {
5     this.majorVersion = "2";
6     this.minorVersion = "0.6.1";
7     this.releaseDate = "2006-05-04";
8
9     this.instances = new Array();
10     this.switchClassCache = new Array();
11     this.windowArgs = new Array();
12     this.loadedFiles = new Array();
13     this.pendingFiles = new Array();
14     this.loadingIndex = 0;
15     this.configs = new Array();
16     this.currentConfig = 0;
17     this.eventHandlers = new Array();
18
19     // Browser check
20     var ua = navigator.userAgent;
21     this.isMSIE = (navigator.appName == "Microsoft Internet Explorer");
22     this.isMSIE5 = this.isMSIE && (ua.indexOf('MSIE 5') != -1);
23     this.isMSIE5_0 = this.isMSIE && (ua.indexOf('MSIE 5.0') != -1);
24     this.isGecko = ua.indexOf('Gecko') != -1;
25     this.isSafari = ua.indexOf('Safari') != -1;
26     this.isOpera = ua.indexOf('Opera') != -1;
27     this.isMac = ua.indexOf('Mac') != -1;
28     this.isNS7 = ua.indexOf('Netscape/7') != -1;
29     this.isNS71 = ua.indexOf('Netscape/7.1') != -1;
30     this.dialogCounter = 0;
31     this.plugins = new Array();
32     this.themes = new Array();
33     this.menus = new Array();
34     this.loadedPlugins = new Array();
35     this.buttonMap = new Array();
36     this.isLoaded = false;
37
38     // Fake MSIE on Opera and if Opera fakes IE, Gecko or Safari cancel those
39     if (this.isOpera) {
40         this.isMSIE = true;
41         this.isGecko = false;
42         this.isSafari =  false;
43     }
44
45     // TinyMCE editor id instance counter
46     this.idCounter = 0;
47 };
48
49 TinyMCE_Engine.prototype = {
50     init : function(settings) {
51         var theme;
52
53         this.settings = settings;
54
55         // Check if valid browser has execcommand support
56         if (typeof(document.execCommand) == 'undefined')
57             return;
58
59         // Get script base path
60         if (!tinyMCE.baseURL) {
61             var elements = document.getElementsByTagName('script');
62
63             for (var i=0; i<elements.length; i++) {
64                 if (elements[i].src && (elements[i].src.indexOf("tiny_mce.js") != -1 || elements[i].src.indexOf("tiny_mce_dev.js") != -1 || elements[i].src.indexOf("tiny_mce_src.js") != -1 || elements[i].src.indexOf("tiny_mce_gzip") != -1)) {
65                     var src = elements[i].src;
66
67                     tinyMCE.srcMode = (src.indexOf('_src') != -1 || src.indexOf('_dev') != -1) ? '_src' : '';
68                     tinyMCE.gzipMode = src.indexOf('_gzip') != -1;
69                     src = src.substring(0, src.lastIndexOf('/'));
70
71                     if (settings.exec_mode == "src" || settings.exec_mode == "normal")
72                         tinyMCE.srcMode = settings.exec_mode == "src" ? '_src' : '';
73
74                     tinyMCE.baseURL = src;
75                     break;
76                 }
77             }
78         }
79
80         // Get document base path
81         this.documentBasePath = document.location.href;
82         if (this.documentBasePath.indexOf('?') != -1)
83             this.documentBasePath = this.documentBasePath.substring(0, this.documentBasePath.indexOf('?'));
84         this.documentURL = this.documentBasePath;
85         this.documentBasePath = this.documentBasePath.substring(0, this.documentBasePath.lastIndexOf('/'));
86
87         // If not HTTP absolute
88         if (tinyMCE.baseURL.indexOf('://') == -1 && tinyMCE.baseURL.charAt(0) != '/') {
89             // If site absolute
90             tinyMCE.baseURL = this.documentBasePath + "/" + tinyMCE.baseURL;
91         }
92
93         // Set default values on settings
94         this._def("mode", "none");
95         this._def("theme", "advanced");
96         this._def("plugins", "", true);
97         this._def("language", "en");
98         this._def("docs_language", this.settings['language']);
99         this._def("elements", "");
100         this._def("textarea_trigger", "mce_editable");
101         this._def("editor_selector", "");
102         this._def("editor_deselector", "mceNoEditor");
103         this._def("valid_elements", "+a[id|style|rel|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],-strike[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|style],-ul[class|style],-li[class|style],br,img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align],-sub[style|class],-sup[style|class],-blockquote[dir|style],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],thead[id|class],tfoot[id|class],-td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],-span[style|class|align],-pre[class|align|style],address[class|align|style],-h1[id|style|dir|class|align],-h2[id|style|dir|class|align],-h3[id|style|dir|class|align],-h4[id|style|dir|class|align],-h5[id|style|dir|class|align],-h6[id|style|dir|class|align],hr[class|style],-font[face|size|style|id|class|dir|color],dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang]");
104         this._def("extended_valid_elements", "");
105         this._def("invalid_elements", "");
106         this._def("encoding", "");
107         this._def("urlconverter_callback", tinyMCE.getParam("urlconvertor_callback", "TinyMCE_Engine.prototype.convertURL"));
108         this._def("save_callback", "");
109         this._def("debug", false);
110         this._def("force_br_newlines", false);
111         this._def("force_p_newlines", true);
112         this._def("add_form_submit_trigger", true);
113         this._def("relative_urls", true);
114         this._def("remove_script_host", true);
115         this._def("focus_alert", true);
116         this._def("document_base_url", this.documentURL);
117         this._def("visual", true);
118         this._def("visual_table_class", "mceVisualAid");
119         this._def("setupcontent_callback", "");
120         this._def("fix_content_duplication", true);
121         this._def("custom_undo_redo", true);
122         this._def("custom_undo_redo_levels", -1);
123         this._def("custom_undo_redo_keyboard_shortcuts", true);
124         this._def("custom_undo_redo_restore_selection", true);
125         this._def("verify_html", true);
126         this._def("apply_source_formatting", false);
127         this._def("directionality", "ltr");
128         this._def("cleanup_on_startup", false);
129         this._def("inline_styles", false);
130         this._def("convert_newlines_to_brs", false);
131         this._def("auto_reset_designmode", true);
132         this._def("entities", "39,#39,160,nbsp,161,iexcl,162,cent,163,pound,164,curren,165,yen,166,brvbar,167,sect,168,uml,169,copy,170,ordf,171,laquo,172,not,173,shy,174,reg,175,macr,176,deg,177,plusmn,178,sup2,179,sup3,180,acute,181,micro,182,para,183,middot,184,cedil,185,sup1,186,ordm,187,raquo,188,frac14,189,frac12,190,frac34,191,iquest,192,Agrave,193,Aacute,194,Acirc,195,Atilde,196,Auml,197,Aring,198,AElig,199,Ccedil,200,Egrave,201,Eacute,202,Ecirc,203,Euml,204,Igrave,205,Iacute,206,Icirc,207,Iuml,208,ETH,209,Ntilde,210,Ograve,211,Oacute,212,Ocirc,213,Otilde,214,Ouml,215,times,216,Oslash,217,Ugrave,218,Uacute,219,Ucirc,220,Uuml,221,Yacute,222,THORN,223,szlig,224,agrave,225,aacute,226,acirc,227,atilde,228,auml,229,aring,230,aelig,231,ccedil,232,egrave,233,eacute,234,ecirc,235,euml,236,igrave,237,iacute,238,icirc,239,iuml,240,eth,241,ntilde,242,ograve,243,oacute,244,ocirc,245,otilde,246,ouml,247,divide,248,oslash,249,ugrave,250,uacute,251,ucirc,252,uuml,253,yacute,254,thorn,255,yuml,402,fnof,913,Alpha,914,Beta,915,Gamma,916,Delta,917,Epsilon,918,Zeta,919,Eta,920,Theta,921,Iota,922,Kappa,923,Lambda,924,Mu,925,Nu,926,Xi,927,Omicron,928,Pi,929,Rho,931,Sigma,932,Tau,933,Upsilon,934,Phi,935,Chi,936,Psi,937,Omega,945,alpha,946,beta,947,gamma,948,delta,949,epsilon,950,zeta,951,eta,952,theta,953,iota,954,kappa,955,lambda,956,mu,957,nu,958,xi,959,omicron,960,pi,961,rho,962,sigmaf,963,sigma,964,tau,965,upsilon,966,phi,967,chi,968,psi,969,omega,977,thetasym,978,upsih,982,piv,8226,bull,8230,hellip,8242,prime,8243,Prime,8254,oline,8260,frasl,8472,weierp,8465,image,8476,real,8482,trade,8501,alefsym,8592,larr,8593,uarr,8594,rarr,8595,darr,8596,harr,8629,crarr,8656,lArr,8657,uArr,8658,rArr,8659,dArr,8660,hArr,8704,forall,8706,part,8707,exist,8709,empty,8711,nabla,8712,isin,8713,notin,8715,ni,8719,prod,8721,sum,8722,minus,8727,lowast,8730,radic,8733,prop,8734,infin,8736,ang,8743,and,8744,or,8745,cap,8746,cup,8747,int,8756,there4,8764,sim,8773,cong,8776,asymp,8800,ne,8801,equiv,8804,le,8805,ge,8834,sub,8835,sup,8836,nsub,8838,sube,8839,supe,8853,oplus,8855,otimes,8869,perp,8901,sdot,8968,lceil,8969,rceil,8970,lfloor,8971,rfloor,9001,lang,9002,rang,9674,loz,9824,spades,9827,clubs,9829,hearts,9830,diams,34,quot,38,amp,60,lt,62,gt,338,OElig,339,oelig,352,Scaron,353,scaron,376,Yuml,710,circ,732,tilde,8194,ensp,8195,emsp,8201,thinsp,8204,zwnj,8205,zwj,8206,lrm,8207,rlm,8211,ndash,8212,mdash,8216,lsquo,8217,rsquo,8218,sbquo,8220,ldquo,8221,rdquo,8222,bdquo,8224,dagger,8225,Dagger,8240,permil,8249,lsaquo,8250,rsaquo,8364,euro", true);
133         this._def("entity_encoding", "named");
134         this._def("cleanup_callback", "");
135         this._def("add_unload_trigger", true);
136         this._def("ask", false);
137         this._def("nowrap", false);
138         this._def("auto_resize", false);
139         this._def("auto_focus", false);
140         this._def("cleanup", true);
141         this._def("remove_linebreaks", true);
142         this._def("button_tile_map", false);
143         this._def("submit_patch", true);
144         this._def("browsers", "msie,safari,gecko,opera", true);
145         this._def("dialog_type", "window");
146         this._def("accessibility_warnings", true);
147         this._def("accessibility_focus", true);
148         this._def("merge_styles_invalid_parents", "");
149         this._def("force_hex_style_colors", true);
150         this._def("trim_span_elements", true);
151         this._def("convert_fonts_to_spans", false);
152         this._def("doctype", '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">');
153         this._def("font_size_classes", '');
154         this._def("font_size_style_values", 'xx-small,x-small,small,medium,large,x-large,xx-large', true);
155         this._def("event_elements", 'a,img', true);
156         this._def("convert_urls", true);
157         this._def("table_inline_editing", false);
158         this._def("object_resizing", true);
159         this._def("custom_shortcuts", true);
160         this._def("convert_on_click", false);
161         this._def("content_css", '');
162         this._def("fix_list_elements", false);
163         this._def("fix_table_elements", false);
164         this._def("strict_loading_mode", document.contentType == 'application/xhtml+xml');
165         this._def("hidden_tab_class", '');
166         this._def("display_tab_class", '');
167
168         // Force strict loading mode to false on non Gecko browsers
169         if (this.isMSIE && !this.isOpera)
170             this.settings.strict_loading_mode = false;
171
172         // Browser check IE
173         if (this.isMSIE && this.settings['browsers'].indexOf('msie') == -1)
174             return;
175
176         // Browser check Gecko
177         if (this.isGecko && this.settings['browsers'].indexOf('gecko') == -1)
178             return;
179
180         // Browser check Safari
181         if (this.isSafari && this.settings['browsers'].indexOf('safari') == -1)
182             return;
183
184         // Browser check Opera
185         if (this.isOpera && this.settings['browsers'].indexOf('opera') == -1)
186             return;
187
188         // If not super absolute make it so
189         var baseHREF = tinyMCE.settings['document_base_url'];
190         var h = document.location.href;
191         var p = h.indexOf('://');
192         if (p > 0 && document.location.protocol != "file:") {
193             p = h.indexOf('/', p + 3);
194             h = h.substring(0, p);
195
196             if (baseHREF.indexOf('://') == -1)
197                 baseHREF = h + baseHREF;
198
199             tinyMCE.settings['document_base_url'] = baseHREF;
200             tinyMCE.settings['document_base_prefix'] = h;
201         }
202
203         // Trim away query part
204         if (baseHREF.indexOf('?') != -1)
205             baseHREF = baseHREF.substring(0, baseHREF.indexOf('?'));
206
207         this.settings['base_href'] = baseHREF.substring(0, baseHREF.lastIndexOf('/')) + "/";
208
209         theme = this.settings['theme'];
210         this.blockRegExp = new RegExp("^(h[1-6]|p|div|address|pre|form|table|li|ol|ul|td|blockquote|center|dl|dt|dd|dir|fieldset|form|noscript|noframes|menu|isindex|samp)$", "i");
211         this.posKeyCodes = new Array(13,45,36,35,33,34,37,38,39,40);
212         this.uniqueURL = 'javascript:TINYMCE_UNIQUEURL();'; // Make unique URL non real URL
213         this.uniqueTag = '<div id="mceTMPElement" style="display: none">TMP</div>';
214         this.callbacks = new Array('onInit', 'getInfo', 'getEditorTemplate', 'setupContent', 'onChange', 'onPageLoad', 'handleNodeChange', 'initInstance', 'execCommand', 'getControlHTML', 'handleEvent', 'cleanup');
215
216         // Theme url
217         this.settings['theme_href'] = tinyMCE.baseURL + "/themes/" + theme;
218
219         if (!tinyMCE.isMSIE)
220             this.settings['force_br_newlines'] = false;
221
222         if (tinyMCE.getParam("popups_css", false)) {
223             var cssPath = tinyMCE.getParam("popups_css", "");
224
225             // Is relative
226             if (cssPath.indexOf('://') == -1 && cssPath.charAt(0) != '/')
227                 this.settings['popups_css'] = this.documentBasePath + "/" + cssPath;
228             else
229                 this.settings['popups_css'] = cssPath;
230         } else
231             this.settings['popups_css'] = tinyMCE.baseURL + "/themes/" + theme + "/css/editor_popup.css";
232
233         if (tinyMCE.getParam("editor_css", false)) {
234             var cssPath = tinyMCE.getParam("editor_css", "");
235
236             // Is relative
237             if (cssPath.indexOf('://') == -1 && cssPath.charAt(0) != '/')
238                 this.settings['editor_css'] = this.documentBasePath + "/" + cssPath;
239             else
240                 this.settings['editor_css'] = cssPath;
241         } else
242             this.settings['editor_css'] = tinyMCE.baseURL + "/themes/" + theme + "/css/editor_ui.css";
243
244         if (tinyMCE.settings['debug']) {
245             var msg = "Debug: \n";
246
247             msg += "baseURL: " + this.baseURL + "\n";
248             msg += "documentBasePath: " + this.documentBasePath + "\n";
249             msg += "content_css: " + this.settings['content_css'] + "\n";
250             msg += "popups_css: " + this.settings['popups_css'] + "\n";
251             msg += "editor_css: " + this.settings['editor_css'] + "\n";
252
253             alert(msg);
254         }
255
256         // Only do this once
257         if (this.configs.length == 0) {
258             // Is Safari enabled
259             if (this.isSafari && this.getParam('safari_warning', false))
260                 alert("Safari support is very limited and should be considered experimental.\nSo there is no need to even submit bugreports on this early version.\nYou can disable this message by setting: safari_warning option to false");
261
262             if (typeof(TinyMCECompressed) == "undefined") {
263                 tinyMCE.addEvent(window, "DOMContentLoaded", TinyMCE_Engine.prototype.onLoad);
264
265                 if (tinyMCE.isMSIE && !tinyMCE.isOpera) {
266                     if (document.body)
267                         tinyMCE.addEvent(document.body, "readystatechange", TinyMCE_Engine.prototype.onLoad);
268                     else
269                         tinyMCE.addEvent(document, "readystatechange", TinyMCE_Engine.prototype.onLoad);
270                 }
271
272                 tinyMCE.addEvent(window, "load", TinyMCE_Engine.prototype.onLoad);
273                 tinyMCE._addUnloadEvents();
274             }
275         }
276
277         this.loadScript(tinyMCE.baseURL + '/themes/' + this.settings['theme'] + '/editor_template' + tinyMCE.srcMode + '.js');
278         this.loadScript(tinyMCE.baseURL + '/langs/' + this.settings['language'] +  '.js');
279         this.loadCSS(this.settings['editor_css']);
280
281         // Add plugins
282         var p = tinyMCE.getParam('plugins', '', true, ',');
283         if (p.length > 0) {
284             for (var i=0; i<p.length; i++) {
285                 if (p[i].charAt(0) != '-')
286                     this.loadScript(tinyMCE.baseURL + '/plugins/' + p[i] + '/editor_plugin' + tinyMCE.srcMode + '.js');
287             }
288         }
289
290         // Setup entities
291         settings['cleanup_entities'] = new Array();
292         var entities = tinyMCE.getParam('entities', '', true, ',');
293         for (var i=0; i<entities.length; i+=2)
294             settings['cleanup_entities']['c' + entities[i]] = entities[i+1];
295
296         // Save away this config
297         settings['index'] = this.configs.length;
298         this.configs[this.configs.length] = settings;
299
300         // Start loading first one in chain
301         this.loadNextScript();
302     },
303
304     _addUnloadEvents : function() {
305         if (tinyMCE.isMSIE) {
306             if (tinyMCE.settings['add_unload_trigger']) {
307                 tinyMCE.addEvent(window, "unload", TinyMCE_Engine.prototype.unloadHandler);
308                 tinyMCE.addEvent(window.document, "beforeunload", TinyMCE_Engine.prototype.unloadHandler);
309             }
310         } else {
311             if (tinyMCE.settings['add_unload_trigger'])
312                 tinyMCE.addEvent(window, "unload", function () {tinyMCE.triggerSave(true, true);});
313         }
314     },
315
316     _def : function(key, def_val, t) {
317         var v = tinyMCE.getParam(key, def_val);
318
319         v = t ? v.replace(/\s+/g,"") : v;
320
321         this.settings[key] = v;
322     },
323
324     hasPlugin : function(n) {
325         return typeof(this.plugins[n]) != "undefined" && this.plugins[n] != null;
326     },
327
328     addPlugin : function(n, p) {
329         var op = this.plugins[n];
330
331         // Use the previous plugin object base URL used when loading external plugins
332         p.baseURL = op ? op.baseURL : tinyMCE.baseURL + "/plugins/" + n;
333         this.plugins[n] = p;
334
335         this.loadNextScript();
336     },
337
338     setPluginBaseURL : function(n, u) {
339         var op = this.plugins[n];
340
341         if (op)
342             op.baseURL = u;
343         else
344             this.plugins[n] = {baseURL : u};
345     },
346
347     loadPlugin : function(n, u) {
348         u = u.indexOf('.js') != -1 ? u.substring(0, u.lastIndexOf('/')) : u;
349         u = u.charAt(u.length-1) == '/' ? u.substring(0, u.length-1) : u;
350         this.plugins[n] = {baseURL : u};
351         this.loadScript(u + "/editor_plugin" + (tinyMCE.srcMode ? '_src' : '') + ".js");
352     },
353
354     hasTheme : function(n) {
355         return typeof(this.themes[n]) != "undefined" && this.themes[n] != null;
356     },
357
358     addTheme : function(n, t) {
359         this.themes[n] = t;
360
361         this.loadNextScript();
362     },
363
364     addMenu : function(n, m) {
365         this.menus[n] = m;
366     },
367
368     hasMenu : function(n) {
369         return typeof(this.plugins[n]) != "undefined" && this.plugins[n] != null;
370     },
371
372     loadScript : function(url) {
373         var i;
374
375         for (i=0; i<this.loadedFiles.length; i++) {
376             if (this.loadedFiles[i] == url)
377                 return;
378         }
379
380         if (tinyMCE.settings.strict_loading_mode)
381             this.pendingFiles[this.pendingFiles.length] = url;
382         else
383             document.write('<sc'+'ript language="javascript" type="text/javascript" src="' + url + '"></script>');
384
385         this.loadedFiles[this.loadedFiles.length] = url;
386     },
387
388     loadNextScript : function() {
389         var d = document, se;
390
391         if (!tinyMCE.settings.strict_loading_mode)
392             return;
393
394         if (this.loadingIndex < this.pendingFiles.length) {
395             se = d.createElementNS('http://www.w3.org/1999/xhtml', 'script');
396             se.setAttribute('language', 'javascript');
397             se.setAttribute('type', 'text/javascript');
398             se.setAttribute('src', this.pendingFiles[this.loadingIndex++]);
399
400             d.getElementsByTagName("head")[0].appendChild(se);
401         } else
402             this.loadingIndex = -1; // Done with loading
403     },
404
405     loadCSS : function(url) {
406         var ar = url.replace(/\s+/, '').split(',');
407         var lflen = 0, csslen = 0;
408         var skip = false;
409         var x = 0, i = 0, nl, le;
410
411         for (x = 0,csslen = ar.length; x<csslen; x++) {
412             ignore_css = false;
413
414             if (ar[x] != null && ar[x] != 'null' && ar[x].length > 0) {
415                 /* Make sure it doesn't exist. */
416                 for (i=0, lflen=this.loadedFiles.length; i<lflen; i++) {
417                     if (this.loadedFiles[i] == ar[x]) {
418                         skip = true;
419                         break;
420                     }
421                 }
422
423                 if (!skip) {
424                     if (tinyMCE.settings.strict_loading_mode) {
425                         nl = document.getElementsByTagName("head");
426
427                         le = document.createElement('link');
428                         le.setAttribute('href', ar[x]);
429                         le.setAttribute('rel', 'stylesheet');
430                         le.setAttribute('type', 'text/css');
431
432                         nl[0].appendChild(le);            
433                     } else
434                         document.write('<link href="' + ar[x] + '" rel="stylesheet" type="text/css" />');
435
436                     this.loadedFiles[this.loadedFiles.length] = ar[x];
437                 }
438             }
439         }
440     },
441
442     importCSS : function(doc, css) {
443         var css_ary = css.replace(/\s+/, '').split(',');
444         var csslen, elm, headArr, x, css_file;
445
446         for (x = 0, csslen = css_ary.length; x<csslen; x++) {
447             css_file = css_ary[x];
448
449             if (css_file != null && css_file != 'null' && css_file.length > 0) {
450                 // Is relative, make absolute
451                 if (css_file.indexOf('://') == -1 && css_file.charAt(0) != '/')
452                     css_file = this.documentBasePath + "/" + css_file;
453
454                 if (typeof(doc.createStyleSheet) == "undefined") {
455                     elm = doc.createElement("link");
456
457                     elm.rel = "stylesheet";
458                     elm.href = css_file;
459
460                     if ((headArr = doc.getElementsByTagName("head")) != null && headArr.length > 0)
461                         headArr[0].appendChild(elm);
462                 } else
463                     doc.createStyleSheet(css_file);
464             }
465         }
466     },
467
468     confirmAdd : function(e, settings) {
469         var elm = tinyMCE.isMSIE ? event.srcElement : e.target;
470         var elementId = elm.name ? elm.name : elm.id;
471
472         tinyMCE.settings = settings;
473
474         if (tinyMCE.settings['convert_on_click'] || (!elm.getAttribute('mce_noask') && confirm(tinyMCELang['lang_edit_confirm'])))
475             tinyMCE.addMCEControl(elm, elementId);
476
477         elm.setAttribute('mce_noask', 'true');
478     },
479
480     updateContent : function(form_element_name) {
481         // Find MCE instance linked to given form element and copy it's value
482         var formElement = document.getElementById(form_element_name);
483         for (var n in tinyMCE.instances) {
484             var inst = tinyMCE.instances[n];
485             if (!tinyMCE.isInstance(inst))
486                 continue;
487
488             inst.switchSettings();
489
490             if (inst.formElement == formElement) {
491                 var doc = inst.getDoc();
492         
493                 tinyMCE._setHTML(doc, inst.formElement.value);
494
495                 if (!tinyMCE.isMSIE)
496                     doc.body.innerHTML = tinyMCE._cleanupHTML(inst, doc, this.settings, doc.body, inst.visualAid);
497             }
498         }
499     },
500
501     addMCEControl : function(replace_element, form_element_name, target_document) {
502         var id = "mce_editor_" + tinyMCE.idCounter++;
503         var inst = new TinyMCE_Control(tinyMCE.settings);
504
505         inst.editorId = id;
506         this.instances[id] = inst;
507
508         inst._onAdd(replace_element, form_element_name, target_document);
509     },
510
511     removeMCEControl : function(editor_id) {
512         var inst = tinyMCE.getInstanceById(editor_id);
513
514         if (inst) {
515             inst.switchSettings();
516
517             editor_id = inst.editorId;
518             var html = tinyMCE.getContent(editor_id);
519
520             // Remove editor instance from instances array
521             var tmpInstances = new Array();
522             for (var instanceName in tinyMCE.instances) {
523                 var instance = tinyMCE.instances[instanceName];
524                 if (!tinyMCE.isInstance(instance))
525                     continue;
526
527                 if (instanceName != editor_id)
528                         tmpInstances[instanceName] = instance;
529             }
530             tinyMCE.instances = tmpInstances;
531
532             tinyMCE.selectedElement = null;
533             tinyMCE.selectedInstance = null;
534
535             // Remove element
536             var replaceElement = document.getElementById(editor_id + "_parent");
537             var oldTargetElement = inst.oldTargetElement;
538             var targetName = oldTargetElement.nodeName.toLowerCase();
539
540             if (targetName == "textarea" || targetName == "input") {
541                 // Just show the old text area
542                 replaceElement.parentNode.removeChild(replaceElement);
543                 oldTargetElement.style.display = "inline";
544                 oldTargetElement.value = html;
545             } else {
546                 oldTargetElement.innerHTML = html;
547                 oldTargetElement.style.display = 'block';
548
549                 replaceElement.parentNode.insertBefore(oldTargetElement, replaceElement);
550                 replaceElement.parentNode.removeChild(replaceElement);
551             }
552         }
553     },
554
555     triggerSave : function(skip_cleanup, skip_callback) {
556         var inst, n;
557
558         // Default to false
559         if (typeof(skip_cleanup) == "undefined")
560             skip_cleanup = false;
561
562         // Default to false
563         if (typeof(skip_callback) == "undefined")
564             skip_callback = false;
565
566         // Cleanup and set all form fields
567         for (n in tinyMCE.instances) {
568             inst = tinyMCE.instances[n];
569
570             if (!tinyMCE.isInstance(inst))
571                 continue;
572
573             inst.triggerSave(skip_cleanup, skip_callback);
574         }
575     },
576
577     resetForm : function(form_index) {
578         var i, inst, n, formObj = document.forms[form_index];
579
580         for (n in tinyMCE.instances) {
581             inst = tinyMCE.instances[n];
582
583             if (!tinyMCE.isInstance(inst))
584                 continue;
585
586             inst.switchSettings();
587
588             for (i=0; i<formObj.elements.length; i++) {
589                 if (inst.formTargetElementId == formObj.elements[i].name)
590                     inst.getBody().innerHTML = inst.startContent;
591             }
592         }
593     },
594
595     execInstanceCommand : function(editor_id, command, user_interface, value, focus) {
596         var inst = tinyMCE.getInstanceById(editor_id);
597         if (inst) {
598             if (typeof(focus) == "undefined")
599                 focus = true;
600
601             if (focus)
602                 inst.contentWindow.focus();
603
604             // Reset design mode if lost
605             inst.autoResetDesignMode();
606
607             this.selectedElement = inst.getFocusElement();
608             this.selectedInstance = inst;
609             tinyMCE.execCommand(command, user_interface, value);
610
611             // Cancel event so it doesn't call onbeforeonunlaod
612             if (tinyMCE.isMSIE && window.event != null)
613                 tinyMCE.cancelEvent(window.event);
614         }
615     },
616
617     execCommand : function(command, user_interface, value) {
618         // Default input
619         user_interface = user_interface ? user_interface : false;
620         value = value ? value : null;
621
622         if (tinyMCE.selectedInstance)
623             tinyMCE.selectedInstance.switchSettings();
624
625         switch (command) {
626             case 'mceHelp':
627                 tinyMCE.openWindow({
628                     file : 'about.htm',
629                     width : 480,
630                     height : 380
631                 }, {
632                     tinymce_version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion,
633                     tinymce_releasedate : tinyMCE.releaseDate,
634                     inline : "yes"
635                 });
636             return;
637
638             case 'mceFocus':
639                 var inst = tinyMCE.getInstanceById(value);
640                 if (inst)
641                     inst.contentWindow.focus();
642             return;
643
644             case "mceAddControl":
645             case "mceAddEditor":
646                 tinyMCE.addMCEControl(tinyMCE._getElementById(value), value);
647                 return;
648
649             case "mceAddFrameControl":
650                 tinyMCE.addMCEControl(tinyMCE._getElementById(value['element'], value['document']), value['element'], value['document']);
651                 return;
652
653             case "mceRemoveControl":
654             case "mceRemoveEditor":
655                 tinyMCE.removeMCEControl(value);
656                 return;
657
658             case "mceResetDesignMode":
659                 // Resets the designmode state of the editors in Gecko
660                 if (!tinyMCE.isMSIE) {
661                     for (var n in tinyMCE.instances) {
662                         if (!tinyMCE.isInstance(tinyMCE.instances[n]))
663                             continue;
664
665                         try {
666                             tinyMCE.instances[n].getDoc().designMode = "on";
667                         } catch (e) {
668                             // Ignore any errors
669                         }
670                     }
671                 }
672
673                 return;
674         }
675
676         if (this.selectedInstance) {
677             this.selectedInstance.execCommand(command, user_interface, value);
678         } else if (tinyMCE.settings['focus_alert'])
679             alert(tinyMCELang['lang_focus_alert']);
680     },
681
682     _createIFrame : function(replace_element, doc, win) {
683         var iframe, id = replace_element.getAttribute("id");
684         var aw, ah;
685
686         if (typeof(doc) == "undefined")
687             doc = document;
688
689         if (typeof(win) == "undefined")
690             win = window;
691
692         iframe = doc.createElement("iframe");
693
694         aw = "" + tinyMCE.settings['area_width'];
695         ah = "" + tinyMCE.settings['area_height'];
696
697         if (aw.indexOf('%') == -1) {
698             aw = parseInt(aw);
699             aw = aw < 0 ? 300 : aw;
700             aw = aw + "px";
701         }
702
703         if (ah.indexOf('%') == -1) {
704             ah = parseInt(ah);
705             ah = ah < 0 ? 240 : ah;
706             ah = ah + "px";
707         }
708
709         iframe.setAttribute("id", id);
710         iframe.setAttribute("class", "mceEditorIframe");
711         iframe.setAttribute("border", "0");
712         iframe.setAttribute("frameBorder", "0");
713         iframe.setAttribute("marginWidth", "0");
714         iframe.setAttribute("marginHeight", "0");
715         iframe.setAttribute("leftMargin", "0");
716         iframe.setAttribute("topMargin", "0");
717         iframe.setAttribute("width", aw);
718         iframe.setAttribute("height", ah);
719         iframe.setAttribute("allowtransparency", "true");
720         iframe.className = 'mceEditorIframe';
721
722         if (tinyMCE.settings["auto_resize"])
723             iframe.setAttribute("scrolling", "no");
724
725         // Must have a src element in MSIE HTTPs breaks aswell as absoute URLs
726         if (tinyMCE.isMSIE && !tinyMCE.isOpera)
727             iframe.setAttribute("src", this.settings['default_document']);
728
729         iframe.style.width = aw;
730         iframe.style.height = ah;
731
732         // Ugly hack for Gecko problem in strict mode
733         if (tinyMCE.settings.strict_loading_mode)
734             iframe.style.marginBottom = '-5px';
735
736         // MSIE 5.0 issue
737         if (tinyMCE.isMSIE && !tinyMCE.isOpera)
738             replace_element.outerHTML = iframe.outerHTML;
739         else
740             replace_element.parentNode.replaceChild(iframe, replace_element);
741
742         if (tinyMCE.isMSIE && !tinyMCE.isOpera)
743             return win.frames[id];
744         else
745             return iframe;
746     },
747
748     setupContent : function(editor_id) {
749         var inst = tinyMCE.instances[editor_id];
750         var doc = inst.getDoc();
751         var head = doc.getElementsByTagName('head').item(0);
752         var content = inst.startContent;
753
754         // HTML values get XML encoded in strict mode
755         if (tinyMCE.settings.strict_loading_mode) {
756             content = content.replace(/&lt;/g, '<');
757             content = content.replace(/&gt;/g, '>');
758             content = content.replace(/&quot;/g, '"');
759             content = content.replace(/&amp;/g, '&');
760         }
761
762         inst.switchSettings();
763
764         // Not loaded correctly hit it again, Mozilla bug #997860
765         if (!tinyMCE.isMSIE && tinyMCE.getParam("setupcontent_reload", false) && doc.title != "blank_page") {
766             // This part will remove the designMode status
767             // Failes first time in Firefox 1.5b2 on Mac
768             try {doc.location.href = tinyMCE.baseURL + "/blank.htm";} catch (ex) {}
769             window.setTimeout("tinyMCE.setupContent('" + editor_id + "');", 1000);
770             return;
771         }
772
773         if (!head) {
774             window.setTimeout("tinyMCE.setupContent('" + editor_id + "');", 10);
775             return;
776         }
777
778         // Import theme specific content CSS the user specific
779         tinyMCE.importCSS(inst.getDoc(), tinyMCE.baseURL + "/themes/" + inst.settings['theme'] + "/css/editor_content.css");
780         tinyMCE.importCSS(inst.getDoc(), inst.settings['content_css']);
781         tinyMCE.dispatchCallback(inst, 'init_instance_callback', 'initInstance', inst);
782
783         // Setup keyboard shortcuts
784         if (tinyMCE.getParam('custom_undo_redo_keyboard_shortcuts')) {
785             inst.addShortcut('ctrl', 'z', 'lang_undo_desc', 'Undo');
786             inst.addShortcut('ctrl', 'y', 'lang_redo_desc', 'Redo');
787         }
788
789         // Add default shortcuts for gecko
790         if (tinyMCE.isGecko) {
791             inst.addShortcut('ctrl', 'b', 'lang_bold_desc', 'Bold');
792             inst.addShortcut('ctrl', 'i', 'lang_italic_desc', 'Italic');
793             inst.addShortcut('ctrl', 'u', 'lang_underline_desc', 'Underline');
794         }
795
796         // Setup span styles
797         if (tinyMCE.getParam("convert_fonts_to_spans"))
798             inst.getDoc().body.setAttribute('id', 'mceSpanFonts');
799
800         if (tinyMCE.settings['nowrap'])
801             doc.body.style.whiteSpace = "nowrap";
802
803         doc.body.dir = this.settings['directionality'];
804         doc.editorId = editor_id;
805
806         // Add on document element in Mozilla
807         if (!tinyMCE.isMSIE)
808             doc.documentElement.editorId = editor_id;
809
810         inst.setBaseHREF(tinyMCE.settings['base_href']);
811
812         // Replace new line characters to BRs
813         if (tinyMCE.settings['convert_newlines_to_brs']) {
814             content = tinyMCE.regexpReplace(content, "\r\n", "<br />", "gi");
815             content = tinyMCE.regexpReplace(content, "\r", "<br />", "gi");
816             content = tinyMCE.regexpReplace(content, "\n", "<br />", "gi");
817         }
818
819         // Open closed anchors
820     //    content = content.replace(new RegExp('<a(.*?)/>', 'gi'), '<a$1></a>');
821
822         // Call custom cleanup code
823         content = tinyMCE.storeAwayURLs(content);
824         content = tinyMCE._customCleanup(inst, "insert_to_editor", content);
825
826         if (tinyMCE.isMSIE) {
827             // Ugly!!!
828             window.setInterval('try{tinyMCE.getCSSClasses(tinyMCE.instances["' + editor_id + '"].getDoc(), "' + editor_id + '");}catch(e){}', 500);
829
830             if (tinyMCE.settings["force_br_newlines"])
831                 doc.styleSheets[0].addRule("p", "margin: 0;");
832
833             var body = inst.getBody();
834             body.editorId = editor_id;
835         }
836
837         content = tinyMCE.cleanupHTMLCode(content);
838
839         // Fix for bug #958637
840         if (!tinyMCE.isMSIE) {
841             var contentElement = inst.getDoc().createElement("body");
842             var doc = inst.getDoc();
843
844             contentElement.innerHTML = content;
845
846             // Remove weridness!
847             if (tinyMCE.isGecko && tinyMCE.settings['remove_lt_gt'])
848                 content = content.replace(new RegExp('&lt;&gt;', 'g'), "");
849
850             if (tinyMCE.settings['cleanup_on_startup'])
851                 tinyMCE.setInnerHTML(inst.getBody(), tinyMCE._cleanupHTML(inst, doc, this.settings, contentElement));
852             else {
853                 // Convert all strong/em to b/i
854                 content = tinyMCE.regexpReplace(content, "<strong", "<b", "gi");
855                 content = tinyMCE.regexpReplace(content, "<em(/?)>", "<i$1>", "gi");
856                 content = tinyMCE.regexpReplace(content, "<em ", "<i ", "gi");
857                 content = tinyMCE.regexpReplace(content, "</strong>", "</b>", "gi");
858                 content = tinyMCE.regexpReplace(content, "</em>", "</i>", "gi");
859                 tinyMCE.setInnerHTML(inst.getBody(), content);
860             }
861
862             tinyMCE.convertAllRelativeURLs(inst.getBody());
863         } else {
864             if (tinyMCE.settings['cleanup_on_startup']) {
865                 tinyMCE._setHTML(inst.getDoc(), content);
866
867                 // Produces permission denied error in MSIE 5.5
868                 eval('try {tinyMCE.setInnerHTML(inst.getBody(), tinyMCE._cleanupHTML(inst, inst.contentDocument, this.settings, inst.getBody()));} catch(e) {}');
869             } else
870                 tinyMCE._setHTML(inst.getDoc(), content);
871         }
872
873         // Fix for bug #957681
874         //inst.getDoc().designMode = inst.getDoc().designMode;
875
876         // Setup element references
877         var parentElm = inst.targetDoc.getElementById(inst.editorId + '_parent');
878         inst.formElement = tinyMCE.isGecko ? parentElm.previousSibling : parentElm.nextSibling;
879
880         tinyMCE.handleVisualAid(inst.getBody(), true, tinyMCE.settings['visual'], inst);
881         tinyMCE.dispatchCallback(inst, 'setupcontent_callback', 'setupContent', editor_id, inst.getBody(), inst.getDoc());
882
883         // Re-add design mode on mozilla
884         if (!tinyMCE.isMSIE)
885             tinyMCE.addEventHandlers(inst);
886
887         // Add blur handler
888         if (tinyMCE.isMSIE) {
889             tinyMCE.addEvent(inst.getBody(), "blur", TinyMCE_Engine.prototype._eventPatch);
890             tinyMCE.addEvent(inst.getBody(), "beforedeactivate", TinyMCE_Engine.prototype._eventPatch); // Bug #1439953
891
892             // Workaround for drag drop/copy paste base href bug
893             if (!tinyMCE.isOpera) {
894                 tinyMCE.addEvent(doc.body, "mousemove", TinyMCE_Engine.prototype.onMouseMove);
895                 tinyMCE.addEvent(doc.body, "beforepaste", TinyMCE_Engine.prototype._eventPatch);
896                 tinyMCE.addEvent(doc.body, "drop", TinyMCE_Engine.prototype._eventPatch);
897             }
898         }
899
900         // Trigger node change, this call locks buttons for tables and so forth
901         tinyMCE.selectedInstance = inst;
902         tinyMCE.selectedElement = inst.contentWindow.document.body;
903
904         // Call custom DOM cleanup
905         tinyMCE._customCleanup(inst, "insert_to_editor_dom", inst.getBody());
906         tinyMCE._customCleanup(inst, "setup_content_dom", inst.getBody());
907         tinyMCE._setEventsEnabled(inst.getBody(), false);
908         tinyMCE.cleanupAnchors(inst.getDoc());
909
910         if (tinyMCE.getParam("convert_fonts_to_spans"))
911             tinyMCE.convertSpansToFonts(inst.getDoc());
912
913         inst.startContent = tinyMCE.trim(inst.getBody().innerHTML);
914         inst.undoRedo.add({ content : inst.startContent });
915
916         // Cleanup any mess left from storyAwayURLs
917         if (tinyMCE.isGecko) {
918             // Remove mce_src from textnodes and comments
919             tinyMCE.selectNodes(inst.getBody(), function(n) {
920                 if (n.nodeType == 3 || n.nodeType == 8) {
921                     n.nodeValue = n.nodeValue.replace(new RegExp('\\smce_src=\"[^\"]*\"', 'gi'), "");
922                     n.nodeValue = n.nodeValue.replace(new RegExp('\\smce_href=\"[^\"]*\"', 'gi'), "");
923                 }
924
925                 return false;
926             });
927         }
928
929         // Cleanup any mess left from storyAwayURLs
930         tinyMCE._removeInternal(inst.getBody());
931
932         tinyMCE.selectedInstance = inst;
933         tinyMCE.triggerNodeChange(false, true);
934     },
935
936     storeAwayURLs : function(s) {
937         // Remove all mce_src, mce_href and replace them with new ones
938     //    s = s.replace(new RegExp('mce_src\\s*=\\s*\"[^ >\"]*\"', 'gi'), '');
939     //    s = s.replace(new RegExp('mce_href\\s*=\\s*\"[^ >\"]*\"', 'gi'), '');
940
941         if (!s.match(/(mce_src|mce_href)/gi, s)) {
942             s = s.replace(new RegExp('src\\s*=\\s*\"([^ >\"]*)\"', 'gi'), 'src="$1" mce_src="$1"');
943             s = s.replace(new RegExp('href\\s*=\\s*\"([^ >\"]*)\"', 'gi'), 'href="$1" mce_href="$1"');
944         }
945
946         return s;
947     },
948
949     _removeInternal : function(n) {
950         if (tinyMCE.isGecko) {
951             // Remove mce_src from textnodes and comments
952             tinyMCE.selectNodes(n, function(n) {
953                 if (n.nodeType == 3 || n.nodeType == 8) {
954                     n.nodeValue = n.nodeValue.replace(new RegExp('\\smce_src=\"[^\"]*\"', 'gi'), "");
955                     n.nodeValue = n.nodeValue.replace(new RegExp('\\smce_href=\"[^\"]*\"', 'gi'), "");
956                 }
957
958                 return false;
959             });
960         }
961     },
962
963     removeTinyMCEFormElements : function(form_obj) {
964         // Check if form is valid
965         if (typeof(form_obj) == "undefined" || form_obj == null)
966             return;
967
968         // If not a form, find the form
969         if (form_obj.nodeName != "FORM") {
970             if (form_obj.form)
971                 form_obj = form_obj.form;
972             else
973                 form_obj = tinyMCE.getParentElement(form_obj, "form");
974         }
975
976         // Still nothing
977         if (form_obj == null)
978             return;
979
980         // Disable all UI form elements that TinyMCE created
981         for (var i=0; i<form_obj.elements.length; i++) {
982             var elementId = form_obj.elements[i].name ? form_obj.elements[i].name : form_obj.elements[i].id;
983
984             if (elementId.indexOf('mce_editor_') == 0)
985                 form_obj.elements[i].disabled = true;
986         }
987     },
988
989     handleEvent : function(e) {
990         var inst = tinyMCE.selectedInstance;
991
992         // Remove odd, error
993         if (typeof(tinyMCE) == "undefined")
994             return true;
995
996         //tinyMCE.debug(e.type + " " + e.target.nodeName + " " + (e.relatedTarget ? e.relatedTarget.nodeName : ""));
997
998         if (tinyMCE.executeCallback(tinyMCE.selectedInstance, 'handle_event_callback', 'handleEvent', e))
999             return false;
1000
1001         switch (e.type) {
1002             case "beforedeactivate": // Was added due to bug #1439953
1003             case "blur":
1004                 if (tinyMCE.selectedInstance)
1005                     tinyMCE.selectedInstance.execCommand('mceEndTyping');
1006
1007                 tinyMCE.hideMenus();
1008
1009                 return;
1010
1011             // Workaround for drag drop/copy paste base href bug
1012             case "drop":
1013             case "beforepaste":
1014                 if (tinyMCE.selectedInstance)
1015                     tinyMCE.selectedInstance.setBaseHREF(null);
1016
1017                 // Fixes odd MSIE bug where drag/droping elements in a iframe with height 100% breaks
1018                 // This logic forces the width/height to be in pixels while the user is drag/dropping
1019                 if (tinyMCE.isMSIE && !tinyMCE.isOpera) {
1020                     var ife = tinyMCE.selectedInstance.iframeElement;
1021
1022                     /*if (ife.style.width.indexOf('%') != -1) {
1023                         ife._oldWidth = ife.width.height;
1024                         ife.style.width = ife.clientWidth;
1025                     }*/
1026
1027                     if (ife.style.height.indexOf('%') != -1) {
1028                         ife._oldHeight = ife.style.height;
1029                         ife.style.height = ife.clientHeight;
1030                     }
1031                 }
1032
1033                 window.setTimeout("tinyMCE.selectedInstance.setBaseHREF(tinyMCE.settings['base_href']);tinyMCE._resetIframeHeight();", 1);
1034                 return;
1035
1036             case "submit":
1037                 tinyMCE.removeTinyMCEFormElements(tinyMCE.isMSIE ? window.event.srcElement : e.target);
1038                 tinyMCE.triggerSave();
1039                 tinyMCE.isNotDirty = true;
1040                 return;
1041
1042             case "reset":
1043                 var formObj = tinyMCE.isMSIE ? window.event.srcElement : e.target;
1044
1045                 for (var i=0; i<document.forms.length; i++) {
1046                     if (document.forms[i] == formObj)
1047                         window.setTimeout('tinyMCE.resetForm(' + i + ');', 10);
1048                 }
1049
1050                 return;
1051
1052             case "keypress":
1053                 if (inst && inst.handleShortcut(e))
1054                     return false;
1055
1056                 if (e.target.editorId) {
1057                     tinyMCE.selectedInstance = tinyMCE.instances[e.target.editorId];
1058                 } else {
1059                     if (e.target.ownerDocument.editorId)
1060                         tinyMCE.selectedInstance = tinyMCE.instances[e.target.ownerDocument.editorId];
1061                 }
1062
1063                 if (tinyMCE.selectedInstance)
1064                     tinyMCE.selectedInstance.switchSettings();
1065
1066                 // Insert P element
1067                 if (tinyMCE.isGecko && tinyMCE.settings['force_p_newlines'] && e.keyCode == 13 && !e.shiftKey) {
1068                     // Insert P element instead of BR
1069                     if (TinyMCE_ForceParagraphs._insertPara(tinyMCE.selectedInstance, e)) {
1070                         // Cancel event
1071                         tinyMCE.execCommand("mceAddUndoLevel");
1072                         tinyMCE.cancelEvent(e);
1073                         return false;
1074                     }
1075                 }
1076
1077                 // Handle backspace
1078                 if (tinyMCE.isGecko && tinyMCE.settings['force_p_newlines'] && (e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey) {
1079                     // Insert P element instead of BR
1080                     if (TinyMCE_ForceParagraphs._handleBackSpace(tinyMCE.selectedInstance, e.type)) {
1081                         // Cancel event
1082                         tinyMCE.execCommand("mceAddUndoLevel");
1083                         tinyMCE.cancelEvent(e);
1084                         return false;
1085                     }
1086                 }
1087
1088                 // Return key pressed
1089                 if (tinyMCE.isMSIE && tinyMCE.settings['force_br_newlines'] && e.keyCode == 13) {
1090                     if (e.target.editorId)
1091                         tinyMCE.selectedInstance = tinyMCE.instances[e.target.editorId];
1092
1093                     if (tinyMCE.selectedInstance) {
1094                         var sel = tinyMCE.selectedInstance.getDoc().selection;
1095                         var rng = sel.createRange();
1096
1097                         if (tinyMCE.getParentElement(rng.parentElement(), "li") != null)
1098                             return false;
1099
1100                         // Cancel event
1101                         e.returnValue = false;
1102                         e.cancelBubble = true;
1103
1104                         // Insert BR element
1105                         rng.pasteHTML("<br />");
1106                         rng.collapse(false);
1107                         rng.select();
1108
1109                         tinyMCE.execCommand("mceAddUndoLevel");
1110                         tinyMCE.triggerNodeChange(false);
1111                         return false;
1112                     }
1113                 }
1114
1115                 // Backspace or delete
1116                 if (e.keyCode == 8 || e.keyCode == 46) {
1117                     tinyMCE.selectedElement = e.target;
1118                     tinyMCE.linkElement = tinyMCE.getParentElement(e.target, "a");
1119                     tinyMCE.imgElement = tinyMCE.getParentElement(e.target, "img");
1120                     tinyMCE.triggerNodeChange(false);
1121                 }
1122
1123                 return false;
1124             break;
1125
1126             case "keyup":
1127             case "keydown":
1128                 tinyMCE.hideMenus();
1129                 tinyMCE.hasMouseMoved = false;
1130
1131                 if (inst && inst.handleShortcut(e))
1132                     return false;
1133
1134                 if (e.target.editorId)
1135                     tinyMCE.selectedInstance = tinyMCE.instances[e.target.editorId];
1136                 else
1137                     return;
1138
1139                 if (tinyMCE.selectedInstance)
1140                     tinyMCE.selectedInstance.switchSettings();
1141
1142                 var inst = tinyMCE.selectedInstance;
1143
1144                 // Handle backspace
1145                 if (tinyMCE.isGecko && tinyMCE.settings['force_p_newlines'] && (e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey) {
1146                     // Insert P element instead of BR
1147                     if (TinyMCE_ForceParagraphs._handleBackSpace(tinyMCE.selectedInstance, e.type)) {
1148                         // Cancel event
1149                         tinyMCE.execCommand("mceAddUndoLevel");
1150                         e.preventDefault();
1151                         return false;
1152                     }
1153                 }
1154
1155                 tinyMCE.selectedElement = null;
1156                 tinyMCE.selectedNode = null;
1157                 var elm = tinyMCE.selectedInstance.getFocusElement();
1158                 tinyMCE.linkElement = tinyMCE.getParentElement(elm, "a");
1159                 tinyMCE.imgElement = tinyMCE.getParentElement(elm, "img");
1160                 tinyMCE.selectedElement = elm;
1161
1162                 // Update visualaids on tabs
1163                 if (tinyMCE.isGecko && e.type == "keyup" && e.keyCode == 9)
1164                     tinyMCE.handleVisualAid(tinyMCE.selectedInstance.getBody(), true, tinyMCE.settings['visual'], tinyMCE.selectedInstance);
1165
1166                 // Fix empty elements on return/enter, check where enter occured
1167                 if (tinyMCE.isMSIE && e.type == "keydown" && e.keyCode == 13)
1168                     tinyMCE.enterKeyElement = tinyMCE.selectedInstance.getFocusElement();
1169
1170                 // Fix empty elements on return/enter
1171                 if (tinyMCE.isMSIE && e.type == "keyup" && e.keyCode == 13) {
1172                     var elm = tinyMCE.enterKeyElement;
1173                     if (elm) {
1174                         var re = new RegExp('^HR|IMG|BR$','g'); // Skip these
1175                         var dre = new RegExp('^H[1-6]$','g'); // Add double on these
1176
1177                         if (!elm.hasChildNodes() && !re.test(elm.nodeName)) {
1178                             if (dre.test(elm.nodeName))
1179                                 elm.innerHTML = "&nbsp;&nbsp;";
1180                             else
1181                                 elm.innerHTML = "&nbsp;";
1182                         }
1183                     }
1184                 }
1185
1186                 // Check if it's a position key
1187                 var keys = tinyMCE.posKeyCodes;
1188                 var posKey = false;
1189                 for (var i=0; i<keys.length; i++) {
1190                     if (keys[i] == e.keyCode) {
1191                         posKey = true;
1192                         break;
1193                     }
1194                 }
1195
1196                 // MSIE custom key handling
1197                 if (tinyMCE.isMSIE && tinyMCE.settings['custom_undo_redo']) {
1198                     var keys = new Array(8,46); // Backspace,Delete
1199                     for (var i=0; i<keys.length; i++) {
1200                         if (keys[i] == e.keyCode) {
1201                             if (e.type == "keyup")
1202                                 tinyMCE.triggerNodeChange(false);
1203                         }
1204                     }
1205                 }
1206
1207                 // If Ctrl key
1208                 if (e.keyCode == 17)
1209                     return true;
1210
1211                 // Handle Undo/Redo when typing content
1212
1213                 // Start typing (non position key)
1214                 if (!posKey && e.type == "keyup")
1215                     tinyMCE.execCommand("mceStartTyping");
1216
1217                 // Store undo bookmark
1218                 if (e.type == "keydown" && (posKey || e.ctrlKey) && inst)
1219                     inst.undoBookmark = inst.selection.getBookmark();
1220
1221                 // End typing (position key) or some Ctrl event
1222                 if (e.type == "keyup" && (posKey || e.ctrlKey))
1223                     tinyMCE.execCommand("mceEndTyping");
1224
1225                 if (posKey && e.type == "keyup")
1226                     tinyMCE.triggerNodeChange(false);
1227
1228                 if (tinyMCE.isMSIE && e.ctrlKey)
1229                     window.setTimeout('tinyMCE.triggerNodeChange(false);', 1);
1230             break;
1231
1232             case "mousedown":
1233             case "mouseup":
1234             case "click":
1235             case "focus":
1236                 tinyMCE.hideMenus();
1237
1238                 if (tinyMCE.selectedInstance) {
1239                     tinyMCE.selectedInstance.switchSettings();
1240                     tinyMCE.selectedInstance.isFocused = true;
1241                 }
1242
1243                 // Check instance event trigged on
1244                 var targetBody = tinyMCE.getParentElement(e.target, "body");
1245                 for (var instanceName in tinyMCE.instances) {
1246                     if (!tinyMCE.isInstance(tinyMCE.instances[instanceName]))
1247                         continue;
1248
1249                     var inst = tinyMCE.instances[instanceName];
1250
1251                     // Reset design mode if lost (on everything just in case)
1252                     inst.autoResetDesignMode();
1253
1254                     if (inst.getBody() == targetBody) {
1255                         tinyMCE.selectedInstance = inst;
1256                         tinyMCE.selectedElement = e.target;
1257                         tinyMCE.linkElement = tinyMCE.getParentElement(tinyMCE.selectedElement, "a");
1258                         tinyMCE.imgElement = tinyMCE.getParentElement(tinyMCE.selectedElement, "img");
1259                         break;
1260                     }
1261                 }
1262
1263                 // Add first bookmark location
1264                 if (!tinyMCE.selectedInstance.undoRedo.undoLevels[0].bookmark)
1265                     tinyMCE.selectedInstance.undoRedo.undoLevels[0].bookmark = tinyMCE.selectedInstance.selection.getBookmark();
1266
1267                 if (tinyMCE.isSafari) {
1268                     tinyMCE.selectedInstance.lastSafariSelection = tinyMCE.selectedInstance.selection.getBookmark();
1269                     tinyMCE.selectedInstance.lastSafariSelectedElement = tinyMCE.selectedElement;
1270
1271                     var lnk = tinyMCE.getParentElement(tinyMCE.selectedElement, "a");
1272
1273                     // Patch the darned link
1274                     if (lnk && e.type == "mousedown") {
1275                         lnk.setAttribute("mce_real_href", lnk.getAttribute("href"));
1276                         lnk.setAttribute("href", "javascript:void(0);");
1277                     }
1278
1279                     // Patch back
1280                     if (lnk && e.type == "click") {
1281                         window.setTimeout(function() {
1282                             lnk.setAttribute("href", lnk.getAttribute("mce_real_href"));
1283                             lnk.removeAttribute("mce_real_href");
1284                         }, 10);
1285                     }
1286                 }
1287
1288                 // Reset selected node
1289                 if (e.type != "focus")
1290                     tinyMCE.selectedNode = null;
1291
1292                 tinyMCE.triggerNodeChange(false);
1293                 tinyMCE.execCommand("mceEndTyping");
1294
1295                 if (e.type == "mouseup")
1296                     tinyMCE.execCommand("mceAddUndoLevel");
1297
1298                 // Just in case
1299                 if (!tinyMCE.selectedInstance && e.target.editorId)
1300                     tinyMCE.selectedInstance = tinyMCE.instances[e.target.editorId];
1301
1302                 return false;
1303             break;
1304         }
1305     },
1306
1307     getButtonHTML : function(id, lang, img, cmd, ui, val) {
1308         var h = '', m, x;
1309
1310         cmd = 'tinyMCE.execInstanceCommand(\'{$editor_id}\',\'' + cmd + '\'';
1311
1312         if (typeof(ui) != "undefined" && ui != null)
1313             cmd += ',' + ui;
1314
1315         if (typeof(val) != "undefined" && val != null)
1316             cmd += ",'" + val + "'";
1317
1318         cmd += ');';
1319
1320         // Use tilemaps when enabled and found and never in MSIE since it loads the tile each time from cache if cahce is disabled
1321         if (tinyMCE.getParam('button_tile_map') && (!tinyMCE.isMSIE || tinyMCE.isOpera) && (m = this.buttonMap[id]) != null && (tinyMCE.getParam("language") == "en" || img.indexOf('$lang') == -1)) {
1322             // Tiled button
1323             x = 0 - (m * 20) == 0 ? '0' : 0 - (m * 20);
1324             h += '<a id="{$editor_id}_' + id + '" href="javascript:' + cmd + '" onclick="' + cmd + 'return false;" onmousedown="return false;" class="mceTiledButton mceButtonNormal" target="_self">';
1325             h += '<img src="{$themeurl}/images/spacer.gif" style="background-position: ' + x + 'px 0" title="{$' + lang + '}" />';
1326             h += '</a>';
1327         } else {
1328             // Normal button
1329             h += '<a id="{$editor_id}_' + id + '" href="javascript:' + cmd + '" onclick="' + cmd + 'return false;" onmousedown="return false;" class="mceButtonNormal" target="_self">';
1330             h += '<img src="' + img + '" title="{$' + lang + '}" />';
1331             h += '</a>';
1332         }
1333
1334         return h;
1335     },
1336
1337     addButtonMap : function(m) {
1338         var i, a = m.replace(/\s+/, '').split(',');
1339
1340         for (i=0; i<a.length; i++)
1341             this.buttonMap[a[i]] = i;
1342     },
1343
1344     submitPatch : function() {
1345         tinyMCE.removeTinyMCEFormElements(this);
1346         tinyMCE.triggerSave();
1347         this.mceOldSubmit();
1348         tinyMCE.isNotDirty = true;
1349     },
1350
1351     onLoad : function() {
1352         // Wait for everything to be loaded first
1353         if (tinyMCE.settings.strict_loading_mode && this.loadingIndex != -1) {
1354             window.setTimeout('tinyMCE.onLoad();', 1);
1355             return;
1356         }
1357
1358         if (tinyMCE.isMSIE && !tinyMCE.isOpera && window.event.type == "readystatechange" && document.readyState != "complete")
1359             return true;
1360
1361         if (tinyMCE.isLoaded)
1362             return true;
1363
1364         tinyMCE.isLoaded = true;
1365
1366         tinyMCE.dispatchCallback(null, 'onpageload', 'onPageLoad');
1367
1368         for (var c=0; c<tinyMCE.configs.length; c++) {
1369             tinyMCE.settings = tinyMCE.configs[c];
1370
1371             var selector = tinyMCE.getParam("editor_selector");
1372             var deselector = tinyMCE.getParam("editor_deselector");
1373             var elementRefAr = new Array();
1374
1375             // Add submit triggers
1376             if (document.forms && tinyMCE.settings['add_form_submit_trigger'] && !tinyMCE.submitTriggers) {
1377                 for (var i=0; i<document.forms.length; i++) {
1378                     var form = document.forms[i];
1379
1380                     tinyMCE.addEvent(form, "submit", TinyMCE_Engine.prototype.handleEvent);
1381                     tinyMCE.addEvent(form, "reset", TinyMCE_Engine.prototype.handleEvent);
1382                     tinyMCE.submitTriggers = true; // Do it only once
1383
1384                     // Patch the form.submit function
1385                     if (tinyMCE.settings['submit_patch']) {
1386                         try {
1387                             form.mceOldSubmit = form.submit;
1388                             form.submit = TinyMCE_Engine.prototype.submitPatch;
1389                         } catch (e) {
1390                             // Do nothing
1391                         }
1392                     }
1393                 }
1394             }
1395
1396             // Add editor instances based on mode
1397             var mode = tinyMCE.settings['mode'];
1398             switch (mode) {
1399                 case "exact":
1400                     var elements = tinyMCE.getParam('elements', '', true, ',');
1401
1402                     for (var i=0; i<elements.length; i++) {
1403                         var element = tinyMCE._getElementById(elements[i]);
1404                         var trigger = element ? element.getAttribute(tinyMCE.settings['textarea_trigger']) : "";
1405
1406                         if (tinyMCE.getAttrib(element, "class").indexOf(deselector) != -1)
1407                             continue;
1408
1409                         if (trigger == "false")
1410                             continue;
1411
1412                         if ((tinyMCE.settings['ask'] || tinyMCE.settings['convert_on_click']) && element) {
1413                             elementRefAr[elementRefAr.length] = element;
1414                             continue;
1415                         }
1416
1417                         if (element)
1418                             tinyMCE.addMCEControl(element, elements[i]);
1419                         else if (tinyMCE.settings['debug'])
1420                             alert("Error: Could not find element by id or name: " + elements[i]);
1421                     }
1422                 break;
1423
1424                 case "specific_textareas":
1425                 case "textareas":
1426                     var nodeList = document.getElementsByTagName("textarea");
1427
1428                     for (var i=0; i<nodeList.length; i++) {
1429                         var elm = nodeList.item(i);
1430                         var trigger = elm.getAttribute(tinyMCE.settings['textarea_trigger']);
1431
1432                         if (selector != '' && tinyMCE.getAttrib(elm, "class").indexOf(selector) == -1)
1433                             continue;
1434
1435                         if (selector != '')
1436                             trigger = selector != "" ? "true" : "";
1437
1438                         if (tinyMCE.getAttrib(elm, "class").indexOf(deselector) != -1)
1439                             continue;
1440
1441                         if ((mode == "specific_textareas" && trigger == "true") || (mode == "textareas" && trigger != "false"))
1442                             elementRefAr[elementRefAr.length] = elm;
1443                     }
1444                 break;
1445             }
1446
1447             for (var i=0; i<elementRefAr.length; i++) {
1448                 var element = elementRefAr[i];
1449                 var elementId = element.name ? element.name : element.id;
1450
1451                 if (tinyMCE.settings['ask'] || tinyMCE.settings['convert_on_click']) {
1452                     // Focus breaks in Mozilla
1453                     if (tinyMCE.isGecko) {
1454                         var settings = tinyMCE.settings;
1455
1456                         tinyMCE.addEvent(element, "focus", function (e) {window.setTimeout(function() {TinyMCE_Engine.prototype.confirmAdd(e, settings);}, 10);});
1457
1458                         if (element.nodeName != "TEXTAREA" && element.nodeName != "INPUT")
1459                             tinyMCE.addEvent(element, "click", function (e) {window.setTimeout(function() {TinyMCE_Engine.prototype.confirmAdd(e, settings);}, 10);});
1460                         // tinyMCE.addEvent(element, "mouseover", function (e) {window.setTimeout(function() {TinyMCE_Engine.prototype.confirmAdd(e, settings);}, 10);});
1461                     } else {
1462                         var settings = tinyMCE.settings;
1463
1464                         tinyMCE.addEvent(element, "focus", function () { TinyMCE_Engine.prototype.confirmAdd(null, settings); });
1465                         tinyMCE.addEvent(element, "click", function () { TinyMCE_Engine.prototype.confirmAdd(null, settings); });
1466                         // tinyMCE.addEvent(element, "mouseenter", function () { TinyMCE_Engine.prototype.confirmAdd(null, settings); });
1467                     }
1468                 } else
1469                     tinyMCE.addMCEControl(element, elementId);
1470             }
1471
1472             // Handle auto focus
1473             if (tinyMCE.settings['auto_focus']) {
1474                 window.setTimeout(function () {
1475                     var inst = tinyMCE.getInstanceById(tinyMCE.settings['auto_focus']);
1476                     inst.selection.selectNode(inst.getBody(), true, true);
1477                     inst.contentWindow.focus();
1478                 }, 10);
1479             }
1480
1481             tinyMCE.dispatchCallback(null, 'oninit', 'onInit');
1482         }
1483     },
1484
1485     isInstance : function(o) {
1486         return o != null && typeof(o) == "object" && o.isTinyMCE_Control;
1487     },
1488
1489     getParam : function(name, default_value, strip_whitespace, split_chr) {
1490         var value = (typeof(this.settings[name]) == "undefined") ? default_value : this.settings[name];
1491
1492         // Fix bool values
1493         if (value == "true" || value == "false")
1494             return (value == "true");
1495
1496         if (strip_whitespace)
1497             value = tinyMCE.regexpReplace(value, "[ \t\r\n]", "");
1498
1499         if (typeof(split_chr) != "undefined" && split_chr != null) {
1500             value = value.split(split_chr);
1501             var outArray = new Array();
1502
1503             for (var i=0; i<value.length; i++) {
1504                 if (value[i] && value[i] != "")
1505                     outArray[outArray.length] = value[i];
1506             }
1507
1508             value = outArray;
1509         }
1510
1511         return value;
1512     },
1513
1514     getLang : function(name, default_value, parse_entities, va) {
1515         var v = (typeof(tinyMCELang[name]) == "undefined") ? default_value : tinyMCELang[name], n;
1516
1517         if (parse_entities)
1518             v = tinyMCE.entityDecode(v);
1519
1520         if (va) {
1521             for (n in va)
1522                 v = this.replaceVar(v, n, va[n]);
1523         }
1524
1525         return v;
1526     },
1527
1528     entityDecode : function(s) {
1529         var e = document.createElement("div");
1530         e.innerHTML = s;
1531         return e.innerHTML;
1532     },
1533
1534     addToLang : function(prefix, ar) {
1535         for (var key in ar) {
1536             if (typeof(ar[key]) == 'function')
1537                 continue;
1538
1539             tinyMCELang[(key.indexOf('lang_') == -1 ? 'lang_' : '') + (prefix != '' ? (prefix + "_") : '') + key] = ar[key];
1540         }
1541
1542         this.loadNextScript();
1543
1544     //    for (var key in ar)
1545     //        tinyMCELang[(key.indexOf('lang_') == -1 ? 'lang_' : '') + (prefix != '' ? (prefix + "_") : '') + key] = "|" + ar[key] + "|";
1546     },
1547
1548     triggerNodeChange : function(focus, setup_content) {
1549         if (tinyMCE.selectedInstance) {
1550             var inst = tinyMCE.selectedInstance;
1551             var editorId = inst.editorId;
1552             var elm = (typeof(setup_content) != "undefined" && setup_content) ? tinyMCE.selectedElement : inst.getFocusElement();
1553             var undoIndex = -1;
1554             var undoLevels = -1;
1555             var anySelection = false;
1556             var selectedText = inst.selection.getSelectedText();
1557
1558             if (setup_content && tinyMCE.isGecko && inst.isHidden())
1559                 elm = inst.getBody();
1560
1561             inst.switchSettings();
1562
1563             if (tinyMCE.settings["auto_resize"]) {
1564                 var doc = inst.getDoc();
1565
1566                 inst.iframeElement.style.width = doc.body.offsetWidth + "px";
1567                 inst.iframeElement.style.height = doc.body.offsetHeight + "px";
1568             }
1569
1570             if (tinyMCE.selectedElement)
1571                 anySelection = (tinyMCE.selectedElement.nodeName.toLowerCase() == "img") || (selectedText && selectedText.length > 0);
1572
1573             if (tinyMCE.settings['custom_undo_redo']) {
1574                 undoIndex = inst.undoRedo.undoIndex;
1575                 undoLevels = inst.undoRedo.undoLevels.length;
1576             }
1577
1578             tinyMCE.dispatchCallback(inst, 'handle_node_change_callback', 'handleNodeChange', editorId, elm, undoIndex, undoLevels, inst.visualAid, anySelection, setup_content);
1579         }
1580
1581         if (this.selectedInstance && (typeof(focus) == "undefined" || focus))
1582             this.selectedInstance.contentWindow.focus();
1583     },
1584
1585     _customCleanup : function(inst, type, content) {
1586         var pl, po, i;
1587
1588         // Call custom cleanup
1589         var customCleanup = tinyMCE.settings['cleanup_callback'];
1590         if (customCleanup != "" && eval("typeof(" + customCleanup + ")") != "undefined")
1591             content = eval(customCleanup + "(type, content, inst);");
1592
1593         // Trigger plugin cleanups
1594         pl = inst.plugins;
1595         for (i=0; i<pl.length; i++) {
1596             po = tinyMCE.plugins[pl[i]];
1597
1598             if (po && po.cleanup)
1599                 content = po.cleanup(type, content, inst);
1600         }
1601
1602         return content;
1603     },
1604
1605     setContent : function(h) {
1606         if (tinyMCE.selectedInstance) {
1607             tinyMCE.selectedInstance.execCommand('mceSetContent', false, h);
1608             tinyMCE.selectedInstance.repaint();
1609         }
1610     },
1611
1612     importThemeLanguagePack : function(name) {
1613         if (typeof(name) == "undefined")
1614             name = tinyMCE.settings['theme'];
1615
1616         tinyMCE.loadScript(tinyMCE.baseURL + '/themes/' + name + '/langs/' + tinyMCE.settings['language'] + '.js');
1617     },
1618
1619     importPluginLanguagePack : function(name, valid_languages) {
1620         var lang = "en", b = tinyMCE.baseURL + '/plugins/' + name;
1621
1622         valid_languages = valid_languages.split(',');
1623         for (var i=0; i<valid_languages.length; i++) {
1624             if (tinyMCE.settings['language'] == valid_languages[i])
1625                 lang = tinyMCE.settings['language'];
1626         }
1627
1628         if (this.plugins[name])
1629             b = this.plugins[name].baseURL;
1630
1631         tinyMCE.loadScript(b + '/langs/' + lang +  '.js');
1632     },
1633
1634     applyTemplate : function(h, as) {
1635         var i, s, ar = h.match(new RegExp('\\{\\$[a-z0-9_]+\\}', 'gi'));
1636
1637         if (ar && ar.length > 0) {
1638             for (i=ar.length-1; i>=0; i--) {
1639                 s = ar[i].substring(2, ar[i].length-1);
1640
1641                 if (s.indexOf('lang_') == 0 && tinyMCELang[s])
1642                     h = tinyMCE.replaceVar(h, s, tinyMCELang[s]);
1643                 else if (as && as[s])
1644                     h = tinyMCE.replaceVar(h, s, as[s]);
1645                 else if (tinyMCE.settings[s])
1646                     h = tinyMCE.replaceVar(h, s, tinyMCE.settings[s]);
1647             }
1648         }
1649
1650         h = tinyMCE.replaceVar(h, "themeurl", tinyMCE.themeURL);
1651
1652         return h;
1653     },
1654
1655     replaceVar : function(h, r, v) {
1656         return h.replace(new RegExp('{\\\$' + r + '}', 'g'), v);
1657     },
1658
1659     openWindow : function(template, args) {
1660         var html, width, height, x, y, resizable, scrollbars, url;
1661
1662         args['mce_template_file'] = template['file'];
1663         args['mce_width'] = template['width'];
1664         args['mce_height'] = template['height'];
1665         tinyMCE.windowArgs = args;
1666
1667         html = template['html'];
1668         if (!(width = parseInt(template['width'])))
1669             width = 320;
1670
1671         if (!(height = parseInt(template['height'])))
1672             height = 200;
1673
1674         // Add to height in M$ due to SP2 WHY DON'T YOU GUYS IMPLEMENT innerWidth of windows!!
1675         if (tinyMCE.isMSIE)
1676             height += 40;
1677         else
1678             height += 20;
1679
1680         x = parseInt(screen.width / 2.0) - (width / 2.0);
1681         y = parseInt(screen.height / 2.0) - (height / 2.0);
1682
1683         resizable = (args && args['resizable']) ? args['resizable'] : "no";
1684         scrollbars = (args && args['scrollbars']) ? args['scrollbars'] : "no";
1685
1686         if (template['file'].charAt(0) != '/' && template['file'].indexOf('://') == -1)
1687             url = tinyMCE.baseURL + "/themes/" + tinyMCE.getParam("theme") + "/" + template['file'];
1688         else
1689             url = template['file'];
1690
1691         // Replace all args as variables in URL
1692         for (var name in args) {
1693             if (typeof(args[name]) == 'function')
1694                 continue;
1695
1696             url = tinyMCE.replaceVar(url, name, escape(args[name]));
1697         }
1698
1699         if (html) {
1700             html = tinyMCE.replaceVar(html, "css", this.settings['popups_css']);
1701             html = tinyMCE.applyTemplate(html, args);
1702
1703             var win = window.open("", "mcePopup" + new Date().getTime(), "top=" + y + ",left=" + x + ",scrollbars=" + scrollbars + ",dialog=yes,minimizable=" + resizable + ",modal=yes,width=" + width + ",height=" + height + ",resizable=" + resizable);
1704             if (win == null) {
1705                 alert(tinyMCELang['lang_popup_blocked']);
1706                 return;
1707             }
1708
1709             win.document.write(html);
1710             win.document.close();
1711             win.resizeTo(width, height);
1712             win.focus();
1713         } else {
1714             if ((tinyMCE.isMSIE && !tinyMCE.isOpera) && resizable != 'yes' && tinyMCE.settings["dialog_type"] == "modal") {
1715                 height += 10;
1716
1717                 var features = "resizable:" + resizable 
1718                     + ";scroll:"
1719                     + scrollbars + ";status:yes;center:yes;help:no;dialogWidth:"
1720                     + width + "px;dialogHeight:" + height + "px;";
1721
1722                 window.showModalDialog(url, window, features);
1723             } else {
1724                 var modal = (resizable == "yes") ? "no" : "yes";
1725
1726                 if (tinyMCE.isGecko && tinyMCE.isMac)
1727                     modal = "no";
1728
1729                 if (template['close_previous'] != "no")
1730                     try {tinyMCE.lastWindow.close();} catch (ex) {}
1731
1732                 var win = window.open(url, "mcePopup" + new Date().getTime(), "top=" + y + ",left=" + x + ",scrollbars=" + scrollbars + ",dialog=" + modal + ",minimizable=" + resizable + ",modal=" + modal + ",width=" + width + ",height=" + height + ",resizable=" + resizable);
1733                 if (win == null) {
1734                     alert(tinyMCELang['lang_popup_blocked']);
1735                     return;
1736                 }
1737
1738                 if (template['close_previous'] != "no")
1739                     tinyMCE.lastWindow = win;
1740
1741                 eval('try { win.resizeTo(width, height); } catch(e) { }');
1742
1743                 // Make it bigger if statusbar is forced
1744                 if (tinyMCE.isGecko) {
1745                     if (win.document.defaultView.statusbar.visible)
1746                         win.resizeBy(0, tinyMCE.isMac ? 10 : 24);
1747                 }
1748
1749                 win.focus();
1750             }
1751         }
1752     },
1753
1754     closeWindow : function(win) {
1755         win.close();
1756     },
1757
1758     getVisualAidClass : function(class_name, state) {
1759         var aidClass = tinyMCE.settings['visual_table_class'];
1760
1761         if (typeof(state) == "undefined")
1762             state = tinyMCE.settings['visual'];
1763
1764         // Split
1765         var classNames = new Array();
1766         var ar = class_name.split(' ');
1767         for (var i=0; i<ar.length; i++) {
1768             if (ar[i] == aidClass)
1769                 ar[i] = "";
1770
1771             if (ar[i] != "")
1772                 classNames[classNames.length] = ar[i];
1773         }
1774
1775         if (state)
1776             classNames[classNames.length] = aidClass;
1777
1778         // Glue
1779         var className = "";
1780         for (var i=0; i<classNames.length; i++) {
1781             if (i > 0)
1782                 className += " ";
1783
1784             className += classNames[i];
1785         }
1786
1787         return className;
1788     },
1789
1790     handleVisualAid : function(el, deep, state, inst, skip_dispatch) {
1791         if (!el)
1792             return;
1793
1794         if (!skip_dispatch)
1795             tinyMCE.dispatchCallback(inst, 'handle_visual_aid_callback', 'handleVisualAid', el, deep, state, inst);
1796
1797         var tableElement = null;
1798
1799         switch (el.nodeName) {
1800             case "TABLE":
1801                 var oldW = el.style.width;
1802                 var oldH = el.style.height;
1803                 var bo = tinyMCE.getAttrib(el, "border");
1804
1805                 bo = bo == "" || bo == "0" ? true : false;
1806
1807                 tinyMCE.setAttrib(el, "class", tinyMCE.getVisualAidClass(tinyMCE.getAttrib(el, "class"), state && bo));
1808
1809                 el.style.width = oldW;
1810                 el.style.height = oldH;
1811
1812                 for (var y=0; y<el.rows.length; y++) {
1813                     for (var x=0; x<el.rows[y].cells.length; x++) {
1814                         var cn = tinyMCE.getVisualAidClass(tinyMCE.getAttrib(el.rows[y].cells[x], "class"), state && bo);
1815                         tinyMCE.setAttrib(el.rows[y].cells[x], "class", cn);
1816                     }
1817                 }
1818
1819                 break;
1820
1821             case "A":
1822                 var anchorName = tinyMCE.getAttrib(el, "name");
1823
1824                 if (anchorName != '' && state) {
1825                     el.title = anchorName;
1826                     el.className = 'mceItemAnchor';
1827                 } else if (anchorName != '' && !state)
1828                     el.className = '';
1829
1830                 break;
1831         }
1832
1833         if (deep && el.hasChildNodes()) {
1834             for (var i=0; i<el.childNodes.length; i++)
1835                 tinyMCE.handleVisualAid(el.childNodes[i], deep, state, inst, true);
1836         }
1837     },
1838
1839     /*
1840     applyClassesToFonts : function(doc, size) {
1841         var f = doc.getElementsByTagName("font");
1842         for (var i=0; i<f.length; i++) {
1843             var s = tinyMCE.getAttrib(f[i], "size");
1844
1845             if (s != "")
1846                 tinyMCE.setAttrib(f[i], 'class', "mceItemFont" + s);
1847         }
1848
1849         if (typeof(size) != "undefined") {
1850             var css = "";
1851
1852             for (var x=0; x<doc.styleSheets.length; x++) {
1853                 for (var i=0; i<doc.styleSheets[x].rules.length; i++) {
1854                     if (doc.styleSheets[x].rules[i].selectorText == '#mceSpanFonts .mceItemFont' + size) {
1855                         css = doc.styleSheets[x].rules[i].style.cssText;
1856                         break;
1857                     }
1858                 }
1859
1860                 if (css != "")
1861                     break;
1862             }
1863
1864             if (doc.styleSheets[0].rules[0].selectorText == "FONT")
1865                 doc.styleSheets[0].removeRule(0);
1866
1867             doc.styleSheets[0].addRule("FONT", css, 0);
1868         }
1869     },
1870     */
1871
1872     fixGeckoBaseHREFBug : function(m, e, h) {
1873         var nl, i, a, n, xsrc, xhref, el;
1874
1875         if (tinyMCE.isGecko) {
1876             if (m == 1) {
1877                 h = h.replace(/\ssrc=/gi, " mce_tsrc=");
1878                 h = h.replace(/\shref=/gi, " mce_thref=");
1879
1880                 return h;
1881             } else {
1882                 el = new Array('a','img','select','area','iframe','base','input','script','embed','object','link');
1883
1884                 for (a=0; a<el.length; a++) {
1885                     n = e.getElementsByTagName(el[a]);
1886
1887                     for (i=0; i<n.length; i++) {
1888                         xsrc = tinyMCE.getAttrib(n[i], "mce_tsrc");
1889                         xhref = tinyMCE.getAttrib(n[i], "mce_thref");
1890
1891                         if (xsrc != "") {
1892                             try {
1893                                 n[i].src = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'], xsrc);
1894                             } catch (e) {
1895                                 // Ignore, Firefox cast exception if local file wasn't found
1896                             }
1897
1898                             n[i].removeAttribute("mce_tsrc");
1899                         }
1900
1901                         if (xhref != "") {
1902                             try {
1903                                 n[i].href = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'], xhref);
1904                             } catch (e) {
1905                                 // Ignore, Firefox cast exception if local file wasn't found
1906                             }
1907
1908                             n[i].removeAttribute("mce_thref");
1909                         }
1910                     }
1911                 }
1912
1913                 el = tinyMCE.selectNodes(e, function(n) {
1914                     if (n.nodeType == 3 || n.nodeType == 8) {
1915                         n.nodeValue = n.nodeValue.replace(/\smce_tsrc=/gi, " src=");
1916                         n.nodeValue = n.nodeValue.replace(/\smce_thref=/gi, " href=");
1917                     }
1918
1919                     return false;
1920                 });
1921             }
1922         }
1923
1924         return h;
1925     },
1926
1927     _setHTML : function(doc, html_content) {
1928         // Force closed anchors open
1929         //html_content = html_content.replace(new RegExp('<a(.*?)/>', 'gi'), '<a$1></a>');
1930
1931         html_content = tinyMCE.cleanupHTMLCode(html_content);
1932
1933         // Try innerHTML if it fails use pasteHTML in MSIE
1934         try {
1935             tinyMCE.setInnerHTML(doc.body, html_content);
1936         } catch (e) {
1937             if (this.isMSIE)
1938                 doc.body.createTextRange().pasteHTML(html_content);
1939         }
1940
1941         // Content duplication bug fix
1942         if (tinyMCE.isMSIE && tinyMCE.settings['fix_content_duplication']) {
1943             // Remove P elements in P elements
1944             var paras = doc.getElementsByTagName("P");
1945             for (var i=0; i<paras.length; i++) {
1946                 var node = paras[i];
1947                 while ((node = node.parentNode) != null) {
1948                     if (node.nodeName == "P")
1949                         node.outerHTML = node.innerHTML;
1950                 }
1951             }
1952
1953             // Content duplication bug fix (Seems to be word crap)
1954             var html = doc.body.innerHTML;
1955 /*
1956             if (html.indexOf('="mso') != -1) {
1957                 for (var i=0; i<doc.body.all.length; i++) {
1958                     var el = doc.body.all[i];
1959                     el.removeAttribute("className","",0);
1960                     el.removeAttribute("style","",0);
1961                 }
1962
1963                 html = doc.body.innerHTML;
1964                 html = tinyMCE.regexpReplace(html, "<o:p><\/o:p>", "<br />");
1965                 html = tinyMCE.regexpReplace(html, "<o:p>&nbsp;<\/o:p>", "");
1966                 html = tinyMCE.regexpReplace(html, "<st1:.*?>", "");
1967                 html = tinyMCE.regexpReplace(html, "<p><\/p>", "");
1968                 html = tinyMCE.regexpReplace(html, "<p><\/p>\r\n<p><\/p>", "");
1969                 html = tinyMCE.regexpReplace(html, "<p>&nbsp;<\/p>", "<br />");
1970                 html = tinyMCE.regexpReplace(html, "<p>\s*(<p>\s*)?", "<p>");
1971                 html = tinyMCE.regexpReplace(html, "<\/p>\s*(<\/p>\s*)?", "</p>");
1972             }*/
1973
1974             // Always set the htmlText output
1975             tinyMCE.setInnerHTML(doc.body, html);
1976         }
1977
1978         tinyMCE.cleanupAnchors(doc);
1979
1980         if (tinyMCE.getParam("convert_fonts_to_spans"))
1981             tinyMCE.convertSpansToFonts(doc);
1982     },
1983
1984     getEditorId : function(form_element) {
1985         var inst = this.getInstanceById(form_element);
1986         if (!inst)
1987             return null;
1988
1989         return inst.editorId;
1990     },
1991
1992     getInstanceById : function(editor_id) {
1993         var inst = this.instances[editor_id];
1994         if (!inst) {
1995             for (var n in tinyMCE.instances) {
1996                 var instance = tinyMCE.instances[n];
1997                 if (!tinyMCE.isInstance(instance))
1998                     continue;
1999
2000                 if (instance.formTargetElementId == editor_id) {
2001                     inst = instance;
2002                     break;
2003                 }
2004             }
2005         }
2006
2007         return inst;
2008     },
2009
2010     queryInstanceCommandValue : function(editor_id, command) {
2011         var inst = tinyMCE.getInstanceById(editor_id);
2012         if (inst)
2013             return inst.queryCommandValue(command);
2014
2015         return false;
2016     },
2017
2018     queryInstanceCommandState : function(editor_id, command) {
2019         var inst = tinyMCE.getInstanceById(editor_id);
2020         if (inst)
2021             return inst.queryCommandState(command);
2022
2023         return null;
2024     },
2025
2026     setWindowArg : function(n, v) {
2027         this.windowArgs[n] = v;
2028     },
2029
2030     getWindowArg : function(n, d) {
2031         return (typeof(this.windowArgs[n]) == "undefined") ? d : this.windowArgs[n];
2032     },
2033
2034     getCSSClasses : function(editor_id, doc) {
2035         var output = new Array();
2036
2037         // Is cached, use that
2038         if (typeof(tinyMCE.cssClasses) != "undefined")
2039             return tinyMCE.cssClasses;
2040
2041         if (typeof(editor_id) == "undefined" && typeof(doc) == "undefined") {
2042             var instance;
2043
2044             for (var instanceName in tinyMCE.instances) {
2045                 instance = tinyMCE.instances[instanceName];
2046                 if (!tinyMCE.isInstance(instance))
2047                     continue;
2048
2049                 break;
2050             }
2051
2052             doc = instance.getDoc();
2053         }
2054
2055         if (typeof(doc) == "undefined") {
2056             var instance = tinyMCE.getInstanceById(editor_id);
2057             doc = instance.getDoc();
2058         }
2059
2060         if (doc) {
2061             var styles = doc.styleSheets;
2062
2063             if (styles && styles.length > 0) {
2064                 for (var x=0; x<styles.length; x++) {
2065                     var csses = null;
2066
2067                     // Just ignore any errors
2068                     eval("try {var csses = tinyMCE.isMSIE ? doc.styleSheets(" + x + ").rules : styles[" + x + "].cssRules;} catch(e) {}");
2069                     if (!csses)
2070                         return new Array();
2071
2072                     for (var i=0; i<csses.length; i++) {
2073                         var selectorText = csses[i].selectorText;
2074
2075                         // Can be multiple rules per selector
2076                         if (selectorText) {
2077                             var rules = selectorText.split(',');
2078                             for (var c=0; c<rules.length; c++) {
2079                                 var rule = rules[c];
2080
2081                                 // Strip spaces between selectors
2082                                 while (rule.indexOf(' ') == 0)
2083                                     rule = rule.substring(1);
2084
2085                                 // Invalid rule
2086                                 if (rule.indexOf(' ') != -1 || rule.indexOf(':') != -1 || rule.indexOf('mceItem') != -1)
2087                                     continue;
2088
2089                                 if (rule.indexOf(tinyMCE.settings['visual_table_class']) != -1 || rule.indexOf('mceEditable') != -1 || rule.indexOf('mceNonEditable') != -1)
2090                                     continue;
2091
2092                                 // Is class rule
2093                                 if (rule.indexOf('.') != -1) {
2094                                     var cssClass = rule.substring(rule.indexOf('.') + 1);
2095                                     var addClass = true;
2096
2097                                     for (var p=0; p<output.length && addClass; p++) {
2098                                         if (output[p] == cssClass)
2099                                             addClass = false;
2100                                     }
2101
2102                                     if (addClass)
2103                                         output[output.length] = cssClass;
2104                                 }
2105                             }
2106                         }
2107                     }
2108                 }
2109             }
2110         }
2111
2112         // Cache em
2113         if (output.length > 0)
2114             tinyMCE.cssClasses = output;
2115
2116         return output;
2117     },
2118
2119     regexpReplace : function(in_str, reg_exp, replace_str, opts) {
2120         if (in_str == null)
2121             return in_str;
2122
2123         if (typeof(opts) == "undefined")
2124             opts = 'g';
2125
2126         var re = new RegExp(reg_exp, opts);
2127         return in_str.replace(re, replace_str);
2128     },
2129
2130     trim : function(s) {
2131         return s.replace(/^\s*|\s*$/g, "");
2132     },
2133
2134     cleanupEventStr : function(s) {
2135         s = "" + s;
2136         s = s.replace('function anonymous()\n{\n', '');
2137         s = s.replace('\n}', '');
2138         s = s.replace(/^return true;/gi, ''); // Remove event blocker
2139
2140         return s;
2141     },
2142
2143     getControlHTML : function(c) {
2144         var i, l, n, o, v;
2145
2146         l = tinyMCE.plugins;
2147         for (n in l) {
2148             o = l[n];
2149
2150             if (o.getControlHTML && (v = o.getControlHTML(c)) != '')
2151                 return tinyMCE.replaceVar(v, "pluginurl", o.baseURL);
2152         }
2153
2154         o = tinyMCE.themes[tinyMCE.settings['theme']];
2155         if (o.getControlHTML && (v = o.getControlHTML(c)) != '')
2156             return v;
2157
2158         return '';
2159     },
2160
2161     evalFunc : function(f, idx, a) {
2162         var s = '(', i;
2163
2164         for (i=idx; i<a.length; i++) {
2165             s += 'a[' + i + ']';
2166
2167             if (i < a.length-1)
2168                 s += ',';
2169         }
2170
2171         s += ');';
2172
2173         return eval("f" + s);
2174     },
2175
2176     dispatchCallback : function(i, p, n) {
2177         return this.callFunc(i, p, n, 0, this.dispatchCallback.arguments);
2178     },
2179
2180     executeCallback : function(i, p, n) {
2181         return this.callFunc(i, p, n, 1, this.executeCallback.arguments);
2182     },
2183
2184     execCommandCallback : function(i, p, n) {
2185         return this.callFunc(i, p, n, 2, this.execCommandCallback.arguments);
2186     },
2187
2188     callFunc : function(ins, p, n, m, a) {
2189         var l, i, on, o, s, v;
2190
2191         s = m == 2;
2192
2193         l = tinyMCE.getParam(p, '');
2194
2195         if (l != '' && (v = tinyMCE.evalFunc(typeof(l) == "function" ? l : eval(l), 3, a)) == s && m > 0)
2196             return true;
2197
2198         if (ins != null) {
2199             for (i=0, l = ins.plugins; i<l.length; i++) {
2200                 o = tinyMCE.plugins[l[i]];
2201
2202                 if (o[n] && (v = tinyMCE.evalFunc(o[n], 3, a)) == s && m > 0)
2203                     return true;
2204             }
2205         }
2206
2207         l = tinyMCE.themes;
2208         for (on in l) {
2209             o = l[on];
2210
2211             if (o[n] && (v = tinyMCE.evalFunc(o[n], 3, a)) == s && m > 0)
2212                 return true;
2213         }
2214
2215         return false;
2216     },
2217
2218     xmlEncode : function(s) {
2219         s = "" + s;
2220         s = s.replace(/&/g, '&amp;');
2221         s = s.replace(new RegExp('"', 'g'), '&quot;');
2222         s = s.replace(/\'/g, '&#39;'); // &apos; is not working in MSIE
2223         s = s.replace(/</g, '&lt;');
2224         s = s.replace(/>/g, '&gt;');
2225
2226         return s;
2227     },
2228
2229     extend : function(p, np) {
2230         var o = {};
2231
2232         o.parent = p;
2233
2234         for (n in p)
2235             o[n] = p[n];
2236
2237         for (n in np)
2238             o[n] = np[n];
2239
2240         return o;
2241     },
2242
2243     hideMenus : function() {
2244         var e = tinyMCE.lastSelectedMenuBtn;
2245
2246         if (tinyMCE.lastMenu) {
2247             tinyMCE.lastMenu.hide();
2248             tinyMCE.lastMenu = null;
2249         }
2250
2251         if (e) {
2252             tinyMCE.switchClass(e, tinyMCE.lastMenuBtnClass);
2253             tinyMCE.lastSelectedMenuBtn = null;
2254         }
2255     },
2256
2257     explode : function(d, s) {
2258         var ar = s.split(d), oar = new Array(), i;
2259     
2260         for (i = 0; i<ar.length; i++) {
2261             if (ar[i] != "")
2262                 oar[oar.length] = ar[i];
2263         }
2264     
2265         return oar;
2266     }
2267 };
2268
2269 // Global instances
2270 var TinyMCE = TinyMCE_Engine; // Compatiblity with gzip compressors
2271 var tinyMCE = new TinyMCE_Engine();
2272 var tinyMCELang = {};
2273
2274 /* file:jscripts/tiny_mce/classes/TinyMCE_Control.class.js */
2275
2276 function TinyMCE_Control(settings) {
2277     var t, i, to, fu, p, x, fn, fu, pn, s = settings;
2278
2279     this.undoRedoLevel = true;
2280     this.isTinyMCE_Control = true;
2281
2282     // Default settings
2283     this.settings = s;
2284     this.settings['theme'] = tinyMCE.getParam("theme", "default");
2285     this.settings['width'] = tinyMCE.getParam("width", -1);
2286     this.settings['height'] = tinyMCE.getParam("height", -1);
2287     this.selection = new TinyMCE_Selection(this);
2288     this.undoRedo = new TinyMCE_UndoRedo(this);
2289     this.cleanup = new TinyMCE_Cleanup();
2290     this.shortcuts = new Array();
2291     this.hasMouseMoved = false;
2292
2293     this.cleanup.init({
2294         valid_elements : s.valid_elements,
2295         extended_valid_elements : s.extended_valid_elements,
2296         entities : s.entities,
2297         entity_encoding : s.entity_encoding,
2298         debug : s.cleanup_debug,
2299         url_converter : 'TinyMCE_Cleanup.prototype._urlConverter',
2300         indent : s.apply_source_formatting,
2301         invalid_elements : s.invalid_elements,
2302         verify_html : s.verify_html,
2303         fix_content_duplication : s.fix_content_duplication
2304     });
2305
2306     // Wrap old theme
2307     t = this.settings['theme'];
2308     if (!tinyMCE.hasTheme(t)) {
2309         fn = tinyMCE.callbacks;
2310         to = {};
2311
2312         for (i=0; i<fn.length; i++) {
2313             if ((fu = window['TinyMCE_' + t + "_" + fn[i]]))
2314                 to[fn[i]] = fu;
2315         }
2316
2317         tinyMCE.addTheme(t, to);
2318     }
2319
2320     // Wrap old plugins
2321     this.plugins = new Array();
2322     p = tinyMCE.getParam('plugins', '', true, ',');
2323     if (p.length > 0) {
2324         for (i=0; i<p.length; i++) {
2325             pn = p[i];
2326
2327             if (pn.charAt(0) == '-')
2328                 pn = pn.substring(1);
2329
2330             if (!tinyMCE.hasPlugin(pn)) {
2331                 fn = tinyMCE.callbacks;
2332                 to = {};
2333
2334                 for (x=0; x<fn.length; x++) {
2335                     if ((fu = window['TinyMCE_' + pn + "_" + fn[x]]))
2336                         to[fn[x]] = fu;
2337                 }
2338
2339                 tinyMCE.addPlugin(pn, to);
2340             }
2341
2342             this.plugins[this.plugins.length] = pn; 
2343         }
2344     }
2345 };
2346
2347 TinyMCE_Control.prototype = {
2348     hasPlugin : function(n) {
2349         var i;
2350
2351         for (i=0; i<this.plugins.length; i++) {
2352             if (this.plugins[i] == n)
2353                 return true;
2354         }
2355
2356         return false;
2357     },
2358
2359     addPlugin : function(n, p) {
2360         if (!this.hasPlugin(n)) {
2361             tinyMCE.addPlugin(n, p);
2362             this.plugins[this.plugins.length] = n;
2363         }
2364     },
2365
2366     repaint : function() {
2367         if (tinyMCE.isMSIE && !tinyMCE.isOpera)
2368             return;
2369
2370         try {
2371             var s = this.selection;
2372             var b = s.getBookmark(true);
2373             this.getBody().style.display = 'none';
2374             this.getDoc().execCommand('selectall', false, null);
2375             this.getSel().collapseToStart();
2376             this.getBody().style.display = 'block';
2377             s.moveToBookmark(b);
2378         } catch (ex) {
2379             // Ignore
2380         }
2381     },
2382
2383     switchSettings : function() {
2384         if (tinyMCE.configs.length > 1 && tinyMCE.currentConfig != this.settings['index']) {
2385             tinyMCE.settings = this.settings;
2386             tinyMCE.currentConfig = this.settings['index'];
2387         }
2388     },
2389
2390     getBody : function() {
2391         return this.getDoc().body;
2392     },
2393
2394     getDoc : function() {
2395         return this.contentWindow.document;
2396     },
2397
2398     getWin : function() {
2399         return this.contentWindow;
2400     },
2401
2402     addShortcut : function(m, k, d, cmd, ui, va) {
2403         var n = typeof(k) == "number", ie = tinyMCE.isMSIE, c, sc, i;
2404         var scl = this.shortcuts;
2405
2406         if (!tinyMCE.getParam('custom_shortcuts'))
2407             return false;
2408
2409         m = m.toLowerCase();
2410         k = ie && !n ? k.toUpperCase() : k;
2411         c = n ? null : k.charCodeAt(0);
2412         d = d && d.indexOf('lang_') == 0 ? tinyMCE.getLang(d) : d;
2413
2414         sc = {
2415             alt : m.indexOf('alt') != -1,
2416             ctrl : m.indexOf('ctrl') != -1,
2417             shift : m.indexOf('shift') != -1,
2418             charCode : c,
2419             keyCode : n ? k : (ie ? c : null),
2420             desc : d,
2421             cmd : cmd,
2422             ui : ui,
2423             val : va
2424         };
2425
2426         for (i=0; i<scl.length; i++) {
2427             if (sc.alt == scl[i].alt && sc.ctrl == scl[i].ctrl && sc.shift == scl[i].shift
2428                 && sc.charCode == scl[i].charCode && sc.keyCode == scl[i].keyCode) {
2429                 return false;
2430             }
2431         }
2432
2433         scl[scl.length] = sc;
2434
2435         return true;
2436     },
2437
2438     handleShortcut : function(e) {
2439         var i, s = this.shortcuts, o;
2440
2441         for (i=0; i<s.length; i++) {
2442             o = s[i];
2443             if (o.alt == e.altKey && o.ctrl == e.ctrlKey && (o.keyCode == e.keyCode || o.charCode == e.charCode)) {
2444                 if (o.cmd && (e.type == "keydown" || (e.type == "keypress" && !tinyMCE.isOpera)))
2445                     tinyMCE.execCommand(o.cmd, o.ui, o.val);
2446
2447                 tinyMCE.cancelEvent(e);
2448                 return true;
2449             }
2450         }
2451
2452         return false;
2453     },
2454
2455     autoResetDesignMode : function() {
2456         // Add fix for tab/style.display none/block problems in Gecko
2457         if (!tinyMCE.isMSIE && this.isHidden() && tinyMCE.getParam('auto_reset_designmode'))
2458             eval('try { this.getDoc().designMode = "On"; } catch(e) {}');
2459     },
2460
2461     isHidden : function() {
2462         if (tinyMCE.isMSIE)
2463             return false;
2464
2465         var s = this.getSel();
2466
2467         // Weird, wheres that cursor selection?
2468         return (!s || !s.rangeCount || s.rangeCount == 0);
2469     },
2470
2471     isDirty : function() {
2472         // Is content modified and not in a submit procedure
2473         return this.startContent != tinyMCE.trim(this.getBody().innerHTML) && !tinyMCE.isNotDirty;
2474     },
2475
2476     _mergeElements : function(scmd, pa, ch, override) {
2477         if (scmd == "removeformat") {
2478             pa.className = "";
2479             pa.style.cssText = "";
2480             ch.className = "";
2481             ch.style.cssText = "";
2482             return;
2483         }
2484
2485         var st = tinyMCE.parseStyle(tinyMCE.getAttrib(pa, "style"));
2486         var stc = tinyMCE.parseStyle(tinyMCE.getAttrib(ch, "style"));
2487         var className = tinyMCE.getAttrib(pa, "class");
2488
2489         className += " " + tinyMCE.getAttrib(ch, "class");
2490
2491         if (override) {
2492             for (var n in st) {
2493                 if (typeof(st[n]) == 'function')
2494                     continue;
2495
2496                 stc[n] = st[n];
2497             }
2498         } else {
2499             for (var n in stc) {
2500                 if (typeof(stc[n]) == 'function')
2501                     continue;
2502
2503                 st[n] = stc[n];
2504             }
2505         }
2506
2507         tinyMCE.setAttrib(pa, "style", tinyMCE.serializeStyle(st));
2508         tinyMCE.setAttrib(pa, "class", tinyMCE.trim(className));
2509         ch.className = "";
2510         ch.style.cssText = "";
2511         ch.removeAttribute("class");
2512         ch.removeAttribute("style");
2513     },
2514
2515     _setUseCSS : function(b) {
2516         var d = this.getDoc();
2517
2518         try {d.execCommand("useCSS", false, !b);} catch (ex) {}
2519         try {d.execCommand("styleWithCSS", false, b);} catch (ex) {}
2520
2521         if (!tinyMCE.getParam("table_inline_editing"))
2522             try {d.execCommand('enableInlineTableEditing', false, "false");} catch (ex) {}
2523
2524         if (!tinyMCE.getParam("object_resizing"))
2525             try {d.execCommand('enableObjectResizing', false, "false");} catch (ex) {}
2526     },
2527
2528     execCommand : function(command, user_interface, value) {
2529         var doc = this.getDoc();
2530         var win = this.getWin();
2531         var focusElm = this.getFocusElement();
2532
2533         // Is non udno specific command
2534         if (!new RegExp('mceStartTyping|mceEndTyping|mceBeginUndoLevel|mceEndUndoLevel|mceAddUndoLevel', 'gi').test(command))
2535             this.undoBookmark = null;
2536
2537         if (this.lastSafariSelection && !new RegExp('mceStartTyping|mceEndTyping|mceBeginUndoLevel|mceEndUndoLevel|mceAddUndoLevel', 'gi').test(command)) {
2538             this.selection.moveToBookmark(this.lastSafariSelection);
2539             tinyMCE.selectedElement = this.lastSafariSelectedElement;
2540         }
2541
2542         // Mozilla issue
2543         if (!tinyMCE.isMSIE && !this.useCSS) {
2544             this._setUseCSS(false);
2545             this.useCSS = true;
2546         }
2547
2548         //debug("command: " + command + ", user_interface: " + user_interface + ", value: " + value);
2549         this.contentDocument = doc; // <-- Strange, unless this is applied Mozilla 1.3 breaks
2550
2551         if (tinyMCE.execCommandCallback(this, 'execcommand_callback', 'execCommand', this.editorId, this.getBody(), command, user_interface, value))
2552             return;
2553
2554         // Fix align on images
2555         if (focusElm && focusElm.nodeName == "IMG") {
2556             var align = focusElm.getAttribute('align');
2557             var img = command == "JustifyCenter" ? focusElm.cloneNode(false) : focusElm;
2558
2559             switch (command) {
2560                 case "JustifyLeft":
2561                     if (align == 'left')
2562                         img.removeAttribute('align');
2563                     else
2564                         img.setAttribute('align', 'left');
2565
2566                     // Remove the div
2567                     var div = focusElm.parentNode;
2568                     if (div && div.nodeName == "DIV" && div.childNodes.length == 1 && div.parentNode)
2569                         div.parentNode.replaceChild(img, div);
2570
2571                     this.selection.selectNode(img);
2572                     this.repaint();
2573                     tinyMCE.triggerNodeChange();
2574                     return;
2575
2576                 case "JustifyCenter":
2577                     img.removeAttribute('align');
2578
2579                     // Is centered
2580                     var div = tinyMCE.getParentElement(focusElm, "div");
2581                     if (div && div.style.textAlign == "center") {
2582                         // Remove div
2583                         if (div.nodeName == "DIV" && div.childNodes.length == 1 && div.parentNode)
2584                             div.parentNode.replaceChild(img, div);
2585                     } else {
2586                         // Add div
2587                         var div = this.getDoc().createElement("div");
2588                         div.style.textAlign = 'center';
2589                         div.appendChild(img);
2590                         focusElm.parentNode.replaceChild(div, focusElm);
2591                     }
2592
2593                     this.selection.selectNode(img);
2594                     this.repaint();
2595                     tinyMCE.triggerNodeChange();
2596                     return;
2597
2598                 case "JustifyRight":
2599                     if (align == 'right')
2600                         img.removeAttribute('align');
2601                     else
2602                         img.setAttribute('align', 'right');
2603
2604                     // Remove the div
2605                     var div = focusElm.parentNode;
2606                     if (div && div.nodeName == "DIV" && div.childNodes.length == 1 && div.parentNode)
2607                         div.parentNode.replaceChild(img, div);
2608
2609                     this.selection.selectNode(img);
2610                     this.repaint();
2611                     tinyMCE.triggerNodeChange();
2612                     return;
2613             }
2614         }
2615
2616         if (tinyMCE.settings['force_br_newlines']) {
2617             var alignValue = "";
2618
2619             if (doc.selection.type != "Control") {
2620                 switch (command) {
2621                         case "JustifyLeft":
2622                             alignValue = "left";
2623                             break;
2624
2625                         case "JustifyCenter":
2626                             alignValue = "center";
2627                             break;
2628
2629                         case "JustifyFull":
2630                             alignValue = "justify";
2631                             break;
2632
2633                         case "JustifyRight":
2634                             alignValue = "right";
2635                             break;
2636                 }
2637
2638                 if (alignValue != "") {
2639                     var rng = doc.selection.createRange();
2640
2641                     if ((divElm = tinyMCE.getParentElement(rng.parentElement(), "div")) != null)
2642                         divElm.setAttribute("align", alignValue);
2643                     else if (rng.pasteHTML && rng.htmlText.length > 0)
2644                         rng.pasteHTML('<div align="' + alignValue + '">' + rng.htmlText + "</div>");
2645
2646                     tinyMCE.triggerNodeChange();
2647                     return;
2648                 }
2649             }
2650         }
2651
2652         switch (command) {
2653             case "mceRepaint":
2654                 this.repaint();
2655                 return true;
2656
2657             case "InsertUnorderedList":
2658             case "InsertOrderedList":
2659                 var tag = (command == "InsertUnorderedList") ? "ul" : "ol";
2660
2661                 if (tinyMCE.isSafari)
2662                     this.execCommand("mceInsertContent", false, "<" + tag + "><li>&nbsp;</li><" + tag + ">");
2663                 else
2664                     this.getDoc().execCommand(command, user_interface, value);
2665
2666                 tinyMCE.triggerNodeChange();
2667                 break;
2668
2669             case "Strikethrough":
2670                 if (tinyMCE.isSafari)
2671                     this.execCommand("mceInsertContent", false, "<strike>" + this.selection.getSelectedHTML() + "</strike>");
2672                 else
2673                     this.getDoc().execCommand(command, user_interface, value);
2674
2675                 tinyMCE.triggerNodeChange();
2676                 break;
2677
2678             case "mceSelectNode":
2679                 this.selection.selectNode(value);
2680                 tinyMCE.triggerNodeChange();
2681                 tinyMCE.selectedNode = value;
2682                 break;
2683
2684             case "FormatBlock":
2685                 if (value == null || value == "") {
2686                     var elm = tinyMCE.getParentElement(this.getFocusElement(), "p,div,h1,h2,h3,h4,h5,h6,pre,address,blockquote,dt,dl,dd,samp");
2687
2688                     if (elm)
2689                         this.execCommand("mceRemoveNode", false, elm);
2690                 } else {
2691                     if (tinyMCE.isGecko && new RegExp('<(div|blockquote|code|dt|dd|dl|samp)>', 'gi').test(value))
2692                         value = value.replace(/[^a-z]/gi, '');
2693
2694                     if (tinyMCE.isMSIE && new RegExp('blockquote|code|samp', 'gi').test(value)) {
2695                         var b = this.selection.getBookmark();
2696                         this.getDoc().execCommand("FormatBlock", false, '<p>');
2697                         tinyMCE.renameElement(tinyMCE.getParentBlockElement(this.getFocusElement()), value);
2698                         this.selection.moveToBookmark(b);
2699                     } else
2700                         this.getDoc().execCommand("FormatBlock", false, value);
2701                 }
2702
2703                 tinyMCE.triggerNodeChange();
2704
2705                 break;
2706
2707             case "mceRemoveNode":
2708                 if (!value)
2709                     value = tinyMCE.getParentElement(this.getFocusElement());
2710
2711                 if (tinyMCE.isMSIE) {
2712                     value.outerHTML = value.innerHTML;
2713                 } else {
2714                     var rng = value.ownerDocument.createRange();
2715                     rng.setStartBefore(value);
2716                     rng.setEndAfter(value);
2717                     rng.deleteContents();
2718                     rng.insertNode(rng.createContextualFragment(value.innerHTML));
2719                 }
2720
2721                 tinyMCE.triggerNodeChange();
2722
2723                 break;
2724
2725             case "mceSelectNodeDepth":
2726                 var parentNode = this.getFocusElement();
2727                 for (var i=0; parentNode; i++) {
2728                     if (parentNode.nodeName.toLowerCase() == "body")
2729                         break;
2730
2731                     if (parentNode.nodeName.toLowerCase() == "#text") {
2732                         i--;
2733                         parentNode = parentNode.parentNode;
2734                         continue;
2735                     }
2736
2737                     if (i == value) {
2738                         this.selection.selectNode(parentNode, false);
2739                         tinyMCE.triggerNodeChange();
2740                         tinyMCE.selectedNode = parentNode;
2741                         return;
2742                     }
2743
2744                     parentNode = parentNode.parentNode;
2745                 }
2746
2747                 break;
2748
2749             case "SetStyleInfo":
2750                 var rng = this.getRng();
2751                 var sel = this.getSel();
2752                 var scmd = value['command'];
2753                 var sname = value['name'];
2754                 var svalue = value['value'] == null ? '' : value['value'];
2755                 //var svalue = value['value'] == null ? '' : value['value'];
2756                 var wrapper = value['wrapper'] ? value['wrapper'] : "span";
2757                 var parentElm = null;
2758                 var invalidRe = new RegExp("^BODY|HTML$", "g");
2759                 var invalidParentsRe = tinyMCE.settings['merge_styles_invalid_parents'] != '' ? new RegExp(tinyMCE.settings['merge_styles_invalid_parents'], "gi") : null;
2760
2761                 // Whole element selected check
2762                 if (tinyMCE.isMSIE) {
2763                     // Control range
2764                     if (rng.item)
2765                         parentElm = rng.item(0);
2766                     else {
2767                         var pelm = rng.parentElement();
2768                         var prng = doc.selection.createRange();
2769                         prng.moveToElementText(pelm);
2770
2771                         if (rng.htmlText == prng.htmlText || rng.boundingWidth == 0) {
2772                             if (invalidParentsRe == null || !invalidParentsRe.test(pelm.nodeName))
2773                                 parentElm = pelm;
2774                         }
2775                     }
2776                 } else {
2777                     var felm = this.getFocusElement();
2778                     if (sel.isCollapsed || (new RegExp('td|tr|tbody|table', 'gi').test(felm.nodeName) && sel.anchorNode == felm.parentNode))
2779                         parentElm = felm;
2780                 }
2781
2782                 // Whole element selected
2783                 if (parentElm && !invalidRe.test(parentElm.nodeName)) {
2784                     if (scmd == "setstyle")
2785                         tinyMCE.setStyleAttrib(parentElm, sname, svalue);
2786
2787                     if (scmd == "setattrib")
2788                         tinyMCE.setAttrib(parentElm, sname, svalue);
2789
2790                     if (scmd == "removeformat") {
2791                         parentElm.style.cssText = '';
2792                         tinyMCE.setAttrib(parentElm, 'class', '');
2793                     }
2794
2795                     // Remove style/attribs from all children
2796                     var ch = tinyMCE.getNodeTree(parentElm, new Array(), 1);
2797                     for (var z=0; z<ch.length; z++) {
2798                         if (ch[z] == parentElm)
2799                             continue;
2800
2801                         if (scmd == "setstyle")
2802                             tinyMCE.setStyleAttrib(ch[z], sname, '');
2803
2804                         if (scmd == "setattrib")
2805                             tinyMCE.setAttrib(ch[z], sname, '');
2806
2807                         if (scmd == "removeformat") {
2808                             ch[z].style.cssText = '';
2809                             tinyMCE.setAttrib(ch[z], 'class', '');
2810                         }
2811                     }
2812                 } else {
2813                     this._setUseCSS(false); // Bug in FF when running in fullscreen
2814                     doc.execCommand("FontName", false, "#mce_temp_font#");
2815                     var elementArray = tinyMCE.getElementsByAttributeValue(this.getBody(), "font", "face", "#mce_temp_font#");
2816
2817                     // Change them all
2818                     for (var x=0; x<elementArray.length; x++) {
2819                         elm = elementArray[x];
2820                         if (elm) {
2821                             var spanElm = doc.createElement(wrapper);
2822
2823                             if (scmd == "setstyle")
2824                                 tinyMCE.setStyleAttrib(spanElm, sname, svalue);
2825
2826                             if (scmd == "setattrib")
2827                                 tinyMCE.setAttrib(spanElm, sname, svalue);
2828
2829                             if (scmd == "removeformat") {
2830                                 spanElm.style.cssText = '';
2831                                 tinyMCE.setAttrib(spanElm, 'class', '');
2832                             }
2833
2834                             if (elm.hasChildNodes()) {
2835                                 for (var i=0; i<elm.childNodes.length; i++)
2836                                     spanElm.appendChild(elm.childNodes[i].cloneNode(true));
2837                             }
2838
2839                             spanElm.setAttribute("mce_new", "true");
2840                             elm.parentNode.replaceChild(spanElm, elm);
2841
2842                             // Remove style/attribs from all children
2843                             var ch = tinyMCE.getNodeTree(spanElm, new Array(), 1);
2844                             for (var z=0; z<ch.length; z++) {
2845                                 if (ch[z] == spanElm)
2846                                     continue;
2847
2848                                 if (scmd == "setstyle")
2849                                     tinyMCE.setStyleAttrib(ch[z], sname, '');
2850
2851                                 if (scmd == "setattrib")
2852                                     tinyMCE.setAttrib(ch[z], sname, '');
2853
2854                                 if (scmd == "removeformat") {
2855                                     ch[z].style.cssText = '';
2856                                     tinyMCE.setAttrib(ch[z], 'class', '');
2857                                 }
2858                             }
2859                         }
2860                     }
2861                 }
2862
2863                 // Cleaup wrappers
2864                 var nodes = doc.getElementsByTagName(wrapper);
2865                 for (var i=nodes.length-1; i>=0; i--) {
2866                     var elm = nodes[i];
2867                     var isNew = tinyMCE.getAttrib(elm, "mce_new") == "true";
2868
2869                     elm.removeAttribute("mce_new");
2870
2871                     // Is only child a element
2872                     if (elm.childNodes && elm.childNodes.length == 1 && elm.childNodes[0].nodeType == 1) {
2873                         //tinyMCE.debug("merge1" + isNew);
2874                         this._mergeElements(scmd, elm, elm.childNodes[0], isNew);
2875                         continue;
2876                     }
2877
2878                     // Is I the only child
2879                     if (elm.parentNode.childNodes.length == 1 && !invalidRe.test(elm.nodeName) && !invalidRe.test(elm.parentNode.nodeName)) {
2880                         //tinyMCE.debug("merge2" + isNew + "," + elm.nodeName + "," + elm.parentNode.nodeName);
2881                         if (invalidParentsRe == null || !invalidParentsRe.test(elm.parentNode.nodeName))
2882                             this._mergeElements(scmd, elm.parentNode, elm, false);
2883                     }
2884                 }
2885
2886                 // Remove empty wrappers
2887                 var nodes = doc.getElementsByTagName(wrapper);
2888                 for (var i=nodes.length-1; i>=0; i--) {
2889                     var elm = nodes[i];
2890                     var isEmpty = true;
2891
2892                     // Check if it has any attribs
2893                     var tmp = doc.createElement("body");
2894                     tmp.appendChild(elm.cloneNode(false));
2895
2896                     // Is empty span, remove it
2897                     tmp.innerHTML = tmp.innerHTML.replace(new RegExp('style=""|class=""', 'gi'), '');
2898                     //tinyMCE.debug(tmp.innerHTML);
2899                     if (new RegExp('<span>', 'gi').test(tmp.innerHTML)) {
2900                         for (var x=0; x<elm.childNodes.length; x++) {
2901                             if (elm.parentNode != null)
2902                                 elm.parentNode.insertBefore(elm.childNodes[x].cloneNode(true), elm);
2903                         }
2904
2905                         elm.parentNode.removeChild(elm);
2906                     }
2907                 }
2908
2909                 // Re add the visual aids
2910                 if (scmd == "removeformat")
2911                     tinyMCE.handleVisualAid(this.getBody(), true, this.visualAid, this);
2912
2913                 tinyMCE.triggerNodeChange();
2914
2915                 break;
2916
2917             case "FontName":
2918                 if (value == null) {
2919                     var s = this.getSel();
2920
2921                     // Find font and select it
2922                     if (tinyMCE.isGecko && s.isCollapsed) {
2923                         var f = tinyMCE.getParentElement(this.getFocusElement(), "font");
2924
2925                         if (f != null)
2926                             this.selection.selectNode(f, false);
2927                     }
2928
2929                     // Remove format
2930                     this.getDoc().execCommand("RemoveFormat", false, null);
2931
2932                     // Collapse range if font was found
2933                     if (f != null && tinyMCE.isGecko) {
2934                         var r = this.getRng().cloneRange();
2935                         r.collapse(true);
2936                         s.removeAllRanges();
2937                         s.addRange(r);
2938                     }
2939                 } else
2940                     this.getDoc().execCommand('FontName', false, value);
2941
2942                 if (tinyMCE.isGecko)
2943                     window.setTimeout('tinyMCE.triggerNodeChange(false);', 1);
2944
2945                 return;
2946
2947             case "FontSize":
2948                 this.getDoc().execCommand('FontSize', false, value);
2949
2950                 if (tinyMCE.isGecko)
2951                     window.setTimeout('tinyMCE.triggerNodeChange(false);', 1);
2952
2953                 return;
2954
2955             case "forecolor":
2956                 this.getDoc().execCommand('forecolor', false, value);
2957                 break;
2958
2959             case "HiliteColor":
2960                 if (tinyMCE.isGecko) {
2961                     this._setUseCSS(true);
2962                     this.getDoc().execCommand('hilitecolor', false, value);
2963                     this._setUseCSS(false);
2964                 } else
2965                     this.getDoc().execCommand('BackColor', false, value);
2966                 break;
2967
2968             case "Cut":
2969             case "Copy":
2970             case "Paste":
2971                 var cmdFailed = false;
2972
2973                 // Try executing command
2974                 eval('try {this.getDoc().execCommand(command, user_interface, value);} catch (e) {cmdFailed = true;}');
2975
2976                 if (tinyMCE.isOpera && cmdFailed)
2977                     alert('Currently not supported by your browser, use keyboard shortcuts instead.');
2978
2979                 // Alert error in gecko if command failed
2980                 if (tinyMCE.isGecko && cmdFailed) {
2981                     // Confirm more info
2982                     if (confirm(tinyMCE.entityDecode(tinyMCE.getLang('lang_clipboard_msg'))))
2983                         window.open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', 'mceExternal');
2984
2985                     return;
2986                 } else
2987                     tinyMCE.triggerNodeChange();
2988             break;
2989
2990             case "mceSetContent":
2991                 if (!value)
2992                     value = "";
2993
2994                 // Call custom cleanup code
2995                 value = tinyMCE.storeAwayURLs(value);
2996                 value = tinyMCE._customCleanup(this, "insert_to_editor", value);
2997                 tinyMCE._setHTML(doc, value);
2998                 tinyMCE.setInnerHTML(doc.body, tinyMCE._cleanupHTML(this, doc, tinyMCE.settings, doc.body));
2999                 tinyMCE.convertAllRelativeURLs(doc.body);
3000
3001                 // Cleanup any mess left from storyAwayURLs
3002                 tinyMCE._removeInternal(this.getBody());
3003
3004                 // When editing always use fonts internaly
3005                 if (tinyMCE.getParam("convert_fonts_to_spans"))
3006                     tinyMCE.convertSpansToFonts(doc);
3007
3008                 tinyMCE.handleVisualAid(doc.body, true, this.visualAid, this);
3009                 tinyMCE._setEventsEnabled(doc.body, false);
3010                 return true;
3011
3012             case "mceCleanup":
3013                 var b = this.selection.getBookmark();
3014                 tinyMCE._setHTML(this.contentDocument, this.getBody().innerHTML);
3015                 tinyMCE.setInnerHTML(this.getBody(), tinyMCE._cleanupHTML(this, this.contentDocument, this.settings, this.getBody(), this.visualAid));
3016                 tinyMCE.convertAllRelativeURLs(doc.body);
3017
3018                 // When editing always use fonts internaly
3019                 if (tinyMCE.getParam("convert_fonts_to_spans"))
3020                     tinyMCE.convertSpansToFonts(doc);
3021
3022                 tinyMCE.handleVisualAid(this.getBody(), true, this.visualAid, this);
3023                 tinyMCE._setEventsEnabled(this.getBody(), false);
3024                 this.repaint();
3025                 this.selection.moveToBookmark(b);
3026                 tinyMCE.triggerNodeChange();
3027             break;
3028
3029             case "mceReplaceContent":
3030                 // Force empty string
3031                 if (!value)
3032                     value = '';
3033
3034                 this.getWin().focus();
3035
3036                 var selectedText = "";
3037
3038                 if (tinyMCE.isMSIE) {
3039                     var rng = doc.selection.createRange();
3040                     selectedText = rng.text;
3041                 } else
3042                     selectedText = this.getSel().toString();
3043
3044                 if (selectedText.length > 0) {
3045                     value = tinyMCE.replaceVar(value, "selection", selectedText);
3046                     tinyMCE.execCommand('mceInsertContent', false, value);
3047                 }
3048
3049                 tinyMCE.triggerNodeChange();
3050             break;
3051
3052             case "mceSetAttribute":
3053                 if (typeof(value) == 'object') {
3054                     var targetElms = (typeof(value['targets']) == "undefined") ? "p,img,span,div,td,h1,h2,h3,h4,h5,h6,pre,address" : value['targets'];
3055                     var targetNode = tinyMCE.getParentElement(this.getFocusElement(), targetElms);
3056
3057                     if (targetNode) {
3058                         targetNode.setAttribute(value['name'], value['value']);
3059                         tinyMCE.triggerNodeChange();
3060                     }
3061                 }
3062             break;
3063
3064             case "mceSetCSSClass":
3065                 this.execCommand("SetStyleInfo", false, {command : "setattrib", name : "class", value : value});
3066             break;
3067
3068             case "mceInsertRawHTML":
3069                 var key = 'tiny_mce_marker';
3070
3071                 this.execCommand('mceBeginUndoLevel');
3072
3073                 // Insert marker key
3074                 this.execCommand('mceInsertContent', false, key);
3075
3076                 // Store away scroll pos
3077                 var scrollX = this.getDoc().body.scrollLeft + this.getDoc().documentElement.scrollLeft;
3078                 var scrollY = this.getDoc().body.scrollTop + this.getDoc().documentElement.scrollTop;
3079
3080                 // Find marker and replace with RAW HTML
3081                 var html = this.getBody().innerHTML;
3082                 if ((pos = html.indexOf(key)) != -1)
3083                     tinyMCE.setInnerHTML(this.getBody(), html.substring(0, pos) + value + html.substring(pos + key.length));
3084
3085                 // Restore scoll pos
3086                 this.contentWindow.scrollTo(scrollX, scrollY);
3087
3088                 this.execCommand('mceEndUndoLevel');
3089
3090                 break;
3091
3092             case "mceInsertContent":
3093                 // Force empty string
3094                 if (!value)
3095                     value = '';
3096
3097                 var insertHTMLFailed = false;
3098                 this.getWin().focus();
3099
3100                 if (tinyMCE.isGecko || tinyMCE.isOpera) {
3101                     try {
3102                         // Is plain text or HTML, &amp;, &nbsp; etc will be encoded wrong in FF
3103                         if (value.indexOf('<') == -1 && !value.match(/(&#38;|&#160;|&#60;|&#62;)/g)) {
3104                             var r = this.getRng();
3105                             var n = this.getDoc().createTextNode(tinyMCE.entityDecode(value));
3106                             var s = this.getSel();
3107                             var r2 = r.cloneRange();
3108
3109                             // Insert text at cursor position
3110                             s.removeAllRanges();
3111                             r.deleteContents();
3112                             r.insertNode(n);
3113
3114                             // Move the cursor to the end of text
3115                             r2.selectNode(n);
3116                             r2.collapse(false);
3117                             s.removeAllRanges();
3118                             s.addRange(r2);
3119                         } else {
3120                             value = tinyMCE.fixGeckoBaseHREFBug(1, this.getDoc(), value);
3121                             this.getDoc().execCommand('inserthtml', false, value);
3122                             tinyMCE.fixGeckoBaseHREFBug(2, this.getDoc(), value);
3123                         }
3124                     } catch (ex) {
3125                         insertHTMLFailed = true;
3126                     }
3127
3128                     if (!insertHTMLFailed) {
3129                         tinyMCE.triggerNodeChange();
3130                         return;
3131                     }
3132                 }
3133
3134                 // Ugly hack in Opera due to non working "inserthtml"
3135                 if (tinyMCE.isOpera && insertHTMLFailed) {
3136                     this.getDoc().execCommand("insertimage", false, tinyMCE.uniqueURL);
3137                     var ar = tinyMCE.getElementsByAttributeValue(this.getBody(), "img", "src", tinyMCE.uniqueURL);
3138                     ar[0].outerHTML = value;
3139                     return;
3140                 }
3141
3142                 if (!tinyMCE.isMSIE) {
3143                     var isHTML = value.indexOf('<') != -1;
3144                     var sel = this.getSel();
3145                     var rng = this.getRng();
3146
3147                     if (isHTML) {
3148                         if (tinyMCE.isSafari) {
3149                             var tmpRng = this.getDoc().createRange();
3150
3151                             tmpRng.setStart(this.getBody(), 0);
3152                             tmpRng.setEnd(this.getBody(), 0);
3153
3154                             value = tmpRng.createContextualFragment(value);
3155                         } else
3156                             value = rng.createContextualFragment(value);
3157                     } else {
3158                         // Setup text node
3159                         var el = document.createElement("div");
3160                         el.innerHTML = value;
3161                         value = el.firstChild.nodeValue;
3162                         value = doc.createTextNode(value);
3163                     }
3164
3165                     // Insert plain text in Safari
3166                     if (tinyMCE.isSafari && !isHTML) {
3167                         this.execCommand('InsertText', false, value.nodeValue);
3168                         tinyMCE.triggerNodeChange();
3169                         return true;
3170                     } else if (tinyMCE.isSafari && isHTML) {
3171                         rng.deleteContents();
3172                         rng.insertNode(value);
3173                         tinyMCE.triggerNodeChange();
3174                         return true;
3175                     }
3176
3177                     rng.deleteContents();
3178
3179                     // If target node is text do special treatment, (Mozilla 1.3 fix)
3180                     if (rng.startContainer.nodeType == 3) {
3181                         var node = rng.startContainer.splitText(rng.startOffset);
3182                         node.parentNode.insertBefore(value, node); 
3183                     } else
3184                         rng.insertNode(value);
3185
3186                     if (!isHTML) {
3187                         // Removes weird selection trails
3188                         sel.selectAllChildren(doc.body);
3189                         sel.removeAllRanges();
3190
3191                         // Move cursor to end of content
3192                         var rng = doc.createRange();
3193
3194                         rng.selectNode(value);
3195                         rng.collapse(false);
3196
3197                         sel.addRange(rng);
3198                     } else
3199                         rng.collapse(false);
3200
3201                     tinyMCE.fixGeckoBaseHREFBug(2, this.getDoc(), value);
3202                 } else {
3203                     var rng = doc.selection.createRange();
3204                     var c = value.indexOf('<!--') != -1;
3205
3206                     // Fix comment bug, add tag before comments
3207                     if (c)
3208                         value = tinyMCE.uniqueTag + value;
3209
3210                     if (rng.item)
3211                         rng.item(0).outerHTML = value;
3212                     else
3213                         rng.pasteHTML(value);
3214
3215                     // Remove unique tag
3216                     if (c) {
3217                         var e = this.getDoc().getElementById('mceTMPElement');
3218                         e.parentNode.removeChild(e);
3219                     }
3220                 }
3221
3222                 tinyMCE.triggerNodeChange();
3223             break;
3224
3225             case "mceStartTyping":
3226                 if (tinyMCE.settings['custom_undo_redo'] && this.undoRedo.typingUndoIndex == -1) {
3227                     this.undoRedo.typingUndoIndex = this.undoRedo.undoIndex;
3228                     this.execCommand('mceAddUndoLevel');
3229                     //tinyMCE.debug("mceStartTyping");
3230                 }
3231                 break;
3232
3233             case "mceEndTyping":
3234                 if (tinyMCE.settings['custom_undo_redo'] && this.undoRedo.typingUndoIndex != -1) {
3235                     this.execCommand('mceAddUndoLevel');
3236                     this.undoRedo.typingUndoIndex = -1;
3237                     //tinyMCE.debug("mceEndTyping");
3238                 }
3239                 break;
3240
3241             case "mceBeginUndoLevel":
3242                 this.undoRedoLevel = false;
3243                 break;
3244
3245             case "mceEndUndoLevel":
3246                 this.undoRedoLevel = true;
3247                 this.execCommand('mceAddUndoLevel');
3248                 break;
3249
3250             case "mceAddUndoLevel":
3251                 if (tinyMCE.settings['custom_undo_redo'] && this.undoRedoLevel) {
3252                     if (this.undoRedo.add())
3253                         tinyMCE.triggerNodeChange(false);
3254                 }
3255                 break;
3256
3257             case "Undo":
3258                 if (tinyMCE.settings['custom_undo_redo']) {
3259                     tinyMCE.execCommand("mceEndTyping");
3260                     this.undoRedo.undo();
3261                     tinyMCE.triggerNodeChange();
3262                 } else
3263                     this.getDoc().execCommand(command, user_interface, value);
3264                 break;
3265
3266             case "Redo":
3267                 if (tinyMCE.settings['custom_undo_redo']) {
3268                     tinyMCE.execCommand("mceEndTyping");
3269                     this.undoRedo.redo();
3270                     tinyMCE.triggerNodeChange();
3271                 } else
3272                     this.getDoc().execCommand(command, user_interface, value);
3273                 break;
3274
3275             case "mceToggleVisualAid":
3276                 this.visualAid = !this.visualAid;
3277                 tinyMCE.handleVisualAid(this.getBody(), true, this.visualAid, this);
3278                 tinyMCE.triggerNodeChange();
3279                 break;
3280
3281             case "Indent":
3282                 this.getDoc().execCommand(command, user_interface, value);
3283                 tinyMCE.triggerNodeChange();
3284                 if (tinyMCE.isMSIE) {
3285                     var n = tinyMCE.getParentElement(this.getFocusElement(), "blockquote");
3286                     do {
3287                         if (n && n.nodeName == "BLOCKQUOTE") {
3288                             n.removeAttribute("dir");
3289                             n.removeAttribute("style");
3290                         }
3291                     } while (n != null && (n = n.parentNode) != null);
3292                 }
3293                 break;
3294
3295             case "removeformat":
3296                 var text = this.selection.getSelectedText();
3297
3298                 if (tinyMCE.isOpera) {
3299                     this.getDoc().execCommand("RemoveFormat", false, null);
3300                     return;
3301                 }
3302
3303                 if (tinyMCE.isMSIE) {
3304                     try {
3305                         var rng = doc.selection.createRange();
3306                         rng.execCommand("RemoveFormat", false, null);
3307                     } catch (e) {
3308                         // Do nothing
3309                     }
3310
3311                     this.execCommand("SetStyleInfo", false, {command : "removeformat"});
3312                 } else {
3313                     this.getDoc().execCommand(command, user_interface, value);
3314
3315                     this.execCommand("SetStyleInfo", false, {command : "removeformat"});
3316                 }
3317
3318                 // Remove class
3319                 if (text.length == 0)
3320                     this.execCommand("mceSetCSSClass", false, "");
3321
3322                 tinyMCE.triggerNodeChange();
3323                 break;
3324
3325             default:
3326                 this.getDoc().execCommand(command, user_interface, value);
3327
3328                 if (tinyMCE.isGecko)
3329                     window.setTimeout('tinyMCE.triggerNodeChange(false);', 1);
3330                 else
3331                     tinyMCE.triggerNodeChange();
3332         }
3333
3334         // Add undo level after modification
3335         if (command != "mceAddUndoLevel" && command != "Undo" && command != "Redo" && command != "mceStartTyping" && command != "mceEndTyping")
3336             tinyMCE.execCommand("mceAddUndoLevel");
3337     },
3338
3339     queryCommandValue : function(c) {
3340         try {
3341             return this.getDoc().queryCommandValue(c);
3342         } catch (e) {
3343             return null;
3344         }
3345     },
3346
3347     queryCommandState : function(c) {
3348         return this.getDoc().queryCommandState(c);
3349     },
3350
3351     _onAdd : function(replace_element, form_element_name, target_document) {
3352         var hc, th, to, editorTemplate;
3353
3354         th = this.settings['theme'];
3355         to = tinyMCE.themes[th];
3356
3357         var targetDoc = target_document ? target_document : document;
3358
3359         this.targetDoc = targetDoc;
3360
3361         tinyMCE.themeURL = tinyMCE.baseURL + "/themes/" + this.settings['theme'];
3362         this.settings['themeurl'] = tinyMCE.themeURL;
3363
3364         if (!replace_element) {
3365             alert("Error: Could not find the target element.");
3366             return false;
3367         }
3368
3369         if (to.getEditorTemplate)
3370             editorTemplate = to.getEditorTemplate(this.settings, this.editorId);
3371
3372         var deltaWidth = editorTemplate['delta_width'] ? editorTemplate['delta_width'] : 0;
3373         var deltaHeight = editorTemplate['delta_height'] ? editorTemplate['delta_height'] : 0;
3374         var html = '<span id="' + this.editorId + '_parent" class="mceEditorContainer">' + editorTemplate['html'];
3375
3376         html = tinyMCE.replaceVar(html, "editor_id", this.editorId);
3377         this.settings['default_document'] = tinyMCE.baseURL + "/blank.htm";
3378
3379         this.settings['old_width'] = this.settings['width'];
3380         this.settings['old_height'] = this.settings['height'];
3381
3382         // Set default width, height
3383         if (this.settings['width'] == -1)
3384             this.settings['width'] = replace_element.offsetWidth;
3385
3386         if (this.settings['height'] == -1)
3387             this.settings['height'] = replace_element.offsetHeight;
3388
3389         // Try the style width
3390         if (this.settings['width'] == 0)
3391             this.settings['width'] = replace_element.style.width;
3392
3393         // Try the style height
3394         if (this.settings['height'] == 0)
3395             this.settings['height'] = replace_element.style.height; 
3396
3397         // If no width/height then default to 320x240, better than nothing
3398         if (this.settings['width'] == 0)
3399             this.settings['width'] = 320;
3400
3401         if (this.settings['height'] == 0)
3402             this.settings['height'] = 240;
3403
3404         this.settings['area_width'] = parseInt(this.settings['width']);
3405         this.settings['area_height'] = parseInt(this.settings['height']);
3406         this.settings['area_width'] += deltaWidth;
3407         this.settings['area_height'] += deltaHeight;
3408
3409         // Special % handling
3410         if (("" + this.settings['width']).indexOf('%') != -1)
3411             this.settings['area_width'] = "100%";
3412
3413         if (("" + this.settings['height']).indexOf('%') != -1)
3414             this.settings['area_height'] = "100%";
3415
3416         if (("" + replace_element.style.width).indexOf('%') != -1) {
3417             this.settings['width'] = replace_element.style.width;
3418             this.settings['area_width'] = "100%";
3419         }
3420
3421         if (("" + replace_element.style.height).indexOf('%') != -1) {
3422             this.settings['height'] = replace_element.style.height;
3423             this.settings['area_height'] = "100%";
3424         }
3425
3426         html = tinyMCE.applyTemplate(html);
3427
3428         this.settings['width'] = this.settings['old_width'];
3429         this.settings['height'] = this.settings['old_height'];
3430
3431         this.visualAid = this.settings['visual'];
3432         this.formTargetElementId = form_element_name;
3433
3434         // Get replace_element contents
3435         if (replace_element.nodeName == "TEXTAREA" || replace_element.nodeName == "INPUT")
3436             this.startContent = replace_element.value;
3437         else
3438             this.startContent = replace_element.innerHTML;
3439
3440         // If not text area or input
3441         if (replace_element.nodeName != "TEXTAREA" && replace_element.nodeName != "INPUT") {
3442             this.oldTargetElement = replace_element;
3443
3444             // Debug mode
3445             if (tinyMCE.settings['debug']) {
3446                 hc = '<textarea wrap="off" id="' + form_element_name + '" name="' + form_element_name + '" cols="100" rows="15"></textarea>';
3447             } else {
3448                 hc = '<input type="hidden" id="' + form_element_name + '" name="' + form_element_name + '" />';
3449                 this.oldTargetElement.style.display = "none";
3450             }
3451
3452             html += '</span>';
3453
3454             if (tinyMCE.isGecko)
3455                 html = hc + html;
3456             else
3457                 html += hc;
3458
3459             // Output HTML and set editable
3460             if (tinyMCE.isGecko) {
3461                 var rng = replace_element.ownerDocument.createRange();
3462                 rng.setStartBefore(replace_element);
3463
3464                 var fragment = rng.createContextualFragment(html);
3465                 tinyMCE.insertAfter(fragment, replace_element);
3466             } else
3467                 replace_element.insertAdjacentHTML("beforeBegin", html);
3468         } else {
3469             html += '</span>';
3470
3471             // Just hide the textarea element
3472             this.oldTargetElement = replace_element;
3473
3474             if (!tinyMCE.settings['debug'])
3475                 this.oldTargetElement.style.display = "none";
3476
3477             // Output HTML and set editable
3478             if (tinyMCE.isGecko) {
3479                 var rng = replace_element.ownerDocument.createRange();
3480                 rng.setStartBefore(replace_element);
3481
3482                 var fragment = rng.createContextualFragment(html);
3483                 tinyMCE.insertAfter(fragment, replace_element);
3484             } else
3485                 replace_element.insertAdjacentHTML("beforeBegin", html);
3486         }
3487
3488         // Setup iframe
3489         var dynamicIFrame = false;
3490         var tElm = targetDoc.getElementById(this.editorId);
3491
3492         if (!tinyMCE.isMSIE) {
3493             // Node case is preserved in XML strict mode
3494             if (tElm && (tElm.nodeName == "SPAN" || tElm.nodeName == "span")) {
3495                 tElm = tinyMCE._createIFrame(tElm, targetDoc);
3496                 dynamicIFrame = true;
3497             }
3498
3499             this.targetElement = tElm;
3500             this.iframeElement = tElm;
3501             this.contentDocument = tElm.contentDocument;
3502             this.contentWindow = tElm.contentWindow;
3503
3504             //this.getDoc().designMode = "on";
3505         } else {
3506             if (tElm && tElm.nodeName == "SPAN")
3507                 tElm = tinyMCE._createIFrame(tElm, targetDoc, targetDoc.parentWindow);
3508             else
3509                 tElm = targetDoc.frames[this.editorId];
3510
3511             this.targetElement = tElm;
3512             this.iframeElement = targetDoc.getElementById(this.editorId);
3513
3514             if (tinyMCE.isOpera) {
3515                 this.contentDocument = this.iframeElement.contentDocument;
3516                 this.contentWindow = this.iframeElement.contentWindow;
3517                 dynamicIFrame = true;
3518             } else {
3519                 this.contentDocument = tElm.window.document;
3520                 this.contentWindow = tElm.window;
3521             }
3522
3523             this.getDoc().designMode = "on";
3524         }
3525
3526         // Setup base HTML
3527         var doc = this.contentDocument;
3528         if (dynamicIFrame) {
3529             var html = tinyMCE.getParam('doctype') + '<html><head xmlns="http://www.w3.org/1999/xhtml"><base href="' + tinyMCE.settings['base_href'] + '" /><title>blank_page</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></head><body class="mceContentBody"></body></html>';
3530
3531             try {
3532                 if (!this.isHidden())
3533                     this.getDoc().designMode = "on";
3534
3535                 doc.open();
3536                 doc.write(html);
3537                 doc.close();
3538             } catch (e) {
3539                 // Failed Mozilla 1.3
3540                 this.getDoc().location.href = tinyMCE.baseURL + "/blank.htm";
3541             }
3542         }
3543
3544         // This timeout is needed in MSIE 5.5 for some odd reason
3545         // it seems that the document.frames isn't initialized yet?
3546         if (tinyMCE.isMSIE)
3547             window.setTimeout("tinyMCE.addEventHandlers(tinyMCE.instances[\"" + this.editorId + "\"]);", 1);
3548
3549         tinyMCE.setupContent(this.editorId, true);
3550
3551         return true;
3552     },
3553
3554     setBaseHREF : function(u) {
3555         var h, b, d, nl;
3556
3557         d = this.getDoc();
3558         nl = d.getElementsByTagName("base");
3559         b = nl.length > 0 ? nl[0] : null;
3560
3561         if (!b) {
3562             nl = d.getElementsByTagName("head");
3563             h = nl.length > 0 ? nl[0] : null;
3564
3565             b = d.createElement("base");
3566             b.setAttribute('href', u);
3567             h.appendChild(b);
3568         } else {
3569             if (u == "" || u == null)
3570                 b.parentNode.removeChild(b);
3571             else
3572                 b.setAttribute('href', u);
3573         }
3574     },
3575
3576     getFocusElement : function() {
3577         return this.selection.getFocusElement();
3578     },
3579
3580     getSel : function() {
3581         return this.selection.getSel();
3582     },
3583
3584     getRng : function() {
3585         return this.selection.getRng();
3586     },
3587
3588     triggerSave : function(skip_cleanup, skip_callback) {
3589         var e, nl = new Array(), i, s;
3590
3591         this.switchSettings();
3592         s = tinyMCE.settings;
3593
3594         // Force hidden tabs visible while serializing
3595         if (tinyMCE.isMSIE && !tinyMCE.isOpera) {
3596             e = this.iframeElement;
3597
3598             do {
3599                 if (e.style && e.style.display == 'none') {
3600                     e.style.display = 'block';
3601                     nl[nl.length] = {elm : e, type : 'style'};
3602                 }
3603
3604                 if (e.style && s.hidden_tab_class.length > 0 && e.className.indexOf(s.hidden_tab_class) != -1) {
3605                     e.className = s.display_tab_class;
3606                     nl[nl.length] = {elm : e, type : 'class'};
3607                 }
3608             } while ((e = e.parentNode) != null)
3609         }
3610
3611         tinyMCE.settings['preformatted'] = false;
3612
3613         // Default to false
3614         if (typeof(skip_cleanup) == "undefined")
3615             skip_cleanup = false;
3616
3617         // Default to false
3618         if (typeof(skip_callback) == "undefined")
3619             skip_callback = false;
3620
3621         tinyMCE._setHTML(this.getDoc(), this.getBody().innerHTML);
3622
3623         // Remove visual aids when cleanup is disabled
3624         if (this.settings['cleanup'] == false) {
3625             tinyMCE.handleVisualAid(this.getBody(), true, false, this);
3626             tinyMCE._setEventsEnabled(this.getBody(), true);
3627         }
3628
3629         tinyMCE._customCleanup(this, "submit_content_dom", this.contentWindow.document.body);
3630         var htm = skip_cleanup ? this.getBody().innerHTML : tinyMCE._cleanupHTML(this, this.getDoc(), this.settings, this.getBody(), tinyMCE.visualAid, true, true);
3631         htm = tinyMCE._customCleanup(this, "submit_content", htm);
3632
3633         if (!skip_callback && tinyMCE.settings['save_callback'] != "")
3634             var content = eval(tinyMCE.settings['save_callback'] + "(this.formTargetElementId,htm,this.getBody());");
3635
3636         // Use callback content if available
3637         if ((typeof(content) != "undefined") && content != null)
3638             htm = content;
3639
3640         // Replace some weird entities (Bug: #1056343)
3641         htm = tinyMCE.regexpReplace(htm, "&#40;", "(", "gi");
3642         htm = tinyMCE.regexpReplace(htm, "&#41;", ")", "gi");
3643         htm = tinyMCE.regexpReplace(htm, "&#59;", ";", "gi");
3644         htm = tinyMCE.regexpReplace(htm, "&#34;", "&quot;", "gi");
3645         htm = tinyMCE.regexpReplace(htm, "&#94;", "^", "gi");
3646
3647         if (this.formElement)
3648             this.formElement.value = htm;
3649
3650         if (tinyMCE.isSafari && this.formElement)
3651             this.formElement.innerText = htm;
3652
3653         // Hide them again (tabs in MSIE)
3654         for (i=0; i<nl.length; i++) {
3655             if (nl[i].type == 'style')
3656                 nl[i].elm.style.display = 'none';
3657             else
3658                 nl[i].elm.className = s.hidden_tab_class;
3659         }
3660     }
3661 };
3662
3663 /* file:jscripts/tiny_mce/classes/TinyMCE_Cleanup.class.js */
3664
3665 TinyMCE_Engine.prototype.cleanupHTMLCode = function(s) {
3666     s = s.replace(new RegExp('<p \\/>', 'gi'), '<p>&nbsp;</p>');
3667     s = s.replace(new RegExp('<p>\\s*<\\/p>', 'gi'), '<p>&nbsp;</p>');
3668
3669     // Fix close BR elements
3670     s = s.replace(new RegExp('<br>\\s*<\\/br>', 'gi'), '<br />');
3671
3672     // Open closed tags like <b/> to <b></b>
3673     s = s.replace(new RegExp('<(h[1-6]|p|div|address|pre|form|table|li|ol|ul|td|b|font|em|strong|i|strike|u|span|a|ul|ol|li|blockquote)([a-z]*)([^\\\\|>]*)\\/>', 'gi'), '<$1$2$3></$1$2>');
3674
3675     // Remove trailing space <b > to <b>
3676     s = s.replace(new RegExp('\\s+></', 'gi'), '></');
3677
3678     // Close tags <img></img> to <img/>
3679     s = s.replace(new RegExp('<(img|br|hr)([^>]*)><\\/(img|br|hr)>', 'gi'), '<$1$2 />');
3680
3681     // Weird MSIE bug, <p><hr /></p> breaks runtime?
3682     if (tinyMCE.isMSIE)
3683         s = s.replace(new RegExp('<p><hr \\/><\\/p>', 'gi'), "<hr>");
3684
3685     // Convert relative anchors to absolute URLs ex: #something to file.htm#something
3686     if (tinyMCE.getParam('convert_urls'))
3687         s = s.replace(new RegExp('(href=\"{0,1})(\\s*#)', 'gi'), '$1' + tinyMCE.settings['document_base_url'] + "#");
3688
3689     return s;
3690 };
3691
3692 TinyMCE_Engine.prototype.parseStyle = function(str) {
3693     var ar = new Array();
3694
3695     if (str == null)
3696         return ar;
3697
3698     var st = str.split(';');
3699
3700     tinyMCE.clearArray(ar);
3701
3702     for (var i=0; i<st.length; i++) {
3703         if (st[i] == '')
3704             continue;
3705
3706         var re = new RegExp('^\\s*([^:]*):\\s*(.*)\\s*$');
3707         var pa = st[i].replace(re, '$1||$2').split('||');
3708 //tinyMCE.debug(str, pa[0] + "=" + pa[1], st[i].replace(re, '$1||$2'));
3709         if (pa.length == 2)
3710             ar[pa[0].toLowerCase()] = pa[1];
3711     }
3712
3713     return ar;
3714 };
3715
3716 TinyMCE_Engine.prototype.compressStyle = function(ar, pr, sf, res) {
3717     var box = new Array();
3718
3719     box[0] = ar[pr + '-top' + sf];
3720     box[1] = ar[pr + '-left' + sf];
3721     box[2] = ar[pr + '-right' + sf];
3722     box[3] = ar[pr + '-bottom' + sf];
3723
3724     for (var i=0; i<box.length; i++) {
3725         if (box[i] == null)
3726             return;
3727
3728         for (var a=0; a<box.length; a++) {
3729             if (box[a] != box[i])
3730                 return;
3731         }
3732     }
3733
3734     // They are all the same
3735     ar[res] = box[0];
3736     ar[pr + '-top' + sf] = null;
3737     ar[pr + '-left' + sf] = null;
3738     ar[pr + '-right' + sf] = null;
3739     ar[pr + '-bottom' + sf] = null;
3740 };
3741
3742 TinyMCE_Engine.prototype.serializeStyle = function(ar) {
3743     var str = "";
3744
3745     // Compress box
3746     tinyMCE.compressStyle(ar, "border", "", "border");
3747     tinyMCE.compressStyle(ar, "border", "-width", "border-width");
3748     tinyMCE.compressStyle(ar, "border", "-color", "border-color");
3749     tinyMCE.compressStyle(ar, "border", "-style", "border-style");
3750     tinyMCE.compressStyle(ar, "padding", "", "padding");
3751     tinyMCE.compressStyle(ar, "margin", "", "margin");
3752
3753     for (var key in ar) {
3754         var val = ar[key];
3755
3756         if (typeof(val) == 'function')
3757             continue;
3758
3759         if (key.indexOf('mso-') == 0)
3760             continue;
3761
3762         if (val != null && val != '') {
3763             val = '' + val; // Force string
3764
3765             // Fix style URL
3766             val = val.replace(new RegExp("url\\(\\'?([^\\']*)\\'?\\)", 'gi'), "url('$1')");
3767
3768             // Convert URL
3769             if (val.indexOf('url(') != -1 && tinyMCE.getParam('convert_urls')) {
3770                 var m = new RegExp("url\\('(.*?)'\\)").exec(val);
3771
3772                 if (m.length > 1)
3773                     val = "url('" + eval(tinyMCE.getParam('urlconverter_callback') + "(m[1], null, true);") + "')";
3774             }
3775
3776             // Force HEX colors
3777             if (tinyMCE.getParam("force_hex_style_colors"))
3778                 val = tinyMCE.convertRGBToHex(val, true);
3779
3780             if (val != "url('')")
3781                 str += key.toLowerCase() + ": " + val + "; ";
3782         }
3783     }
3784
3785     if (new RegExp('; $').test(str))
3786         str = str.substring(0, str.length - 2);
3787
3788     return str;
3789 };
3790
3791 TinyMCE_Engine.prototype.convertRGBToHex = function(s, k) {
3792     if (s.toLowerCase().indexOf('rgb') != -1) {
3793         var re = new RegExp("(.*?)rgb\\s*?\\(\\s*?([0-9]+).*?,\\s*?([0-9]+).*?,\\s*?([0-9]+).*?\\)(.*?)", "gi");
3794         var rgb = s.replace(re, "$1,$2,$3,$4,$5").split(',');
3795         if (rgb.length == 5) {
3796             r = parseInt(rgb[1]).toString(16);
3797             g = parseInt(rgb[2]).toString(16);
3798             b = parseInt(rgb[3]).toString(16);
3799
3800             r = r.length == 1 ? '0' + r : r;
3801             g = g.length == 1 ? '0' + g : g;
3802             b = b.length == 1 ? '0' + b : b;
3803
3804             s = "#" + r + g + b;
3805
3806             if (k)
3807                 s = rgb[0] + s + rgb[4];
3808         }
3809     }
3810
3811     return s;
3812 };
3813
3814 TinyMCE_Engine.prototype.convertHexToRGB = function(s) {
3815     if (s.indexOf('#') != -1) {
3816         s = s.replace(new RegExp('[^0-9A-F]', 'gi'), '');
3817         return "rgb(" + parseInt(s.substring(0, 2), 16) + "," + parseInt(s.substring(2, 4), 16) + "," + parseInt(s.substring(4, 6), 16) + ")";
3818     }
3819
3820     return s;
3821 };
3822
3823 TinyMCE_Engine.prototype.convertSpansToFonts = function(doc) {
3824     var sizes = tinyMCE.getParam('font_size_style_values').replace(/\s+/, '').split(',');
3825
3826     var h = doc.body.innerHTML;
3827     h = h.replace(/<span/gi, '<font');
3828     h = h.replace(/<\/span/gi, '</font');
3829     doc.body.innerHTML = h;
3830
3831     var s = doc.getElementsByTagName("font");
3832     for (var i=0; i<s.length; i++) {
3833         var size = tinyMCE.trim(s[i].style.fontSize).toLowerCase();
3834         var fSize = 0;
3835
3836         for (var x=0; x<sizes.length; x++) {
3837             if (sizes[x] == size) {
3838                 fSize = x + 1;
3839                 break;
3840             }
3841         }
3842
3843         if (fSize > 0) {
3844             tinyMCE.setAttrib(s[i], 'size', fSize);
3845             s[i].style.fontSize = '';
3846         }
3847
3848         var fFace = s[i].style.fontFamily;
3849         if (fFace != null && fFace != "") {
3850             tinyMCE.setAttrib(s[i], 'face', fFace);
3851             s[i].style.fontFamily = '';
3852         }
3853
3854         var fColor = s[i].style.color;
3855         if (fColor != null && fColor != "") {
3856             tinyMCE.setAttrib(s[i], 'color', tinyMCE.convertRGBToHex(fColor));
3857             s[i].style.color = '';
3858         }
3859     }
3860 };
3861
3862 TinyMCE_Engine.prototype.convertFontsToSpans = function(doc) {
3863     var sizes = tinyMCE.getParam('font_size_style_values').replace(/\s+/, '').split(',');
3864
3865     var h = doc.body.innerHTML;
3866     h = h.replace(/<font/gi, '<span');
3867     h = h.replace(/<\/font/gi, '</span');
3868     doc.body.innerHTML = h;
3869
3870     var fsClasses = tinyMCE.getParam('font_size_classes');
3871     if (fsClasses != '')
3872         fsClasses = fsClasses.replace(/\s+/, '').split(',');
3873     else
3874         fsClasses = null;
3875
3876     var s = doc.getElementsByTagName("span");
3877     for (var i=0; i<s.length; i++) {
3878         var fSize, fFace, fColor;
3879
3880         fSize = tinyMCE.getAttrib(s[i], 'size');
3881         fFace = tinyMCE.getAttrib(s[i], 'face');
3882         fColor = tinyMCE.getAttrib(s[i], 'color');
3883
3884         if (fSize != "") {
3885             fSize = parseInt(fSize);
3886
3887             if (fSize > 0 && fSize < 8) {
3888                 if (fsClasses != null)
3889                     tinyMCE.setAttrib(s[i], 'class', fsClasses[fSize-1]);
3890                 else
3891                     s[i].style.fontSize = sizes[fSize-1];
3892             }
3893
3894             s[i].removeAttribute('size');
3895         }
3896
3897         if (fFace != "") {
3898             s[i].style.fontFamily = fFace;
3899             s[i].removeAttribute('face');
3900         }
3901
3902         if (fColor != "") {
3903             s[i].style.color = fColor;
3904             s[i].removeAttribute('color');
3905         }
3906     }
3907 };
3908
3909 TinyMCE_Engine.prototype.cleanupAnchors = function(doc) {
3910     var i, cn, x, an = doc.getElementsByTagName("a");
3911
3912     // Loops backwards due to bug #1467987
3913     for (i=an.length-1; i>=0; i--) {
3914         if (tinyMCE.getAttrib(an[i], "name") != "" && tinyMCE.getAttrib(an[i], "href") == "") {
3915             cn = an[i].childNodes;
3916
3917             for (x=cn.length-1; x>=0; x--)
3918                 tinyMCE.insertAfter(cn[x], an[i]);
3919         }
3920     }
3921 };
3922
3923 TinyMCE_Engine.prototype.getContent = function(editor_id) {
3924     var h;
3925
3926     if (typeof(editor_id) != "undefined")
3927         tinyMCE.selectedInstance = tinyMCE.getInstanceById(editor_id);
3928
3929     if (tinyMCE.selectedInstance) {
3930         h = tinyMCE._cleanupHTML(this.selectedInstance, this.selectedInstance.getDoc(), tinyMCE.settings, this.selectedInstance.getBody(), false, true);
3931
3932         // When editing always use fonts internaly
3933         if (tinyMCE.getParam("convert_fonts_to_spans"))
3934             tinyMCE.convertSpansToFonts(this.selectedInstance.getDoc());
3935
3936         return h;
3937     }
3938
3939     return null;
3940 };
3941
3942 TinyMCE_Engine.prototype._fixListElements = function(d) {
3943     var nl, x, a = ['ol', 'ul'], i, n, p, r = new RegExp('^(OL|UL)$'), np;
3944
3945     for (x=0; x<a.length; x++) {
3946         nl = d.getElementsByTagName(a[x]);
3947
3948         for (i=0; i<nl.length; i++) {
3949             n = nl[i];
3950             p = n.parentNode;
3951
3952             if (r.test(p.nodeName)) {
3953                 np = tinyMCE.prevNode(n, 'LI');
3954
3955                 if (!np) {
3956                     np = d.createElement('li');
3957                     np.innerHTML = '&nbsp;';
3958                     np.appendChild(n);
3959                     p.insertBefore(np, p.firstChild);
3960                 } else
3961                     np.appendChild(n);
3962             }
3963         }
3964     }
3965 };
3966
3967 TinyMCE_Engine.prototype._fixTables = function(d) {
3968     var nl, i, n, p, np, x, t;
3969
3970     nl = d.getElementsByTagName('table');
3971     for (i=0; i<nl.length; i++) {
3972         n = nl[i];
3973
3974         if ((p = tinyMCE.getParentElement(n, 'p,div,h1,h2,h3,h4,h5,h6')) != null) {
3975             np = p.cloneNode(false);
3976             np.removeAttribute('id');
3977
3978             t = n;
3979
3980             while ((n = n.nextSibling))
3981                 np.appendChild(n);
3982
3983             tinyMCE.insertAfter(np, p);
3984             tinyMCE.insertAfter(t, p);
3985         }
3986     }
3987 };
3988
3989 TinyMCE_Engine.prototype._cleanupHTML = function(inst, doc, config, elm, visual, on_save, on_submit) {
3990     var h, d, t1, t2, t3, t4, t5, c, s;
3991
3992     if (!tinyMCE.getParam('cleanup'))
3993         return elm.innerHTML;
3994
3995     on_save = typeof(on_save) == 'undefined' ? false : on_save;
3996
3997     c = inst.cleanup;
3998     s = inst.settings;
3999     d = c.settings.debug;
4000
4001     if (d)
4002         t1 = new Date().getTime();
4003
4004     if (tinyMCE.getParam("convert_fonts_to_spans"))
4005         tinyMCE.convertFontsToSpans(doc);
4006
4007     if (tinyMCE.getParam("fix_list_elements"))
4008         tinyMCE._fixListElements(doc);
4009
4010     if (tinyMCE.getParam("fix_table_elements"))
4011         tinyMCE._fixTables(doc);
4012
4013     // Call custom cleanup code
4014     tinyMCE._customCleanup(inst, on_save ? "get_from_editor_dom" : "insert_to_editor_dom", doc.body);
4015
4016     if (d)
4017         t2 = new Date().getTime();
4018
4019     c.settings.on_save = on_save;
4020     //for (var i=0; i<100; i++)
4021
4022     c.idCount = 0;
4023     c.serializationId++;
4024     c.serializedNodes = new Array();
4025     c.sourceIndex = -1;
4026
4027     if (s.cleanup_serializer == "xml")
4028         h = c.serializeNodeAsXML(elm);
4029     else
4030         h = c.serializeNodeAsHTML(elm);
4031
4032     if (d)
4033         t3 = new Date().getTime();
4034
4035     // Post processing
4036     h = h.replace(/<\/?(body|head|html)[^>]*>/gi, '');
4037     h = h.replace(new RegExp(' (rowspan="1"|colspan="1")', 'g'), '');
4038     h = h.replace(/<p><hr \/><\/p>/g, '<hr />');
4039     h = h.replace(/<p>(&nbsp;|&#160;)<\/p><hr \/><p>(&nbsp;|&#160;)<\/p>/g, '<hr />');
4040     h = h.replace(/<td>\s*<br \/>\s*<\/td>/g, '<td>&nbsp;</td>');
4041     h = h.replace(/<p>\s*<br \/>\s*<\/p>/g, '<p>&nbsp;</p>');
4042     h = h.replace(/<p>\s*(&nbsp;|&#160;)\s*<br \/>\s*(&nbsp;|&#160;)\s*<\/p>/g, '<p>&nbsp;</p>');
4043     h = h.replace(/<p>\s*(&nbsp;|&#160;)\s*<br \/>\s*<\/p>/g, '<p>&nbsp;</p>');
4044     h = h.replace(/<p>\s*<br \/>\s*&nbsp;\s*<\/p>/g, '<p>&nbsp;</p>');
4045     h = h.replace(new RegExp('<a>(.*?)<\\/a>', 'g'), '$1');
4046     h = h.replace(/<p([^>]*)>\s*<\/p>/g, '<p$1>&nbsp;</p>');
4047
4048     // Clean body
4049     if (/^\s*(<br \/>|<p>&nbsp;<\/p>|<p>&#160;<\/p>|<p><\/p>)\s*$/.test(h))
4050         h = '';
4051
4052     // If preformatted
4053     if (s.preformatted) {
4054         h = h.replace(/^<pre>/, '');
4055         h = h.replace(/<\/pre>$/, '');
4056         h = '<pre>' + h + '</pre>';
4057     }
4058
4059     // Gecko specific processing
4060     if (tinyMCE.isGecko) {
4061         h = h.replace(/<o:p _moz-userdefined="" \/>/g, '');
4062         h = h.replace(/<td([^>]*)>\s*<br \/>\s*<\/td>/g, '<td$1>&nbsp;</td>');
4063     }
4064
4065     if (s.force_br_newlines)
4066         h = h.replace(/<p>(&nbsp;|&#160;)<\/p>/g, '<br />');
4067
4068     // Call custom cleanup code
4069     h = tinyMCE._customCleanup(inst, on_save ? "get_from_editor" : "insert_to_editor", h);
4070
4071     // Remove internal classes
4072     if (on_save) {
4073         h = h.replace(new RegExp(' ?(mceItem[a-zA-Z0-9]*|' + s.visual_table_class + ')', 'g'), '');
4074         h = h.replace(new RegExp(' ?class=""', 'g'), '');
4075     }
4076
4077     if (s.remove_linebreaks && !c.settings.indent)
4078         h = h.replace(/\n|\r/g, ' ');
4079
4080     if (d)
4081         t4 = new Date().getTime();
4082
4083     if (on_save && c.settings.indent)
4084         h = c.formatHTML(h);
4085
4086     // If encoding (not recommended option)
4087     if (on_submit && (s.encoding == "xml" || s.encoding == "html"))
4088         h = c.xmlEncode(h);
4089
4090     if (d)
4091         t5 = new Date().getTime();
4092
4093     if (c.settings.debug)
4094         tinyMCE.debug("Cleanup in ms: Pre=" + (t2-t1) + ", Serialize: " + (t3-t2) + ", Post: " + (t4-t3) + ", Format: " + (t5-t4) + ", Sum: " + (t5-t1) + ".");
4095
4096     return h;
4097 };
4098
4099 function TinyMCE_Cleanup() {
4100     this.isMSIE = (navigator.appName == "Microsoft Internet Explorer");
4101     this.rules = tinyMCE.clearArray(new Array());
4102
4103     // Default config
4104     this.settings = {
4105         indent_elements : 'head,table,tbody,thead,tfoot,form,tr,ul,ol,blockquote,object',
4106         newline_before_elements : 'h1,h2,h3,h4,h5,h6,pre,address,div,ul,ol,li,meta,option,area,title,link,base,script,td',
4107         newline_after_elements : 'br,hr,p,pre,address,div,ul,ol,meta,option,area,link,base,script',
4108         newline_before_after_elements : 'html,head,body,table,thead,tbody,tfoot,tr,form,ul,ol,blockquote,p,object,param,hr,div',
4109         indent_char : '\t',
4110         indent_levels : 1,
4111         entity_encoding : 'raw',
4112         valid_elements : '*[*]',
4113         entities : '',
4114         url_converter : '',
4115         invalid_elements : '',
4116         verify_html : false
4117     };
4118
4119     this.vElements = tinyMCE.clearArray(new Array());
4120     this.vElementsRe = '';
4121     this.closeElementsRe = /^(IMG|BR|HR|LINK|META|BASE|INPUT|BUTTON|AREA)$/;
4122     this.codeElementsRe = /^(SCRIPT|STYLE)$/;
4123     this.serializationId = 0;
4124     this.mceAttribs = {
4125         href : 'mce_href',
4126         src : 'mce_src',
4127         type : 'mce_type'
4128     };
4129 }
4130
4131 TinyMCE_Cleanup.prototype = {
4132     init : function(s) {
4133         var n, a, i, ir, or, st;
4134
4135         for (n in s)
4136             this.settings[n] = s[n];
4137
4138         // Setup code formating
4139         s = this.settings;
4140
4141         // Setup regexps
4142         this.inRe = this._arrayToRe(s.indent_elements.split(','), '', '^<(', ')[^>]*');
4143         this.ouRe = this._arrayToRe(s.indent_elements.split(','), '', '^<\\/(', ')[^>]*');
4144         this.nlBeforeRe = this._arrayToRe(s.newline_before_elements.split(','), 'gi', '<(',  ')([^>]*)>');
4145         this.nlAfterRe = this._arrayToRe(s.newline_after_elements.split(','), 'gi', '<(',  ')([^>]*)>');
4146         this.nlBeforeAfterRe = this._arrayToRe(s.newline_before_after_elements.split(','), 'gi', '<(\\/?)(', ')([^>]*)>');
4147
4148         if (s.invalid_elements != '')
4149             this.iveRe = this._arrayToRe(s.invalid_elements.toUpperCase().split(','), 'g', '^(', ')$');
4150         else
4151             this.iveRe = null;
4152
4153         // Setup separator
4154         st = '';
4155         for (i=0; i<s.indent_levels; i++)
4156             st += s.indent_char;
4157
4158         this.inStr = st;
4159
4160         // If verify_html if false force *[*]
4161         if (!s.verify_html) {
4162             s.valid_elements = '*[*]';
4163             s.extended_valid_elements = '';
4164         }
4165
4166         this.fillStr = s.entity_encoding == "named" ? "&nbsp;" : "&#160;";
4167         this.idCount = 0;
4168     },
4169
4170     addRuleStr : function(s) {
4171         var r = this.parseRuleStr(s);
4172         var n;
4173
4174         for (n in r) {
4175             if (r[n])
4176                 this.rules[n] = r[n];
4177         }
4178
4179         this.vElements = tinyMCE.clearArray(new Array());
4180
4181         for (n in this.rules) {
4182             if (this.rules[n])
4183                 this.vElements[this.vElements.length] = this.rules[n].tag;
4184         }
4185
4186         this.vElementsRe = this._arrayToRe(this.vElements, '');
4187     },
4188
4189     parseRuleStr : function(s) {
4190         var ta, p, r, a, i, x, px, t, tn, y, av, or = tinyMCE.clearArray(new Array()), dv;
4191
4192         if (s == null || s.length == 0)
4193             return or;
4194
4195         ta = s.split(',');
4196         for (x=0; x<ta.length; x++) {
4197             s = ta[x];
4198             if (s.length == 0)
4199                 continue;
4200
4201             // Split tag/attrs
4202             p = this.split(/\[|\]/, s);
4203             if (p == null || p.length < 1)
4204                 t = s.toUpperCase();
4205             else
4206                 t = p[0].toUpperCase();
4207
4208             // Handle all tag names
4209             tn = this.split('/', t);
4210             for (y=0; y<tn.length; y++) {
4211                 r = {};
4212
4213                 r.tag = tn[y];
4214                 r.forceAttribs = null;
4215                 r.defaultAttribs = null;
4216                 r.validAttribValues = null;
4217
4218                 // Handle prefixes
4219                 px = r.tag.charAt(0);
4220                 r.forceOpen = px == '+';
4221                 r.removeEmpty = px == '-';
4222                 r.fill = px == '#';
4223                 r.tag = r.tag.replace(/\+|-|#/g, '');
4224                 r.oTagName = tn[0].replace(/\+|-|#/g, '').toLowerCase();
4225                 r.isWild = new RegExp('\\*|\\?|\\+', 'g').test(r.tag);
4226                 r.validRe = new RegExp(this._wildcardToRe('^' + r.tag + '$'));
4227
4228                 // Setup valid attributes
4229                 if (p.length > 1) {
4230                     r.vAttribsRe = '^(';
4231                     a = this.split(/\|/, p[1]);
4232
4233                     for (i=0; i<a.length; i++) {
4234                         t = a[i];
4235
4236                         av = new RegExp('(=|:|<)(.*?)$').exec(t);
4237                         t = t.replace(new RegExp('(=|:|<).*?$'), '');
4238                         if (av && av.length > 0) {
4239                             if (av[0].charAt(0) == ':') {
4240                                 if (!r.forceAttribs)
4241                                     r.forceAttribs = tinyMCE.clearArray(new Array());
4242
4243                                 r.forceAttribs[t.toLowerCase()] = av[0].substring(1);
4244                             } else if (av[0].charAt(0) == '=') {
4245                                 if (!r.defaultAttribs)
4246                                     r.defaultAttribs = tinyMCE.clearArray(new Array());
4247
4248                                 dv = av[0].substring(1);
4249
4250                                 r.defaultAttribs[t.toLowerCase()] = dv == "" ? "mce_empty" : dv;
4251                             } else if (av[0].charAt(0) == '<') {
4252                                 if (!r.validAttribValues)
4253                                     r.validAttribValues = tinyMCE.clearArray(new Array());
4254
4255                                 r.validAttribValues[t.toLowerCase()] = this._arrayToRe(this.split('?', av[0].substring(1)), '');
4256                             }
4257                         }
4258
4259                         r.vAttribsRe += '' + t.toLowerCase() + (i != a.length - 1 ? '|' : '');
4260
4261                         a[i] = t.toLowerCase();
4262                     }
4263
4264                     r.vAttribsRe += ')$';
4265                     r.vAttribsRe = this._wildcardToRe(r.vAttribsRe);
4266                     r.vAttribsReIsWild = new RegExp('\\*|\\?|\\+', 'g').test(r.vAttribsRe);
4267                     r.vAttribsRe = new RegExp(r.vAttribsRe);
4268                     r.vAttribs = a.reverse();
4269
4270                     //tinyMCE.debug(r.tag, r.oTagName, r.vAttribsRe, r.vAttribsReWC);
4271                 } else {
4272                     r.vAttribsRe = '';
4273                     r.vAttribs = tinyMCE.clearArray(new Array());
4274                     r.vAttribsReIsWild = false;
4275                 }
4276
4277                 or[r.tag] = r;
4278             }
4279         }
4280
4281         return or;
4282     },
4283
4284     serializeNodeAsXML : function(n) {
4285         var s, b;
4286
4287         if (!this.xmlDoc) {
4288             if (this.isMSIE) {
4289                 try {this.xmlDoc = new ActiveXObject('MSXML2.DOMDocument');} catch (e) {}
4290
4291                 if (!this.xmlDoc)
4292                     try {this.xmlDoc = new ActiveXObject('Microsoft.XmlDom');} catch (e) {}
4293             } else
4294                 this.xmlDoc = document.implementation.createDocument('', '', null);
4295
4296             if (!this.xmlDoc)
4297                 alert("Error XML Parser could not be found.");
4298         }
4299
4300         if (this.xmlDoc.firstChild)
4301             this.xmlDoc.removeChild(this.xmlDoc.firstChild);
4302
4303         b = this.xmlDoc.createElement("html");
4304         b = this.xmlDoc.appendChild(b);
4305
4306         this._convertToXML(n, b);
4307
4308         if (this.isMSIE)
4309             return this.xmlDoc.xml;
4310         else
4311             return new XMLSerializer().serializeToString(this.xmlDoc);
4312     },
4313
4314     _convertToXML : function(n, xn) {
4315         var xd, el, i, l, cn, at, no, hc = false;
4316
4317         if (this._isDuplicate(n))
4318             return;
4319
4320         xd = this.xmlDoc;
4321
4322         switch (n.nodeType) {
4323             case 1: // Element
4324                 hc = n.hasChildNodes();
4325
4326                 el = xd.createElement(n.nodeName.toLowerCase());
4327
4328                 at = n.attributes;
4329                 for (i=at.length-1; i>-1; i--) {
4330                     no = at[i];
4331
4332                     if (no.specified && no.nodeValue)
4333                         el.setAttribute(no.nodeName.toLowerCase(), no.nodeValue);
4334                 }
4335
4336                 if (!hc && !this.closeElementsRe.test(n.nodeName))
4337                     el.appendChild(xd.createTextNode(""));
4338
4339                 xn = xn.appendChild(el);
4340                 break;
4341
4342             case 3: // Text
4343                 xn.appendChild(xd.createTextNode(n.nodeValue));
4344                 return;
4345
4346             case 8: // Comment
4347                 xn.appendChild(xd.createComment(n.nodeValue));
4348                 return;
4349         }
4350
4351         if (hc) {
4352             cn = n.childNodes;
4353
4354             for (i=0, l=cn.length; i<l; i++)
4355                 this._convertToXML(cn[i], xn);
4356         }
4357     },
4358
4359     serializeNodeAsHTML : function(n) {
4360         var en, no, h = '', i, l, r, cn, va = false, f = false, at, hc;
4361
4362         this._setupRules(); // Will initialize cleanup rules
4363
4364         if (this._isDuplicate(n))
4365             return '';
4366
4367         switch (n.nodeType) {
4368             case 1: // Element
4369                 hc = n.hasChildNodes();
4370
4371                 // MSIE sometimes produces <//tag>
4372                 if ((tinyMCE.isMSIE && !tinyMCE.isOpera) && n.nodeName.indexOf('/') != -1)
4373                     break;
4374
4375                 if (this.vElementsRe.test(n.nodeName) && (!this.iveRe || !this.iveRe.test(n.nodeName))) {
4376                     va = true;
4377
4378                     r = this.rules[n.nodeName];
4379                     if (!r) {
4380                         at = this.rules;
4381                         for (no in at) {
4382                             if (at[no] && at[no].validRe.test(n.nodeName)) {
4383                                 r = at[no];
4384                                 break;
4385                             }
4386                         }
4387                     }
4388
4389                     en = r.isWild ? n.nodeName.toLowerCase() : r.oTagName;
4390                     f = r.fill;
4391
4392                     if (r.removeEmpty && !hc)
4393                         return "";
4394
4395                     h += '<' + en;
4396
4397                     if (r.vAttribsReIsWild) {
4398                         // Serialize wildcard attributes
4399                         at = n.attributes;
4400                         for (i=at.length-1; i>-1; i--) {
4401                             no = at[i];
4402                             if (no.specified && r.vAttribsRe.test(no.nodeName))
4403                                 h += this._serializeAttribute(n, r, no.nodeName);
4404                         }
4405                     } else {
4406                         // Serialize specific attributes
4407                         for (i=r.vAttribs.length-1; i>-1; i--)
4408                             h += this._serializeAttribute(n, r, r.vAttribs[i]);
4409                     }
4410
4411                     // Serialize mce_ atts
4412                     if (!this.settings.on_save) {
4413                         at = this.mceAttribs;
4414
4415                         for (no in at) {
4416                             if (at[no])
4417                                 h += this._serializeAttribute(n, r, at[no]);
4418                         }
4419                     }
4420
4421                     // Close these
4422                     if (this.closeElementsRe.test(n.nodeName))
4423                         return h + ' />';
4424
4425                     h += '>';
4426
4427                     if (this.isMSIE && this.codeElementsRe.test(n.nodeName))
4428                         h += n.innerHTML;
4429                 }
4430             break;
4431
4432             case 3: // Text
4433                 if (n.parentNode && this.codeElementsRe.test(n.parentNode.nodeName))
4434                     return this.isMSIE ? '' : n.nodeValue;
4435
4436                 return this.xmlEncode(n.nodeValue);
4437
4438             case 8: // Comment
4439                 return "<!--" + this._trimComment(n.nodeValue) + "-->";
4440         }
4441
4442         if (hc) {
4443             cn = n.childNodes;
4444
4445             for (i=0, l=cn.length; i<l; i++)
4446                 h += this.serializeNodeAsHTML(cn[i]);
4447         }
4448
4449         // Fill empty nodes
4450         if (f && !hc)
4451             h += this.fillStr;
4452
4453         // End element
4454         if (va)
4455             h += '</' + en + '>';
4456
4457         return h;
4458     },
4459
4460     _serializeAttribute : function(n, r, an) {
4461         var av = '', t, os = this.settings.on_save;
4462
4463         if (os && (an.indexOf('mce_') == 0 || an.indexOf('_moz') == 0))
4464             return '';
4465
4466         if (os && this.mceAttribs[an])
4467             av = this._getAttrib(n, this.mceAttribs[an]);
4468
4469         if (av.length == 0)
4470             av = this._getAttrib(n, an);
4471
4472         if (av.length == 0 && r.defaultAttribs && (t = r.defaultAttribs[an])) {
4473             av = t;
4474
4475             if (av == "mce_empty")
4476                 return " " + an + '=""';
4477         }
4478
4479         if (r.forceAttribs && (t = r.forceAttribs[an]))
4480             av = t;
4481
4482         if (os && av.length != 0 && this.settings.url_converter.length != 0 && /^(src|href|longdesc)$/.test(an))
4483             av = eval(this.settings.url_converter + '(this, n, av)');
4484
4485         if (av.length != 0 && r.validAttribValues && r.validAttribValues[an] && !r.validAttribValues[an].test(av))
4486             return "";
4487
4488         if (av.length != 0 && av == "{$uid}")
4489             av = "uid_" + (this.idCount++);
4490
4491         if (av.length != 0)
4492             return " " + an + "=" + '"' + this.xmlEncode(av) + '"';
4493
4494         return "";
4495     },
4496
4497     formatHTML : function(h) {
4498         var s = this.settings, p = '', i = 0, li = 0, o = '', l;
4499
4500         h = h.replace(/\r/g, ''); // Windows sux, isn't carriage return a thing of the past :)
4501         h = '\n' + h;
4502         h = h.replace(new RegExp('\\n\\s+', 'gi'), '\n'); // Remove previous formatting
4503         h = h.replace(this.nlBeforeRe, '\n<$1$2>');
4504         h = h.replace(this.nlAfterRe, '<$1$2>\n');
4505         h = h.replace(this.nlBeforeAfterRe, '\n<$1$2$3>\n');
4506         h += '\n';
4507
4508         //tinyMCE.debug(h);
4509
4510         while ((i = h.indexOf('\n', i + 1)) != -1) {
4511             if ((l = h.substring(li + 1, i)).length != 0) {
4512                 if (this.ouRe.test(l) && p.length >= s.indent_levels)
4513                     p = p.substring(s.indent_levels);
4514
4515                 o += p + l + '\n';
4516     
4517                 if (this.inRe.test(l))
4518                     p += this.inStr;
4519             }
4520
4521             li = i;
4522         }
4523
4524         //tinyMCE.debug(h);
4525
4526         return o;
4527     },
4528
4529     xmlEncode : function(s) {
4530         var i, l, e, o = '', c;
4531
4532         this._setupEntities(); // Will intialize lookup table
4533
4534         switch (this.settings.entity_encoding) {
4535             case "raw":
4536                 return tinyMCE.xmlEncode(s);
4537
4538             case "named":
4539                 for (i=0, l=s.length; i<l; i++) {
4540                     c = s.charCodeAt(i);
4541                     e = this.entities[c];
4542
4543                     if (e && e != '')
4544                         o += '&' + e + ';';
4545                     else
4546                         o += String.fromCharCode(c);
4547                 }
4548
4549                 return o;
4550
4551             case "numeric":
4552                 for (i=0, l=s.length; i<l; i++) {
4553                     c = s.charCodeAt(i);
4554
4555                     if (c > 127 || c == 60 || c == 62 || c == 38 || c == 39 || c == 34)
4556                         o += '&#' + c + ";";
4557                     else
4558                         o += String.fromCharCode(c);
4559                 }
4560
4561                 return o;
4562         }
4563
4564         return s;
4565     },
4566
4567     split : function(re, s) {
4568         var c = s.split(re);
4569         var i, l, o = new Array();
4570
4571         for (i=0, l=c.length; i<l; i++) {
4572             if (c[i] != '')
4573                 o[i] = c[i];
4574         }
4575
4576         return o;
4577     },
4578
4579     _trimComment : function(s) {
4580         // Remove mce_src, mce_href
4581         s = s.replace(new RegExp('\\smce_src=\"[^\"]*\"', 'gi'), "");
4582         s = s.replace(new RegExp('\\smce_href=\"[^\"]*\"', 'gi'), "");
4583
4584         return s;
4585     },
4586
4587     _getAttrib : function(e, n, d) {
4588         if (typeof(d) == "undefined")
4589             d = "";
4590
4591         if (!e || e.nodeType != 1)
4592             return d;
4593
4594         var v = e.getAttribute(n, 0);
4595
4596         if (n == "class" && !v)
4597             v = e.className;
4598
4599         if (this.isMSIE && n == "http-equiv")
4600             v = e.httpEquiv;
4601
4602         if (this.isMSIE && e.nodeName == "FORM" && n == "enctype" && v == "application/x-www-form-urlencoded")
4603             v = "";
4604
4605         if (this.isMSIE && e.nodeName == "INPUT" && n == "size" && v == "20")
4606             v = "";
4607
4608         if (this.isMSIE && e.nodeName == "INPUT" && n == "maxlength" && v == "2147483647")
4609             v = "";
4610
4611         if (n == "style" && !tinyMCE.isOpera)
4612             v = e.style.cssText;
4613
4614         if (n == 'style')
4615             v = tinyMCE.serializeStyle(tinyMCE.parseStyle(v));
4616
4617         if (this.settings.on_save && n.indexOf('on') != -1 && this.settings.on_save && v && v != "")
4618             v = tinyMCE.cleanupEventStr(v);
4619
4620         return (v && v != "") ? '' + v : d;
4621     },
4622
4623     _urlConverter : function(c, n, v) {
4624         if (!c.settings.on_save)
4625             return tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings.base_href, v);
4626         else if (tinyMCE.getParam('convert_urls'))
4627             return eval(tinyMCE.settings.urlconverter_callback + "(v, n, true);");
4628
4629         return v;
4630     },
4631
4632     _arrayToRe : function(a, op, be, af) {
4633         var i, r;
4634
4635         op = typeof(op) == "undefined" ? "gi" : op;
4636         be = typeof(be) == "undefined" ? "^(" : be;
4637         af = typeof(af) == "undefined" ? ")$" : af;
4638
4639         r = be;
4640
4641         for (i=0; i<a.length; i++)
4642             r += this._wildcardToRe(a[i]) + (i != a.length-1 ? "|" : "");
4643
4644         r += af;
4645
4646         return new RegExp(r, op);
4647     },
4648
4649     _wildcardToRe : function(s) {
4650         s = s.replace(/\?/g, '(\\S?)');
4651         s = s.replace(/\+/g, '(\\S+)');
4652         s = s.replace(/\*/g, '(\\S*)');
4653
4654         return s;
4655     },
4656
4657     _setupEntities : function() {
4658         var n, a, i, s = this.settings;
4659
4660         // Setup entities
4661         if (!this.entitiesDone) {
4662             if (s.entity_encoding == "named") {
4663                 n = tinyMCE.clearArray(new Array());
4664                 a = this.split(',', s.entities);
4665                 for (i=0; i<a.length; i+=2)
4666                     n[a[i]] = a[i+1];
4667
4668                 this.entities = n;
4669             }
4670
4671             this.entitiesDone = true;
4672         }
4673     },
4674
4675     _setupRules : function() {
4676         var s = this.settings;
4677
4678         // Setup default rule
4679         if (!this.rulesDone) {
4680             this.addRuleStr(s.valid_elements);
4681             this.addRuleStr(s.extended_valid_elements);
4682
4683             this.rulesDone = true;
4684         }
4685     },
4686
4687     _isDuplicate : function(n) {
4688         var i;
4689
4690         if (!this.settings.fix_content_duplication)
4691             return false;
4692
4693         if (tinyMCE.isMSIE && !tinyMCE.isOpera && n.nodeType == 1) {
4694             // Mark elements
4695             if (n.mce_serialized == this.serializationId)
4696                 return true;
4697
4698             n.setAttribute('mce_serialized', this.serializationId);
4699         } else {
4700             // Search lookup table for text nodes  and comments
4701             for (i=0; i<this.serializedNodes.length; i++) {
4702                 if (this.serializedNodes[i] == n)
4703                     return true;
4704             }
4705
4706             this.serializedNodes[this.serializedNodes.length] = n;
4707         }
4708
4709         return false;
4710     }
4711 };
4712
4713 /* file:jscripts/tiny_mce/classes/TinyMCE_DOMUtils.class.js */
4714
4715 TinyMCE_Engine.prototype.getElementByAttributeValue = function(n, e, a, v) {
4716     return (n = this.getElementsByAttributeValue(n, e, a, v)).length == 0 ? null : n[0];
4717 };
4718
4719 TinyMCE_Engine.prototype.getElementsByAttributeValue = function(n, e, a, v) {
4720     var i, nl = n.getElementsByTagName(e), o = new Array();
4721
4722     for (i=0; i<nl.length; i++) {
4723         if (tinyMCE.getAttrib(nl[i], a).indexOf(v) != -1)
4724             o[o.length] = nl[i];
4725     }
4726
4727     return o;
4728 };
4729
4730 TinyMCE_Engine.prototype.isBlockElement = function(n) {
4731     return n != null && n.nodeType == 1 && this.blockRegExp.test(n.nodeName);
4732 };
4733
4734 TinyMCE_Engine.prototype.getParentBlockElement = function(n) {
4735     while (n) {
4736         if (this.isBlockElement(n))
4737             return n;
4738
4739         n = n.parentNode;
4740     }
4741
4742     return null;
4743 };
4744
4745 TinyMCE_Engine.prototype.insertAfter = function(n, r){
4746     if (r.nextSibling)
4747         r.parentNode.insertBefore(n, r.nextSibling);
4748     else
4749         r.parentNode.appendChild(n);
4750 };
4751
4752 TinyMCE_Engine.prototype.setInnerHTML = function(e, h) {
4753     var i, nl, n;
4754
4755     if (tinyMCE.isMSIE && !tinyMCE.isOpera) {
4756         // Since MSIE handles invalid HTML better that valid XHTML we
4757         // need to make some things invalid. <hr /> gets converted to <hr>.
4758         h = h.replace(/\s\/>/g, '>');
4759
4760         // Since MSIE auto generated emtpy P tags some times we must tell it to keep the real ones
4761         h = h.replace(/<p([^>]*)>\u00A0?<\/p>/gi, '<p$1 mce_keep="true">&nbsp;</p>'); // Keep empty paragraphs
4762         h = h.replace(/<p([^>]*)>\s*&nbsp;\s*<\/p>/gi, '<p$1 mce_keep="true">&nbsp;</p>'); // Keep empty paragraphs
4763         h = h.replace(/<p([^>]*)>\s+<\/p>/gi, '<p$1 mce_keep="true">&nbsp;</p>'); // Keep empty paragraphs
4764
4765         // Remove first comment
4766         e.innerHTML = tinyMCE.uniqueTag + h;
4767         e.firstChild.removeNode(true);
4768
4769         // Remove weird auto generated empty paragraphs unless it's supposed to be there
4770         nl = e.getElementsByTagName("p");
4771         for (i=nl.length-1; i>=0; i--) {
4772             n = nl[i];
4773
4774             if (n.nodeName == 'P' && !n.hasChildNodes() && !n.mce_keep)
4775                 n.parentNode.removeChild(n);
4776         }
4777     } else {
4778         h = this.fixGeckoBaseHREFBug(1, e, h);
4779         e.innerHTML = h;
4780         this.fixGeckoBaseHREFBug(2, e, h);
4781     }
4782 };
4783
4784 TinyMCE_Engine.prototype.getOuterHTML = function(e) {
4785     if (tinyMCE.isMSIE)
4786         return e.outerHTML;
4787
4788     var d = e.ownerDocument.createElement("body");
4789     d.appendChild(e);
4790     return d.innerHTML;
4791 };
4792
4793 TinyMCE_Engine.prototype.setOuterHTML = function(e, h) {
4794     if (tinyMCE.isMSIE) {
4795         e.outerHTML = h;
4796         return;
4797     }
4798
4799     var d = e.ownerDocument.createElement("body");
4800     d.innerHTML = h;
4801     e.parentNode.replaceChild(d.firstChild, e);
4802 };
4803
4804 TinyMCE_Engine.prototype._getElementById = function(id, d) {
4805     var e, i, j, f;
4806
4807     if (typeof(d) == "undefined")
4808         d = document;
4809
4810     e = d.getElementById(id);
4811     if (!e) {
4812         f = d.forms;
4813
4814         for (i=0; i<f.length; i++) {
4815             for (j=0; j<f[i].elements.length; j++) {
4816                 if (f[i].elements[j].name == id) {
4817                     e = f[i].elements[j];
4818                     break;
4819                 }
4820             }
4821         }
4822     }
4823
4824     return e;
4825 };
4826
4827 TinyMCE_Engine.prototype.getNodeTree = function(n, na, t, nn) {
4828     var i;
4829
4830     if (typeof(t) == "undefined" || n.nodeType == t && (typeof(nn) == "undefined" || n.nodeName == nn))
4831         na[na.length] = n;
4832
4833     if (n.hasChildNodes()) {
4834         for (i=0; i<n.childNodes.length; i++)
4835             tinyMCE.getNodeTree(n.childNodes[i], na, t, nn);
4836     }
4837
4838     return na;
4839 };
4840
4841 TinyMCE_Engine.prototype.getParentElement = function(node, names, attrib_name, attrib_value) {
4842     if (typeof(names) == "undefined") {
4843         if (node.nodeType == 1)
4844             return node;
4845
4846         // Find parent node that is a element
4847         while ((node = node.parentNode) != null && node.nodeType != 1) ;
4848
4849         return node;
4850     }
4851
4852     if (node == null)
4853         return null;
4854
4855     var namesAr = names.toUpperCase().split(',');
4856
4857     do {
4858         for (var i=0; i<namesAr.length; i++) {
4859             if (node.nodeName == namesAr[i] || names == "*") {
4860                 if (typeof(attrib_name) == "undefined")
4861                     return node;
4862                 else if (node.getAttribute(attrib_name)) {
4863                     if (typeof(attrib_value) == "undefined") {
4864                         if (node.getAttribute(attrib_name) != "")
4865                             return node;
4866                     } else if (node.getAttribute(attrib_name) == attrib_value)
4867                         return node;
4868                 }
4869             }
4870         }
4871     } while ((node = node.parentNode) != null);
4872
4873     return null;
4874 };
4875
4876 TinyMCE_Engine.prototype.getParentNode = function(n, f) {
4877     while (n) {
4878         if (f(n))
4879             return n;
4880
4881         n = n.parentNode;
4882     }
4883
4884     return null;
4885 };
4886
4887 TinyMCE_Engine.prototype.getAttrib = function(elm, name, default_value) {
4888     if (typeof(default_value) == "undefined")
4889         default_value = "";
4890
4891     // Not a element
4892     if (!elm || elm.nodeType != 1)
4893         return default_value;
4894
4895     var v = elm.getAttribute(name);
4896
4897     // Try className for class attrib
4898     if (name == "class" && !v)
4899         v = elm.className;
4900
4901     // Workaround for a issue with Firefox 1.5rc2+
4902     if (tinyMCE.isGecko && name == "src" && elm.src != null && elm.src != "")
4903         v = elm.src;
4904
4905     // Workaround for a issue with Firefox 1.5rc2+
4906     if (tinyMCE.isGecko && name == "href" && elm.href != null && elm.href != "")
4907         v = elm.href;
4908
4909     if (name == "http-equiv" && tinyMCE.isMSIE)
4910         v = elm.httpEquiv;
4911
4912     if (name == "style" && !tinyMCE.isOpera)
4913         v = elm.style.cssText;
4914
4915     return (v && v != "") ? v : default_value;
4916 };
4917
4918 TinyMCE_Engine.prototype.setAttrib = function(element, name, value, fix_value) {
4919     if (typeof(value) == "number" && value != null)
4920         value = "" + value;
4921
4922     if (fix_value) {
4923         if (value == null)
4924             value = "";
4925
4926         var re = new RegExp('[^0-9%]', 'g');
4927         value = value.replace(re, '');
4928     }
4929
4930     if (name == "style")
4931         element.style.cssText = value;
4932
4933     if (name == "class")
4934         element.className = value;
4935
4936     if (value != null && value != "" && value != -1)
4937         element.setAttribute(name, value);
4938     else
4939         element.removeAttribute(name);
4940 };
4941
4942 TinyMCE_Engine.prototype.setStyleAttrib = function(elm, name, value) {
4943     eval('elm.style.' + name + '=value;');
4944
4945     // Style attrib deleted
4946     if (tinyMCE.isMSIE && value == null || value == '') {
4947         var str = tinyMCE.serializeStyle(tinyMCE.parseStyle(elm.style.cssText));
4948         elm.style.cssText = str;
4949         elm.setAttribute("style", str);
4950     }
4951 };
4952
4953 TinyMCE_Engine.prototype.switchClass = function(ei, c) {
4954     var e;
4955
4956     if (tinyMCE.switchClassCache[ei])
4957         e = tinyMCE.switchClassCache[ei];
4958     else
4959         e = tinyMCE.switchClassCache[ei] = document.getElementById(ei);
4960
4961     if (e) {
4962         // Keep tile mode
4963         if (tinyMCE.settings.button_tile_map && e.className && e.className.indexOf('mceTiledButton') == 0)
4964             c = 'mceTiledButton ' + c;
4965
4966         e.className = c;
4967     }
4968 };
4969
4970 TinyMCE_Engine.prototype.getAbsPosition = function(n) {
4971     var p = {absLeft : 0, absTop : 0};
4972
4973     while (n) {
4974         p.absLeft += n.offsetLeft;
4975         p.absTop += n.offsetTop;
4976         n = n.offsetParent;
4977     }
4978
4979     return p;
4980 };
4981
4982 TinyMCE_Engine.prototype.prevNode = function(e, n) {
4983     var a = n.split(','), i;
4984
4985     while ((e = e.previousSibling) != null) {
4986         for (i=0; i<a.length; i++) {
4987             if (e.nodeName == a[i])
4988                 return e;
4989         }
4990     }
4991
4992     return null;
4993 };
4994
4995 TinyMCE_Engine.prototype.nextNode = function(e, n) {
4996     var a = n.split(','), i;
4997
4998     while ((e = e.nextSibling) != null) {
4999         for (i=0; i<a.length; i++) {
5000             if (e.nodeName == a[i])
5001                 return e;
5002         }
5003     }
5004
5005     return null;
5006 };
5007
5008 TinyMCE_Engine.prototype.selectNodes = function(n, f, a) {
5009     var i;
5010
5011     if (!a)
5012         a = new Array();
5013
5014     if (f(n))
5015         a[a.length] = n;
5016
5017     if (n.hasChildNodes()) {
5018         for (i=0; i<n.childNodes.length; i++)
5019             tinyMCE.selectNodes(n.childNodes[i], f, a);
5020     }
5021
5022     return a;
5023 };
5024
5025 TinyMCE_Engine.prototype.addCSSClass = function(e, c, b) {
5026     var o = this.removeCSSClass(e, c);
5027
5028     return e.className = b ? c + (o != '' ? (' ' + o) : '') : (o != '' ? (o + ' ') : '') + c;
5029 };
5030
5031 TinyMCE_Engine.prototype.removeCSSClass = function(e, c) {
5032     var a = this.explode(' ', e.className), i;
5033
5034     for (i=0; i<a.length; i++) {
5035         if (a[i] == c)
5036             a[i] = '';
5037     }
5038
5039     return e.className = a.join(' ');
5040 };
5041
5042 TinyMCE_Engine.prototype.renameElement = function(e, n, d) {
5043     var ne, i, ar;
5044
5045     d = typeof(d) == "undefined" ? tinyMCE.selectedInstance.getDoc() : d;
5046
5047     if (e) {
5048         ne = d.createElement(n);
5049
5050         ar = e.attributes;
5051         for (i=ar.length-1; i>-1; i--) {
5052             if (ar[i].specified && ar[i].nodeValue)
5053                 ne.setAttribute(ar[i].nodeName.toLowerCase(), ar[i].nodeValue);
5054         }
5055
5056         ar = e.childNodes;
5057         for (i=0; i<ar.length; i++)
5058             ne.appendChild(ar[i].cloneNode(true));
5059
5060         e.parentNode.replaceChild(ne, e);
5061     }
5062 };
5063
5064 /* file:jscripts/tiny_mce/classes/TinyMCE_URL.class.js */
5065
5066 TinyMCE_Engine.prototype.parseURL = function(url_str) {
5067     var urlParts = new Array();
5068
5069     if (url_str) {
5070         var pos, lastPos;
5071
5072         // Parse protocol part
5073         pos = url_str.indexOf('://');
5074         if (pos != -1) {
5075             urlParts['protocol'] = url_str.substring(0, pos);
5076             lastPos = pos + 3;
5077         }
5078
5079         // Find port or path start
5080         for (var i=lastPos; i<url_str.length; i++) {
5081             var chr = url_str.charAt(i);
5082
5083             if (chr == ':')
5084                 break;
5085
5086             if (chr == '/')
5087                 break;
5088         }
5089         pos = i;
5090
5091         // Get host
5092         urlParts['host'] = url_str.substring(lastPos, pos);
5093
5094         // Get port
5095         urlParts['port'] = "";
5096         lastPos = pos;
5097         if (url_str.charAt(pos) == ':') {
5098             pos = url_str.indexOf('/', lastPos);
5099             urlParts['port'] = url_str.substring(lastPos+1, pos);
5100         }
5101
5102         // Get path
5103         lastPos = pos;
5104         pos = url_str.indexOf('?', lastPos);
5105
5106         if (pos == -1)
5107             pos = url_str.indexOf('#', lastPos);
5108
5109         if (pos == -1)
5110             pos = url_str.length;
5111
5112         urlParts['path'] = url_str.substring(lastPos, pos);
5113
5114         // Get query
5115         lastPos = pos;
5116         if (url_str.charAt(pos) == '?') {
5117             pos = url_str.indexOf('#');
5118             pos = (pos == -1) ? url_str.length : pos;
5119             urlParts['query'] = url_str.substring(lastPos+1, pos);
5120         }
5121
5122         // Get anchor
5123         lastPos = pos;
5124         if (url_str.charAt(pos) == '#') {
5125             pos = url_str.length;
5126             urlParts['anchor'] = url_str.substring(lastPos+1, pos);
5127         }
5128     }
5129
5130     return urlParts;
5131 };
5132
5133 TinyMCE_Engine.prototype.serializeURL = function(up) {
5134     var o = "";
5135
5136     if (up['protocol'])
5137         o += up['protocol'] + "://";
5138
5139     if (up['host'])
5140         o += up['host'];
5141
5142     if (up['port'])
5143         o += ":" + up['port'];
5144
5145     if (up['path'])
5146         o += up['path'];
5147
5148     if (up['query'])
5149         o += "?" + up['query'];
5150
5151     if (up['anchor'])
5152         o += "#" + up['anchor'];
5153
5154     return o;
5155 };
5156
5157 TinyMCE_Engine.prototype.convertAbsoluteURLToRelativeURL = function(base_url, url_to_relative) {
5158     var baseURL = this.parseURL(base_url);
5159     var targetURL = this.parseURL(url_to_relative);
5160     var strTok1;
5161     var strTok2;
5162     var breakPoint = 0;
5163     var outPath = "";
5164     var forceSlash = false;
5165
5166     if (targetURL.path == "")
5167         targetURL.path = "/";
5168     else
5169         forceSlash = true;
5170
5171     // Crop away last path part
5172     base_url = baseURL.path.substring(0, baseURL.path.lastIndexOf('/'));
5173     strTok1 = base_url.split('/');
5174     strTok2 = targetURL.path.split('/');
5175
5176     if (strTok1.length >= strTok2.length) {
5177         for (var i=0; i<strTok1.length; i++) {
5178             if (i >= strTok2.length || strTok1[i] != strTok2[i]) {
5179                 breakPoint = i + 1;
5180                 break;
5181             }
5182         }
5183     }
5184
5185     if (strTok1.length < strTok2.length) {
5186         for (var i=0; i<strTok2.length; i++) {
5187             if (i >= strTok1.length || strTok1[i] != strTok2[i]) {
5188                 breakPoint = i + 1;
5189                 break;
5190             }
5191         }
5192     }
5193
5194     if (breakPoint == 1)
5195         return targetURL.path;
5196
5197     for (var i=0; i<(strTok1.length-(breakPoint-1)); i++)
5198         outPath += "../";
5199
5200     for (var i=breakPoint-1; i<strTok2.length; i++) {
5201         if (i != (breakPoint-1))
5202             outPath += "/" + strTok2[i];
5203         else
5204             outPath += strTok2[i];
5205     }
5206
5207     targetURL.protocol = null;
5208     targetURL.host = null;
5209     targetURL.port = null;
5210     targetURL.path = outPath == "" && forceSlash ? "/" : outPath;
5211
5212     // Remove document prefix from local anchors
5213     var fileName = baseURL.path;
5214     var pos;
5215
5216     if ((pos = fileName.lastIndexOf('/')) != -1)
5217         fileName = fileName.substring(pos + 1);
5218
5219     // Is local anchor
5220     if (fileName == targetURL.path && targetURL.anchor != "")
5221         targetURL.path = "";
5222
5223     // If empty and not local anchor force filename or slash
5224     if (targetURL.path == "" && !targetURL.anchor)
5225         targetURL.path = fileName != "" ? fileName : "/";
5226
5227     return this.serializeURL(targetURL);
5228 };
5229
5230 TinyMCE_Engine.prototype.convertRelativeToAbsoluteURL = function(base_url, relative_url) {
5231     var baseURL = this.parseURL(base_url);
5232     var relURL = this.parseURL(relative_url);
5233
5234     if (relative_url == "" || relative_url.charAt(0) == '/' || relative_url.indexOf('://') != -1 || relative_url.indexOf('mailto:') != -1 || relative_url.indexOf('javascript:') != -1)
5235         return relative_url;
5236
5237     // Split parts
5238     baseURLParts = baseURL['path'].split('/');
5239     relURLParts = relURL['path'].split('/');
5240
5241     // Remove empty chunks
5242     var newBaseURLParts = new Array();
5243     for (var i=baseURLParts.length-1; i>=0; i--) {
5244         if (baseURLParts[i].length == 0)
5245             continue;
5246
5247         newBaseURLParts[newBaseURLParts.length] = baseURLParts[i];
5248     }
5249     baseURLParts = newBaseURLParts.reverse();
5250
5251     // Merge relURLParts chunks
5252     var newRelURLParts = new Array();
5253     var numBack = 0;
5254     for (var i=relURLParts.length-1; i>=0; i--) {
5255         if (relURLParts[i].length == 0 || relURLParts[i] == ".")
5256             continue;
5257
5258         if (relURLParts[i] == '..') {
5259             numBack++;
5260             continue;
5261         }
5262
5263         if (numBack > 0) {
5264             numBack--;
5265             continue;
5266         }
5267
5268         newRelURLParts[newRelURLParts.length] = relURLParts[i];
5269     }
5270
5271     relURLParts = newRelURLParts.reverse();
5272
5273     // Remove end from absolute path
5274     var len = baseURLParts.length-numBack;
5275     var absPath = (len <= 0 ? "" : "/") + baseURLParts.slice(0, len).join('/') + "/" + relURLParts.join('/');
5276     var start = "", end = "";
5277
5278     // Build output URL
5279     relURL.protocol = baseURL.protocol;
5280     relURL.host = baseURL.host;
5281     relURL.port = baseURL.port;
5282
5283     // Re-add trailing slash if it's removed
5284     if (relURL.path.charAt(relURL.path.length-1) == "/")
5285         absPath += "/";
5286
5287     relURL.path = absPath;
5288
5289     return this.serializeURL(relURL);
5290 };
5291
5292 TinyMCE_Engine.prototype.convertURL = function(url, node, on_save) {
5293     var prot = document.location.protocol;
5294     var host = document.location.hostname;
5295     var port = document.location.port;
5296
5297     // Pass through file protocol
5298     if (prot == "file:")
5299         return url;
5300
5301     // Something is wrong, remove weirdness
5302     url = tinyMCE.regexpReplace(url, '(http|https):///', '/');
5303
5304     // Mailto link or anchor (Pass through)
5305     if (url.indexOf('mailto:') != -1 || url.indexOf('javascript:') != -1 || tinyMCE.regexpReplace(url,'[ \t\r\n\+]|%20','').charAt(0) == "#")
5306         return url;
5307
5308     // Fix relative/Mozilla
5309     if (!tinyMCE.isMSIE && !on_save && url.indexOf("://") == -1 && url.charAt(0) != '/')
5310         return tinyMCE.settings['base_href'] + url;
5311
5312     // Handle relative URLs
5313     if (on_save && tinyMCE.getParam('relative_urls')) {
5314         var curl = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'], url);
5315         if (curl.charAt(0) == '/')
5316             curl = tinyMCE.settings['document_base_prefix'] + curl;
5317
5318         var urlParts = tinyMCE.parseURL(curl);
5319         var tmpUrlParts = tinyMCE.parseURL(tinyMCE.settings['document_base_url']);
5320
5321         // Force relative
5322         if (urlParts['host'] == tmpUrlParts['host'] && (urlParts['port'] == tmpUrlParts['port']))
5323             return tinyMCE.convertAbsoluteURLToRelativeURL(tinyMCE.settings['document_base_url'], curl);
5324     }
5325
5326     // Handle absolute URLs
5327     if (!tinyMCE.getParam('relative_urls')) {
5328         var urlParts = tinyMCE.parseURL(url);
5329         var baseUrlParts = tinyMCE.parseURL(tinyMCE.settings['base_href']);
5330
5331         // Force absolute URLs from relative URLs
5332         url = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'], url);
5333
5334         // If anchor and path is the same page
5335         if (urlParts['anchor'] && urlParts['path'] == baseUrlParts['path'])
5336             return "#" + urlParts['anchor'];
5337     }
5338
5339     // Remove current domain
5340     if (tinyMCE.getParam('remove_script_host')) {
5341         var start = "", portPart = "";
5342
5343         if (port != "")
5344             portPart = ":" + port;
5345
5346         start = prot + "//" + host + portPart + "/";
5347
5348         if (url.indexOf(start) == 0)
5349             url = url.substring(start.length-1);
5350     }
5351
5352     return url;
5353 };
5354
5355 TinyMCE_Engine.prototype.convertAllRelativeURLs = function(body) {
5356     // Convert all image URL:s to absolute URL
5357     var elms = body.getElementsByTagName("img");
5358     for (var i=0; i<elms.length; i++) {
5359         var src = tinyMCE.getAttrib(elms[i], 'src');
5360
5361         var msrc = tinyMCE.getAttrib(elms[i], 'mce_src');
5362         if (msrc != "")
5363             src = msrc;
5364
5365         if (src != "") {
5366             src = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'], src);
5367             elms[i].setAttribute("src", src);
5368         }
5369     }
5370
5371     // Convert all link URL:s to absolute URL
5372     var elms = body.getElementsByTagName("a");
5373     for (var i=0; i<elms.length; i++) {
5374         var href = tinyMCE.getAttrib(elms[i], 'href');
5375
5376         var mhref = tinyMCE.getAttrib(elms[i], 'mce_href');
5377         if (mhref != "")
5378             href = mhref;
5379
5380         if (href && href != "") {
5381             href = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'], href);
5382             elms[i].setAttribute("href", href);
5383         }
5384     }
5385 };
5386
5387 /* file:jscripts/tiny_mce/classes/TinyMCE_Array.class.js */
5388
5389 TinyMCE_Engine.prototype.clearArray = function(a) {
5390     for (var k in a)
5391         a[k] = null;
5392
5393     return a;
5394 };
5395
5396 /* file:jscripts/tiny_mce/classes/TinyMCE_Event.class.js */
5397
5398 TinyMCE_Engine.prototype._setEventsEnabled = function(node, state) {
5399     var events = new Array('onfocus','onblur','onclick','ondblclick',
5400                 'onmousedown','onmouseup','onmouseover','onmousemove',
5401                 'onmouseout','onkeypress','onkeydown','onkeydown','onkeyup');
5402
5403     var evs = tinyMCE.settings['event_elements'].split(',');
5404     for (var y=0; y<evs.length; y++){
5405         var elms = node.getElementsByTagName(evs[y]);
5406         for (var i=0; i<elms.length; i++) {
5407             var event = "";
5408
5409             for (var x=0; x<events.length; x++) {
5410                 if ((event = tinyMCE.getAttrib(elms[i], events[x])) != '') {
5411                     event = tinyMCE.cleanupEventStr("" + event);
5412
5413                     if (!state)
5414                         event = "return true;" + event;
5415                     else
5416                         event = event.replace(/^return true;/gi, '');
5417
5418                     elms[i].removeAttribute(events[x]);
5419                     elms[i].setAttribute(events[x], event);
5420                 }
5421             }
5422         }
5423     }
5424 };
5425
5426 TinyMCE_Engine.prototype._eventPatch = function(editor_id) {
5427     var n, inst, win, e;
5428
5429     // Remove odd, error
5430     if (typeof(tinyMCE) == "undefined")
5431         return true;
5432
5433     try {
5434         // Try selected instance first
5435         if (tinyMCE.selectedInstance) {
5436             win = tinyMCE.selectedInstance.getWin();
5437
5438             if (win && win.event) {
5439                 e = win.event;
5440
5441                 if (!e.target)
5442                     e.target = e.srcElement;
5443
5444                 TinyMCE_Engine.prototype.handleEvent(e);
5445                 return;
5446             }
5447         }
5448
5449         // Search for it
5450         for (n in tinyMCE.instances) {
5451             inst = tinyMCE.instances[n];
5452
5453             if (!tinyMCE.isInstance(inst))
5454                 continue;
5455
5456             tinyMCE.selectedInstance = inst;
5457             win = inst.getWin();
5458
5459             if (win && win.event) {
5460                 e = win.event;
5461
5462                 if (!e.target)
5463                     e.target = e.srcElement;
5464
5465                 TinyMCE_Engine.prototype.handleEvent(e);
5466                 return;
5467             }
5468         }
5469     } catch (ex) {
5470         // Ignore error if iframe is pointing to external URL
5471     }
5472 };
5473
5474 TinyMCE_Engine.prototype.unloadHandler = function() {
5475     tinyMCE.triggerSave(true, true);
5476 };
5477
5478 TinyMCE_Engine.prototype.addEventHandlers = function(inst) {
5479     var doc = inst.getDoc();
5480
5481     inst.switchSettings();
5482
5483     if (tinyMCE.isMSIE) {
5484         tinyMCE.addEvent(doc, "keypress", TinyMCE_Engine.prototype._eventPatch);
5485         tinyMCE.addEvent(doc, "keyup", TinyMCE_Engine.prototype._eventPatch);
5486         tinyMCE.addEvent(doc, "keydown", TinyMCE_Engine.prototype._eventPatch);
5487         tinyMCE.addEvent(doc, "mouseup", TinyMCE_Engine.prototype._eventPatch);
5488         tinyMCE.addEvent(doc, "mousedown", TinyMCE_Engine.prototype._eventPatch);
5489         tinyMCE.addEvent(doc, "click", TinyMCE_Engine.prototype._eventPatch);
5490     } else {
5491         tinyMCE.addEvent(doc, "keypress", tinyMCE.handleEvent);
5492         tinyMCE.addEvent(doc, "keydown", tinyMCE.handleEvent);
5493         tinyMCE.addEvent(doc, "keyup", tinyMCE.handleEvent);
5494         tinyMCE.addEvent(doc, "click", tinyMCE.handleEvent);
5495         tinyMCE.addEvent(doc, "mouseup", tinyMCE.handleEvent);
5496         tinyMCE.addEvent(doc, "mousedown", tinyMCE.handleEvent);
5497         tinyMCE.addEvent(doc, "focus", tinyMCE.handleEvent);
5498         tinyMCE.addEvent(doc, "blur", tinyMCE.handleEvent);
5499
5500         eval('try { doc.designMode = "On"; } catch(e) {}'); // Force designmode
5501     }
5502 };
5503
5504 TinyMCE_Engine.prototype.onMouseMove = function() {
5505     var inst;
5506
5507     if (!tinyMCE.hasMouseMoved) {
5508         inst = tinyMCE.selectedInstance;
5509
5510         // Workaround for bug #1437457 (Odd MSIE bug)
5511         if (inst.isFocused) {
5512             inst.undoBookmark = inst.selection.getBookmark();
5513             tinyMCE.hasMouseMoved = true;
5514         }
5515     }
5516
5517 //    tinyMCE.cancelEvent(inst.getWin().event);
5518 //    return false;
5519 };
5520
5521 TinyMCE_Engine.prototype.cancelEvent = function(e) {
5522     if (tinyMCE.isMSIE) {
5523         e.returnValue = false;
5524         e.cancelBubble = true;
5525     } else
5526         e.preventDefault();
5527 };
5528
5529 TinyMCE_Engine.prototype.addEvent = function(o, n, h) {
5530     if (o.attachEvent)
5531         o.attachEvent("on" + n, h);
5532     else
5533         o.addEventListener(n, h, false);
5534 };
5535
5536 TinyMCE_Engine.prototype.addSelectAccessibility = function(e, s, w) {
5537     // Add event handlers 
5538     if (!s._isAccessible) {
5539         s.onkeydown = tinyMCE.accessibleEventHandler;
5540         s.onblur = tinyMCE.accessibleEventHandler;
5541         s._isAccessible = true;
5542         s._win = w;
5543     }
5544
5545     return false;
5546 };
5547
5548 TinyMCE_Engine.prototype.accessibleEventHandler = function(e) {
5549     var win = this._win;
5550     e = tinyMCE.isMSIE ? win.event : e;
5551     var elm = tinyMCE.isMSIE ? e.srcElement : e.target;
5552
5553     // Unpiggyback onchange on blur
5554     if (e.type == "blur") {
5555         if (elm.oldonchange) {
5556             elm.onchange = elm.oldonchange;
5557             elm.oldonchange = null;
5558         }
5559
5560         return true;
5561     }
5562
5563     // Piggyback onchange
5564     if (elm.nodeName == "SELECT" && !elm.oldonchange) {
5565         elm.oldonchange = elm.onchange;
5566         elm.onchange = null;
5567     }
5568
5569     // Execute onchange and remove piggyback
5570     if (e.keyCode == 13 || e.keyCode == 32) {
5571         elm.onchange = elm.oldonchange;
5572         elm.onchange();
5573         elm.oldonchange = null;
5574
5575         tinyMCE.cancelEvent(e);
5576         return false;
5577     }
5578
5579     return true;
5580 };
5581
5582 TinyMCE_Engine.prototype._resetIframeHeight = function() {
5583     var ife;
5584
5585     if (tinyMCE.isMSIE && !tinyMCE.isOpera) {
5586         ife = tinyMCE.selectedInstance.iframeElement;
5587
5588 /*        if (ife._oldWidth) {
5589             ife.style.width = ife._oldWidth;
5590             ife.width = ife._oldWidth;
5591         }*/
5592
5593         if (ife._oldHeight) {
5594             ife.style.height = ife._oldHeight;
5595             ife.height = ife._oldHeight;
5596         }
5597     }
5598 };
5599
5600 /* file:jscripts/tiny_mce/classes/TinyMCE_Selection.class.js */
5601
5602 function TinyMCE_Selection(inst) {
5603     this.instance = inst;
5604 };
5605
5606 TinyMCE_Selection.prototype = {
5607     getSelectedHTML : function() {
5608         var inst = this.instance;
5609         var e, r = this.getRng(), h;
5610
5611         if (tinyMCE.isSafari) {
5612             // Not realy perfect!!
5613             return r.toString();
5614         }
5615
5616         e = document.createElement("body");
5617
5618         if (tinyMCE.isGecko)
5619             e.appendChild(r.cloneContents());
5620         else 
5621             e.innerHTML = r.item ? r.item(0).outerHTML : r.htmlText;
5622
5623         h = tinyMCE._cleanupHTML(inst, inst.contentDocument, inst.settings, e, e, false, true, false);
5624
5625         // When editing always use fonts internaly
5626         if (tinyMCE.getParam("convert_fonts_to_spans"))
5627             tinyMCE.convertSpansToFonts(inst.getDoc());
5628
5629         return h;
5630     },
5631
5632     getSelectedText : function() {
5633         var inst = this.instance;
5634         var d, r, s, t;
5635
5636         if (tinyMCE.isMSIE) {
5637             d = inst.getDoc();
5638
5639             if (d.selection.type == "Text") {
5640                 r = d.selection.createRange();
5641                 t = r.text;
5642             } else
5643                 t = '';
5644         } else {
5645             s = this.getSel();
5646
5647             if (s && s.toString)
5648                 t = s.toString();
5649             else
5650                 t = '';
5651         }
5652
5653         return t;
5654     },
5655
5656     getBookmark : function(simple) {
5657         var rng = this.getRng();
5658         var doc = this.instance.getDoc();
5659         var sp, le, s, e, nl, i, si, ei;
5660         var trng, sx, sy, xx = -999999999;
5661
5662         // Skip Opera for now
5663         if (tinyMCE.isOpera)
5664             return null;
5665
5666         sx = doc.body.scrollLeft + doc.documentElement.scrollLeft;
5667         sy = doc.body.scrollTop + doc.documentElement.scrollTop;
5668
5669         if (tinyMCE.isSafari || tinyMCE.isGecko)
5670             return {rng : rng, scrollX : sx, scrollY : sy};
5671
5672         if (tinyMCE.isMSIE) {
5673             if (simple)
5674                 return {rng : rng};
5675
5676             if (rng.item) {
5677                 e = rng.item(0);
5678
5679                 nl = doc.getElementsByTagName(e.nodeName);
5680                 for (i=0; i<nl.length; i++) {
5681                     if (e == nl[i]) {
5682                         sp = i;
5683                         break;
5684                     }
5685                 }
5686
5687                 return {
5688                     tag : e.nodeName,
5689                     index : sp,
5690                     scrollX : sx,
5691                     scrollY : sy
5692                 };
5693             } else {
5694                 trng = rng.duplicate();
5695                 trng.collapse(true);
5696                 sp = Math.abs(trng.move('character', xx));
5697
5698                 trng = rng.duplicate();
5699                 trng.collapse(false);
5700                 le = Math.abs(trng.move('character', xx)) - sp;
5701
5702                 return {
5703                     start : sp,
5704                     length : le,
5705                     scrollX : sx,
5706                     scrollY : sy
5707                 };
5708             }
5709         }
5710
5711         if (tinyMCE.isGecko) {
5712             s = tinyMCE.getParentElement(rng.startContainer);
5713             for (si=0; si<s.childNodes.length && s.childNodes[si] != rng.startContainer; si++) ;
5714
5715             nl = doc.getElementsByTagName(s.nodeName);
5716             for (i=0; i<nl.length; i++) {
5717                     if (s == nl[i]) {
5718                         sp = i;
5719                         break;
5720                     }
5721             }
5722
5723             e = tinyMCE.getParentElement(rng.endContainer);
5724             for (ei=0; ei<e.childNodes.length && e.childNodes[ei] != rng.endContainer; ei++) ;
5725
5726             nl = doc.getElementsByTagName(e.nodeName);
5727             for (i=0; i<nl.length; i++) {
5728                     if (e == nl[i]) {
5729                         le = i;
5730                         break;
5731                     }
5732             }
5733
5734             //tinyMCE.debug(s.nodeName, sp, rng.startOffset, '-' , e.nodeName, le, rng.endOffset);
5735             //tinyMCE.debug(sx, sy);
5736
5737             return {
5738                 startTag : s.nodeName,
5739                 start : sp,
5740                 startIndex : si,
5741                 endTag : e.nodeName,
5742                 end : le,
5743                 endIndex : ei,
5744                 startOffset : rng.startOffset,
5745                 endOffset : rng.endOffset,
5746                 scrollX : sx,
5747                 scrollY : sy
5748             };
5749         }
5750
5751         return null;
5752     },
5753
5754     moveToBookmark : function(bookmark) {
5755         var rng, nl, i;
5756         var inst = this.instance;
5757         var doc = inst.getDoc();
5758         var win = inst.getWin();
5759         var sel = this.getSel();
5760
5761         if (!bookmark)
5762             return false;
5763
5764         if (tinyMCE.isSafari) {
5765             sel.setBaseAndExtent(bookmark.startContainer, bookmark.startOffset, bookmark.endContainer, bookmark.endOffset);
5766             return true;
5767         }
5768
5769         if (tinyMCE.isMSIE) {
5770             if (bookmark.rng) {
5771                 bookmark.rng.select();
5772                 return true;
5773             }
5774
5775             win.focus();
5776
5777             if (bookmark.tag) {
5778                 rng = inst.getBody().createControlRange();
5779
5780                 nl = doc.getElementsByTagName(bookmark.tag);
5781
5782                 if (nl.length > bookmark.index) {
5783                     try {
5784                         rng.addElement(nl[bookmark.index]);
5785                     } catch (ex) {
5786                         // Might be thrown if the node no longer exists
5787                     }
5788                 }
5789             } else {
5790                 rng = inst.getSel().createRange();
5791                 rng.moveToElementText(inst.getBody());
5792                 rng.collapse(true);
5793                 rng.moveStart('character', bookmark.start);
5794                 rng.moveEnd('character', bookmark.length);
5795             }
5796
5797             rng.select();
5798
5799             win.scrollTo(bookmark.scrollX, bookmark.scrollY);
5800             return true;
5801         }
5802
5803         if (tinyMCE.isGecko && bookmark.rng) {
5804             sel.removeAllRanges();
5805             sel.addRange(bookmark.rng);
5806             win.scrollTo(bookmark.scrollX, bookmark.scrollY);
5807             return true;
5808         }
5809
5810         if (tinyMCE.isGecko) {
5811     //        try {
5812                 rng = doc.createRange();
5813
5814                 nl = doc.getElementsByTagName(bookmark.startTag);
5815                 if (nl.length > bookmark.start)
5816                     rng.setStart(nl[bookmark.start].childNodes[bookmark.startIndex], bookmark.startOffset);
5817
5818                 nl = doc.getElementsByTagName(bookmark.endTag);
5819                 if (nl.length > bookmark.end)
5820                     rng.setEnd(nl[bookmark.end].childNodes[bookmark.endIndex], bookmark.endOffset);
5821
5822                 sel.removeAllRanges();
5823                 sel.addRange(rng);
5824     /*        } catch {
5825                 // Ignore
5826             }*/
5827
5828             win.scrollTo(bookmark.scrollX, bookmark.scrollY);
5829             return true;
5830         }
5831
5832         return false;
5833     },
5834
5835     selectNode : function(node, collapse, select_text_node, to_start) {
5836         var inst = this.instance, sel, rng, nodes;
5837
5838         if (!node)
5839             return;
5840
5841         if (typeof(collapse) == "undefined")
5842             collapse = true;
5843
5844         if (typeof(select_text_node) == "undefined")
5845             select_text_node = false;
5846
5847         if (typeof(to_start) == "undefined")
5848             to_start = true;
5849
5850         if (tinyMCE.isMSIE) {
5851             rng = inst.getBody().createTextRange();
5852
5853             try {
5854                 rng.moveToElementText(node);
5855
5856                 if (collapse)
5857                     rng.collapse(to_start);
5858
5859                 rng.select();
5860             } catch (e) {
5861                 // Throws illigal agrument in MSIE some times
5862             }
5863         } else {
5864             sel = this.getSel();
5865
5866             if (!sel)
5867                 return;
5868
5869             if (tinyMCE.isSafari) {
5870                 sel.setBaseAndExtent(node, 0, node, node.innerText.length);
5871
5872                 if (collapse) {
5873                     if (to_start)
5874                         sel.collapseToStart();
5875                     else
5876                         sel.collapseToEnd();
5877                 }
5878
5879                 this.scrollToNode(node);
5880
5881                 return;
5882             }
5883
5884             rng = inst.getDoc().createRange();
5885
5886             if (select_text_node) {
5887                 // Find first textnode in tree
5888                 nodes = tinyMCE.getNodeTree(node, new Array(), 3);
5889                 if (nodes.length > 0)
5890                     rng.selectNodeContents(nodes[0]);
5891                 else
5892                     rng.selectNodeContents(node);
5893             } else
5894                 rng.selectNode(node);
5895
5896             if (collapse) {
5897                 // Special treatment of textnode collapse
5898                 if (!to_start && node.nodeType == 3) {
5899                     rng.setStart(node, node.nodeValue.length);
5900                     rng.setEnd(node, node.nodeValue.length);
5901                 } else
5902                     rng.collapse(to_start);
5903             }
5904
5905             sel.removeAllRanges();
5906             sel.addRange(rng);
5907         }
5908
5909         this.scrollToNode(node);
5910
5911         // Set selected element
5912         tinyMCE.selectedElement = null;
5913         if (node.nodeType == 1)
5914             tinyMCE.selectedElement = node;
5915     },
5916
5917     scrollToNode : function(node) {
5918         var inst = this.instance;
5919         var pos, doc, scrollX, scrollY, height;
5920
5921         // Scroll to node position
5922         pos = tinyMCE.getAbsPosition(node);
5923         doc = inst.getDoc();
5924         scrollX = doc.body.scrollLeft + doc.documentElement.scrollLeft;
5925         scrollY = doc.body.scrollTop + doc.documentElement.scrollTop;
5926         height = tinyMCE.isMSIE ? document.getElementById(inst.editorId).style.pixelHeight : inst.targetElement.clientHeight;
5927
5928         // Only scroll if out of visible area
5929         if (!tinyMCE.settings['auto_resize'] && !(pos.absTop > scrollY && pos.absTop < (scrollY - 25 + height)))
5930             inst.contentWindow.scrollTo(pos.absLeft, pos.absTop - height + 25); 
5931     },
5932
5933     getSel : function() {
5934         var inst = this.instance;
5935
5936         if (tinyMCE.isMSIE && !tinyMCE.isOpera)
5937             return inst.getDoc().selection;
5938
5939         return inst.contentWindow.getSelection();
5940     },
5941
5942     getRng : function() {
5943         var inst = this.instance;
5944         var sel = this.getSel();
5945
5946         if (sel == null)
5947             return null;
5948
5949         if (tinyMCE.isMSIE && !tinyMCE.isOpera)
5950             return sel.createRange();
5951
5952         if (tinyMCE.isSafari && !sel.getRangeAt)
5953             return '' + window.getSelection();
5954
5955         return sel.getRangeAt(0);
5956     },
5957
5958     getFocusElement : function() {
5959         var inst = this.instance;
5960
5961         if (tinyMCE.isMSIE && !tinyMCE.isOpera) {
5962             var doc = inst.getDoc();
5963             var rng = doc.selection.createRange();
5964
5965     //        if (rng.collapse)
5966     //            rng.collapse(true);
5967
5968             var elm = rng.item ? rng.item(0) : rng.parentElement();
5969         } else {
5970             if (inst.isHidden())
5971                 return inst.getBody();
5972
5973             var sel = this.getSel();
5974             var rng = this.getRng();
5975
5976             if (!sel || !rng)
5977                 return null;
5978
5979             var elm = rng.commonAncestorContainer;
5980             //var elm = (sel && sel.anchorNode) ? sel.anchorNode : null;
5981
5982             // Handle selection a image or other control like element such as anchors
5983             if (!rng.collapsed) {
5984                 // Is selection small
5985                 if (rng.startContainer == rng.endContainer) {
5986                     if (rng.startOffset - rng.endOffset < 2) {
5987                         if (rng.startContainer.hasChildNodes())
5988                             elm = rng.startContainer.childNodes[rng.startOffset];
5989                     }
5990                 }
5991             }
5992
5993             // Get the element parent of the node
5994             elm = tinyMCE.getParentElement(elm);
5995
5996             //if (tinyMCE.selectedElement != null && tinyMCE.selectedElement.nodeName.toLowerCase() == "img")
5997             //    elm = tinyMCE.selectedElement;
5998         }
5999
6000         return elm;
6001     }
6002 };
6003
6004 /* file:jscripts/tiny_mce/classes/TinyMCE_UndoRedo.class.js */
6005
6006 function TinyMCE_UndoRedo(inst) {
6007     this.instance = inst;
6008     this.undoLevels = new Array();
6009     this.undoIndex = 0;
6010     this.typingUndoIndex = -1;
6011     this.undoRedo = true;
6012 };
6013
6014 TinyMCE_UndoRedo.prototype = {
6015     add : function(l) {
6016         var b;
6017
6018         if (l) {
6019             this.undoLevels[this.undoLevels.length] = l;
6020             return true;
6021         }
6022
6023         var inst = this.instance;
6024
6025         if (this.typingUndoIndex != -1) {
6026             this.undoIndex = this.typingUndoIndex;
6027             // tinyMCE.debug("Override: " + this.undoIndex);
6028         }
6029
6030         var newHTML = tinyMCE.trim(inst.getBody().innerHTML);
6031         if (this.undoLevels[this.undoIndex] && newHTML != this.undoLevels[this.undoIndex].content) {
6032             //tinyMCE.debug(newHTML, this.undoLevels[this.undoIndex]);
6033
6034             tinyMCE.dispatchCallback(inst, 'onchange_callback', 'onChange', inst);
6035
6036             // Time to compress
6037             var customUndoLevels = tinyMCE.settings['custom_undo_redo_levels'];
6038             if (customUndoLevels != -1 && this.undoLevels.length > customUndoLevels) {
6039                 for (var i=0; i<this.undoLevels.length-1; i++) {
6040                     //tinyMCE.debug(this.undoLevels[i] + "=" + this.undoLevels[i+1]);
6041                     this.undoLevels[i] = this.undoLevels[i+1];
6042                 }
6043
6044                 this.undoLevels.length--;
6045                 this.undoIndex--;
6046             }
6047
6048             b = inst.undoBookmark;
6049             if (!b)
6050                 b = inst.selection.getBookmark();
6051
6052             this.undoIndex++;
6053             this.undoLevels[this.undoIndex] = {
6054                 content : newHTML,
6055                 bookmark : b
6056             };
6057
6058             this.undoLevels.length = this.undoIndex + 1;
6059
6060             //tinyMCE.debug("level added" + this.undoIndex);
6061             return true;
6062
6063             // tinyMCE.debug(this.undoIndex + "," + (this.undoLevels.length-1));
6064         }
6065
6066         return false;
6067     },
6068
6069     undo : function() {
6070         var inst = this.instance;
6071
6072         // Do undo
6073         if (this.undoIndex > 0) {
6074             this.undoIndex--;
6075             tinyMCE.setInnerHTML(inst.getBody(), this.undoLevels[this.undoIndex].content);
6076             inst.repaint();
6077             if (inst.settings.custom_undo_redo_restore_selection)
6078                 inst.selection.moveToBookmark(this.undoLevels[this.undoIndex].bookmark);
6079         }
6080
6081         // tinyMCE.debug("Undo - undo levels:" + this.undoLevels.length + ", undo index: " + this.undoIndex);
6082     },
6083
6084     redo : function() {
6085         var inst = this.instance;
6086
6087         tinyMCE.execCommand("mceEndTyping");
6088
6089         if (this.undoIndex < (this.undoLevels.length-1)) {
6090             this.undoIndex++;
6091             tinyMCE.setInnerHTML(inst.getBody(), this.undoLevels[this.undoIndex].content);
6092             inst.repaint();
6093 //                    if (this.undoIndex > 0)
6094 //                        inst.selection.moveToBookmark(this.undoLevels[this.undoIndex-1].bookmark);
6095             if (inst.settings.custom_undo_redo_restore_selection)
6096                 inst.selection.moveToBookmark(this.undoLevels[this.undoIndex].bookmark);
6097             // tinyMCE.debug("Redo - undo levels:" + this.undoLevels.length + ", undo index: " + this.undoIndex);
6098         }
6099
6100         tinyMCE.triggerNodeChange();
6101     }
6102 };
6103
6104 /* file:jscripts/tiny_mce/classes/TinyMCE_ForceParagraphs.class.js */
6105
6106 var TinyMCE_ForceParagraphs = {
6107     _insertPara : function(inst, e) {
6108         function isEmpty(para) {
6109             function isEmptyHTML(html) {
6110                 return html.replace(new RegExp('[ \t\r\n]+', 'g'), '').toLowerCase() == "";
6111             }
6112
6113             // Check for images
6114             if (para.getElementsByTagName("img").length > 0)
6115                 return false;
6116
6117             // Check for tables
6118             if (para.getElementsByTagName("table").length > 0)
6119                 return false;
6120
6121             // Check for HRs
6122             if (para.getElementsByTagName("hr").length > 0)
6123                 return false;
6124
6125             // Check all textnodes
6126             var nodes = tinyMCE.getNodeTree(para, new Array(), 3);
6127             for (var i=0; i<nodes.length; i++) {
6128                 if (!isEmptyHTML(nodes[i].nodeValue))
6129                     return false;
6130             }
6131
6132             // No images, no tables, no hrs, no text content then it's empty
6133             return true;
6134         }
6135
6136         var doc = inst.getDoc();
6137         var sel = inst.getSel();
6138         var win = inst.contentWindow;
6139         var rng = sel.getRangeAt(0);
6140         var body = doc.body;
6141         var rootElm = doc.documentElement;
6142         var blockName = "P";
6143
6144     //    tinyMCE.debug(body.innerHTML);
6145
6146     //    debug(e.target, sel.anchorNode.nodeName, sel.focusNode.nodeName, rng.startContainer, rng.endContainer, rng.commonAncestorContainer, sel.anchorOffset, sel.focusOffset, rng.toString());
6147
6148         // Setup before range
6149         var rngBefore = doc.createRange();
6150         rngBefore.setStart(sel.anchorNode, sel.anchorOffset);
6151         rngBefore.collapse(true);
6152
6153         // Setup after range
6154         var rngAfter = doc.createRange();
6155         rngAfter.setStart(sel.focusNode, sel.focusOffset);
6156         rngAfter.collapse(true);
6157
6158         // Setup start/end points
6159         var direct = rngBefore.compareBoundaryPoints(rngBefore.START_TO_END, rngAfter) < 0;
6160         var startNode = direct ? sel.anchorNode : sel.focusNode;
6161         var startOffset = direct ? sel.anchorOffset : sel.focusOffset;
6162         var endNode = direct ? sel.focusNode : sel.anchorNode;
6163         var endOffset = direct ? sel.focusOffset : sel.anchorOffset;
6164
6165         startNode = startNode.nodeName == "BODY" ? startNode.firstChild : startNode;
6166         endNode = endNode.nodeName == "BODY" ? endNode.firstChild : endNode;
6167
6168         // tinyMCE.debug(startNode, endNode);
6169
6170         // Get block elements
6171         var startBlock = tinyMCE.getParentBlockElement(startNode);
6172         var endBlock = tinyMCE.getParentBlockElement(endNode);
6173
6174         // If absolute force paragraph generation within
6175         if (startBlock && new RegExp('absolute|relative|static', 'gi').test(startBlock.style.position))
6176             startBlock = null;
6177
6178         if (endBlock && new RegExp('absolute|relative|static', 'gi').test(endBlock.style.position))
6179             endBlock = null;
6180
6181         // Use current block name
6182         if (startBlock != null) {
6183             blockName = startBlock.nodeName;
6184
6185             // Use P instead
6186             if (blockName == "TD" || blockName == "TABLE" || (blockName == "DIV" && new RegExp('left|right', 'gi').test(startBlock.style.cssFloat)))
6187                 blockName = "P";
6188         }
6189
6190         // Within a list use normal behaviour
6191         if (tinyMCE.getParentElement(startBlock, "OL,UL") != null)
6192             return false;
6193
6194         // Within a table create new paragraphs
6195         if ((startBlock != null && startBlock.nodeName == "TABLE") || (endBlock != null && endBlock.nodeName == "TABLE"))
6196             startBlock = endBlock = null;
6197
6198         // Setup new paragraphs
6199         var paraBefore = (startBlock != null && startBlock.nodeName == blockName) ? startBlock.cloneNode(false) : doc.createElement(blockName);
6200         var paraAfter = (endBlock != null && endBlock.nodeName == blockName) ? endBlock.cloneNode(false) : doc.createElement(blockName);
6201
6202         // Is header, then force paragraph under
6203         if (/^(H[1-6])$/.test(blockName))
6204             paraAfter = doc.createElement("p");
6205
6206         // Setup chop nodes
6207         var startChop = startNode;
6208         var endChop = endNode;
6209
6210         // Get startChop node
6211         node = startChop;
6212         do {
6213             if (node == body || node.nodeType == 9 || tinyMCE.isBlockElement(node))
6214                 break;
6215
6216             startChop = node;
6217         } while ((node = node.previousSibling ? node.previousSibling : node.parentNode));
6218
6219         // Get endChop node
6220         node = endChop;
6221         do {
6222             if (node == body || node.nodeType == 9 || tinyMCE.isBlockElement(node))
6223                 break;
6224
6225             endChop = node;
6226         } while ((node = node.nextSibling ? node.nextSibling : node.parentNode));
6227
6228         // Fix when only a image is within the TD
6229         if (startChop.nodeName == "TD")
6230             startChop = startChop.firstChild;
6231
6232         if (endChop.nodeName == "TD")
6233             endChop = endChop.lastChild;
6234
6235         // If not in a block element
6236         if (startBlock == null) {
6237             // Delete selection
6238             rng.deleteContents();
6239             sel.removeAllRanges();
6240
6241             if (startChop != rootElm && endChop != rootElm) {
6242                 // Insert paragraph before
6243                 rngBefore = rng.cloneRange();
6244
6245                 if (startChop == body)
6246                     rngBefore.setStart(startChop, 0);
6247                 else
6248                     rngBefore.setStartBefore(startChop);
6249
6250                 paraBefore.appendChild(rngBefore.cloneContents());
6251
6252                 // Insert paragraph after
6253                 if (endChop.parentNode.nodeName == blockName)
6254                     endChop = endChop.parentNode;
6255
6256                 // If not after image
6257                 //if (rng.startContainer.nodeName != "BODY" && rng.endContainer.nodeName != "BODY")
6258                     rng.setEndAfter(endChop);
6259
6260                 if (endChop.nodeName != "#text" && endChop.nodeName != "BODY")
6261                     rngBefore.setEndAfter(endChop);
6262
6263                 var contents = rng.cloneContents();
6264                 if (contents.firstChild && (contents.firstChild.nodeName == blockName || contents.firstChild.nodeName == "BODY"))
6265                     paraAfter.innerHTML = contents.firstChild.innerHTML;
6266                 else
6267                     paraAfter.appendChild(contents);
6268
6269                 // Check if it's a empty paragraph
6270                 if (isEmpty(paraBefore))
6271                     paraBefore.innerHTML = "&nbsp;";
6272
6273                 // Check if it's a empty paragraph
6274                 if (isEmpty(paraAfter))
6275                     paraAfter.innerHTML = "&nbsp;";
6276
6277                 // Delete old contents
6278                 rng.deleteContents();
6279                 rngAfter.deleteContents();
6280                 rngBefore.deleteContents();
6281
6282                 // Insert new paragraphs
6283                 paraAfter.normalize();
6284                 rngBefore.insertNode(paraAfter);
6285                 paraBefore.normalize();
6286                 rngBefore.insertNode(paraBefore);
6287
6288                 // tinyMCE.debug("1: ", paraBefore.innerHTML, paraAfter.innerHTML);
6289             } else {
6290                 body.innerHTML = "<" + blockName + ">&nbsp;</" + blockName + "><" + blockName + ">&nbsp;</" + blockName + ">";
6291                 paraAfter = body.childNodes[1];
6292             }
6293
6294             inst.selection.selectNode(paraAfter, true, true);
6295
6296             return true;
6297         }
6298
6299         // Place first part within new paragraph
6300         if (startChop.nodeName == blockName)
6301             rngBefore.setStart(startChop, 0);
6302         else
6303             rngBefore.setStartBefore(startChop);
6304
6305         rngBefore.setEnd(startNode, startOffset);
6306         paraBefore.appendChild(rngBefore.cloneContents());
6307
6308         // Place secound part within new paragraph
6309         rngAfter.setEndAfter(endChop);
6310         rngAfter.setStart(endNode, endOffset);
6311         var contents = rngAfter.cloneContents();
6312
6313         if (contents.firstChild && contents.firstChild.nodeName == blockName) {
6314     /*        var nodes = contents.firstChild.childNodes;
6315             for (var i=0; i<nodes.length; i++) {
6316                 //tinyMCE.debug(nodes[i].nodeName);
6317                 if (nodes[i].nodeName != "BODY")
6318                     paraAfter.appendChild(nodes[i]);
6319             }
6320     */
6321             paraAfter.innerHTML = contents.firstChild.innerHTML;
6322         } else
6323             paraAfter.appendChild(contents);
6324
6325         // Check if it's a empty paragraph
6326         if (isEmpty(paraBefore))
6327             paraBefore.innerHTML = "&nbsp;";
6328
6329         // Check if it's a empty paragraph
6330         if (isEmpty(paraAfter))
6331             paraAfter.innerHTML = "&nbsp;";
6332
6333         // Create a range around everything
6334         var rng = doc.createRange();
6335
6336         if (!startChop.previousSibling && startChop.parentNode.nodeName.toUpperCase() == blockName) {
6337             rng.setStartBefore(startChop.parentNode);
6338         } else {
6339             if (rngBefore.startContainer.nodeName.toUpperCase() == blockName && rngBefore.startOffset == 0)
6340                 rng.setStartBefore(rngBefore.startContainer);
6341             else
6342                 rng.setStart(rngBefore.startContainer, rngBefore.startOffset);
6343         }
6344
6345         if (!endChop.nextSibling && endChop.parentNode.nodeName.toUpperCase() == blockName)
6346             rng.setEndAfter(endChop.parentNode);
6347         else
6348             rng.setEnd(rngAfter.endContainer, rngAfter.endOffset);
6349
6350         // Delete all contents and insert new paragraphs
6351         rng.deleteContents();
6352         rng.insertNode(paraAfter);
6353         rng.insertNode(paraBefore);
6354         //tinyMCE.debug("2", paraBefore.innerHTML, paraAfter.innerHTML);
6355
6356         // Normalize
6357         paraAfter.normalize();
6358         paraBefore.normalize();
6359
6360         inst.selection.selectNode(paraAfter, true, true);
6361
6362         return true;
6363     },
6364
6365     _handleBackSpace : function(inst) {
6366         var r = inst.getRng(), sn = r.startContainer, nv, s = false;
6367
6368         if (sn && sn.nextSibling && sn.nextSibling.nodeName == "BR") {
6369             nv = sn.nodeValue;
6370
6371             // Handle if a backspace is pressed after a space character #bug 1466054
6372             if (nv != null && nv.length >= r.startOffset && nv.charAt(r.startOffset - 1) == ' ')
6373                 s = true;
6374
6375             // Only remove BRs if we are at the end of line #bug 1464152
6376             if (nv != null && r.startOffset == nv.length)
6377                 sn.nextSibling.parentNode.removeChild(sn.nextSibling);
6378         }
6379
6380         return s;
6381     }
6382 };
6383
6384 /* file:jscripts/tiny_mce/classes/TinyMCE_Layer.class.js */
6385
6386 function TinyMCE_Layer(id, bm) {
6387     this.id = id;
6388     this.blockerElement = null;
6389     this.events = false;
6390     this.element = null;
6391     this.blockMode = typeof(bm) != 'undefined' ? bm : true;
6392     this.doc = document;
6393 };
6394
6395 TinyMCE_Layer.prototype = {
6396     moveRelativeTo : function(re, p) {
6397         var rep = this.getAbsPosition(re);
6398         var w = parseInt(re.offsetWidth);
6399         var h = parseInt(re.offsetHeight);
6400         var e = this.getElement();
6401         var ew = parseInt(e.offsetWidth);
6402         var eh = parseInt(e.offsetHeight);
6403         var x, y;
6404
6405         switch (p) {
6406             case "tl":
6407                 x = rep.absLeft;
6408                 y = rep.absTop;
6409                 break;
6410
6411             case "tr":
6412                 x = rep.absLeft + w;
6413                 y = rep.absTop;
6414                 break;
6415
6416             case "bl":
6417                 x = rep.absLeft;
6418                 y = rep.absTop + h;
6419                 break;
6420
6421             case "br":
6422                 x = rep.absLeft + w;
6423                 y = rep.absTop + h;
6424                 break;
6425
6426             case "cc":
6427                 x = rep.absLeft + (w / 2) - (ew / 2);
6428                 y = rep.absTop + (h / 2) - (eh / 2);
6429                 break;
6430         }
6431
6432         this.moveTo(x, y);
6433     },
6434
6435     moveBy : function(x, y) {
6436         var e = this.getElement();
6437         this.moveTo(parseInt(e.style.left) + x, parseInt(e.style.top) + y);
6438     },
6439
6440     moveTo : function(x, y) {
6441         var e = this.getElement();
6442
6443         e.style.left = x + "px";
6444         e.style.top = y + "px";
6445
6446         this.updateBlocker();
6447     },
6448
6449     resizeBy : function(w, h) {
6450         var e = this.getElement();
6451         this.resizeTo(parseInt(e.style.width) + w, parseInt(e.style.height) + h);
6452     },
6453
6454     resizeTo : function(w, h) {
6455         var e = this.getElement();
6456
6457         if (w != null)
6458             e.style.width = w + "px";
6459
6460         if (h != null)
6461             e.style.height = h + "px";
6462
6463         this.updateBlocker();
6464     },
6465
6466     show : function() {
6467         this.getElement().style.display = 'block';
6468         this.updateBlocker();
6469     },
6470
6471     hide : function() {
6472         this.getElement().style.display = 'none';
6473         this.updateBlocker();
6474     },
6475
6476     isVisible : function() {
6477         return this.getElement().style.display == 'block';
6478     },
6479
6480     getElement : function() {
6481         if (!this.element)
6482             this.element = this.doc.getElementById(this.id);
6483
6484         return this.element;
6485     },
6486
6487     setBlockMode : function(s) {
6488         this.blockMode = s;
6489     },
6490
6491     updateBlocker : function() {
6492         var e, b, x, y, w, h;
6493
6494         b = this.getBlocker();
6495         if (b) {
6496             if (this.blockMode) {
6497                 e = this.getElement();
6498                 x = this.parseInt(e.style.left);
6499                 y = this.parseInt(e.style.top);
6500                 w = this.parseInt(e.offsetWidth);
6501                 h = this.parseInt(e.offsetHeight);
6502
6503                 b.style.left = x + 'px';
6504                 b.style.top = y + 'px';
6505                 b.style.width = w + 'px';
6506                 b.style.height = h + 'px';
6507                 b.style.display = e.style.display;
6508             } else
6509                 b.style.display = 'none';
6510         }
6511     },
6512
6513     getBlocker : function() {
6514         var d, b;
6515
6516         if (!this.blockerElement && this.blockMode) {
6517             d = this.doc;
6518             b = d.createElement("iframe");
6519
6520             b.style.cssText = 'display: none; position: absolute; left: 0; top: 0';
6521             b.src = 'javascript:false;';
6522             b.frameBorder = '0';
6523             b.scrolling = 'no';
6524
6525             d.body.appendChild(b);
6526             this.blockerElement = b;
6527         }
6528
6529         return this.blockerElement;
6530     },
6531
6532     getAbsPosition : function(n) {
6533         var p = {absLeft : 0, absTop : 0};
6534
6535         while (n) {
6536             p.absLeft += n.offsetLeft;
6537             p.absTop += n.offsetTop;
6538             n = n.offsetParent;
6539         }
6540
6541         return p;
6542     },
6543
6544     create : function(n, c, p) {
6545         var d = this.doc, e = d.createElement(n);
6546
6547         e.setAttribute('id', this.id);
6548
6549         if (c)
6550             e.className = c;
6551
6552         if (!p)
6553             p = d.body;
6554
6555         p.appendChild(e);
6556
6557         return this.element = e;
6558     },
6559
6560     parseInt : function(s) {
6561         if (s == null || s == '')
6562             return 0;
6563
6564         return parseInt(s);
6565     }
6566 };
6567
6568 /* file:jscripts/tiny_mce/classes/TinyMCE_Menu.class.js */
6569
6570 function TinyMCE_Menu() {
6571     var id;
6572
6573     if (typeof(tinyMCE.menuCounter) == "undefined")
6574         tinyMCE.menuCounter = 0;
6575
6576     id = "mc_menu_" + tinyMCE.menuCounter++;
6577
6578     TinyMCE_Layer.call(this, id, true);
6579
6580     this.id = id;
6581     this.items = new Array();
6582     this.needsUpdate = true;
6583 };
6584
6585 // Extends the TinyMCE_Layer class
6586 TinyMCE_Menu.prototype = tinyMCE.extend(TinyMCE_Layer.prototype, {
6587     init : function(s) {
6588         var n;
6589
6590         // Default params
6591         this.settings = {
6592             separator_class : 'mceMenuSeparator',
6593             title_class : 'mceMenuTitle',
6594             disabled_class : 'mceMenuDisabled',
6595             menu_class : 'mceMenu',
6596             drop_menu : true
6597         };
6598
6599         for (n in s)
6600             this.settings[n] = s[n];
6601
6602         this.create('div', this.settings.menu_class);
6603     },
6604
6605     clear : function() {
6606         this.items = new Array();
6607     },
6608
6609     addTitle : function(t) {
6610         this.add({type : 'title', text : t});
6611     },
6612
6613     addDisabled : function(t) {
6614         this.add({type : 'disabled', text : t});
6615     },
6616
6617     addSeparator : function() {
6618         this.add({type : 'separator'});
6619     },
6620
6621     addItem : function(t, js) {
6622         this.add({text : t, js : js});
6623     },
6624
6625     add : function(mi) {
6626         this.items[this.items.length] = mi;
6627         this.needsUpdate = true;
6628     },
6629
6630     update : function() {
6631         var e = this.getElement(), h = '', i, t, m = this.items, s = this.settings;
6632
6633         if (this.settings.drop_menu)
6634             h += '<span class="mceMenuLine"></span>';
6635
6636         h += '<table border="0" cellpadding="0" cellspacing="0">';
6637
6638         for (i=0; i<m.length; i++) {
6639             t = tinyMCE.xmlEncode(m[i].text);
6640             c = m[i].class_name ? ' class="' + m[i].class_name + '"' : '';
6641
6642             switch (m[i].type) {
6643                 case 'separator':
6644                     h += '<tr class="' + s.separator_class + '"><td>';
6645                     break;
6646
6647                 case 'title':
6648                     h += '<tr class="' + s.title_class + '"><td><span' + c +'>' + t + '</span>';
6649                     break;
6650
6651                 case 'disabled':
6652                     h += '<tr class="' + s.disabled_class + '"><td><span' + c +'>' + t + '</span>';
6653                     break;
6654
6655                 default:
6656                     h += '<tr><td><a href="javascript:void(0);" onmousedown="' + tinyMCE.xmlEncode(m[i].js) + ';return false;"><span' + c +'>' + t + '</span></a>';
6657             }
6658
6659             h += '</td></tr>';
6660         }
6661
6662         h += '</table>';
6663
6664         e.innerHTML = h;
6665
6666         this.needsUpdate = false;
6667         this.updateBlocker();
6668     },
6669
6670     show : function() {
6671         var nl, i;
6672
6673         if (tinyMCE.lastMenu == this)
6674             return;
6675
6676         if (this.needsUpdate)
6677             this.update();
6678
6679         if (tinyMCE.lastMenu && tinyMCE.lastMenu != this)
6680             tinyMCE.lastMenu.hide();
6681
6682         TinyMCE_Layer.prototype.show.call(this);
6683
6684         if (!tinyMCE.isOpera) {
6685             // Accessibility stuff
6686 /*            nl = this.getElement().getElementsByTagName("a");
6687             if (nl.length > 0)
6688                 nl[0].focus();*/
6689         }
6690
6691         tinyMCE.lastMenu = this;
6692     }
6693 });
6694
6695 /* file:jscripts/tiny_mce/classes/TinyMCE_Debug.class.js */
6696
6697 TinyMCE_Engine.prototype.debug = function() {
6698     var m = "", e, a, i;
6699
6700     e = document.getElementById("tinymce_debug");
6701     if (!e) {
6702         var d = document.createElement("div");
6703         d.setAttribute("className", "debugger");
6704         d.className = "debugger";
6705         d.innerHTML = 'Debug output:<textarea id="tinymce_debug" style="width: 100%; height: 300px" wrap="nowrap" mce_editable="false"></textarea>';
6706
6707         document.body.appendChild(d);
6708         e = document.getElementById("tinymce_debug");
6709     }
6710
6711     a = this.debug.arguments;
6712     for (i=0; i<a.length; i++) {
6713         m += a[i];
6714         if (i<a.length-1)
6715             m += ', ';
6716     }
6717
6718     e.value += m + "\n";
6719 };