2 * $RCSfile: tiny_mce_src.js,v $
4 * $Date: 2005/12/02 08:12:07 $
7 * @copyright Copyright © 2004, Moxiecode Systems AB, All rights reserved.
11 this.majorVersion = "2";
12 this.minorVersion = "0";
13 this.releaseDate = "2005-12-01";
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();
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;
36 // Fake MSIE on Opera and if Opera fakes IE, Gecko or Safari cancel those
40 this.isSafari = false;
43 // TinyMCE editor id instance counter
47 TinyMCE.prototype.defParam = function(key, def_val) {
48 this.settings[key] = tinyMCE.getParam(key, def_val);
51 TinyMCE.prototype.init = function(settings) {
54 this.settings = settings;
56 // Check if valid browser has execcommand support
57 if (typeof(document.execCommand) == 'undefined')
60 // Get script base path
61 if (!tinyMCE.baseURL) {
62 var elements = document.getElementsByTagName('script');
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;
68 tinyMCE.srcMode = (src.indexOf('_src') != -1) ? '_src' : '';
69 src = src.substring(0, src.lastIndexOf('/'));
71 tinyMCE.baseURL = src;
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('/'));
84 // If not HTTP absolute
85 if (tinyMCE.baseURL.indexOf('://') == -1 && tinyMCE.baseURL.charAt(0) != '/') {
87 tinyMCE.baseURL = this.documentBasePath + "/" + tinyMCE.baseURL;
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);
157 if (this.isMSIE && this.settings['browsers'].indexOf('msie') == -1)
160 // Browser check Gecko
161 if (this.isGecko && this.settings['browsers'].indexOf('gecko') == -1)
164 // Browser check Safari
165 if (this.isSafari && this.settings['browsers'].indexOf('safari') == -1)
168 // Browser check Opera
169 if (this.isOpera && this.settings['browsers'].indexOf('opera') == -1)
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);
180 if (baseHREF.indexOf('://') == -1)
181 baseHREF = h + baseHREF;
183 tinyMCE.settings['document_base_url'] = baseHREF;
184 tinyMCE.settings['document_base_prefix'] = h;
187 // Trim away query part
188 if (baseHREF.indexOf('?') != -1)
189 baseHREF = baseHREF.substring(0, baseHREF.indexOf('?'));
191 this.settings['base_href'] = baseHREF.substring(0, baseHREF.lastIndexOf('/')) + "/";
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>';
200 this.settings['theme_href'] = tinyMCE.baseURL + "/themes/" + theme;
203 this.settings['force_br_newlines'] = false;
205 if (tinyMCE.getParam("content_css", false)) {
206 var cssPath = tinyMCE.getParam("content_css", "");
209 if (cssPath.indexOf('://') == -1 && cssPath.charAt(0) != '/')
210 this.settings['content_css'] = this.documentBasePath + "/" + cssPath;
212 this.settings['content_css'] = cssPath;
214 this.settings['content_css'] = '';
216 if (tinyMCE.getParam("popups_css", false)) {
217 var cssPath = tinyMCE.getParam("popups_css", "");
220 if (cssPath.indexOf('://') == -1 && cssPath.charAt(0) != '/')
221 this.settings['popups_css'] = this.documentBasePath + "/" + cssPath;
223 this.settings['popups_css'] = cssPath;
225 this.settings['popups_css'] = tinyMCE.baseURL + "/themes/" + theme + "/css/editor_popup.css";
227 if (tinyMCE.getParam("editor_css", false)) {
228 var cssPath = tinyMCE.getParam("editor_css", "");
231 if (cssPath.indexOf('://') == -1 && cssPath.charAt(0) != '/')
232 this.settings['editor_css'] = this.documentBasePath + "/" + cssPath;
234 this.settings['editor_css'] = cssPath;
236 this.settings['editor_css'] = tinyMCE.baseURL + "/themes/" + theme + "/css/editor_ui.css";
238 if (tinyMCE.settings['debug']) {
239 var msg = "Debug: \n";
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";
254 if (this.configs.length == 0) {
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");
259 tinyMCE.addEvent(window, "load", TinyMCE.prototype.onLoad);
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);
267 if (tinyMCE.settings['add_unload_trigger'])
268 tinyMCE.addEvent(window, "unload", function () {tinyMCE.triggerSave(true, true);});
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']);
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');
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];
289 // Save away this config
290 settings['index'] = this.configs.length;
291 this.configs[this.configs.length] = settings;
294 TinyMCE.prototype.loadScript = function(url) {
295 for (var i=0; i<this.loadedFiles.length; i++) {
296 if (this.loadedFiles[i] == url)
300 document.write('<sc'+'ript language="javascript" type="text/javascript" src="' + url + '"></script>');
302 this.loadedFiles[this.loadedFiles.length] = url;
305 TinyMCE.prototype.loadCSS = function(url) {
306 for (var i=0; i<this.loadedFiles.length; i++) {
307 if (this.loadedFiles[i] == url)
311 document.write('<link href="' + url + '" rel="stylesheet" type="text/css" />');
313 this.loadedFiles[this.loadedFiles.length] = url;
316 TinyMCE.prototype.importCSS = function(doc, css_file) {
320 if (typeof(doc.createStyleSheet) == "undefined") {
321 var elm = doc.createElement("link");
323 elm.rel = "stylesheet";
326 if ((headArr = doc.getElementsByTagName("head")) != null && headArr.length > 0)
327 headArr[0].appendChild(elm);
329 var styleSheet = doc.createStyleSheet(css_file);
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;
336 tinyMCE.settings = settings;
338 if (!elm.getAttribute('mce_noask') && confirm(tinyMCELang['lang_edit_confirm']))
339 tinyMCE.addMCEControl(elm, elementId);
341 elm.setAttribute('mce_noask', 'true');
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))
352 inst.switchSettings();
354 if (inst.formElement == formElement) {
355 var doc = inst.getDoc();
357 tinyMCE._setHTML(doc, inst.formElement.value);
360 doc.body.innerHTML = tinyMCE._cleanupHTML(inst, doc, this.settings, doc.body, inst.visualAid);
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);
370 this.instances[id] = inst;
372 inst.onAdd(replace_element, form_element_name, target_document);
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))
382 inst.switchSettings();
384 tinyMCE.settings['preformatted'] = false;
387 if (typeof(skip_cleanup) == "undefined")
388 skip_cleanup = false;
391 if (typeof(skip_callback) == "undefined")
392 skip_callback = false;
394 tinyMCE._setHTML(inst.getDoc(), inst.getBody().innerHTML);
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);
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);
406 if (tinyMCE.settings["encoding"] == "xml" || tinyMCE.settings["encoding"] == "html")
407 htm = tinyMCE.convertStringToXML(htm);
409 if (!skip_callback && tinyMCE.settings['save_callback'] != "")
410 var content = eval(tinyMCE.settings['save_callback'] + "(inst.formTargetElementId,htm,inst.getBody());");
412 // Use callback content if available
413 if ((typeof(content) != "undefined") && content != null)
416 // Replace some weird entities (Bug: #1056343)
417 htm = tinyMCE.regexpReplace(htm, "(", "(", "gi");
418 htm = tinyMCE.regexpReplace(htm, ")", ")", "gi");
419 htm = tinyMCE.regexpReplace(htm, ";", ";", "gi");
420 htm = tinyMCE.regexpReplace(htm, """, """, "gi");
421 htm = tinyMCE.regexpReplace(htm, "^", "^", "gi");
423 if (inst.formElement)
424 inst.formElement.value = htm;
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');
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++) {
439 for (var x=0; x<events.length; x++) {
440 if ((event = tinyMCE.getAttrib(elms[i], events[x])) != '') {
441 event = tinyMCE.cleanupEventStr("" + event);
444 event = "return true;" + event;
446 event = event.replace(/^return true;/gi, '');
448 elms[i].removeAttribute(events[x]);
449 elms[i].setAttribute(events[x], event);
456 TinyMCE.prototype.resetForm = function(form_index) {
457 var formObj = document.forms[form_index];
459 for (var n in tinyMCE.instances) {
460 var inst = tinyMCE.instances[n];
461 if (!tinyMCE.isInstance(inst))
464 inst.switchSettings();
466 for (var i=0; i<formObj.elements.length; i++) {
467 if (inst.formTargetElementId == formObj.elements[i].name)
468 inst.getBody().innerHTML = inst.startContent;
473 TinyMCE.prototype.execInstanceCommand = function(editor_id, command, user_interface, value, focus) {
474 var inst = tinyMCE.getInstanceById(editor_id);
476 if (typeof(focus) == "undefined")
480 inst.contentWindow.focus();
482 // Reset design mode if lost
483 inst.autoResetDesignMode();
485 this.selectedElement = inst.getFocusElement();
486 this.selectedInstance = inst;
487 tinyMCE.execCommand(command, user_interface, value);
489 // Cancel event so it doesn't call onbeforeonunlaod
490 if (tinyMCE.isMSIE && window.event != null)
491 tinyMCE.cancelEvent(window.event);
495 TinyMCE.prototype.execCommand = function(command, user_interface, value) {
497 user_interface = user_interface ? user_interface : false;
498 value = value ? value : null;
500 if (tinyMCE.selectedInstance)
501 tinyMCE.selectedInstance.switchSettings();
505 var template = new Array();
507 template['file'] = 'about.htm';
508 template['width'] = 480;
509 template['height'] = 380;
511 tinyMCE.openWindow(template, {
512 tinymce_version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion,
513 tinymce_releasedate : tinyMCE.releaseDate,
519 var inst = tinyMCE.getInstanceById(value);
521 inst.contentWindow.focus();
524 case "mceAddControl":
526 tinyMCE.addMCEControl(tinyMCE._getElementById(value), value);
529 case "mceAddFrameControl":
530 tinyMCE.addMCEControl(tinyMCE._getElementById(value), value['element'], value['document']);
533 case "mceRemoveControl":
534 case "mceRemoveEditor":
535 tinyMCE.removeMCEControl(value);
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]))
546 tinyMCE.instances[n].getDoc().designMode = "on";
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']);
562 TinyMCE.prototype.eventPatch = function(editor_id) {
564 if (typeof(tinyMCE) == "undefined")
567 for (var i=0; i<document.frames.length; i++) {
569 if (document.frames[i].event) {
570 var event = document.frames[i].event;
573 event.target = event.srcElement;
575 TinyMCE.prototype.handleEvent(event);
579 // Ignore error if iframe is pointing to external URL
584 TinyMCE.prototype.unloadHandler = function() {
585 tinyMCE.triggerSave(true, true);
588 TinyMCE.prototype.addEventHandlers = function(editor_id) {
589 if (tinyMCE.isMSIE) {
590 var doc = document.frames[editor_id].document;
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);
599 var inst = tinyMCE.instances[editor_id];
600 var doc = inst.getDoc();
602 inst.switchSettings();
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);
613 eval('try { doc.designMode = "On"; } catch(e) {}');
617 TinyMCE.prototype._createIFrame = function(replace_element) {
618 var iframe = document.createElement("iframe");
619 var id = replace_element.getAttribute("id");
622 aw = "" + tinyMCE.settings['area_width'];
623 ah = "" + tinyMCE.settings['area_height'];
625 if (aw.indexOf('%') == -1) {
627 aw = aw < 0 ? 300 : aw;
631 if (ah.indexOf('%') == -1) {
633 ah = ah < 0 ? 240 : ah;
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");
649 if (tinyMCE.settings["auto_resize"])
650 iframe.setAttribute("scrolling", "no");
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']);
656 iframe.style.width = aw;
657 iframe.style.height = ah;
660 if (tinyMCE.isMSIE && !tinyMCE.isOpera)
661 replace_element.outerHTML = iframe.outerHTML;
663 replace_element.parentNode.replaceChild(iframe, replace_element);
666 return window.frames[id];
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;
677 tinyMCE.operaOpacityCounter = 100 * tinyMCE.idCounter;
679 inst.switchSettings();
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);
691 window.setTimeout("tinyMCE.setupContent('" + editor_id + "');", 10);
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);
701 if (tinyMCE.getParam("convert_fonts_to_spans"))
702 inst.getDoc().body.setAttribute('id', 'mceSpanFonts');
704 if (tinyMCE.settings['nowrap'])
705 doc.body.style.whiteSpace = "nowrap";
707 doc.body.dir = this.settings['directionality'];
708 doc.editorId = editor_id;
710 // Add on document element in Mozilla
712 doc.documentElement.editorId = editor_id;
714 // Setup base element
715 var base = doc.createElement("base");
716 base.setAttribute('href', tinyMCE.settings['base_href']);
717 head.appendChild(base);
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");
726 // Open closed anchors
727 // content = content.replace(new RegExp('<a(.*?)/>', 'gi'), '<a$1></a>');
729 // Call custom cleanup code
730 content = tinyMCE.storeAwayURLs(content);
731 content = tinyMCE._customCleanup(inst, "insert_to_editor", content);
733 if (tinyMCE.isMSIE) {
735 window.setInterval('try{tinyMCE.getCSSClasses(document.frames["' + editor_id + '"].document, "' + editor_id + '");}catch(e){}', 500);
737 if (tinyMCE.settings["force_br_newlines"])
738 document.frames[editor_id].document.styleSheets[0].addRule("p", "margin: 0px;");
740 var body = document.frames[editor_id].document.body;
742 tinyMCE.addEvent(body, "beforepaste", TinyMCE.prototype.eventPatch);
743 tinyMCE.addEvent(body, "beforecut", TinyMCE.prototype.eventPatch);
745 body.editorId = editor_id;
748 content = tinyMCE.cleanupHTMLCode(content);
750 // Fix for bug #958637
751 if (!tinyMCE.isMSIE) {
752 var contentElement = inst.getDoc().createElement("body");
753 var doc = inst.getDoc();
755 contentElement.innerHTML = content;
758 if (tinyMCE.isGecko && tinyMCE.settings['remove_lt_gt'])
759 content = content.replace(new RegExp('<>', 'g'), "");
761 if (tinyMCE.settings['cleanup_on_startup'])
762 tinyMCE.setInnerHTML(inst.getBody(), tinyMCE._cleanupHTML(inst, doc, this.settings, contentElement));
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);
773 inst.convertAllRelativeURLs();
775 if (tinyMCE.settings['cleanup_on_startup']) {
776 tinyMCE._setHTML(inst.getDoc(), content);
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) {}');
781 tinyMCE._setHTML(inst.getDoc(), content);
784 // Fix for bug #957681
785 //inst.getDoc().designMode = inst.getDoc().designMode;
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;
792 inst.formElement = tinyMCE.isGecko ? parentElm.previousSibling : parentElm.nextSibling;
794 tinyMCE.handleVisualAid(inst.getBody(), true, tinyMCE.settings['visual'], inst);
795 tinyMCE.executeCallback('setupcontent_callback', '_setupContent', 0, editor_id, inst.getBody(), inst.getDoc());
797 // Re-add design mode on mozilla
799 TinyMCE.prototype.addEventHandlers(editor_id);
803 tinyMCE.addEvent(inst.getBody(), "blur", TinyMCE.prototype.eventPatch);
805 // Trigger node change, this call locks buttons for tables and so forth
806 tinyMCE.selectedInstance = inst;
807 tinyMCE.selectedElement = inst.contentWindow.document.body;
809 if (!inst.isHidden())
810 tinyMCE.triggerNodeChange(false, true);
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());
818 if (tinyMCE.getParam("convert_fonts_to_spans"))
819 tinyMCE.convertSpansToFonts(inst.getDoc());
821 inst.startContent = tinyMCE.trim(inst.getBody().innerHTML);
822 inst.undoLevels[inst.undoLevels.length] = inst.startContent;
824 tinyMCE.operaOpacityCounter = -1;
827 TinyMCE.prototype.cleanupHTMLCode = function(s) {
828 s = s.replace(/<p \/>/gi, '<p> </p>');
829 s = s.replace(/<p>\s*<\/p>/gi, '<p> </p>');
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);
836 // Remove trailing space <b > to <b>
837 s = s.replace(new RegExp('\\s+></', 'gi'), '></');
839 // Close tags <img></img> to <img/>
840 s = s.replace(/<(img|br|hr)(.*?)><\/(img|br|hr)>/gi, '<$1$2 />');
842 // Weird MSIE bug, <p><hr /></p> breaks runtime?
844 s = s.replace(/<p><hr \/><\/p>/gi, "<hr>");
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'] + "#");
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"');
862 TinyMCE.prototype.cancelEvent = function(e) {
863 if (tinyMCE.isMSIE) {
864 e.returnValue = false;
865 e.cancelBubble = true;
870 TinyMCE.prototype.removeTinyMCEFormElements = function(form_obj) {
871 // Check if form is valid
872 if (typeof(form_obj) == "undefined" || form_obj == null)
875 // If not a form, find the form
876 if (form_obj.nodeName != "FORM") {
878 form_obj = form_obj.form;
880 form_obj = tinyMCE.getParentElement(form_obj, "form");
884 if (form_obj == null)
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;
891 if (elementId.indexOf('mce_editor_') == 0)
892 form_obj.elements[i].disabled = true;
896 TinyMCE.prototype.accessibleEventHandler = function(e) {
898 e = tinyMCE.isMSIE ? win.event : e;
899 var elm = tinyMCE.isMSIE ? e.srcElement : e.target;
901 // Piggyback onchange
902 if (elm.nodeName == "SELECT" && !elm.oldonchange) {
903 elm.oldonchange = elm.onchange;
907 // Execute onchange and remove piggyback
908 if (e.keyCode == 13 || e.keyCode == 32) {
909 elm.onchange = elm.oldonchange;
911 elm.oldonchange = null;
912 tinyMCE.cancelEvent(e);
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;
925 TinyMCE.prototype.handleEvent = function(e) {
927 if (typeof(tinyMCE) == "undefined")
930 //tinyMCE.debug(e.type + " " + e.target.nodeName + " " + (e.relatedTarget ? e.relatedTarget.nodeName : ""));
934 if (tinyMCE.selectedInstance)
935 tinyMCE.selectedInstance.execCommand('mceEndTyping');
940 tinyMCE.removeTinyMCEFormElements(tinyMCE.isMSIE ? window.event.srcElement : e.target);
941 tinyMCE.triggerSave();
942 tinyMCE.isNotDirty = true;
946 var formObj = tinyMCE.isMSIE ? window.event.srcElement : e.target;
948 for (var i=0; i<document.forms.length; i++) {
949 if (document.forms[i] == formObj)
950 window.setTimeout('tinyMCE.resetForm(' + i + ');', 10);
956 if (e.target.editorId) {
957 tinyMCE.selectedInstance = tinyMCE.instances[e.target.editorId];
959 if (e.target.ownerDocument.editorId)
960 tinyMCE.selectedInstance = tinyMCE.instances[e.target.ownerDocument.editorId];
963 if (tinyMCE.selectedInstance)
964 tinyMCE.selectedInstance.switchSettings();
966 // Insert space instead of
967 /* if (tinyMCE.isGecko && e.charCode == 32) {
968 if (tinyMCE.selectedInstance._insertSpace()) {
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)) {
980 tinyMCE.execCommand("mceAddUndoLevel");
981 tinyMCE.cancelEvent(e);
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)) {
991 tinyMCE.execCommand("mceAddUndoLevel");
992 tinyMCE.cancelEvent(e);
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);
1006 if (e.charCode == 121) { // Ctrl+Y
1007 tinyMCE.selectedInstance.execCommand("Redo");
1008 tinyMCE.cancelEvent(e);
1013 if (e.charCode == 98) { // Ctrl+B
1014 tinyMCE.selectedInstance.execCommand("Bold");
1015 tinyMCE.cancelEvent(e);
1019 if (e.charCode == 105) { // Ctrl+I
1020 tinyMCE.selectedInstance.execCommand("Italic");
1021 tinyMCE.cancelEvent(e);
1025 if (e.charCode == 117) { // Ctrl+U
1026 tinyMCE.selectedInstance.execCommand("Underline");
1027 tinyMCE.cancelEvent(e);
1031 if (e.charCode == 118) { // Ctrl+V
1032 tinyMCE.selectedInstance.execCommand("mceInsertContent", false, '<geckopastefix/>');
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];
1041 if (tinyMCE.selectedInstance) {
1042 var sel = tinyMCE.selectedInstance.getDoc().selection;
1043 var rng = sel.createRange();
1045 if (tinyMCE.getParentElement(rng.parentElement(), "li") != null)
1049 e.returnValue = false;
1050 e.cancelBubble = true;
1052 // Insert BR element
1053 rng.pasteHTML("<br />");
1054 rng.collapse(false);
1057 tinyMCE.execCommand("mceAddUndoLevel");
1058 tinyMCE.triggerNodeChange(false);
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);
1076 if (e.target.editorId)
1077 tinyMCE.selectedInstance = tinyMCE.instances[e.target.editorId];
1081 if (tinyMCE.selectedInstance)
1082 tinyMCE.selectedInstance.switchSettings();
1084 var inst = tinyMCE.selectedInstance;
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)) {
1091 tinyMCE.execCommand("mceAddUndoLevel");
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;
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);
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();
1112 // Fix empty elements on return/enter
1113 if (tinyMCE.isMSIE && e.type == "keyup" && e.keyCode == 13) {
1114 var elm = tinyMCE.enterKeyElement;
1116 var re = new RegExp('^HR|IMG|BR$','g'); // Skip these
1117 var dre = new RegExp('^H[1-6]$','g'); // Add double on these
1119 if (!elm.hasChildNodes() && !re.test(elm.nodeName)) {
1120 if (dre.test(elm.nodeName))
1121 elm.innerHTML = " ";
1123 elm.innerHTML = " ";
1128 // Check if it's a position key
1129 var keys = tinyMCE.posKeyCodes;
1131 for (var i=0; i<keys.length; i++) {
1132 if (keys[i] == e.keyCode) {
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);
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);
1154 if (e.keyCode == 89 && (e.ctrlKey && !e.altKey) && e.type == "keydown") { // Ctrl+Y
1155 tinyMCE.selectedInstance.execCommand("Redo");
1156 tinyMCE.triggerNodeChange(false);
1159 if ((e.keyCode == 90 || e.keyCode == 89) && (e.ctrlKey && !e.altKey)) {
1161 e.returnValue = false;
1162 e.cancelBubble = true;
1169 if ((e.keyCode == 90 || e.keyCode == 89) && (e.ctrlKey && !e.altKey))
1173 if (e.keyCode == 17)
1176 // Handle Undo/Redo when typing content
1178 // Start typing (non position key)
1179 if (!posKey && e.type == "keyup")
1180 tinyMCE.execCommand("mceStartTyping");
1182 // End typing (position key) or some Ctrl event
1183 if (e.type == "keyup" && (posKey || e.ctrlKey))
1184 tinyMCE.execCommand("mceEndTyping");
1186 if (posKey && e.type == "keyup")
1187 tinyMCE.triggerNodeChange(false);
1189 if (tinyMCE.isMSIE && e.ctrlKey)
1190 window.setTimeout('tinyMCE.triggerNodeChange(false);', 1);
1197 if (tinyMCE.selectedInstance)
1198 tinyMCE.selectedInstance.switchSettings();
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]))
1206 var inst = tinyMCE.instances[instanceName];
1208 // Reset design mode if lost (on everything just in case)
1209 inst.autoResetDesignMode();
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");
1220 if (tinyMCE.isSafari) {
1221 tinyMCE.selectedInstance.lastSafariSelection = tinyMCE.selectedInstance.getBookmark();
1222 tinyMCE.selectedInstance.lastSafariSelectedElement = tinyMCE.selectedElement;
1224 var lnk = tinyMCE.getParentElement(tinyMCE.selectedElement, "a");
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);");
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");
1241 // Reset selected node
1242 if (e.type != "focus")
1243 tinyMCE.selectedNode = null;
1245 tinyMCE.triggerNodeChange(false);
1246 tinyMCE.execCommand("mceEndTyping");
1248 if (e.type == "mouseup")
1249 tinyMCE.execCommand("mceAddUndoLevel");
1252 if (!tinyMCE.selectedInstance && e.target.editorId)
1253 tinyMCE.selectedInstance = tinyMCE.instances[e.target.editorId];
1260 TinyMCE.prototype.switchClass = function(element, class_name, lock_state) {
1261 var lockChanged = false;
1263 if (typeof(lock_state) != "undefined" && element != null) {
1264 element.classLock = lock_state;
1268 if (element != null && (lockChanged || !element.classLock)) {
1269 element.oldClassName = element.className;
1270 element.className = class_name;
1274 TinyMCE.prototype.restoreAndSwitchClass = function(element, class_name) {
1275 if (element != null && !element.classLock) {
1276 this.restoreClass(element);
1277 this.switchClass(element, class_name);
1281 TinyMCE.prototype.switchClassSticky = function(element_name, class_name, lock_state) {
1282 var element, lockChanged = false;
1284 // Performance issue
1285 if (!this.stickyClassesLookup[element_name])
1286 this.stickyClassesLookup[element_name] = document.getElementById(element_name);
1288 // element = document.getElementById(element_name);
1289 element = this.stickyClassesLookup[element_name];
1291 if (typeof(lock_state) != "undefined" && element != null) {
1292 element.classLock = lock_state;
1296 if (element != null && (lockChanged || !element.classLock)) {
1297 element.className = class_name;
1298 element.oldClassName = class_name;
1300 // Fix opacity in Opera
1301 if (tinyMCE.isOpera) {
1302 if (class_name == "mceButtonDisabled") {
1305 if (!element.mceOldSrc)
1306 element.mceOldSrc = element.src;
1308 if (this.operaOpacityCounter > -1)
1309 suffix = '?rnd=' + this.operaOpacityCounter++;
1311 element.src = tinyMCE.baseURL + "/themes/" + tinyMCE.getParam("theme") + "/images/opacity.png" + suffix;
1312 element.style.backgroundImage = "url('" + element.mceOldSrc + "')";
1314 if (element.mceOldSrc) {
1315 element.src = element.mceOldSrc;
1316 element.parentNode.style.backgroundImage = "";
1317 element.mceOldSrc = null;
1324 TinyMCE.prototype.restoreClass = function(element) {
1325 if (element != null && element.oldClassName && !element.classLock) {
1326 element.className = element.oldClassName;
1327 element.oldClassName = null;
1331 TinyMCE.prototype.setClassLock = function(element, lock_state) {
1332 if (element != null)
1333 element.classLock = lock_state;
1336 TinyMCE.prototype.addEvent = function(obj, name, handler) {
1337 if (tinyMCE.isMSIE) {
1338 obj.attachEvent("on" + name, handler);
1340 obj.addEventListener(name, handler, false);
1343 TinyMCE.prototype.submitPatch = function() {
1344 tinyMCE.removeTinyMCEFormElements(this);
1345 tinyMCE.triggerSave();
1346 this.mceOldSubmit();
1347 tinyMCE.isNotDirty = true;
1350 TinyMCE.prototype.onLoad = function() {
1351 for (var c=0; c<tinyMCE.configs.length; c++) {
1352 tinyMCE.settings = tinyMCE.configs[c];
1354 var selector = tinyMCE.getParam("editor_selector");
1355 var deselector = tinyMCE.getParam("editor_deselector");
1356 var elementRefAr = new Array();
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];
1363 tinyMCE.addEvent(form, "submit", TinyMCE.prototype.handleEvent);
1364 tinyMCE.addEvent(form, "reset", TinyMCE.prototype.handleEvent);
1365 tinyMCE.submitTriggers = true; // Do it only once
1367 // Patch the form.submit function
1368 if (tinyMCE.settings['submit_patch']) {
1370 form.mceOldSubmit = form.submit;
1371 form.submit = TinyMCE.prototype.submitPatch;
1379 // Add editor instances based on mode
1380 var mode = tinyMCE.settings['mode'];
1383 var elements = tinyMCE.getParam('elements', '', true, ',');
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']) : "";
1389 if (tinyMCE.getAttrib(element, "class").indexOf(deselector) != -1)
1392 if (trigger == "false")
1395 if (tinyMCE.settings['ask'] && element) {
1396 elementRefAr[elementRefAr.length] = 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]);
1407 case "specific_textareas":
1409 var nodeList = document.getElementsByTagName("textarea");
1411 for (var i=0; i<nodeList.length; i++) {
1412 var elm = nodeList.item(i);
1413 var trigger = elm.getAttribute(tinyMCE.settings['textarea_trigger']);
1415 if (selector != '' && tinyMCE.getAttrib(elm, "class").indexOf(selector) == -1)
1419 trigger = selector != "" ? "true" : "";
1421 if (tinyMCE.getAttrib(elm, "class").indexOf(deselector) != -1)
1424 if ((mode == "specific_textareas" && trigger == "true") || (mode == "textareas" && trigger != "false"))
1425 elementRefAr[elementRefAr.length] = elm;
1430 for (var i=0; i<elementRefAr.length; i++) {
1431 var element = elementRefAr[i];
1432 var elementId = element.name ? element.name : element.id;
1434 if (tinyMCE.settings['ask']) {
1435 // Focus breaks in Mozilla
1436 if (tinyMCE.isGecko) {
1437 var settings = tinyMCE.settings;
1439 tinyMCE.addEvent(element, "focus", function (e) {window.setTimeout(function() {TinyMCE.prototype.confirmAdd(e, settings);}, 10);});
1441 var settings = tinyMCE.settings;
1443 tinyMCE.addEvent(element, "focus", function () { TinyMCE.prototype.confirmAdd(null, settings); });
1446 tinyMCE.addMCEControl(element, elementId);
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();
1458 tinyMCE.executeCallback('oninit', '_oninit', 0);
1462 TinyMCE.prototype.removeMCEControl = function(editor_id) {
1463 var inst = tinyMCE.getInstanceById(editor_id);
1466 inst.switchSettings();
1468 editor_id = inst.editorId;
1469 var html = tinyMCE.getContent(editor_id);
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))
1478 if (instanceName != editor_id)
1479 tmpInstances[instanceName] = instance;
1481 tinyMCE.instances = tmpInstances;
1483 tinyMCE.selectedElement = null;
1484 tinyMCE.selectedInstance = null;
1487 var replaceElement = document.getElementById(editor_id + "_parent");
1488 var oldTargetElement = inst.oldTargetElement;
1489 var targetName = oldTargetElement.nodeName.toLowerCase();
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;
1497 oldTargetElement.innerHTML = html;
1499 replaceElement.parentNode.insertBefore(oldTargetElement, replaceElement);
1500 replaceElement.parentNode.removeChild(replaceElement);
1505 TinyMCE.prototype._cleanupElementName = function(element_name, element) {
1508 element_name = element_name.toLowerCase();
1510 // Never include body
1511 if (element_name == "body")
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)
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];
1529 if (elmMatch.charAt(0) == '+' || elmMatch.charAt(0) == '-')
1530 elmMatch = elmMatch.substring(1);
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;
1545 // Handle non regexp
1546 if (element_name == elmMatch) {
1547 elementAttribs = tinyMCE.cleanup_validElements[i];
1548 validElement = true;
1549 element_name = elementAttribs[0][0];
1559 if (element_name.charAt(0) == '+' || element_name.charAt(0) == '-')
1560 name = element_name.substring(1);
1562 // Special Mozilla stuff
1563 if (!tinyMCE.isMSIE) {
1564 // Fix for bug #958498
1565 if (name == "strong" && !tinyMCE.cleanup_on_save)
1567 else if (name == "em" && !tinyMCE.cleanup_on_save)
1571 var elmData = new Object();
1573 elmData.element_name = element_name;
1574 elmData.valid_attribs = elementAttribs;
1580 * This function moves CSS styles to/from attributes.
1582 TinyMCE.prototype._moveStyle = function(elm, style, attrib) {
1583 if (tinyMCE.cleanup_inline_styles) {
1584 var val = tinyMCE.getAttrib(elm, attrib);
1591 val = "url('" + val + "')";
1595 if (elm.style.borderStyle == '' || elm.style.borderStyle == 'none')
1596 elm.style.borderStyle = 'solid';
1602 if (attrib == "border" && elm.style.borderWidth > 0)
1605 if (val.indexOf('%') == -1)
1611 elm.style.marginTop = val + "px";
1612 elm.style.marginBottom = val + "px";
1613 elm.removeAttribute(attrib);
1617 if (elm.nodeName == "IMG") {
1619 elm.style.styleFloat = val;
1621 elm.style.cssFloat = val;
1623 elm.style.textAlign = val;
1625 elm.removeAttribute(attrib);
1630 eval('elm.style.' + style + ' = val;');
1631 elm.removeAttribute(attrib);
1638 var val = eval('elm.style.' + style) == '' ? tinyMCE.getAttrib(elm, attrib) : eval('elm.style.' + style);
1639 val = val == null ? '' : '' + val;
1642 // Always move background to style
1644 if (val.indexOf('url') == -1 && val != '')
1645 val = "url('" + val + "');";
1648 elm.style.backgroundImage = val;
1649 elm.removeAttribute(attrib);
1656 val = val.replace('px', '');
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;
1675 elm.removeAttribute(attrib);
1676 elm.setAttribute(attrib, val);
1677 eval('elm.style.' + style + ' = "";');
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;
1688 // Mozilla attibute, remove them
1689 if (attribName.indexOf('moz_') != -1)
1692 if (!tinyMCE.cleanup_on_save && (attribName == "mce_href" || attribName == "mce_src"))
1693 return {name : attribName, value : attribValue};
1696 if (tinyMCE.cleanup_verify_html && !verified) {
1697 for (var i=1; i<valid_attributes.length; i++) {
1698 var attribMatch = valid_attributes[i][0];
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');
1710 if ((re && attribName.match(re) != null) || attribName == attribMatch) {
1712 attribMustBeValue = valid_attributes[i][3];
1722 // Treat some attribs diffrent
1723 switch (attribName) {
1725 if (tinyMCE.isMSIE5 && element_name == "font")
1726 attribValue = element_node.size;
1732 // Old MSIE needs this
1733 if (tinyMCE.isMSIE5)
1734 attribValue = eval("element_node." + attribName);
1738 attribValue = attribValue.toLowerCase();
1742 if (tinyMCE.isMSIE5)
1743 attribValue = element_node.cellSpacing;
1747 if (tinyMCE.isMSIE5)
1748 attribValue = element_node.cellPadding;
1752 if (tinyMCE.isMSIE5 && element_name == "font")
1753 attribValue = element_node.color;
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, '');
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);
1767 if (!tinyMCE._verifyClass(element_node) || attribValue == "")
1785 attribValue = tinyMCE.cleanupEventStr("" + attribValue);
1787 if (attribValue.indexOf('return false;') == 0)
1788 attribValue = attribValue.substring(14);
1793 attribValue = tinyMCE.serializeStyle(tinyMCE.parseStyle(tinyMCE.getAttrib(element_node, "style")));
1796 // Convert the URLs of these
1800 attribValue = tinyMCE.getAttrib(element_node, attribName);
1802 // Use mce_href instead
1803 var href = tinyMCE.getAttrib(element_node, "mce_href");
1804 if (attribName == "href" && href != "")
1807 // Use mce_src instead
1808 var src = tinyMCE.getAttrib(element_node, "mce_src");
1809 if (attribName == "src" && src != "")
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);");
1823 if (attribValue == "1")
1828 case "_moz-userdefined":
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]) {
1849 var attrib = new Object();
1851 attrib.name = attribName;
1852 attrib.value = attribValue;
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
1865 TinyMCE.prototype.isInstance = function(inst) {
1866 return inst != null && typeof(inst) == "object" && inst.isTinyMCEControl;
1869 TinyMCE.prototype.parseStyle = function(str) {
1870 var ar = new Array();
1875 var st = str.split(';');
1877 tinyMCE.clearArray(ar);
1879 for (var i=0; i<st.length; i++) {
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'));
1887 ar[pa[0].toLowerCase()] = pa[1];
1893 TinyMCE.prototype.compressStyle = function(ar, pr, sf, res) {
1894 var box = new Array();
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];
1901 for (var i=0; i<box.length; i++) {
1905 for (var a=0; a<box.length; a++) {
1906 if (box[a] != box[i])
1911 // They are all the same
1913 ar[pr + '-top' + sf] = null;
1914 ar[pr + '-left' + sf] = null;
1915 ar[pr + '-right' + sf] = null;
1916 ar[pr + '-bottom' + sf] = null;
1919 TinyMCE.prototype.serializeStyle = function(ar) {
1923 tinyMCE.compressStyle(ar, "border", "", "border");
1924 tinyMCE.compressStyle(ar, "border", "-width", "border-width");
1925 tinyMCE.compressStyle(ar, "border", "-color", "border-color");
1927 for (var key in ar) {
1929 if (typeof(val) == 'function')
1932 if (val != null && val != '') {
1933 val = '' + val; // Force string
1936 val = val.replace(new RegExp("url\\(\\'?([^\\']*)\\'?\\)", 'gi'), "url('$1')");
1939 if (val.indexOf('url(') != -1 && tinyMCE.getParam('convert_urls')) {
1940 var m = new RegExp("url\\('(.*?)'\\)").exec(val);
1943 val = "url('" + eval(tinyMCE.getParam('urlconverter_callback') + "(m[1], null, true);") + "')";
1947 if (tinyMCE.getParam("force_hex_style_colors"))
1948 val = tinyMCE.convertRGBToHex(val, true);
1950 if (val != "url('')")
1951 str += key.toLowerCase() + ": " + val + "; ";
1955 if (new RegExp('; $').test(str))
1956 str = str.substring(0, str.length - 2);
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);
1970 r = r.length == 1 ? '0' + r : r;
1971 g = g.length == 1 ? '0' + g : g;
1972 b = b.length == 1 ? '0' + b : b;
1974 s = "#" + r + g + b;
1977 s = rgb[0] + s + rgb[4];
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) + ")";
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');
2003 var className = node.getAttribute('className');
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;
2015 if (nonDefinedCSS && className.indexOf('mce_') != 0) {
2016 node.removeAttribute('className');
2017 node.removeAttribute('class');
2025 TinyMCE.prototype.cleanupNode = function(node) {
2028 switch (node.nodeType) {
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;
2036 if (elementName != null && elementName.charAt(0) == '+') {
2037 elementName = elementName.substring(1);
2041 if (elementName != null && elementName.charAt(0) == '-') {
2042 elementName = elementName.substring(1);
2046 // Checking DOM tree for MSIE weirdness!!
2047 if (tinyMCE.isMSIE && tinyMCE.settings['fix_content_duplication']) {
2048 var lookup = tinyMCE.cleanup_elementLookupTable;
2050 for (var i=0; i<lookup.length; i++) {
2051 // Found element reference else were, hmm?
2052 if (lookup[i] == node)
2056 // Add element to lookup table
2057 lookup[lookup.length] = node;
2060 // Element not valid (only render children)
2062 if (node.hasChildNodes()) {
2063 for (var i=0; i<node.childNodes.length; i++)
2064 output += this.cleanupNode(node.childNodes[i]);
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]);
2077 return '<a name="' + this.convertStringToXML(node.getAttribute("name")) + '"></a>' + output;
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");
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 = '';
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");
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');
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');
2124 if (node.nodeName == "FONT")
2125 tinyMCE._moveStyle(node, 'color', 'color');
2129 if (elementValidAttribs) {
2130 for (var a=1; a<elementValidAttribs.length; a++) {
2131 var attribName, attribDefaultValue, attribForceValue, attribValue;
2133 attribName = elementValidAttribs[a][0];
2134 attribDefaultValue = elementValidAttribs[a][1];
2135 attribForceValue = elementValidAttribs[a][2];
2137 if (attribDefaultValue != null || attribForceValue != null) {
2138 var attribValue = node.getAttribute(attribName);
2140 if (node.getAttribute(attribName) == null || node.getAttribute(attribName) == "")
2141 attribValue = attribDefaultValue;
2143 attribValue = attribForceValue ? attribForceValue : attribValue;
2145 // Is to generate id
2146 if (attribValue == "{$uid}")
2147 attribValue = "uid_" + (tinyMCE.cleanup_idCount++);
2149 // Add visual aid class
2150 if (attribName == "class")
2151 attribValue = tinyMCE.getVisualAidClass(attribValue, tinyMCE.cleanup_on_save);
2153 node.setAttribute(attribName, attribValue);
2154 //alert(attribName + "=" + attribValue);
2159 if ((tinyMCE.isMSIE && !tinyMCE.isOpera) && elementName == "style")
2160 return "<style>" + node.innerHTML + "</style>";
2162 // Remove empty tables
2163 if (elementName == "table" && !node.hasChildNodes())
2166 // Handle element attributes
2167 if (node.attributes.length > 0) {
2168 var lastAttrib = "";
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)
2177 lastAttrib = node.attributes[i].nodeName;
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) + '"';
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');
2192 elementAttribs += " summary=" + '"' + this.convertStringToXML(summary) + '"';
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");
2199 for (var u=0; u<ma.length; u++) {
2200 if (node.getAttribute(ma[u]) != null) {
2201 var s = tinyMCE.getAttrib(node, ma[u]);
2204 elementAttribs += " " + ma[u] + "=" + '"' + this.convertStringToXML(s) + '"';
2209 // MSIE form element issue
2210 if (tinyMCE.isMSIE && elementName == "input") {
2212 if (!elementAttribs.match(/ type=/g))
2213 elementAttribs += " type=" + '"' + node.type + '"';
2217 if (!elementAttribs.match(/ value=/g))
2218 elementAttribs += " value=" + '"' + node.value + '"';
2222 // Add nbsp to some elements
2223 if ((elementName == "p" || elementName == "td") && (node.innerHTML == "" || node.innerHTML == " "))
2224 return "<" + elementName + elementAttribs + ">" + this.convertStringToXML(String.fromCharCode(160)) + "</" + elementName + ">";
2226 // Is MSIE script element
2227 if (tinyMCE.isMSIE && elementName == "script")
2228 return "<" + elementName + elementAttribs + ">" + node.text + "</" + elementName + ">";
2230 // Clean up children
2231 if (node.hasChildNodes()) {
2232 // If not empty span
2233 if (!(elementName == "span" && elementAttribs == "" && tinyMCE.getParam("trim_span_elements"))) {
2235 if (elementName == "p" && tinyMCE.cleanup_force_br_newlines)
2236 output += "<div" + elementAttribs + ">";
2238 output += "<" + elementName + elementAttribs + ">";
2241 for (var i=0; i<node.childNodes.length; i++)
2242 output += this.cleanupNode(node.childNodes[i]);
2244 // If not empty span
2245 if (!(elementName == "span" && elementAttribs == "" && tinyMCE.getParam("trim_span_elements"))) {
2247 if (elementName == "p" && tinyMCE.cleanup_force_br_newlines)
2248 output += "</div><br />";
2250 output += "</" + elementName + ">";
2255 output += "<" + elementName + elementAttribs + "></" + elementName + ">";
2257 output += "<" + elementName + elementAttribs + " />";
2264 // Do not convert script elements
2265 if (node.parentNode.nodeName == "SCRIPT" || node.parentNode.nodeName == "NOSCRIPT" || node.parentNode.nodeName == "STYLE")
2266 return node.nodeValue;
2268 return this.convertStringToXML(node.nodeValue);
2271 return "<!--" + node.nodeValue + "-->";
2274 return "[UNKNOWN NODETYPE " + node.nodeType + "]";
2278 TinyMCE.prototype.convertStringToXML = function(html_data) {
2281 for (var i=0; i<html_data.length; i++) {
2282 var chr = html_data.charCodeAt(i);
2285 if (tinyMCE.settings['entity_encoding'] == "numeric") {
2287 output += '&#' + chr + ";";
2289 output += String.fromCharCode(chr);
2295 if (tinyMCE.settings['entity_encoding'] == "raw") {
2296 output += String.fromCharCode(chr);
2301 if (typeof(tinyMCE.settings['cleanup_entities']["c" + chr]) != 'undefined' && tinyMCE.settings['cleanup_entities']["c" + chr] != '')
2302 output += '&' + tinyMCE.settings['cleanup_entities']["c" + chr] + ';';
2304 output += '' + String.fromCharCode(chr);
2310 TinyMCE.prototype._getCleanupElementName = function(chunk) {
2313 if (chunk.charAt(0) == '+')
2314 chunk = chunk.substring(1);
2316 if (chunk.charAt(0) == '-')
2317 chunk = chunk.substring(1);
2319 if ((pos = chunk.indexOf('/')) != -1)
2320 chunk = chunk.substring(0, pos);
2322 if ((pos = chunk.indexOf('[')) != -1)
2323 chunk = chunk.substring(0, pos);
2328 TinyMCE.prototype._initCleanup = function() {
2329 // Parse valid elements and attributes
2330 var validElements = tinyMCE.settings["valid_elements"];
2331 validElements = validElements.split(',');
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;
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];
2350 validElements[validElements.length] = extendedValidElements[i];
2353 for (var i=0; i<validElements.length; i++) {
2354 var item = validElements[i];
2356 item = item.replace('[','|');
2357 item = item.replace(']','');
2359 // Split and convert
2360 var attribs = item.split('|');
2361 for (var x=0; x<attribs.length; x++)
2362 attribs[x] = attribs[x].toLowerCase();
2364 // Handle change elements
2365 attribs[0] = attribs[0].split('/');
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;
2375 if ((pos = attribName.indexOf('=')) != -1) {
2376 attribDefault = attribName.substring(pos+1);
2377 attribName = attribName.substring(0, pos);
2381 if ((pos = attribName.indexOf(':')) != -1) {
2382 attribForce = attribName.substring(pos+1);
2383 attribName = attribName.substring(0, pos);
2387 if ((pos = attribName.indexOf('<')) != -1) {
2388 attribMustBe = attribName.substring(pos+1).split('?');
2389 attribName = attribName.substring(0, pos);
2392 attribs[x] = new Array(attribName, attribDefault, attribForce, attribMustBe);
2395 validElements[i] = attribs;
2398 var invalidElements = tinyMCE.settings['invalid_elements'].split(',');
2399 for (var i=0; i<invalidElements.length; i++)
2400 invalidElements[i] = invalidElements[i].toLowerCase();
2402 // Set these for performance
2403 tinyMCE.settings['cleanup_validElements'] = validElements;
2404 tinyMCE.settings['cleanup_invalidElements'] = invalidElements;
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);
2411 var html = element.innerHTML;
2413 // Remove mce_href/mce_src
2414 html = html.replace(new RegExp('(mce_href|mce_src)=".*?"', 'gi'), '');
2415 html = html.replace(/\s+>/gi, '>');
2420 if (on_save && tinyMCE.getParam("convert_fonts_to_spans"))
2421 tinyMCE.convertFontsToSpans(doc);
2423 // Call custom cleanup code
2424 tinyMCE._customCleanup(inst, on_save ? "get_from_editor_dom" : "insert_to_editor_dom", doc.body);
2426 // Move bgcolor to style
2427 var n = doc.getElementsByTagName("font");
2428 for (var i=0; i<n.length; i++) {
2430 if ((c = tinyMCE.getAttrib(n[i], "bgcolor")) != "") {
2431 n[i].style.backgroundColor = c;
2432 tinyMCE.setAttrib(n[i], "bgcolor", "");
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();
2451 var startTime = new Date().getTime();
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");
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'));
2466 var html = this.cleanupNode(element);
2468 if (tinyMCE.settings['debug'])
2469 tinyMCE.debug("Cleanup process executed in: " + (new Date().getTime()-startTime) + " ms.");
2471 // Remove pesky HR paragraphs and other crap
2472 html = tinyMCE.regexpReplace(html, '<p><hr /></p>', '<hr />');
2473 html = tinyMCE.regexpReplace(html, '<p> </p><hr /><p> </p>', '<hr />');
2474 html = tinyMCE.regexpReplace(html, '<td>\\s*<br />\\s*</td>', '<td> </td>');
2475 html = tinyMCE.regexpReplace(html, '<p>\\s*<br />\\s*</p>', '<p> </p>');
2476 html = tinyMCE.regexpReplace(html, '<p>\\s* \\s*<br />\\s* \\s*</p>', '<p> </p>');
2477 html = tinyMCE.regexpReplace(html, '<p>\\s* \\s*<br />\\s*</p>', '<p> </p>');
2478 html = tinyMCE.regexpReplace(html, '<p>\\s*<br />\\s* \\s*</p>', '<p> </p>');
2480 // Remove empty anchors
2481 html = html.replace(new RegExp('<a>(.*?)</a>', 'gi'), '$1');
2483 // Remove some mozilla crap
2484 if (!tinyMCE.isMSIE)
2485 html = html.replace(new RegExp('<o:p _moz-userdefined="" />', 'g'), "");
2487 if (tinyMCE.settings['remove_linebreaks'])
2488 html = html.replace(new RegExp('\r|\n', 'g'), ' ');
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");
2496 if (tinyMCE.settings['force_br_newlines']) {
2497 var re = new RegExp('<p> </p>', 'g');
2498 html = html.replace(re, "<br />");
2501 if (tinyMCE.isGecko && tinyMCE.settings['remove_lt_gt']) {
2502 // Remove weridness!
2503 var re = new RegExp('<>', 'g');
2504 html = html.replace(re, "");
2507 // Call custom cleanup code
2508 html = tinyMCE._customCleanup(inst, on_save ? "get_from_editor" : "insert_to_editor", html);
2510 // Emtpy node, return empty
2511 var chk = tinyMCE.regexpReplace(html, "[ \t\r\n]", "").toLowerCase();
2512 if (chk == "<br/>" || chk == "<br>" || chk == "<p> </p>" || chk == "<p> </p>" || chk == "<p></p>")
2515 if (tinyMCE.settings["preformatted"])
2516 return "<pre>" + html + "</pre>";
2521 TinyMCE.prototype.insertLink = function(href, target, title, onclick, style_class) {
2522 tinyMCE.execCommand('mceBeginUndoLevel');
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;
2530 linkElement = doc.createElement("a");
2535 var thref = eval(tinyMCE.settings['urlconverter_callback'] + "(href, linkElement);");
2536 mhref = tinyMCE.getParam('convert_urls') ? href : mhref;
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);
2546 linkElement.appendChild(this.selectedElement.cloneNode(true));
2547 this.selectedElement.parentNode.replaceChild(linkElement, this.selectedElement);
2553 if (!this.linkElement && this.selectedInstance) {
2554 if (tinyMCE.isSafari) {
2555 tinyMCE.execCommand("mceInsertContent", false, '<a href="' + tinyMCE.uniqueURL + '">' + this.selectedInstance.getSelectedHTML() + '</a>');
2557 this.selectedInstance.contentDocument.execCommand("createlink", false, tinyMCE.uniqueURL);
2559 tinyMCE.linkElement = this.getElementByAttributeValue(this.selectedInstance.contentDocument.body, "a", "href", tinyMCE.uniqueURL);
2561 var elementArray = this.getElementsByAttributeValue(this.selectedInstance.contentDocument.body, "a", "href", tinyMCE.uniqueURL);
2563 for (var i=0; i<elementArray.length; i++) {
2565 var thref = eval(tinyMCE.settings['urlconverter_callback'] + "(href, elementArray[i]);");
2566 mhref = tinyMCE.getParam('convert_urls') ? href : mhref;
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);
2576 tinyMCE.linkElement = elementArray[0];
2579 if (this.linkElement) {
2581 href = eval(tinyMCE.settings['urlconverter_callback'] + "(href, this.linkElement);");
2582 mhref = tinyMCE.getParam('convert_urls') ? href : mhref;
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);
2592 tinyMCE.execCommand('mceEndUndoLevel');
2595 TinyMCE.prototype.insertImage = function(src, alt, border, hspace, vspace, width, height, align, title, onmouseover, onmouseout) {
2596 tinyMCE.execCommand('mceBeginUndoLevel');
2601 if (!this.imgElement && tinyMCE.isSafari) {
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 + '" />';
2609 tinyMCE.execCommand("mceInsertContent", false, html);
2611 if (!this.imgElement && this.selectedInstance) {
2612 if (tinyMCE.isSafari)
2613 tinyMCE.execCommand("mceInsertContent", false, '<img src="' + tinyMCE.uniqueURL + '" />');
2615 this.selectedInstance.contentDocument.execCommand("insertimage", false, tinyMCE.uniqueURL);
2617 tinyMCE.imgElement = this.getElementByAttributeValue(this.selectedInstance.contentDocument.body, "img", "src", tinyMCE.uniqueURL);
2621 if (this.imgElement) {
2622 var needsRepaint = false;
2625 src = eval(tinyMCE.settings['urlconverter_callback'] + "(src, tinyMCE.imgElement);");
2627 if (tinyMCE.getParam('convert_urls'))
2630 if (onmouseover && onmouseover != "")
2631 onmouseover = "this.src='" + eval(tinyMCE.settings['urlconverter_callback'] + "(onmouseover, tinyMCE.imgElement);") + "';";
2633 if (onmouseout && onmouseout != "")
2634 onmouseout = "this.src='" + eval(tinyMCE.settings['urlconverter_callback'] + "(onmouseout, tinyMCE.imgElement);") + "';";
2636 // Use alt as title if it's undefined
2637 if (typeof(title) == "undefined")
2640 if (width != this.imgElement.getAttribute("width") || height != this.imgElement.getAttribute("height") || align != this.imgElement.getAttribute("align"))
2641 needsRepaint = true;
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);
2656 // Fix for bug #989846 - Image resize bug
2657 if (width && width != "")
2658 this.imgElement.style.pixelWidth = width;
2660 if (height && height != "")
2661 this.imgElement.style.pixelHeight = height;
2664 tinyMCE.selectedInstance.repaint();
2667 tinyMCE.execCommand('mceEndUndoLevel');
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)
2678 TinyMCE.prototype.getElementsByAttributeValue = function(node, element_name, attrib, value) {
2679 var elements = new Array();
2681 if (node && node.nodeName.toLowerCase() == element_name) {
2682 if (node.getAttribute(attrib) && node.getAttribute(attrib).indexOf(value) != -1)
2683 elements[elements.length] = node;
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];
2697 TinyMCE.prototype.isBlockElement = function(node) {
2698 return node != null && node.nodeType == 1 && this.blockRegExp.test(node.nodeName);
2701 TinyMCE.prototype.getParentBlockElement = function(node) {
2702 // Search up the tree for block element
2704 if (this.blockRegExp.test(node.nodeName))
2707 node = node.parentNode;
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;
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);
2725 TinyMCE.prototype.getParentElement = function(node, names, attrib_name, attrib_value) {
2726 if (typeof(names) == "undefined") {
2727 if (node.nodeType == 1)
2730 // Find parent node that is a element
2731 while ((node = node.parentNode) != null && node.nodeType != 1) ;
2736 var namesAr = names.split(',');
2742 for (var i=0; i<namesAr.length; i++) {
2743 if (node.nodeName.toLowerCase() == namesAr[i].toLowerCase() || names == "*") {
2744 if (typeof(attrib_name) == "undefined")
2746 else if (node.getAttribute(attrib_name)) {
2747 if (typeof(attrib_value) == "undefined") {
2748 if (node.getAttribute(attrib_name) != "")
2750 } else if (node.getAttribute(attrib_name) == attrib_value)
2755 } while ((node = node.parentNode) != null);
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;
2765 // Pass through file protocol
2766 if (prot == "file:")
2769 // Something is wrong, remove weirdness
2770 url = tinyMCE.regexpReplace(url, '(http|https):///', '/');
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) == "#")
2776 // Fix relative/Mozilla
2777 if (!tinyMCE.isMSIE && !on_save && url.indexOf("://") == -1 && url.charAt(0) != '/')
2778 return tinyMCE.settings['base_href'] + url;
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;
2786 var urlParts = tinyMCE.parseURL(curl);
2787 var tmpUrlParts = tinyMCE.parseURL(tinyMCE.settings['document_base_url']);
2790 if (urlParts['host'] == tmpUrlParts['host'] && (urlParts['port'] == tmpUrlParts['port']))
2791 return tinyMCE.convertAbsoluteURLToRelativeURL(tinyMCE.settings['document_base_url'], curl);
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']);
2799 // Force absolute URLs from relative URLs
2800 url = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'], url);
2802 // If anchor and path is the same page
2803 if (urlParts['anchor'] && urlParts['path'] == baseUrlParts['path'])
2804 return "#" + urlParts['anchor'];
2807 // Remove current domain
2808 if (tinyMCE.getParam('remove_script_host')) {
2809 var start = "", portPart = "";
2812 portPart = ":" + port;
2814 start = prot + "//" + host + portPart + "/";
2816 if (url.indexOf(start) == 0)
2817 url = url.substring(start.length-1);
2824 * Parses a URL in to its diffrent components.
2826 TinyMCE.prototype.parseURL = function(url_str) {
2827 var urlParts = new Array();
2832 // Parse protocol part
2833 pos = url_str.indexOf('://');
2835 urlParts['protocol'] = url_str.substring(0, pos);
2839 // Find port or path start
2840 for (var i=lastPos; i<url_str.length; i++) {
2841 var chr = url_str.charAt(i);
2852 urlParts['host'] = url_str.substring(lastPos, pos);
2855 urlParts['port'] = "";
2857 if (url_str.charAt(pos) == ':') {
2858 pos = url_str.indexOf('/', lastPos);
2859 urlParts['port'] = url_str.substring(lastPos+1, pos);
2864 pos = url_str.indexOf('?', lastPos);
2867 pos = url_str.indexOf('#', lastPos);
2870 pos = url_str.length;
2872 urlParts['path'] = url_str.substring(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);
2884 if (url_str.charAt(pos) == '#') {
2885 pos = url_str.length;
2886 urlParts['anchor'] = url_str.substring(lastPos+1, pos);
2893 TinyMCE.prototype.serializeURL = function(up) {
2897 url += up['protocol'] + "://";
2903 url += ":" + up['port'];
2909 url += "?" + up['query'];
2912 url += "#" + up['anchor'];
2918 * Converts an absolute path to relative path.
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);
2927 var forceSlash = false;
2929 if (targetURL.path == "")
2930 targetURL.path = "/";
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('/');
2939 if (strTok1.length >= strTok2.length) {
2940 for (var i=0; i<strTok1.length; i++) {
2941 if (i >= strTok2.length || strTok1[i] != strTok2[i]) {
2948 if (strTok1.length < strTok2.length) {
2949 for (var i=0; i<strTok2.length; i++) {
2950 if (i >= strTok1.length || strTok1[i] != strTok2[i]) {
2957 if (breakPoint == 1)
2958 return targetURL.path;
2960 for (var i=0; i<(strTok1.length-(breakPoint-1)); i++)
2963 for (var i=breakPoint-1; i<strTok2.length; i++) {
2964 if (i != (breakPoint-1))
2965 outPath += "/" + strTok2[i];
2967 outPath += strTok2[i];
2970 targetURL.protocol = null;
2971 targetURL.host = null;
2972 targetURL.port = null;
2973 targetURL.path = outPath == "" && forceSlash ? "/" : outPath;
2975 // Remove document prefix from local anchors
2976 var fileName = baseURL.path;
2979 if ((pos = fileName.lastIndexOf('/')) != -1)
2980 fileName = fileName.substring(pos + 1);
2983 if (fileName == targetURL.path && targetURL.anchor != "")
2984 targetURL.path = "";
2986 return this.serializeURL(targetURL);
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);
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;
2997 baseURLParts = baseURL['path'].split('/');
2998 relURLParts = relURL['path'].split('/');
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)
3006 newBaseURLParts[newBaseURLParts.length] = baseURLParts[i];
3008 baseURLParts = newBaseURLParts.reverse();
3010 // Merge relURLParts chunks
3011 var newRelURLParts = new Array();
3013 for (var i=relURLParts.length-1; i>=0; i--) {
3014 if (relURLParts[i].length == 0 || relURLParts[i] == ".")
3017 if (relURLParts[i] == '..') {
3027 newRelURLParts[newRelURLParts.length] = relURLParts[i];
3030 relURLParts = newRelURLParts.reverse();
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 = "";
3038 relURL.protocol = baseURL.protocol;
3039 relURL.host = baseURL.host;
3040 relURL.port = baseURL.port;
3042 // Re-add trailing slash if it's removed
3043 if (relURL.path.charAt(relURL.path.length-1) == "/")
3046 relURL.path = absPath;
3048 return TinyMCE.prototype.serializeURL(relURL);
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];
3055 if (value == "true" || value == "false")
3056 return (value == "true");
3058 if (strip_whitespace)
3059 value = tinyMCE.regexpReplace(value, "[ \t\r\n]", "");
3061 if (typeof(split_chr) != "undefined" && split_chr != null) {
3062 value = value.split(split_chr);
3063 var outArray = new Array();
3065 for (var i=0; i<value.length; i++) {
3066 if (value[i] && value[i] != "")
3067 outArray[outArray.length] = value[i];
3076 TinyMCE.prototype.getLang = function(name, default_value, parse_entities) {
3077 var value = (typeof(tinyMCELang[name]) == "undefined") ? default_value : tinyMCELang[name];
3080 value = tinyMCE.entityDecode(value);
3085 TinyMCE.prototype.entityDecode = function(s) {
3086 var e = document.createElement("div");
3091 TinyMCE.prototype.addToLang = function(prefix, ar) {
3092 for (var key in ar) {
3093 if (typeof(ar[key]) == 'function')
3096 tinyMCELang[(key.indexOf('lang_') == -1 ? 'lang_' : '') + (prefix != '' ? (prefix + "_") : '') + key] = ar[key];
3099 // for (var key in ar)
3100 // tinyMCELang[(key.indexOf('lang_') == -1 ? 'lang_' : '') + (prefix != '' ? (prefix + "_") : '') + key] = "|" + ar[key] + "|";
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);
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')
3114 replace_haystack = tinyMCE.replaceVar(replace_haystack, key, value);
3117 return replace_haystack;
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();
3127 var undoLevels = -1;
3128 var anySelection = false;
3129 var selectedText = inst.getSelectedText();
3131 inst.switchSettings();
3133 if (tinyMCE.settings["auto_resize"]) {
3134 var doc = inst.getDoc();
3136 inst.iframeElement.style.width = doc.body.offsetWidth + "px";
3137 inst.iframeElement.style.height = doc.body.offsetHeight + "px";
3140 if (tinyMCE.selectedElement)
3141 anySelection = (tinyMCE.selectedElement.nodeName.toLowerCase() == "img") || (selectedText && selectedText.length > 0);
3143 if (tinyMCE.settings['custom_undo_redo']) {
3144 undoIndex = inst.undoIndex;
3145 undoLevels = inst.undoLevels.length;
3148 tinyMCE.executeCallback('handleNodeChangeCallback', '_handleNodeChange', 0, editorId, elm, undoIndex, undoLevels, inst.visualAid, anySelection, setup_content);
3152 if (this.selectedInstance && (typeof(focus) == "undefined" || focus))
3153 this.selectedInstance.contentWindow.focus();
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);");
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);");
3172 TinyMCE.prototype.getContent = function(editor_id) {
3173 if (typeof(editor_id) != "undefined")
3174 tinyMCE.selectedInstance = tinyMCE.getInstanceById(editor_id);
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);
3186 TinyMCE.prototype.setContent = function(html_content) {
3187 if (tinyMCE.selectedInstance) {
3188 tinyMCE.selectedInstance.execCommand('mceSetContent', false, html_content);
3189 tinyMCE.selectedInstance.repaint();
3193 TinyMCE.prototype.importThemeLanguagePack = function(name) {
3194 if (typeof(name) == "undefined")
3195 name = tinyMCE.settings['theme'];
3197 tinyMCE.loadScript(tinyMCE.baseURL + '/themes/' + name + '/langs/' + tinyMCE.settings['language'] + '.js');
3200 TinyMCE.prototype.importPluginLanguagePack = function(name, valid_languages) {
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'];
3209 tinyMCE.loadScript(tinyMCE.baseURL + '/plugins/' + name + '/langs/' + lang + '.js');
3213 * Adds themeurl, settings and lang to HTML code.
3215 TinyMCE.prototype.applyTemplate = function(html, args) {
3216 html = tinyMCE.replaceVar(html, "themeurl", tinyMCE.themeURL);
3218 if (typeof(args) != "undefined")
3219 html = tinyMCE.replaceVars(html, args);
3221 html = tinyMCE.replaceVars(html, tinyMCE.settings);
3222 html = tinyMCE.replaceVars(html, tinyMCELang);
3227 TinyMCE.prototype.openWindow = function(template, args) {
3228 var html, width, height, x, y, resizable, scrollbars, url;
3230 args['mce_template_file'] = template['file'];
3231 args['mce_width'] = template['width'];
3232 args['mce_height'] = template['height'];
3233 tinyMCE.windowArgs = args;
3235 html = template['html'];
3236 if (!(width = parseInt(template['width'])))
3239 if (!(height = parseInt(template['height'])))
3242 // Add to height in M$ due to SP2 WHY DON'T YOU GUYS IMPLEMENT innerWidth of windows!!
3248 x = parseInt(screen.width / 2.0) - (width / 2.0);
3249 y = parseInt(screen.height / 2.0) - (height / 2.0);
3251 resizable = (args && args['resizable']) ? args['resizable'] : "no";
3252 scrollbars = (args && args['scrollbars']) ? args['scrollbars'] : "no";
3254 if (template['file'].charAt(0) != '/' && template['file'].indexOf('://') == -1)
3255 url = tinyMCE.baseURL + "/themes/" + tinyMCE.getParam("theme") + "/" + template['file'];
3257 url = template['file'];
3259 // Replace all args as variables in URL
3260 for (var name in args) {
3261 if (typeof(args[name]) == 'function')
3264 url = tinyMCE.replaceVar(url, name, escape(args[name]));
3268 html = tinyMCE.replaceVar(html, "css", this.settings['popups_css']);
3269 html = tinyMCE.applyTemplate(html, args);
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);
3273 alert(tinyMCELang['lang_popup_blocked']);
3277 win.document.write(html);
3278 win.document.close();
3279 win.resizeTo(width, height);
3282 if ((tinyMCE.isMSIE && !tinyMCE.isOpera) && resizable != 'yes' && tinyMCE.settings["dialog_type"] == "modal") {
3283 var features = "resizable:" + resizable
3285 + scrollbars + ";status:yes;center:yes;help:no;dialogWidth:"
3286 + width + "px;dialogHeight:" + height + "px;";
3288 window.showModalDialog(url, window, features);
3290 var modal = (resizable == "yes") ? "no" : "yes";
3292 if (tinyMCE.isGecko && tinyMCE.isMac)
3295 if (template['close_previous'] != "no")
3296 try {tinyMCE.lastWindow.close();} catch (ex) {}
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);
3300 alert(tinyMCELang['lang_popup_blocked']);
3304 if (template['close_previous'] != "no")
3305 tinyMCE.lastWindow = win;
3307 eval('try { win.resizeTo(width, height); } catch(e) { }');
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);
3320 TinyMCE.prototype.closeWindow = function(win) {
3324 TinyMCE.prototype.getVisualAidClass = function(class_name, state) {
3325 var aidClass = tinyMCE.settings['visual_table_class'];
3327 if (typeof(state) == "undefined")
3328 state = tinyMCE.settings['visual'];
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)
3338 classNames[classNames.length] = ar[i];
3342 classNames[classNames.length] = aidClass;
3346 for (var i=0; i<classNames.length; i++) {
3350 className += classNames[i];
3356 TinyMCE.prototype.handleVisualAid = function(el, deep, state, inst) {
3360 var tableElement = null;
3362 switch (el.nodeName) {
3364 var oldW = el.style.width;
3365 var oldH = el.style.height;
3366 var bo = tinyMCE.getAttrib(el, "border");
3368 bo = bo == "" || bo == "0" ? true : false;
3370 tinyMCE.setAttrib(el, "class", tinyMCE.getVisualAidClass(tinyMCE.getAttrib(el, "class"), state && bo));
3372 el.style.width = oldW;
3373 el.style.height = oldH;
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);
3385 var anchorName = tinyMCE.getAttrib(el, "name");
3387 if (anchorName != '' && state) {
3388 el.title = anchorName;
3389 el.className = 'mceItemAnchor';
3390 } else if (anchorName != '' && !state)
3396 if (deep && el.hasChildNodes()) {
3397 for (var i=0; i<el.childNodes.length; i++)
3398 tinyMCE.handleVisualAid(el.childNodes[i], deep, state, inst);
3402 TinyMCE.prototype.getAttrib = function(elm, name, default_value) {
3403 if (typeof(default_value) == "undefined")
3407 if (!elm || elm.nodeType != 1)
3408 return default_value;
3410 var v = elm.getAttribute(name);
3412 // Try className for class attrib
3413 if (name == "class" && !v)
3416 // Workaround for a issue with Firefox 1.5rc2+
3417 if (tinyMCE.isGecko && name == "src" && elm.src != null && elm.src != "")
3420 // Workaround for a issue with Firefox 1.5rc2+
3421 if (tinyMCE.isGecko && name == "href" && elm.href != null && elm.href != "")
3424 if (name == "style" && !tinyMCE.isOpera)
3425 v = elm.style.cssText;
3427 return (v && v != "") ? v : default_value;
3430 TinyMCE.prototype.setAttrib = function(element, name, value, fix_value) {
3431 if (typeof(value) == "number" && value != null)
3438 var re = new RegExp('[^0-9%]', 'g');
3439 value = value.replace(re, '');
3442 if (name == "style")
3443 element.style.cssText = value;
3445 if (name == "class")
3446 element.className = value;
3448 if (value != null && value != "" && value != -1)
3449 element.setAttribute(name, value);
3451 element.removeAttribute(name);
3454 TinyMCE.prototype.setStyleAttrib = function(elm, name, value) {
3455 eval('elm.style.' + name + '=value;');
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);
3465 TinyMCE.prototype.convertSpansToFonts = function(doc) {
3466 var sizes = tinyMCE.getParam('font_size_style_values').replace(/\s+/, '').split(',');
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;
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();
3478 for (var x=0; x<sizes.length; x++) {
3479 if (sizes[x] == size) {
3486 tinyMCE.setAttrib(s[i], 'size', fSize);
3487 s[i].style.fontSize = '';
3490 var fFace = s[i].style.fontFamily;
3491 if (fFace != null && fFace != "") {
3492 tinyMCE.setAttrib(s[i], 'face', fFace);
3493 s[i].style.fontFamily = '';
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 = '';
3504 TinyMCE.prototype.convertFontsToSpans = function(doc) {
3505 var sizes = tinyMCE.getParam('font_size_style_values').replace(/\s+/, '').split(',');
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;
3512 var fsClasses = tinyMCE.getParam('font_size_classes');
3513 if (fsClasses != '')
3514 fsClasses = fsClasses.replace(/\s+/, '').split(',');
3518 var s = doc.getElementsByTagName("span");
3519 for (var i=0; i<s.length; i++) {
3520 var fSize, fFace, fColor;
3522 fSize = tinyMCE.getAttrib(s[i], 'size');
3523 fFace = tinyMCE.getAttrib(s[i], 'face');
3524 fColor = tinyMCE.getAttrib(s[i], 'color');
3527 fSize = parseInt(fSize);
3529 if (fSize > 0 && fSize < 8) {
3530 if (fsClasses != null)
3531 tinyMCE.setAttrib(s[i], 'class', fsClasses[fSize-1]);
3533 s[i].style.fontSize = sizes[fSize-1];
3536 s[i].removeAttribute('size');
3540 s[i].style.fontFamily = fFace;
3541 s[i].removeAttribute('face');
3545 s[i].style.color = fColor;
3546 s[i].removeAttribute('color');
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");
3558 tinyMCE.setAttrib(f[i], 'class', "mceItemFont" + s);
3561 if (typeof(size) != "undefined") {
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;
3576 if (doc.styleSheets[0].rules[0].selectorText == "FONT")
3577 doc.styleSheets[0].removeRule(0);
3579 doc.styleSheets[0].addRule("FONT", css, 0);
3584 TinyMCE.prototype.setInnerHTML = function(e, h) {
3585 if (tinyMCE.isMSIE && !tinyMCE.isOpera) {
3586 e.innerHTML = tinyMCE.uniqueTag + h;
3587 e.firstChild.removeNode(true);
3589 h = this.fixGeckoBaseHREFBug(1, e, h);
3591 this.fixGeckoBaseHREFBug(2, e, h);
3595 TinyMCE.prototype.fixGeckoBaseHREFBug = function(m, e, h) {
3596 if (tinyMCE.isGecko) {
3598 h = h.replace(/\ssrc=/gi, " xsrc=");
3599 h = h.replace(/\shref=/gi, " xhref=");
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");
3609 n[i].src = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'], xsrc);
3610 n[i].removeAttribute("xsrc");
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");
3620 n[i].src = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'], xsrc);
3621 n[i].removeAttribute("xsrc");
3626 var n = e.getElementsByTagName("iframe");
3627 for (var i=0; i<n.length; i++) {
3628 var xsrc = tinyMCE.getAttrib(n[i], "xsrc");
3631 n[i].src = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'], xsrc);
3632 n[i].removeAttribute("xsrc");
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");
3643 n[i].href = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'], xhref);
3644 n[i].removeAttribute("xhref");
3654 TinyMCE.prototype.getOuterHTML = function(e) {
3658 var d = e.ownerDocument.createElement("body");
3663 TinyMCE.prototype.setOuterHTML = function(doc, e, h) {
3664 if (tinyMCE.isMSIE) {
3669 var d = e.ownerDocument.createElement("body");
3671 e.parentNode.replaceChild(d.firstChild, e);
3674 TinyMCE.prototype.insertAfter = function(nc, rc){
3676 rc.parentNode.insertBefore(nc, rc.nextSibling);
3678 rc.parentNode.appendChild(nc);
3681 TinyMCE.prototype.cleanupAnchors = function(doc) {
3682 var an = doc.getElementsByTagName("a");
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]);
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>');
3697 html_content = tinyMCE.cleanupHTMLCode(html_content);
3699 // Try innerHTML if it fails use pasteHTML in MSIE
3701 tinyMCE.setInnerHTML(doc.body, html_content);
3704 doc.body.createTextRange().pasteHTML(html_content);
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;
3719 // Content duplication bug fix (Seems to be word crap)
3720 var html = doc.body.innerHTML;
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);
3729 html = doc.body.innerHTML;
3730 html = tinyMCE.regexpReplace(html, "<o:p><\/o:p>", "<br />");
3731 html = tinyMCE.regexpReplace(html, "<o:p> <\/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> <\/p>", "<br />");
3736 html = tinyMCE.regexpReplace(html, "<p>\s*(<p>\s*)?", "<p>");
3737 html = tinyMCE.regexpReplace(html, "<\/p>\s*(<\/p>\s*)?", "</p>");
3740 // Always set the htmlText output
3741 tinyMCE.setInnerHTML(doc.body, html);
3744 tinyMCE.cleanupAnchors(doc);
3746 if (tinyMCE.getParam("convert_fonts_to_spans"))
3747 tinyMCE.convertSpansToFonts(doc);
3750 TinyMCE.prototype.getImageSrc = function(str) {
3756 if ((pos = str.indexOf('this.src=')) != -1) {
3757 var src = str.substring(pos + 10);
3759 src = src.substring(0, src.indexOf('\''));
3767 TinyMCE.prototype._getElementById = function(element_id) {
3768 var elm = document.getElementById(element_id);
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];
3784 TinyMCE.prototype.getEditorId = function(form_element) {
3785 var inst = this.getInstanceById(form_element);
3789 return inst.editorId;
3792 TinyMCE.prototype.getInstanceById = function(editor_id) {
3793 var inst = this.instances[editor_id];
3795 for (var n in tinyMCE.instances) {
3796 var instance = tinyMCE.instances[n];
3797 if (!tinyMCE.isInstance(instance))
3800 if (instance.formTargetElementId == editor_id) {
3810 TinyMCE.prototype.queryInstanceCommandValue = function(editor_id, command) {
3811 var inst = tinyMCE.getInstanceById(editor_id);
3813 return inst.queryCommandValue(command);
3818 TinyMCE.prototype.queryInstanceCommandState = function(editor_id, command) {
3819 var inst = tinyMCE.getInstanceById(editor_id);
3821 return inst.queryCommandState(command);
3826 TinyMCE.prototype.setWindowArg = function(name, value) {
3827 this.windowArgs[name] = value;
3830 TinyMCE.prototype.getWindowArg = function(name, default_value) {
3831 return (typeof(this.windowArgs[name]) == "undefined") ? default_value : this.windowArgs[name];
3834 TinyMCE.prototype.getCSSClasses = function(editor_id, doc) {
3835 var output = new Array();
3837 // Is cached, use that
3838 if (typeof(tinyMCE.cssClasses) != "undefined")
3839 return tinyMCE.cssClasses;
3841 if (typeof(editor_id) == "undefined" && typeof(doc) == "undefined") {
3844 for (var instanceName in tinyMCE.instances) {
3845 instance = tinyMCE.instances[instanceName];
3846 if (!tinyMCE.isInstance(instance))
3852 doc = instance.getDoc();
3855 if (typeof(doc) == "undefined") {
3856 var instance = tinyMCE.getInstanceById(editor_id);
3857 doc = instance.getDoc();
3861 var styles = tinyMCE.isMSIE ? doc.styleSheets : doc.styleSheets;
3863 if (styles && styles.length > 0) {
3864 for (var x=0; x<styles.length; x++) {
3867 // Just ignore any errors
3868 eval("try {var csses = tinyMCE.isMSIE ? doc.styleSheets(" + x + ").rules : doc.styleSheets[" + x + "].cssRules;} catch(e) {}");
3872 for (var i=0; i<csses.length; i++) {
3873 var selectorText = csses[i].selectorText;
3875 // Can be multiple rules per selector
3877 var rules = selectorText.split(',');
3878 for (var c=0; c<rules.length; c++) {
3880 if (rules[c].indexOf(' ') != -1 || rules[c].indexOf(':') != -1 || rules[c].indexOf('mceItem') != -1)
3883 if (rules[c] == "." + tinyMCE.settings['visual_table_class'])
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);
3899 if (output.length > 0)
3900 tinyMCE.cssClasses = output;
3905 TinyMCE.prototype.regexpReplace = function(in_str, reg_exp, replace_str, opts) {
3909 if (typeof(opts) == "undefined")
3912 var re = new RegExp(reg_exp, opts);
3913 return in_str.replace(re, replace_str);
3916 TinyMCE.prototype.trim = function(str) {
3917 return str.replace(/^\s*|\s*$/g, "");
3920 TinyMCE.prototype.cleanupEventStr = function(str) {
3922 str = str.replace('function anonymous()\n{\n', '');
3923 str = str.replace('\n}', '');
3924 str = str.replace(/^return true;/gi, ''); // Remove event blocker
3929 TinyMCE.prototype.getAbsPosition = function(node) {
3930 var pos = new Object();
3932 pos.absLeft = pos.absTop = 0;
3934 var parentNode = node;
3935 while (parentNode) {
3936 pos.absLeft += parentNode.offsetLeft;
3937 pos.absTop += parentNode.offsetTop;
3939 parentNode = parentNode.offsetParent;
3945 TinyMCE.prototype.getControlHTML = function(control_name) {
3946 var themePlugins = tinyMCE.getParam('plugins', '', true, ',');
3947 var templateFunction;
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 + "');");
3955 return tinyMCE.replaceVar(html, "pluginurl", tinyMCE.baseURL + "/plugins/" + themePlugins[i]);
3959 return eval('TinyMCE_' + tinyMCE.settings['theme'] + "_getControlHTML" + "('" + control_name + "');");
3962 TinyMCE.prototype._themeExecCommand = function(editor_id, element, command, user_interface, value) {
3963 var themePlugins = tinyMCE.getParam('plugins', '', true, ',');
3964 var templateFunction;
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);"))
3976 templateFunction = 'TinyMCE_' + tinyMCE.settings['theme'] + "_execCommand";
3977 if (eval("typeof(" + templateFunction + ")") != 'undefined')
3978 return eval(templateFunction + "(editor_id, element, command, user_interface, value);");
3984 TinyMCE.prototype._getThemeFunction = function(suffix, skip_plugins) {
3986 return 'TinyMCE_' + tinyMCE.settings['theme'] + suffix;
3988 var themePlugins = tinyMCE.getParam('plugins', '', true, ',');
3989 var templateFunction;
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;
3998 return 'TinyMCE_' + tinyMCE.settings['theme'] + suffix;
4002 TinyMCE.prototype.isFunc = function(func_name) {
4003 if (func_name == null || func_name == "")
4006 return eval("typeof(" + func_name + ")") != "undefined";
4009 TinyMCE.prototype.exec = function(func_name, args) {
4010 var str = func_name + '(';
4012 // Add all arguments
4013 for (var i=3; i<args.length; i++) {
4014 str += 'args[' + i + ']';
4016 if (i < args.length-1)
4025 TinyMCE.prototype.executeCallback = function(param, suffix, mode) {
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);
4041 // Execute theme callback
4042 var func = 'TinyMCE_' + tinyMCE.settings['theme'] + suffix;
4043 if (tinyMCE.isFunc(func)) {
4044 tinyMCE.exec(func, this.executeCallback.arguments);
4048 // Execute settings callback
4049 var func = tinyMCE.getParam(param, '');
4050 if (tinyMCE.isFunc(func)) {
4051 tinyMCE.exec(func, this.executeCallback.arguments);
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))
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))
4076 // Execute settings callback
4077 var func = tinyMCE.getParam(param, '');
4078 if (tinyMCE.isFunc(func)) {
4079 if (tinyMCE.exec(func, this.executeCallback.arguments))
4087 TinyMCE.prototype.debug = function() {
4090 var elm = document.getElementById("tinymce_debug");
4092 var debugDiv = document.createElement("div");
4093 debugDiv.setAttribute("className", "debugger");
4094 debugDiv.className = "debugger";
4095 debugDiv.innerHTML = '\
4097 <textarea id="tinymce_debug" style="width: 100%; height: 300px" wrap="nowrap"></textarea>';
4099 document.body.appendChild(debugDiv);
4100 elm = document.getElementById("tinymce_debug");
4103 var args = this.debug.arguments;
4104 for (var i=0; i<args.length; i++) {
4106 if (i<args.length-1)
4110 elm.value += msg + "\n";
4114 function TinyMCEControl(settings) {
4116 this.undoLevels = new Array();
4118 this.typingUndoIndex = -1;
4119 this.undoRedo = true;
4120 this.isTinyMCEControl = true;
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);
4129 TinyMCEControl.prototype.repaint = function() {
4130 if (tinyMCE.isMSIE && !tinyMCE.isOpera)
4133 // Ugly mozilla hack to remove ghost resize handles
4135 this.getBody().style.display = 'none';
4136 this.getDoc().execCommand('selectall', false, null);
4137 this.getSel().collapseToStart();
4138 this.getBody().style.display = 'block';
4140 // Could I care less!!
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'];
4151 TinyMCEControl.prototype.convertAllRelativeURLs = function() {
4152 var body = this.getBody();
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');
4159 var msrc = tinyMCE.getAttrib(elms[i], 'mce_src');
4164 src = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'], src);
4165 elms[i].setAttribute("src", src);
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');
4174 var mhref = tinyMCE.getAttrib(elms[i], 'mce_href');
4178 if (href && href != "") {
4179 href = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'], href);
4180 elms[i].setAttribute("href", href);
4185 TinyMCEControl.prototype.getSelectedHTML = function() {
4186 if (tinyMCE.isSafari) {
4187 // Not realy perfect!!
4189 return this.getRng().toString();
4192 var elm = document.createElement("body");
4194 if (tinyMCE.isGecko)
4195 elm.appendChild(this.getRng().cloneContents());
4197 elm.innerHTML = this.getRng().htmlText;
4199 return tinyMCE._cleanupHTML(this, this.contentDocument, this.settings, elm, this.visualAid);
4202 TinyMCEControl.prototype.getBookmark = function() {
4203 var rng = this.getRng();
4205 if (tinyMCE.isSafari)
4211 if (tinyMCE.isGecko)
4212 return rng.cloneRange();
4217 TinyMCEControl.prototype.moveToBookmark = function(bookmark) {
4218 if (tinyMCE.isSafari) {
4219 var sel = this.getSel().realSelection;
4221 sel.setBaseAndExtent(bookmark.startContainer, bookmark.startOffset, bookmark.endContainer, bookmark.endOffset);
4227 return bookmark.select();
4229 if (tinyMCE.isGecko) {
4230 var rng = this.getDoc().createRange();
4231 var sel = this.getSel();
4233 rng.setStart(bookmark.startContainer, bookmark.startOffset);
4234 rng.setEnd(bookmark.endContainer, bookmark.endOffset);
4236 sel.removeAllRanges();
4245 TinyMCEControl.prototype.getSelectedText = function() {
4246 if (tinyMCE.isMSIE) {
4247 var doc = this.getDoc();
4249 if (doc.selection.type == "Text") {
4250 var rng = doc.selection.createRange();
4251 selectedText = rng.text;
4255 var sel = this.getSel();
4257 if (sel && sel.toString)
4258 selectedText = sel.toString();
4263 return selectedText;
4266 TinyMCEControl.prototype.selectNode = function(node, collapse, select_text_node, to_start) {
4270 if (typeof(collapse) == "undefined")
4273 if (typeof(select_text_node) == "undefined")
4274 select_text_node = false;
4276 if (typeof(to_start) == "undefined")
4279 if (tinyMCE.isMSIE) {
4280 var rng = this.getBody().createTextRange();
4283 rng.moveToElementText(node);
4286 rng.collapse(to_start);
4290 // Throws illigal agrument in MSIE some times
4293 var sel = this.getSel();
4298 if (tinyMCE.isSafari) {
4299 sel.realSelection.setBaseAndExtent(node, 0, node, node.innerText.length);
4303 sel.realSelection.collapseToStart();
4305 sel.realSelection.collapseToEnd();
4308 this.scrollToNode(node);
4313 var rng = this.getDoc().createRange();
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]);
4321 rng.selectNodeContents(node);
4323 rng.selectNode(node);
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);
4331 rng.collapse(to_start);
4334 sel.removeAllRanges();
4338 this.scrollToNode(node);
4340 // Set selected element
4341 tinyMCE.selectedElement = null;
4342 if (node.nodeType == 1)
4343 tinyMCE.selectedElement = node;
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;
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);
4359 TinyMCEControl.prototype.getBody = function() {
4360 return this.getDoc().body;
4363 TinyMCEControl.prototype.getDoc = function() {
4364 return this.contentWindow.document;
4367 TinyMCEControl.prototype.getWin = function() {
4368 return this.contentWindow;
4371 TinyMCEControl.prototype.getSel = function() {
4372 if (tinyMCE.isMSIE && !tinyMCE.isOpera)
4373 return this.getDoc().selection;
4375 var sel = this.contentWindow.getSelection();
4378 if (tinyMCE.isSafari && !sel.getRangeAt) {
4379 var newSel = new Object();
4380 var doc = this.getDoc();
4382 function getRangeAt(idx) {
4383 var rng = new Object();
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");
4393 elm.innerHTML = html;
4395 return elm.firstChild;
4398 return doc.createTextNode("UNSUPPORTED, DUE TO LIMITATIONS IN SAFARI!");
4401 rng.deleteContents = function () {
4402 doc.execCommand("Delete", false, "");
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;
4418 newSel.toString = function () {return this.text;};
4426 TinyMCEControl.prototype.getRng = function() {
4427 var sel = this.getSel();
4431 if (tinyMCE.isMSIE && !tinyMCE.isOpera)
4432 return sel.createRange();
4434 if (tinyMCE.isSafari) {
4435 var rng = this.getDoc().createRange();
4436 var sel = this.getSel().realSelection;
4438 rng.setStart(sel.baseNode, sel.baseOffset);
4439 rng.setEnd(sel.extentNode, sel.extentOffset);
4444 return this.getSel().getRangeAt(0);
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() == "";
4454 if (para.getElementsByTagName("img").length > 0)
4458 if (para.getElementsByTagName("table").length > 0)
4462 if (para.getElementsByTagName("hr").length > 0)
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))
4472 // No images, no tables, no hrs, no text content then it's empty
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;
4483 var blockName = "P";
4485 // tinyMCE.debug(body.innerHTML);
4487 // debug(e.target, sel.anchorNode.nodeName, sel.focusNode.nodeName, rng.startContainer, rng.endContainer, rng.commonAncestorContainer, sel.anchorOffset, sel.focusOffset, rng.toString());
4489 // Setup before range
4490 var rngBefore = doc.createRange();
4491 rngBefore.setStart(sel.anchorNode, sel.anchorOffset);
4492 rngBefore.collapse(true);
4494 // Setup after range
4495 var rngAfter = doc.createRange();
4496 rngAfter.setStart(sel.focusNode, sel.focusOffset);
4497 rngAfter.collapse(true);
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;
4506 startNode = startNode.nodeName == "BODY" ? startNode.firstChild : startNode;
4507 endNode = endNode.nodeName == "BODY" ? endNode.firstChild : endNode;
4509 // tinyMCE.debug(startNode, endNode);
4511 // Get block elements
4512 var startBlock = tinyMCE.getParentBlockElement(startNode);
4513 var endBlock = tinyMCE.getParentBlockElement(endNode);
4515 // Use current block name
4516 if (startBlock != null) {
4517 blockName = startBlock.nodeName;
4520 if (blockName == "TD" || blockName == "TABLE" || (blockName == "DIV" && new RegExp('left|right', 'gi').test(startBlock.style.cssFloat)))
4524 // Within a list use normal behaviour
4525 if (tinyMCE.getParentElement(startBlock, "OL,UL") != null)
4528 // Within a table create new paragraphs
4529 if ((startBlock != null && startBlock.nodeName == "TABLE") || (endBlock != null && endBlock.nodeName == "TABLE"))
4530 startBlock = endBlock = null;
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);
4536 // Is header, then force paragraph under
4537 if (/^(H[1-6])$/.test(blockName))
4538 paraAfter = doc.createElement("p");
4541 var startChop = startNode;
4542 var endChop = endNode;
4544 // Get startChop node
4547 if (node == body || node.nodeType == 9 || tinyMCE.isBlockElement(node))
4551 } while ((node = node.previousSibling ? node.previousSibling : node.parentNode));
4556 if (node == body || node.nodeType == 9 || tinyMCE.isBlockElement(node))
4560 } while ((node = node.nextSibling ? node.nextSibling : node.parentNode));
4562 // Fix when only a image is within the TD
4563 if (startChop.nodeName == "TD")
4564 startChop = startChop.firstChild;
4566 if (endChop.nodeName == "TD")
4567 endChop = endChop.lastChild;
4569 // If not in a block element
4570 if (startBlock == null) {
4572 rng.deleteContents();
4573 sel.removeAllRanges();
4575 if (startChop != rootElm && endChop != rootElm) {
4576 // Insert paragraph before
4577 rngBefore = rng.cloneRange();
4579 if (startChop == body)
4580 rngBefore.setStart(startChop, 0);
4582 rngBefore.setStartBefore(startChop);
4584 paraBefore.appendChild(rngBefore.cloneContents());
4586 // Insert paragraph after
4587 if (endChop.parentNode.nodeName == blockName)
4588 endChop = endChop.parentNode;
4590 // If not after image
4591 //if (rng.startContainer.nodeName != "BODY" && rng.endContainer.nodeName != "BODY")
4592 rng.setEndAfter(endChop);
4594 if (endChop.nodeName != "#text" && endChop.nodeName != "BODY")
4595 rngBefore.setEndAfter(endChop);
4597 var contents = rng.cloneContents();
4598 if (contents.firstChild && (contents.firstChild.nodeName == blockName || contents.firstChild.nodeName == "BODY"))
4599 paraAfter.innerHTML = contents.firstChild.innerHTML;
4601 paraAfter.appendChild(contents);
4603 // Check if it's a empty paragraph
4604 if (isEmpty(paraBefore))
4605 paraBefore.innerHTML = " ";
4607 // Check if it's a empty paragraph
4608 if (isEmpty(paraAfter))
4609 paraAfter.innerHTML = " ";
4611 // Delete old contents
4612 rng.deleteContents();
4613 rngAfter.deleteContents();
4614 rngBefore.deleteContents();
4616 // Insert new paragraphs
4617 paraAfter.normalize();
4618 rngBefore.insertNode(paraAfter);
4619 paraBefore.normalize();
4620 rngBefore.insertNode(paraBefore);
4622 // tinyMCE.debug("1: ", paraBefore.innerHTML, paraAfter.innerHTML);
4624 body.innerHTML = "<" + blockName + "> </" + blockName + "><" + blockName + "> </" + blockName + ">";
4625 paraAfter = body.childNodes[1];
4628 this.selectNode(paraAfter, true, true);
4633 // Place first part within new paragraph
4634 if (startChop.nodeName == blockName)
4635 rngBefore.setStart(startChop, 0);
4637 rngBefore.setStartBefore(startChop);
4639 rngBefore.setEnd(startNode, startOffset);
4640 paraBefore.appendChild(rngBefore.cloneContents());
4642 // Place secound part within new paragraph
4643 rngAfter.setEndAfter(endChop);
4644 rngAfter.setStart(endNode, endOffset);
4645 var contents = rngAfter.cloneContents();
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]);
4655 paraAfter.innerHTML = contents.firstChild.innerHTML;
4657 paraAfter.appendChild(contents);
4659 // Check if it's a empty paragraph
4660 if (isEmpty(paraBefore))
4661 paraBefore.innerHTML = " ";
4663 // Check if it's a empty paragraph
4664 if (isEmpty(paraAfter))
4665 paraAfter.innerHTML = " ";
4667 // Create a range around everything
4668 var rng = doc.createRange();
4670 if (!startChop.previousSibling && startChop.parentNode.nodeName.toUpperCase() == blockName) {
4671 rng.setStartBefore(startChop.parentNode);
4673 if (rngBefore.startContainer.nodeName.toUpperCase() == blockName && rngBefore.startOffset == 0)
4674 rng.setStartBefore(rngBefore.startContainer);
4676 rng.setStart(rngBefore.startContainer, rngBefore.startOffset);
4679 if (!endChop.nextSibling && endChop.parentNode.nodeName.toUpperCase() == blockName)
4680 rng.setEndAfter(endChop.parentNode);
4682 rng.setEnd(rngAfter.endContainer, rngAfter.endOffset);
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);
4691 paraAfter.normalize();
4692 paraBefore.normalize();
4694 this.selectNode(paraAfter, true, true);
4699 TinyMCEControl.prototype._handleBackSpace = function(evt_type) {
4700 var doc = this.getDoc();
4701 var sel = this.getSel();
4705 var rng = sel.getRangeAt(0);
4706 var node = rng.startContainer;
4707 var elm = node.nodeType == 3 ? node.parentNode : node;
4712 // Empty node, wrap contents in paragraph
4713 if (elm && elm.nodeName == "") {
4714 var para = doc.createElement("p");
4716 while (elm.firstChild)
4717 para.appendChild(elm.firstChild);
4719 elm.parentNode.insertBefore(para, elm);
4720 elm.parentNode.removeChild(elm);
4722 var rng = rng.cloneRange();
4723 rng.setStartBefore(node.nextSibling);
4724 rng.setEndAfter(node.nextSibling);
4725 rng.extractContents();
4727 this.selectNode(node.nextSibling, true, true);
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);
4736 // Empty node, we do the killing!!
4737 if (htm == "" || htm == " " || block.nodeName.toLowerCase() == "li") {
4738 var prevElm = para.previousSibling;
4740 while (prevElm != null && prevElm.nodeType != 1)
4741 prevElm = prevElm.previousSibling;
4743 if (prevElm == null)
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];
4750 // Select the last text node and move curstor to end
4751 if (lastTextNode != null)
4752 this.selectNode(lastTextNode, true, false, false);
4754 // Remove the empty paragrapsh
4755 para.parentNode.removeChild(para);
4757 //debug("within p element" + para.innerHTML);
4758 //showHTML(this.getBody().innerHTML);
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
4771 //showHTML(this.getBody().innerHTML);
4776 TinyMCEControl.prototype._insertSpace = function() {
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) {}');
4786 TinyMCEControl.prototype.isHidden = function() {
4790 var sel = this.getSel();
4792 // Weird, wheres that cursor selection?
4793 return (!sel || !sel.rangeCount || sel.rangeCount == 0);
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;
4801 TinyMCEControl.prototype._mergeElements = function(scmd, pa, ch, override) {
4802 if (scmd == "removeformat") {
4804 pa.style.cssText = "";
4806 ch.style.cssText = "";
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");
4814 className += " " + tinyMCE.getAttrib(ch, "class");
4818 if (typeof(st[n]) == 'function')
4824 for (var n in stc) {
4825 if (typeof(stc[n]) == 'function')
4832 tinyMCE.setAttrib(pa, "style", tinyMCE.serializeStyle(st));
4833 tinyMCE.setAttrib(pa, "class", tinyMCE.trim(className));
4835 ch.style.cssText = "";
4836 ch.removeAttribute("class");
4837 ch.removeAttribute("style");
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) {}
4845 if (!tinyMCE.getParam("table_inline_editing"))
4846 try {doc.execCommand('enableInlineTableEditing', false, "false");} catch (ex) {}
4848 if (!tinyMCE.getParam("object_resizing"))
4849 try {doc.execCommand('enableObjectResizing', false, "false");} catch (ex) {}
4852 TinyMCEControl.prototype.execCommand = function(command, user_interface, value) {
4853 var doc = this.getDoc();
4854 var win = this.getWin();
4855 var focusElm = this.getFocusElement();
4857 if (this.lastSafariSelection && !new RegExp('mceStartTyping|mceEndTyping|mceBeginUndoLevel|mceEndUndoLevel|mceAddUndoLevel', 'gi').test(command)) {
4858 this.moveToBookmark(this.lastSafariSelection);
4859 tinyMCE.selectedElement = this.lastSafariSelectedElement;
4863 if (!tinyMCE.isMSIE && !this.useCSS) {
4864 this.setUseCSS(false);
4868 //debug("command: " + command + ", user_interface: " + user_interface + ", value: " + value);
4869 this.contentDocument = doc; // <-- Strange, unless this is applied Mozilla 1.3 breaks
4871 // Call theme execcommand
4872 if (tinyMCE._themeExecCommand(this.editorId, this.getBody(), command, user_interface, value))
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;
4882 if (align == 'left')
4883 img.removeAttribute('align');
4885 img.setAttribute('align', 'left');
4888 var div = focusElm.parentNode;
4889 if (div && div.nodeName == "DIV" && div.childNodes.length == 1 && div.parentNode)
4890 div.parentNode.replaceChild(img, div);
4892 this.selectNode(img);
4894 tinyMCE.triggerNodeChange();
4897 case "JustifyCenter":
4898 img.removeAttribute('align');
4901 var div = tinyMCE.getParentElement(focusElm, "div");
4902 if (div && div.style.textAlign == "center") {
4904 if (div.nodeName == "DIV" && div.childNodes.length == 1 && div.parentNode)
4905 div.parentNode.replaceChild(img, div);
4908 var div = this.getDoc().createElement("div");
4909 div.style.textAlign = 'center';
4910 div.appendChild(img);
4911 focusElm.parentNode.replaceChild(div, focusElm);
4914 this.selectNode(img);
4916 tinyMCE.triggerNodeChange();
4919 case "JustifyRight":
4920 if (align == 'right')
4921 img.removeAttribute('align');
4923 img.setAttribute('align', 'right');
4926 var div = focusElm.parentNode;
4927 if (div && div.nodeName == "DIV" && div.childNodes.length == 1 && div.parentNode)
4928 div.parentNode.replaceChild(img, div);
4930 this.selectNode(img);
4932 tinyMCE.triggerNodeChange();
4937 if (tinyMCE.settings['force_br_newlines']) {
4938 var alignValue = "";
4940 if (doc.selection.type != "Control") {
4943 alignValue = "left";
4946 case "JustifyCenter":
4947 alignValue = "center";
4951 alignValue = "justify";
4954 case "JustifyRight":
4955 alignValue = "right";
4959 if (alignValue != "") {
4960 var rng = doc.selection.createRange();
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>");
4967 tinyMCE.triggerNodeChange();
4978 case "mceStoreSelection":
4979 this.selectionBookmark = this.getBookmark();
4982 case "mceRestoreSelection":
4983 this.moveToBookmark(this.selectionBookmark);
4986 case "InsertUnorderedList":
4987 case "InsertOrderedList":
4988 var tag = (command == "InsertUnorderedList") ? "ul" : "ol";
4990 if (tinyMCE.isSafari)
4991 this.execCommand("mceInsertContent", false, "<" + tag + "><li> </li><" + tag + ">");
4993 this.getDoc().execCommand(command, user_interface, value);
4995 tinyMCE.triggerNodeChange();
4998 case "Strikethrough":
4999 if (tinyMCE.isSafari)
5000 this.execCommand("mceInsertContent", false, "<strike>" + this.getSelectedHTML() + "</strike>");
5002 this.getDoc().execCommand(command, user_interface, value);
5004 tinyMCE.triggerNodeChange();
5007 case "mceSelectNode":
5008 this.selectNode(value);
5009 tinyMCE.triggerNodeChange();
5010 tinyMCE.selectedNode = value;
5014 if (value == null || value == "") {
5015 var elm = tinyMCE.getParentElement(this.getFocusElement(), "p,div,h1,h2,h3,h4,h5,h6,pre,address");
5018 this.execCommand("mceRemoveNode", false, elm);
5020 this.getDoc().execCommand("FormatBlock", false, value);
5022 tinyMCE.triggerNodeChange();
5026 case "mceRemoveNode":
5028 value = tinyMCE.getParentElement(this.getFocusElement());
5030 if (tinyMCE.isMSIE) {
5031 value.outerHTML = value.innerHTML;
5033 var rng = value.ownerDocument.createRange();
5034 rng.setStartBefore(value);
5035 rng.setEndAfter(value);
5036 rng.deleteContents();
5037 rng.insertNode(rng.createContextualFragment(value.innerHTML));
5040 tinyMCE.triggerNodeChange();
5044 case "mceSelectNodeDepth":
5045 var parentNode = this.getFocusElement();
5046 for (var i=0; parentNode; i++) {
5047 if (parentNode.nodeName.toLowerCase() == "body")
5050 if (parentNode.nodeName.toLowerCase() == "#text") {
5052 parentNode = parentNode.parentNode;
5057 this.selectNode(parentNode, false);
5058 tinyMCE.triggerNodeChange();
5059 tinyMCE.selectedNode = parentNode;
5063 parentNode = parentNode.parentNode;
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;
5080 // Whole element selected check
5081 if (tinyMCE.isMSIE) {
5084 parentElm = rng.item(0);
5086 var pelm = rng.parentElement();
5087 var prng = doc.selection.createRange();
5088 prng.moveToElementText(pelm);
5090 if (rng.htmlText == prng.htmlText || rng.boundingWidth == 0) {
5091 if (invalidParentsRe == null || !invalidParentsRe.test(pelm.nodeName))
5096 var felm = this.getFocusElement();
5097 if (sel.isCollapsed || (/td|tr|tbody|table/ig.test(felm.nodeName) && sel.anchorNode == felm.parentNode))
5101 // Whole element selected
5102 if (parentElm && !invalidRe.test(parentElm.nodeName)) {
5103 if (scmd == "setstyle")
5104 tinyMCE.setStyleAttrib(parentElm, sname, svalue);
5106 if (scmd == "setattrib")
5107 tinyMCE.setAttrib(parentElm, sname, svalue);
5109 if (scmd == "removeformat") {
5110 parentElm.style.cssText = '';
5111 tinyMCE.setAttrib(parentElm, 'class', '');
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)
5120 if (scmd == "setstyle")
5121 tinyMCE.setStyleAttrib(ch[z], sname, '');
5123 if (scmd == "setattrib")
5124 tinyMCE.setAttrib(ch[z], sname, '');
5126 if (scmd == "removeformat") {
5127 ch[z].style.cssText = '';
5128 tinyMCE.setAttrib(ch[z], 'class', '');
5132 doc.execCommand("fontname", false, "#mce_temp_font#");
5133 var elementArray = tinyMCE.getElementsByAttributeValue(this.getBody(), "font", "face", "#mce_temp_font#");
5136 for (var x=0; x<elementArray.length; x++) {
5137 elm = elementArray[x];
5139 var spanElm = doc.createElement(wrapper);
5141 if (scmd == "setstyle")
5142 tinyMCE.setStyleAttrib(spanElm, sname, svalue);
5144 if (scmd == "setattrib")
5145 tinyMCE.setAttrib(spanElm, sname, svalue);
5147 if (scmd == "removeformat") {
5148 spanElm.style.cssText = '';
5149 tinyMCE.setAttrib(spanElm, 'class', '');
5152 if (elm.hasChildNodes()) {
5153 for (var i=0; i<elm.childNodes.length; i++)
5154 spanElm.appendChild(elm.childNodes[i].cloneNode(true));
5157 spanElm.setAttribute("mce_new", "true");
5158 elm.parentNode.replaceChild(spanElm, elm);
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)
5166 if (scmd == "setstyle")
5167 tinyMCE.setStyleAttrib(ch[z], sname, '');
5169 if (scmd == "setattrib")
5170 tinyMCE.setAttrib(ch[z], sname, '');
5172 if (scmd == "removeformat") {
5173 ch[z].style.cssText = '';
5174 tinyMCE.setAttrib(ch[z], 'class', '');
5182 var nodes = doc.getElementsByTagName(wrapper);
5183 for (var i=nodes.length-1; i>=0; i--) {
5185 var isNew = tinyMCE.getAttrib(elm, "mce_new") == "true";
5187 elm.removeAttribute("mce_new");
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);
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);
5204 // Remove empty wrappers
5205 var nodes = doc.getElementsByTagName(wrapper);
5206 for (var i=nodes.length-1; i>=0; i--) {
5210 // Check if it has any attribs
5211 var tmp = doc.createElement("body");
5212 tmp.appendChild(elm.cloneNode(false));
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);
5223 elm.parentNode.removeChild(elm);
5227 // Re add the visual aids
5228 if (scmd == "removeformat")
5229 tinyMCE.handleVisualAid(this.getBody(), true, this.visualAid, this);
5231 tinyMCE.triggerNodeChange();
5236 if (value == null) {
5237 var s = this.getSel();
5239 // Find font and select it
5240 if (tinyMCE.isGecko && s.isCollapsed) {
5241 var f = tinyMCE.getParentElement(this.getFocusElement(), "font");
5244 this.selectNode(f, false);
5248 this.getDoc().execCommand("RemoveFormat", false, null);
5250 // Collapse range if font was found
5251 if (f != null && tinyMCE.isGecko) {
5252 var r = this.getRng().cloneRange();
5254 s.removeAllRanges();
5258 this.getDoc().execCommand('FontName', false, value);
5260 if (tinyMCE.isGecko)
5261 window.setTimeout('tinyMCE.triggerNodeChange(false);', 1);
5266 this.getDoc().execCommand('FontSize', false, value);
5268 if (tinyMCE.isGecko)
5269 window.setTimeout('tinyMCE.triggerNodeChange(false);', 1);
5274 this.getDoc().execCommand('forecolor', false, value);
5278 if (tinyMCE.isGecko) {
5279 this.setUseCSS(true);
5280 this.getDoc().execCommand('hilitecolor', false, value);
5281 this.setUseCSS(false);
5283 this.getDoc().execCommand('BackColor', false, value);
5289 var cmdFailed = false;
5291 // Try executing command
5292 eval('try {this.getDoc().execCommand(command, user_interface, value);} catch (e) {cmdFailed = true;}');
5294 if (tinyMCE.isOpera && cmdFailed)
5295 alert('Currently not supported by your browser, use keyboard shortcuts instead.');
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');
5305 tinyMCE.triggerNodeChange();
5308 case "mceSetContent":
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);
5323 var selectedText = "";
5325 if (tinyMCE.isMSIE) {
5326 var rng = doc.selection.createRange();
5327 selectedText = rng.text;
5329 selectedText = this.getSel().toString();
5331 if (!tinyMCE.linkElement) {
5332 if ((tinyMCE.selectedElement.nodeName.toLowerCase() != "img") && (selectedText.length <= 0))
5336 var href = "", target = "", title = "", onclick = "", action = "insert", style_class = "";
5338 if (tinyMCE.selectedElement.nodeName.toLowerCase() == "a")
5339 tinyMCE.linkElement = tinyMCE.selectedElement;
5341 // Is anchor not a link
5342 if (tinyMCE.linkElement != null && tinyMCE.getAttrib(tinyMCE.linkElement, 'href') == "")
5343 tinyMCE.linkElement = null;
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');
5352 // Try old onclick to if copy/pasted content
5354 onclick = tinyMCE.getAttrib(tinyMCE.linkElement, 'onclick');
5356 onclick = tinyMCE.cleanupEventStr(onclick);
5358 href = eval(tinyMCE.settings['urlconverter_callback'] + "(href, tinyMCE.linkElement, true);");
5360 // Use mce_href if defined
5361 mceRealHref = tinyMCE.getAttrib(tinyMCE.linkElement, 'mce_href');
5362 if (mceRealHref != "") {
5365 if (tinyMCE.getParam('convert_urls'))
5366 href = eval(tinyMCE.settings['urlconverter_callback'] + "(href, tinyMCE.linkElement, true);");
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']);
5377 tinyMCE.openWindow(this.insertLinkTemplate, {href : href, target : target, title : title, onclick : onclick, action : action, className : style_class, inline : "yes"});
5382 var src = "", alt = "", border = "", hspace = "", vspace = "", width = "", height = "", align = "";
5383 var title = "", onmouseover = "", onmouseout = "", action = "insert";
5384 var img = tinyMCE.imgElement;
5386 if (tinyMCE.selectedElement != null && tinyMCE.selectedElement.nodeName.toLowerCase() == "img") {
5387 img = tinyMCE.selectedElement;
5388 tinyMCE.imgElement = img;
5392 // Is it a internal MCE visual aid image, then skip this one.
5393 if (tinyMCE.getAttrib(img, 'name').indexOf('mce_') == 0)
5396 src = tinyMCE.getAttrib(img, 'src');
5397 alt = tinyMCE.getAttrib(img, 'alt');
5399 // Try polling out the title
5401 alt = tinyMCE.getAttrib(img, 'title');
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);
5409 var h = img.style.height;
5410 if (h != null && h != "")
5411 img.setAttribute("height", h);
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');
5424 // Is realy specified?
5425 if (tinyMCE.isMSIE) {
5426 width = img.attributes['width'].specified ? width : "";
5427 height = img.attributes['height'].specified ? height : "";
5430 onmouseover = tinyMCE.getImageSrc(tinyMCE.cleanupEventStr(onmouseover));
5431 onmouseout = tinyMCE.getImageSrc(tinyMCE.cleanupEventStr(onmouseout));
5433 src = eval(tinyMCE.settings['urlconverter_callback'] + "(src, img, true);");
5435 // Use mce_src if defined
5436 mceRealSrc = tinyMCE.getAttrib(img, 'mce_src');
5437 if (mceRealSrc != "") {
5440 if (tinyMCE.getParam('convert_urls'))
5441 src = eval(tinyMCE.settings['urlconverter_callback'] + "(src, img, true);");
5444 if (onmouseover != "")
5445 onmouseover = eval(tinyMCE.settings['urlconverter_callback'] + "(onmouseover, img, true);");
5447 if (onmouseout != "")
5448 onmouseout = eval(tinyMCE.settings['urlconverter_callback'] + "(onmouseout, img, true);");
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']);
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"});
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);
5468 tinyMCE.triggerNodeChange();
5471 case "mceReplaceContent":
5472 this.getWin().focus();
5474 var selectedText = "";
5476 if (tinyMCE.isMSIE) {
5477 var rng = doc.selection.createRange();
5478 selectedText = rng.text;
5480 selectedText = this.getSel().toString();
5482 if (selectedText.length > 0) {
5483 value = tinyMCE.replaceVar(value, "selection", selectedText);
5484 tinyMCE.execCommand('mceInsertContent', false, value);
5487 tinyMCE.triggerNodeChange();
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);
5496 targetNode.setAttribute(value['name'], value['value']);
5497 tinyMCE.triggerNodeChange();
5502 case "mceSetCSSClass":
5503 this.execCommand("SetStyleInfo", false, {command : "setattrib", name : "class", value : value});
5506 case "mceInsertRawHTML":
5507 var key = 'tiny_mce_marker';
5509 this.execCommand('mceBeginUndoLevel');
5511 // Insert marker key
5512 this.execCommand('mceInsertContent', false, key);
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;
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));
5523 // Restore scoll pos
5524 this.contentWindow.scrollTo(scrollX, scrollY);
5526 this.execCommand('mceEndUndoLevel');
5530 case "mceInsertContent":
5531 var insertHTMLFailed = false;
5532 this.getWin().focus();
5534 if (tinyMCE.isGecko || tinyMCE.isOpera) {
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();
5543 // Insert text at cursor position
5544 s.removeAllRanges();
5548 // Move the cursor to the end of text
5551 s.removeAllRanges();
5554 value = tinyMCE.fixGeckoBaseHREFBug(1, this.getDoc(), value);
5555 this.getDoc().execCommand('inserthtml', false, value);
5556 tinyMCE.fixGeckoBaseHREFBug(2, this.getDoc(), value);
5559 insertHTMLFailed = true;
5562 if (!insertHTMLFailed) {
5563 tinyMCE.triggerNodeChange();
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;
5576 if (!tinyMCE.isMSIE) {
5577 var isHTML = value.indexOf('<') != -1;
5578 var sel = this.getSel();
5579 var rng = this.getRng();
5582 if (tinyMCE.isSafari) {
5583 var tmpRng = this.getDoc().createRange();
5585 tmpRng.setStart(this.getBody(), 0);
5586 tmpRng.setEnd(this.getBody(), 0);
5588 value = tmpRng.createContextualFragment(value);
5590 value = rng.createContextualFragment(value);
5593 var el = document.createElement("div");
5594 el.innerHTML = value;
5595 value = el.firstChild.nodeValue;
5596 value = doc.createTextNode(value);
5599 // Insert plain text in Safari
5600 if (tinyMCE.isSafari && !isHTML) {
5601 this.execCommand('InsertText', false, value.nodeValue);
5602 tinyMCE.triggerNodeChange();
5604 } else if (tinyMCE.isSafari && isHTML) {
5605 rng.deleteContents();
5606 rng.insertNode(value);
5607 tinyMCE.triggerNodeChange();
5611 rng.deleteContents();
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);
5618 rng.insertNode(value);
5621 // Removes weird selection trails
5622 sel.selectAllChildren(doc.body);
5623 sel.removeAllRanges();
5625 // Move cursor to end of content
5626 var rng = doc.createRange();
5628 rng.selectNode(value);
5629 rng.collapse(false);
5633 rng.collapse(false);
5635 var rng = doc.selection.createRange();
5636 var c = value.indexOf('<!--') != -1;
5638 // Fix comment bug, add tag before comments
5640 value = tinyMCE.uniqueTag + value;
5643 rng.item(0).outerHTML = value;
5645 rng.pasteHTML(value);
5647 // Remove unique tag
5649 var e = this.getDoc().getElementById('mceTMPElement');
5650 e.parentNode.removeChild(e);
5654 tinyMCE.triggerNodeChange();
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");
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");
5673 case "mceBeginUndoLevel":
5674 this.undoRedo = false;
5677 case "mceEndUndoLevel":
5678 this.undoRedo = true;
5679 this.execCommand('mceAddUndoLevel');
5682 case "mceAddUndoLevel":
5683 if (tinyMCE.settings['custom_undo_redo'] && this.undoRedo) {
5684 // tinyMCE.debug("add level");
5686 if (this.typingUndoIndex != -1) {
5687 this.undoIndex = this.typingUndoIndex;
5688 // tinyMCE.debug("Override: " + this.undoIndex);
5691 var newHTML = tinyMCE.trim(this.getBody().innerHTML);
5692 if (newHTML != this.undoLevels[this.undoIndex]) {
5693 tinyMCE.executeCallback('onchange_callback', '_onchange', 0, this);
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];
5703 this.undoLevels.length--;
5708 this.undoLevels[this.undoIndex] = newHTML;
5709 this.undoLevels.length = this.undoIndex + 1;
5711 // tinyMCE.debug("level added" + this.undoIndex);
5712 tinyMCE.triggerNodeChange(false);
5714 // tinyMCE.debug(this.undoIndex + "," + (this.undoLevels.length-1));
5720 if (tinyMCE.settings['custom_undo_redo']) {
5721 tinyMCE.execCommand("mceEndTyping");
5724 if (this.undoIndex > 0) {
5726 tinyMCE.setInnerHTML(this.getBody(), this.undoLevels[this.undoIndex]);
5730 // tinyMCE.debug("Undo - undo levels:" + this.undoLevels.length + ", undo index: " + this.undoIndex);
5731 tinyMCE.triggerNodeChange();
5733 this.getDoc().execCommand(command, user_interface, value);
5737 if (tinyMCE.settings['custom_undo_redo']) {
5738 tinyMCE.execCommand("mceEndTyping");
5740 if (this.undoIndex < (this.undoLevels.length-1)) {
5742 tinyMCE.setInnerHTML(this.getBody(), this.undoLevels[this.undoIndex]);
5744 // tinyMCE.debug("Redo - undo levels:" + this.undoLevels.length + ", undo index: " + this.undoIndex);
5747 tinyMCE.triggerNodeChange();
5749 this.getDoc().execCommand(command, user_interface, value);
5752 case "mceToggleVisualAid":
5753 this.visualAid = !this.visualAid;
5754 tinyMCE.handleVisualAid(this.getBody(), true, this.visualAid, this);
5755 tinyMCE.triggerNodeChange();
5759 this.getDoc().execCommand(command, user_interface, value);
5760 tinyMCE.triggerNodeChange();
5761 if (tinyMCE.isMSIE) {
5762 var n = tinyMCE.getParentElement(this.getFocusElement(), "blockquote");
5764 if (n && n.nodeName == "BLOCKQUOTE") {
5765 n.removeAttribute("dir");
5766 n.removeAttribute("style");
5768 } while (n != null && (n = n.parentNode) != null);
5772 case "removeformat":
5773 var text = this.getSelectedText();
5775 if (tinyMCE.isOpera) {
5776 this.getDoc().execCommand("RemoveFormat", false, null);
5780 if (tinyMCE.isMSIE) {
5782 var rng = doc.selection.createRange();
5783 rng.execCommand("RemoveFormat", false, null);
5788 this.execCommand("SetStyleInfo", false, {command : "removeformat"});
5790 this.getDoc().execCommand(command, user_interface, value);
5792 this.execCommand("SetStyleInfo", false, {command : "removeformat"});
5796 if (text.length == 0)
5797 this.execCommand("mceSetCSSClass", false, "");
5799 tinyMCE.triggerNodeChange();
5803 this.getDoc().execCommand(command, user_interface, value);
5805 if (tinyMCE.isGecko)
5806 window.setTimeout('tinyMCE.triggerNodeChange(false);', 1);
5808 tinyMCE.triggerNodeChange();
5811 // Add undo level after modification
5812 if (command != "mceAddUndoLevel" && command != "Undo" && command != "Redo" && command != "mceStartTyping" && command != "mceEndTyping")
5813 tinyMCE.execCommand("mceAddUndoLevel");
5816 TinyMCEControl.prototype.queryCommandValue = function(command) {
5818 return this.getDoc().queryCommandValue(command);
5824 TinyMCEControl.prototype.queryCommandState = function(command) {
5825 return this.getDoc().queryCommandState(command);
5828 TinyMCEControl.prototype.onAdd = function(replace_element, form_element_name, target_document) {
5829 var targetDoc = target_document ? target_document : document;
5831 this.targetDoc = targetDoc;
5833 tinyMCE.themeURL = tinyMCE.baseURL + "/themes/" + this.settings['theme'];
5834 this.settings['themeurl'] = tinyMCE.themeURL;
5836 if (!replace_element) {
5837 alert("Error: Could not find the target element.");
5841 var templateFunction = tinyMCE._getThemeFunction('_getInsertLinkTemplate');
5842 if (eval("typeof(" + templateFunction + ")") != 'undefined')
5843 this.insertLinkTemplate = eval(templateFunction + '(this.settings);');
5845 var templateFunction = tinyMCE._getThemeFunction('_getInsertImageTemplate');
5846 if (eval("typeof(" + templateFunction + ")") != 'undefined')
5847 this.insertImageTemplate = eval(templateFunction + '(this.settings);');
5849 var templateFunction = tinyMCE._getThemeFunction('_getEditorTemplate');
5850 if (eval("typeof(" + templateFunction + ")") == 'undefined') {
5851 alert("Error: Could not find the template function: " + templateFunction);
5855 var editorTemplate = eval(templateFunction + '(this.settings, this.editorId);');
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'];
5861 var templateFunction = tinyMCE._getThemeFunction('_handleNodeChange', true);
5862 if (eval("typeof(" + templateFunction + ")") != 'undefined')
5863 this.settings['handleNodeChangeCallback'] = templateFunction;
5865 html = tinyMCE.replaceVar(html, "editor_id", this.editorId);
5866 this.settings['default_document'] = tinyMCE.baseURL + "/blank.htm";
5868 this.settings['old_width'] = this.settings['width'];
5869 this.settings['old_height'] = this.settings['height'];
5871 // Set default width, height
5872 if (this.settings['width'] == -1)
5873 this.settings['width'] = replace_element.offsetWidth;
5875 if (this.settings['height'] == -1)
5876 this.settings['height'] = replace_element.offsetHeight;
5878 // Try the style width
5879 if (this.settings['width'] == 0)
5880 this.settings['width'] = replace_element.style.width;
5882 // Try the style height
5883 if (this.settings['height'] == 0)
5884 this.settings['height'] = replace_element.style.height;
5886 // If no width/height then default to 320x240, better than nothing
5887 if (this.settings['width'] == 0)
5888 this.settings['width'] = 320;
5890 if (this.settings['height'] == 0)
5891 this.settings['height'] = 240;
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;
5898 // Special % handling
5899 if (("" + this.settings['width']).indexOf('%') != -1)
5900 this.settings['area_width'] = "100%";
5902 if (("" + this.settings['height']).indexOf('%') != -1)
5903 this.settings['area_height'] = "100%";
5905 if (("" + replace_element.style.width).indexOf('%') != -1) {
5906 this.settings['width'] = replace_element.style.width;
5907 this.settings['area_width'] = "100%";
5910 if (("" + replace_element.style.height).indexOf('%') != -1) {
5911 this.settings['height'] = replace_element.style.height;
5912 this.settings['area_height'] = "100%";
5915 html = tinyMCE.applyTemplate(html);
5917 this.settings['width'] = this.settings['old_width'];
5918 this.settings['height'] = this.settings['old_height'];
5920 this.visualAid = this.settings['visual'];
5921 this.formTargetElementId = form_element_name;
5923 // Get replace_element contents
5924 if (replace_element.nodeName == "TEXTAREA" || replace_element.nodeName == "INPUT")
5925 this.startContent = replace_element.value;
5927 this.startContent = replace_element.innerHTML;
5930 if (replace_element.nodeName.toLowerCase() != "textarea") {
5931 this.oldTargetElement = replace_element.cloneNode(true);
5934 if (tinyMCE.settings['debug'])
5935 html += '<textarea wrap="off" id="' + form_element_name + '" name="' + form_element_name + '" cols="100" rows="15"></textarea>';
5937 html += '<input type="hidden" type="text" id="' + form_element_name + '" name="' + form_element_name + '" />';
5941 // Output HTML and set editable
5942 if (!tinyMCE.isMSIE) {
5943 var rng = replace_element.ownerDocument.createRange();
5944 rng.setStartBefore(replace_element);
5946 var fragment = rng.createContextualFragment(html);
5947 replace_element.parentNode.replaceChild(fragment, replace_element);
5949 replace_element.outerHTML = html;
5953 // Just hide the textarea element
5954 this.oldTargetElement = replace_element;
5956 if (!tinyMCE.settings['debug'])
5957 this.oldTargetElement.style.display = "none";
5959 // Output HTML and set editable
5960 if (!tinyMCE.isMSIE) {
5961 var rng = replace_element.ownerDocument.createRange();
5962 rng.setStartBefore(replace_element);
5964 var fragment = rng.createContextualFragment(html);
5966 if (tinyMCE.isGecko)
5967 tinyMCE.insertAfter(fragment, replace_element);
5969 replace_element.parentNode.insertBefore(fragment, replace_element);
5971 replace_element.insertAdjacentHTML("beforeBegin", html);
5975 var dynamicIFrame = false;
5976 var tElm = targetDoc.getElementById(this.editorId);
5978 if (!tinyMCE.isMSIE) {
5979 if (tElm && tElm.nodeName.toLowerCase() == "span") {
5980 tElm = tinyMCE._createIFrame(tElm);
5981 dynamicIFrame = true;
5984 this.targetElement = tElm;
5985 this.iframeElement = tElm;
5986 this.contentDocument = tElm.contentDocument;
5987 this.contentWindow = tElm.contentWindow;
5989 //this.getDoc().designMode = "on";
5991 if (tElm && tElm.nodeName.toLowerCase() == "span")
5992 tElm = tinyMCE._createIFrame(tElm);
5994 tElm = targetDoc.frames[this.editorId];
5996 this.targetElement = tElm;
5997 this.iframeElement = targetDoc.getElementById(this.editorId);
5999 if (tinyMCE.isOpera) {
6000 this.contentDocument = this.iframeElement.contentDocument;
6001 this.contentWindow = this.iframeElement.contentWindow;
6002 dynamicIFrame = true;
6004 this.contentDocument = tElm.window.document;
6005 this.contentWindow = tElm.window;
6008 this.getDoc().designMode = "on";
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>';
6017 if (!this.isHidden())
6018 this.getDoc().designMode = "on";
6024 // Failed Mozilla 1.3
6025 this.getDoc().location.href = tinyMCE.baseURL + "/blank.htm";
6029 // This timeout is needed in MSIE 5.5 for some odd reason
6030 // it seems that the document.frames isn't initialized yet?
6032 window.setTimeout("TinyMCE.prototype.addEventHandlers('" + this.editorId + "');", 1);
6034 tinyMCE.setupContent(this.editorId, true);
6039 TinyMCEControl.prototype.getFocusElement = function() {
6040 if (tinyMCE.isMSIE && !tinyMCE.isOpera) {
6041 var doc = this.getDoc();
6042 var rng = doc.selection.createRange();
6044 // if (rng.collapse)
6045 // rng.collapse(true);
6047 var elm = rng.item ? rng.item(0) : rng.parentElement();
6049 if (this.isHidden())
6050 return this.getBody();
6052 var sel = this.getSel();
6053 var rng = this.getRng();
6055 var elm = rng.commonAncestorContainer;
6056 //var elm = (sel && sel.anchorNode) ? sel.anchorNode : null;
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];
6069 // Get the element parent of the node
6070 elm = tinyMCE.getParentElement(elm);
6072 //if (tinyMCE.selectedElement != null && tinyMCE.selectedElement.nodeName.toLowerCase() == "img")
6073 // elm = tinyMCE.selectedElement;
6080 var tinyMCE = new TinyMCE();
6081 var tinyMCELang = new Array();