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