Browse code

tinyMCE '2' is now 2.1.2 - Seems tinyMCE '2' was actually 3.0.9 (my fault for not checking), so '2' is now 2.1.2, and '3' is now 3.4.8... eFiction 3.4.3 used tinyMCE 2.1.0, and 3.5 upgraded that to 3.0.9.

Clarissa Walker authored on 2026/07/28 07:20:33
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,3061 @@
1
+/**
2
+ * $Id$
3
+ *
4
+ * @author Moxiecode
5
+ * @copyright Copyright � 2004-2007, Moxiecode Systems AB, All rights reserved.
6
+ */
7
+
8
+/**
9
+ * Core engine class for TinyMCE, a instance of this class is available as a global called tinyMCE.
10
+ *
11
+ * @constructor
12
+ */
13
+function TinyMCE_Engine() {
14
+	var ua;
15
+
16
+	this.majorVersion = "2";
17
+	this.minorVersion = "1.2";
18
+	this.releaseDate = "2007-08-21";
19
+
20
+	this.instances = [];
21
+	this.switchClassCache = [];
22
+	this.windowArgs = [];
23
+	this.loadedFiles = [];
24
+	this.pendingFiles = [];
25
+	this.loadingIndex = 0;
26
+	this.configs = [];
27
+	this.currentConfig = 0;
28
+	this.eventHandlers = [];
29
+	this.log = [];
30
+	this.undoLevels = [];
31
+	this.undoIndex = 0;
32
+	this.typingUndoIndex = -1;
33
+	this.settings = [];
34
+
35
+	// Browser check
36
+	ua = navigator.userAgent;
37
+	this.isMSIE = (navigator.appName == "Microsoft Internet Explorer");
38
+	this.isMSIE5 = this.isMSIE && (ua.indexOf('MSIE 5') != -1);
39
+	this.isMSIE5_0 = this.isMSIE && (ua.indexOf('MSIE 5.0') != -1);
40
+	this.isMSIE7 = this.isMSIE && (ua.indexOf('MSIE 7') != -1);
41
+	this.isGecko = ua.indexOf('Gecko') != -1; // Will also be true on Safari
42
+	this.isSafari = ua.indexOf('Safari') != -1;
43
+	this.isOpera = window['opera'] && opera.buildNumber ? true : false;
44
+	this.isMac = ua.indexOf('Mac') != -1;
45
+	this.isNS7 = ua.indexOf('Netscape/7') != -1;
46
+	this.isNS71 = ua.indexOf('Netscape/7.1') != -1;
47
+	this.dialogCounter = 0;
48
+	this.plugins = [];
49
+	this.themes = [];
50
+	this.menus = [];
51
+	this.loadedPlugins = [];
52
+	this.buttonMap = [];
53
+	this.isLoaded = false;
54
+
55
+	// Fake MSIE on Opera and if Opera fakes IE, Gecko or Safari cancel those
56
+	if (this.isOpera) {
57
+		this.isMSIE = true;
58
+		this.isGecko = false;
59
+		this.isSafari =  false;
60
+	}
61
+
62
+	this.isIE = this.isMSIE;
63
+	this.isRealIE = this.isMSIE && !this.isOpera;
64
+
65
+	// TinyMCE editor id instance counter
66
+	this.idCounter = 0;
67
+};
68
+
69
+/**#@+
70
+ * @member TinyMCE_Engine
71
+ */
72
+TinyMCE_Engine.prototype = {
73
+	/**#@+
74
+	 * @method
75
+	 */
76
+
77
+	/**
78
+	 * Initializes TinyMCE with the specific configuration settings. This method
79
+	 * may be called multiple times when multiple instances with diffrent settings is to be created.
80
+	 *
81
+	 * @param {Array} Name/Value array of initialization settings.
82
+	 */
83
+	init : function(settings) {
84
+		var theme, nl, baseHREF = "", i, cssPath, entities, h, p, src, elements = [], head;
85
+
86
+		// IE 5.0x is no longer supported since 5.5, 6.0 and 7.0 now exists. We can't support old browsers forever, sorry.
87
+		if (this.isMSIE5_0)
88
+			return;
89
+
90
+		this.settings = settings;
91
+
92
+		// Check if valid browser has execcommand support
93
+		if (typeof(document.execCommand) == 'undefined')
94
+			return;
95
+
96
+		// Get script base path
97
+		if (!tinyMCE.baseURL) {
98
+			// Search through head
99
+			head = document.getElementsByTagName('head')[0];
100
+
101
+			if (head) {
102
+				for (i=0, nl = head.getElementsByTagName('script'); i<nl.length; i++)
103
+					elements.push(nl[i]);
104
+			}
105
+
106
+			// Search through rest of document
107
+			for (i=0, nl = document.getElementsByTagName('script'); i<nl.length; i++)
108
+				elements.push(nl[i]);
109
+
110
+			// If base element found, add that infront of baseURL
111
+			nl = document.getElementsByTagName('base');
112
+			for (i=0; i<nl.length; i++) {
113
+				if (nl[i].href)
114
+					baseHREF = nl[i].href;
115
+			}
116
+
117
+			for (i=0; i<elements.length; i++) {
118
+				if (elements[i].src && (elements[i].src.indexOf("tiny_mce.js") != -1 || elements[i].src.indexOf("tiny_mce_dev.js") != -1 || elements[i].src.indexOf("tiny_mce_src.js") != -1 || elements[i].src.indexOf("tiny_mce_gzip") != -1)) {
119
+					src = elements[i].src;
120
+
121
+					tinyMCE.srcMode = (src.indexOf('_src') != -1 || src.indexOf('_dev') != -1) ? '_src' : '';
122
+					tinyMCE.gzipMode = src.indexOf('_gzip') != -1;
123
+					src = src.substring(0, src.lastIndexOf('/'));
124
+
125
+					if (settings.exec_mode == "src" || settings.exec_mode == "normal")
126
+						tinyMCE.srcMode = settings.exec_mode == "src" ? '_src' : '';
127
+
128
+					// Force it absolute if page has a base href
129
+					if (baseHREF !== '' && src.indexOf('://') == -1)
130
+						tinyMCE.baseURL = baseHREF + src;
131
+					else
132
+						tinyMCE.baseURL = src;
133
+
134
+					break;
135
+				}
136
+			}
137
+		}
138
+
139
+		// Get document base path
140
+		this.documentBasePath = document.location.href;
141
+		if (this.documentBasePath.indexOf('?') != -1)
142
+			this.documentBasePath = this.documentBasePath.substring(0, this.documentBasePath.indexOf('?'));
143
+		this.documentURL = this.documentBasePath;
144
+		this.documentBasePath = this.documentBasePath.substring(0, this.documentBasePath.lastIndexOf('/'));
145
+
146
+		// If not HTTP absolute
147
+		if (tinyMCE.baseURL.indexOf('://') == -1 && tinyMCE.baseURL.charAt(0) != '/') {
148
+			// If site absolute
149
+			tinyMCE.baseURL = this.documentBasePath + "/" + tinyMCE.baseURL;
150
+		}
151
+
152
+		// Set default values on settings
153
+		this._def("mode", "none");
154
+		this._def("theme", "advanced");
155
+		this._def("plugins", "", true);
156
+		this._def("language", "en");
157
+		this._def("docs_language", this.settings.language);
158
+		this._def("elements", "");
159
+		this._def("textarea_trigger", "mce_editable");
160
+		this._def("editor_selector", "");
161
+		this._def("editor_deselector", "mceNoEditor");
162
+		this._def("valid_elements", "+a[id|style|rel|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],-strike[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|style],-ul[class|style],-li[class|style],br,img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align],-sub[style|class],-sup[style|class],-blockquote[dir|style],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],-span[style|class|align],-pre[class|align|style],address[class|align|style],-h1[id|style|dir|class|align],-h2[id|style|dir|class|align],-h3[id|style|dir|class|align],-h4[id|style|dir|class|align],-h5[id|style|dir|class|align],-h6[id|style|dir|class|align],hr[class|style],-font[face|size|style|id|class|dir|color],dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],cite[title|id|class|style|dir|lang],abbr[title|id|class|style|dir|lang],acronym[title|id|class|style|dir|lang],del[title|id|class|style|dir|lang|datetime|cite],ins[title|id|class|style|dir|lang|datetime|cite]");
163
+		this._def("extended_valid_elements", "");
164
+		this._def("invalid_elements", "");
165
+		this._def("encoding", "");
166
+		this._def("urlconverter_callback", tinyMCE.getParam("urlconvertor_callback", "TinyMCE_Engine.prototype.convertURL"));
167
+		this._def("save_callback", "");
168
+		this._def("force_br_newlines", false);
169
+		this._def("force_p_newlines", true);
170
+		this._def("add_form_submit_trigger", true);
171
+		this._def("relative_urls", true);
172
+		this._def("remove_script_host", true);
173
+		this._def("focus_alert", true);
174
+		this._def("document_base_url", this.documentURL);
175
+		this._def("visual", true);
176
+		this._def("visual_table_class", "mceVisualAid");
177
+		this._def("setupcontent_callback", "");
178
+		this._def("fix_content_duplication", true);
179
+		this._def("custom_undo_redo", true);
180
+		this._def("custom_undo_redo_levels", -1);
181
+		this._def("custom_undo_redo_keyboard_shortcuts", true);
182
+		this._def("custom_undo_redo_restore_selection", true);
183
+		this._def("custom_undo_redo_global", false);
184
+		this._def("verify_html", true);
185
+		this._def("apply_source_formatting", false);
186
+		this._def("directionality", "ltr");
187
+		this._def("cleanup_on_startup", false);
188
+		this._def("inline_styles", false);
189
+		this._def("convert_newlines_to_brs", false);
190
+		this._def("auto_reset_designmode", true);
191
+		this._def("entities", "39,#39,160,nbsp,161,iexcl,162,cent,163,pound,164,curren,165,yen,166,brvbar,167,sect,168,uml,169,copy,170,ordf,171,laquo,172,not,173,shy,174,reg,175,macr,176,deg,177,plusmn,178,sup2,179,sup3,180,acute,181,micro,182,para,183,middot,184,cedil,185,sup1,186,ordm,187,raquo,188,frac14,189,frac12,190,frac34,191,iquest,192,Agrave,193,Aacute,194,Acirc,195,Atilde,196,Auml,197,Aring,198,AElig,199,Ccedil,200,Egrave,201,Eacute,202,Ecirc,203,Euml,204,Igrave,205,Iacute,206,Icirc,207,Iuml,208,ETH,209,Ntilde,210,Ograve,211,Oacute,212,Ocirc,213,Otilde,214,Ouml,215,times,216,Oslash,217,Ugrave,218,Uacute,219,Ucirc,220,Uuml,221,Yacute,222,THORN,223,szlig,224,agrave,225,aacute,226,acirc,227,atilde,228,auml,229,aring,230,aelig,231,ccedil,232,egrave,233,eacute,234,ecirc,235,euml,236,igrave,237,iacute,238,icirc,239,iuml,240,eth,241,ntilde,242,ograve,243,oacute,244,ocirc,245,otilde,246,ouml,247,divide,248,oslash,249,ugrave,250,uacute,251,ucirc,252,uuml,253,yacute,254,thorn,255,yuml,402,fnof,913,Alpha,914,Beta,915,Gamma,916,Delta,917,Epsilon,918,Zeta,919,Eta,920,Theta,921,Iota,922,Kappa,923,Lambda,924,Mu,925,Nu,926,Xi,927,Omicron,928,Pi,929,Rho,931,Sigma,932,Tau,933,Upsilon,934,Phi,935,Chi,936,Psi,937,Omega,945,alpha,946,beta,947,gamma,948,delta,949,epsilon,950,zeta,951,eta,952,theta,953,iota,954,kappa,955,lambda,956,mu,957,nu,958,xi,959,omicron,960,pi,961,rho,962,sigmaf,963,sigma,964,tau,965,upsilon,966,phi,967,chi,968,psi,969,omega,977,thetasym,978,upsih,982,piv,8226,bull,8230,hellip,8242,prime,8243,Prime,8254,oline,8260,frasl,8472,weierp,8465,image,8476,real,8482,trade,8501,alefsym,8592,larr,8593,uarr,8594,rarr,8595,darr,8596,harr,8629,crarr,8656,lArr,8657,uArr,8658,rArr,8659,dArr,8660,hArr,8704,forall,8706,part,8707,exist,8709,empty,8711,nabla,8712,isin,8713,notin,8715,ni,8719,prod,8721,sum,8722,minus,8727,lowast,8730,radic,8733,prop,8734,infin,8736,ang,8743,and,8744,or,8745,cap,8746,cup,8747,int,8756,there4,8764,sim,8773,cong,8776,asymp,8800,ne,8801,equiv,8804,le,8805,ge,8834,sub,8835,sup,8836,nsub,8838,sube,8839,supe,8853,oplus,8855,otimes,8869,perp,8901,sdot,8968,lceil,8969,rceil,8970,lfloor,8971,rfloor,9001,lang,9002,rang,9674,loz,9824,spades,9827,clubs,9829,hearts,9830,diams,34,quot,38,amp,60,lt,62,gt,338,OElig,339,oelig,352,Scaron,353,scaron,376,Yuml,710,circ,732,tilde,8194,ensp,8195,emsp,8201,thinsp,8204,zwnj,8205,zwj,8206,lrm,8207,rlm,8211,ndash,8212,mdash,8216,lsquo,8217,rsquo,8218,sbquo,8220,ldquo,8221,rdquo,8222,bdquo,8224,dagger,8225,Dagger,8240,permil,8249,lsaquo,8250,rsaquo,8364,euro", true);
192
+		this._def("entity_encoding", "named");
193
+		this._def("cleanup_callback", "");
194
+		this._def("add_unload_trigger", true);
195
+		this._def("ask", false);
196
+		this._def("nowrap", false);
197
+		this._def("auto_resize", false);
198
+		this._def("auto_focus", false);
199
+		this._def("cleanup", true);
200
+		this._def("remove_linebreaks", true);
201
+		this._def("button_tile_map", false);
202
+		this._def("submit_patch", true);
203
+		this._def("browsers", "msie,safari,gecko,opera", true);
204
+		this._def("dialog_type", "window");
205
+		this._def("accessibility_warnings", true);
206
+		this._def("accessibility_focus", true);
207
+		this._def("merge_styles_invalid_parents", "");
208
+		this._def("force_hex_style_colors", true);
209
+		this._def("trim_span_elements", true);
210
+		this._def("convert_fonts_to_spans", false);
211
+		this._def("doctype", '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">');
212
+		this._def("font_size_classes", '');
213
+		this._def("font_size_style_values", 'xx-small,x-small,small,medium,large,x-large,xx-large', true);
214
+		this._def("event_elements", 'a,img', true);
215
+		this._def("convert_urls", true);
216
+		this._def("table_inline_editing", false);
217
+		this._def("object_resizing", true);
218
+		this._def("custom_shortcuts", true);
219
+		this._def("convert_on_click", false);
220
+		this._def("content_css", '');
221
+		this._def("fix_list_elements", true);
222
+		this._def("fix_table_elements", false);
223
+		this._def("strict_loading_mode", document.contentType == 'application/xhtml+xml');
224
+		this._def("hidden_tab_class", '');
225
+		this._def("display_tab_class", '');
226
+		this._def("gecko_spellcheck", false);
227
+		this._def("hide_selects_on_submit", true);
228
+		this._def("forced_root_block", false);
229
+		this._def("remove_trailing_nbsp", false);
230
+		this._def("save_on_tinymce_forms", false);
231
+
232
+		// Force strict loading mode to false on non Gecko browsers
233
+		if (this.isMSIE && !this.isOpera)
234
+			this.settings.strict_loading_mode = false;
235
+
236
+		// Browser check IE
237
+		if (this.isMSIE && this.settings.browsers.indexOf('msie') == -1)
238
+			return;
239
+
240
+		// Browser check Gecko
241
+		if (this.isGecko && this.settings.browsers.indexOf('gecko') == -1)
242
+			return;
243
+
244
+		// Browser check Safari
245
+		if (this.isSafari && this.settings.browsers.indexOf('safari') == -1)
246
+			return;
247
+
248
+		// Browser check Opera
249
+		if (this.isOpera && this.settings.browsers.indexOf('opera') == -1)
250
+			return;
251
+
252
+		// If not super absolute make it so
253
+		baseHREF = tinyMCE.settings.document_base_url;
254
+		h = document.location.href;
255
+		p = h.indexOf('://');
256
+		if (p > 0 && document.location.protocol != "file:") {
257
+			p = h.indexOf('/', p + 3);
258
+			h = h.substring(0, p);
259
+
260
+			if (baseHREF.indexOf('://') == -1)
261
+				baseHREF = h + baseHREF;
262
+
263
+			tinyMCE.settings.document_base_url = baseHREF;
264
+			tinyMCE.settings.document_base_prefix = h;
265
+		}
266
+
267
+		// Trim away query part
268
+		if (baseHREF.indexOf('?') != -1)
269
+			baseHREF = baseHREF.substring(0, baseHREF.indexOf('?'));
270
+
271
+		this.settings.base_href = baseHREF.substring(0, baseHREF.lastIndexOf('/')) + "/";
272
+
273
+		theme = this.settings.theme;
274
+		this.inlineStrict = 'A|BR|SPAN|BDO|MAP|OBJECT|IMG|TT|I|B|BIG|SMALL|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|#text|#comment';
275
+		this.inlineTransitional = 'A|BR|SPAN|BDO|OBJECT|APPLET|IMG|MAP|IFRAME|TT|I|B|U|S|STRIKE|BIG|SMALL|FONT|BASEFONT|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|INPUT|SELECT|TEXTAREA|LABEL|BUTTON|#text|#comment';
276
+		this.blockElms = 'H[1-6]|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|FORM|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP';
277
+		this.blockRegExp = new RegExp("^(" + this.blockElms + ")$", "i");
278
+		this.posKeyCodes = [13,45,36,35,33,34,37,38,39,40];
279
+		this.uniqueURL = 'javascript:void(091039730);'; // Make unique URL non real URL
280
+		this.uniqueTag = '<div id="mceTMPElement" style="display: none">TMP</div>';
281
+		this.callbacks = ['onInit', 'getInfo', 'getEditorTemplate', 'setupContent', 'onChange', 'onPageLoad', 'handleNodeChange', 'initInstance', 'execCommand', 'getControlHTML', 'handleEvent', 'cleanup', 'removeInstance'];
282
+
283
+		// Theme url
284
+		this.settings.theme_href = tinyMCE.baseURL + "/themes/" + theme;
285
+
286
+		if (!tinyMCE.isIE || tinyMCE.isOpera)
287
+			this.settings.force_br_newlines = false;
288
+
289
+		if (tinyMCE.getParam("popups_css", false)) {
290
+			cssPath = tinyMCE.getParam("popups_css", "");
291
+
292
+			// Is relative
293
+			if (cssPath.indexOf('://') == -1 && cssPath.charAt(0) != '/')
294
+				this.settings.popups_css = this.documentBasePath + "/" + cssPath;
295
+			else
296
+				this.settings.popups_css = cssPath;
297
+		} else
298
+			this.settings.popups_css = tinyMCE.baseURL + "/themes/" + theme + "/css/editor_popup.css";
299
+
300
+		if (tinyMCE.getParam("editor_css", false)) {
301
+			cssPath = tinyMCE.getParam("editor_css", "");
302
+
303
+			// Is relative
304
+			if (cssPath.indexOf('://') == -1 && cssPath.charAt(0) != '/')
305
+				this.settings.editor_css = this.documentBasePath + "/" + cssPath;
306
+			else
307
+				this.settings.editor_css = cssPath;
308
+		} else {
309
+			if (this.settings.editor_css !== '')
310
+				this.settings.editor_css = tinyMCE.baseURL + "/themes/" + theme + "/css/editor_ui.css";
311
+		}
312
+
313
+		// Only do this once
314
+		if (this.configs.length == 0) {
315
+			if (typeof(TinyMCECompressed) == "undefined") {
316
+				tinyMCE.addEvent(window, "DOMContentLoaded", TinyMCE_Engine.prototype.onLoad);
317
+
318
+				if (tinyMCE.isRealIE) {
319
+					if (document.body)
320
+						tinyMCE.addEvent(document.body, "readystatechange", TinyMCE_Engine.prototype.onLoad);
321
+					else
322
+						tinyMCE.addEvent(document, "readystatechange", TinyMCE_Engine.prototype.onLoad);
323
+				}
324
+
325
+				tinyMCE.addEvent(window, "load", TinyMCE_Engine.prototype.onLoad);
326
+				tinyMCE._addUnloadEvents();
327
+			}
328
+		}
329
+
330
+		this.loadScript(tinyMCE.baseURL + '/themes/' + this.settings.theme + '/editor_template' + tinyMCE.srcMode + '.js');
331
+		this.loadScript(tinyMCE.baseURL + '/langs/' + this.settings.language +  '.js');
332
+		this.loadCSS(this.settings.editor_css);
333
+
334
+		// Add plugins
335
+		p = tinyMCE.getParam('plugins', '', true, ',');
336
+		if (p.length > 0) {
337
+			for (i=0; i<p.length; i++) {
338
+				if (p[i].charAt(0) != '-')
339
+					this.loadScript(tinyMCE.baseURL + '/plugins/' + p[i] + '/editor_plugin' + tinyMCE.srcMode + '.js');
340
+			}
341
+		}
342
+
343
+		// Setup entities
344
+		if (tinyMCE.getParam('entity_encoding') == 'named') {
345
+			settings.cleanup_entities = [];
346
+			entities = tinyMCE.getParam('entities', '', true, ',');
347
+			for (i=0; i<entities.length; i+=2)
348
+				settings.cleanup_entities['c' + entities[i]] = entities[i+1];
349
+		}
350
+
351
+		// Save away this config
352
+		settings.index = this.configs.length;
353
+		this.configs[this.configs.length] = settings;
354
+
355
+		// Start loading first one in chain
356
+		this.loadNextScript();
357
+
358
+		// Force flicker free CSS backgrounds in IE
359
+		if (this.isIE && !this.isOpera) {
360
+			try {
361
+				document.execCommand('BackgroundImageCache', false, true);
362
+			} catch (e) {
363
+				// Ignore
364
+			}
365
+		}
366
+
367
+		// Setup XML encoding regexps
368
+		this.xmlEncodeRe = new RegExp('[<>&"]', 'g');
369
+	},
370
+
371
+	/**
372
+	 * Adds unload event handles to execute triggerSave.
373
+	 *
374
+	 * @private
375
+	 */
376
+	_addUnloadEvents : function() {
377
+		var st = tinyMCE.settings.add_unload_trigger;
378
+
379
+		if (tinyMCE.isIE) {
380
+			if (st) {
381
+				tinyMCE.addEvent(window, "unload", TinyMCE_Engine.prototype.unloadHandler);
382
+				tinyMCE.addEvent(window.document, "beforeunload", TinyMCE_Engine.prototype.unloadHandler);
383
+			}
384
+		} else {
385
+			if (st)
386
+				tinyMCE.addEvent(window, "unload", function () {tinyMCE.triggerSave(true, true);});
387
+		}
388
+	},
389
+
390
+	/**
391
+	 * Assigns a default value for a specific config parameter.
392
+	 *
393
+	 * @param {string} key Settings key to add default value to.
394
+	 * @param {object} def_val Default value to assign if the settings option isn't defined.
395
+	 * @param {boolean} t Trim all white space, if true all whitespace will be removed from option value.
396
+	 * @private
397
+	 */
398
+	_def : function(key, def_val, t) {
399
+		var v = tinyMCE.getParam(key, def_val);
400
+
401
+		v = t ? v.replace(/\s+/g, "") : v;
402
+
403
+		this.settings[key] = v;
404
+	},
405
+
406
+	/**
407
+	 * Returns true/false if the specified plugin is loaded or not.
408
+	 *
409
+	 * @param {string} n Plugin name to look for.
410
+	 * @return true/false if the specified plugin is loaded or not.
411
+	 * @type boolean
412
+	 */
413
+	hasPlugin : function(n) {
414
+		return typeof(this.plugins[n]) != "undefined" && this.plugins[n] != null;
415
+	},
416
+
417
+	/**
418
+	 * Adds the specified plugin to the list of loaded plugins, this will also setup the baseURL
419
+	 * property of the plugin.
420
+	 *
421
+	 * @param {string} Plugin name/id.
422
+	 * @param {TinyMCE_Plugin} p Plugin instance to add.
423
+	 */
424
+	addPlugin : function(n, p) {
425
+		var op = this.plugins[n];
426
+
427
+		// Use the previous plugin object base URL used when loading external plugins
428
+		p.baseURL = op ? op.baseURL : tinyMCE.baseURL + "/plugins/" + n;
429
+		this.plugins[n] = p;
430
+
431
+		this.loadNextScript();
432
+	},
433
+
434
+	/**
435
+	 * Sets the baseURL of the specified plugin, this is useful if the plugin is loaded from
436
+	 * a external location.
437
+	 *
438
+	 * @param {string} n Plugin name/id to set base URL on. This have to be added before.
439
+	 * @param {string} u Base URL of plugin, this string should be the URL prefix for the plugin without a trailing slash.
440
+	 */
441
+	setPluginBaseURL : function(n, u) {
442
+		var op = this.plugins[n];
443
+
444
+		if (op)
445
+			op.baseURL = u;
446
+		else
447
+			this.plugins[n] = {baseURL : u};
448
+	},
449
+
450
+	/**
451
+	 * Load plugin from external URL.
452
+	 *
453
+	 * @param {string} n Plugin name for example \"emotions\".
454
+	 * @param {string} u URL of plugin directory to load.
455
+	 */
456
+	loadPlugin : function(n, u) {
457
+		u = u.indexOf('.js') != -1 ? u.substring(0, u.lastIndexOf('/')) : u;
458
+		u = u.charAt(u.length-1) == '/' ? u.substring(0, u.length-1) : u;
459
+		this.plugins[n] = {baseURL : u};
460
+		this.loadScript(u + "/editor_plugin" + (tinyMCE.srcMode ? '_src' : '') + ".js");
461
+	},
462
+
463
+	/**
464
+	 * Returns true/false if the specified theme is loaded or not.
465
+	 *
466
+	 * @param {string} n Theme name/id to check for.
467
+	 * @return true/false if the specified theme is loaded or not.
468
+	 * @type boolean
469
+	 */
470
+	hasTheme : function(n) {
471
+		return typeof(this.themes[n]) != "undefined" && this.themes[n] != null;
472
+	},
473
+
474
+	/**
475
+	 * Adds the specified theme in to the list of loaded themes.
476
+	 *
477
+	 * @param {string} n Theme name/id to add the object reference to.
478
+	 * @param {TinyMCE_Theme} t Theme instance to add to the loaded list.
479
+	 */
480
+	addTheme : function(n, t) {
481
+		this.themes[n] = t;
482
+
483
+		this.loadNextScript();
484
+	},
485
+
486
+	/**
487
+	 * Adds a floating menu instance to TinyMCE.
488
+	 *
489
+	 * @param {string} n TinyMCE menu id.
490
+	 * @param {TinyMCE_Menu} m TinyMCE menu instance.
491
+	 */
492
+	addMenu : function(n, m) {
493
+		this.menus[n] = m;
494
+	},
495
+
496
+	/**
497
+	 * Checks if the specified menu by name is added to TinyMCE.
498
+	 *
499
+	 * @param {string} n TinyMCE menu id.
500
+	 * @return true/false if it exists or not.
501
+	 * @type boolean
502
+	 */
503
+	hasMenu : function(n) {
504
+		return typeof(this.plugins[n]) != "undefined" && this.plugins[n] != null;
505
+	},
506
+
507
+	/**
508
+	 * Loads the specified script by writing the a script tag to the current page.
509
+	 * This will also check if the file has been loaded before. This function should only be used
510
+	 * when the page is loading.
511
+	 *
512
+	 * @param {string} url Script URL to load.
513
+	 */
514
+	loadScript : function(url) {
515
+		var i;
516
+
517
+		for (i=0; i<this.loadedFiles.length; i++) {
518
+			if (this.loadedFiles[i] == url)
519
+				return;
520
+		}
521
+
522
+		if (tinyMCE.settings.strict_loading_mode)
523
+			this.pendingFiles[this.pendingFiles.length] = url;
524
+		else
525
+			document.write('<sc'+'ript language="javascript" type="text/javascript" src="' + url + '"></script>');
526
+
527
+		this.loadedFiles[this.loadedFiles.length] = url;
528
+	},
529
+
530
+	/**
531
+	 * Loads the next script in chain.
532
+	 */
533
+	loadNextScript : function() {
534
+		var d = document, se;
535
+
536
+		if (!tinyMCE.settings.strict_loading_mode)
537
+			return;
538
+
539
+		if (this.loadingIndex < this.pendingFiles.length) {
540
+			se = d.createElementNS('http://www.w3.org/1999/xhtml', 'script');
541
+			se.setAttribute('language', 'javascript');
542
+			se.setAttribute('type', 'text/javascript');
543
+			se.setAttribute('src', this.pendingFiles[this.loadingIndex++]);
544
+
545
+			d.getElementsByTagName("head")[0].appendChild(se);
546
+		} else
547
+			this.loadingIndex = -1; // Done with loading
548
+	},
549
+
550
+	/**
551
+	 * Loads the specified CSS by writing the a link tag to the current page.
552
+	 * This will also check if the file has been loaded before. This function should only be used
553
+	 * when the page is loading.
554
+	 *
555
+	 * @param {string} url CSS file URL to load or comma separated list of files.
556
+	 */
557
+	loadCSS : function(url) {
558
+		var ar = url.replace(/\s+/, '').split(',');
559
+		var lflen = 0, csslen = 0, skip = false;
560
+		var x = 0, i = 0, nl, le;
561
+
562
+		for (x = 0,csslen = ar.length; x<csslen; x++) {
563
+			if (ar[x] != null && ar[x] != 'null' && ar[x].length > 0) {
564
+				/* Make sure it doesn't exist. */
565
+				for (i=0, lflen=this.loadedFiles.length; i<lflen; i++) {
566
+					if (this.loadedFiles[i] == ar[x]) {
567
+						skip = true;
568
+						break;
569
+					}
570
+				}
571
+
572
+				if (!skip) {
573
+					if (tinyMCE.settings.strict_loading_mode) {
574
+						nl = document.getElementsByTagName("head");
575
+
576
+						le = document.createElement('link');
577
+						le.setAttribute('href', ar[x]);
578
+						le.setAttribute('rel', 'stylesheet');
579
+						le.setAttribute('type', 'text/css');
580
+
581
+						nl[0].appendChild(le);			
582
+					} else
583
+						document.write('<link href="' + ar[x] + '" rel="stylesheet" type="text/css" />');
584
+
585
+					this.loadedFiles[this.loadedFiles.length] = ar[x];
586
+				}
587
+			}
588
+		}
589
+	},
590
+
591
+	/**
592
+	 * Imports a CSS file into a allready loaded document. This will add a link element
593
+	 * to the head element of the document.
594
+	 *
595
+	 * @param {DOMDocument} doc DOM Document to load CSS into.
596
+	 * @param {string} css CSS File URL to load or comma separated list of files.
597
+	 */
598
+	importCSS : function(doc, css) {
599
+		var css_ary = css.replace(/\s+/, '').split(',');
600
+		var csslen, elm, headArr, x, css_file;
601
+
602
+		for (x = 0, csslen = css_ary.length; x<csslen; x++) {
603
+			css_file = css_ary[x];
604
+
605
+			if (css_file != null && css_file != 'null' && css_file.length > 0) {
606
+				// Is relative, make absolute
607
+				if (css_file.indexOf('://') == -1 && css_file.charAt(0) != '/')
608
+					css_file = this.documentBasePath + "/" + css_file;
609
+
610
+				if (typeof(doc.createStyleSheet) == "undefined") {
611
+					elm = doc.createElement("link");
612
+
613
+					elm.rel = "stylesheet";
614
+					elm.href = css_file;
615
+
616
+					if ((headArr = doc.getElementsByTagName("head")) != null && headArr.length > 0)
617
+						headArr[0].appendChild(elm);
618
+				} else
619
+					doc.createStyleSheet(css_file);
620
+			}
621
+		}
622
+	},
623
+
624
+	/**
625
+	 * Displays a confirm dialog when a user clicks/focus a textarea that is to be converted into
626
+	 * a TinyMCE instance.
627
+	 *
628
+	 * @param {DOMEvent} e DOM event instance.
629
+	 * @param {Array} settings Name/Value array of initialization settings.
630
+	 */
631
+	confirmAdd : function(e, settings) {
632
+		var elm = tinyMCE.isIE ? event.srcElement : e.target;
633
+		var elementId = elm.name ? elm.name : elm.id;
634
+
635
+		tinyMCE.settings = settings;
636
+
637
+		if (tinyMCE.settings.convert_on_click || (!elm.getAttribute('mce_noask') && confirm(tinyMCELang.lang_edit_confirm)))
638
+			tinyMCE.addMCEControl(elm, elementId);
639
+
640
+		elm.setAttribute('mce_noask', 'true');
641
+	},
642
+
643
+	/**
644
+	 * Moves the contents from the hidden textarea to the editor that gets inserted.
645
+	 *
646
+	 * @param {string} form_element_name Form element name to move contents from.
647
+	 * @deprecated
648
+	 */
649
+	updateContent : function(form_element_name) {
650
+		var formElement, n, inst, doc;
651
+
652
+		// Find MCE instance linked to given form element and copy it's value
653
+		formElement = document.getElementById(form_element_name);
654
+		for (n in tinyMCE.instances) {
655
+			inst = tinyMCE.instances[n];
656
+
657
+			if (!tinyMCE.isInstance(inst))
658
+				continue;
659
+
660
+			inst.switchSettings();
661
+
662
+			if (inst.formElement == formElement) {
663
+				doc = inst.getDoc();
664
+
665
+				tinyMCE._setHTML(doc, inst.formElement.value);
666
+
667
+				if (!tinyMCE.isIE)
668
+					doc.body.innerHTML = tinyMCE._cleanupHTML(inst, doc, this.settings, doc.body, inst.visualAid);
669
+			}
670
+		}
671
+	},
672
+
673
+	/**
674
+	 * Adds a TinyMCE editor control instance to a specific form element.
675
+	 *
676
+	 * @param {HTMLElement} replace_element HTML element object to replace.
677
+	 * @param {string} form_element_name HTML form element name,
678
+	 * @param {DOMDocument} target_document Target document that holds the element.
679
+	 */
680
+	addMCEControl : function(replace_element, form_element_name, target_document) {
681
+		var id = "mce_editor_" + tinyMCE.idCounter++;
682
+		var inst = new TinyMCE_Control(tinyMCE.settings);
683
+
684
+		inst.editorId = id;
685
+		this.instances[id] = inst;
686
+
687
+		inst._onAdd(replace_element, form_element_name, target_document);
688
+	},
689
+
690
+	/**
691
+	 * Removes the specified instance from TinyMCE Engine.
692
+	 *
693
+	 * @param {MCEControl} ti Target instance to remove from TinyMCE.
694
+	 * @return Removed MCEControl instance.
695
+	 * @type MCEControl
696
+	 */
697
+	removeInstance : function(ti) {
698
+		var t = [], n, i;
699
+
700
+		// Remove from instances
701
+		for (n in tinyMCE.instances) {
702
+			i = tinyMCE.instances[n];
703
+
704
+			if (tinyMCE.isInstance(i) && ti != i)
705
+					t[n] = i;
706
+		}
707
+
708
+		tinyMCE.instances = t;
709
+
710
+		// Remove from global undo/redo
711
+		n = [];
712
+		t = tinyMCE.undoLevels;
713
+
714
+		for (i=0; i<t.length; i++) {
715
+			if (t[i] != ti)
716
+				n.push(t[i]);
717
+		}
718
+
719
+		tinyMCE.undoLevels = n;
720
+		tinyMCE.undoIndex = n.length;
721
+
722
+		// Dispatch remove instance call
723
+		tinyMCE.dispatchCallback(ti, 'remove_instance_callback', 'removeInstance', ti);
724
+
725
+		return ti;
726
+	},
727
+
728
+	/**
729
+	 * Removes a TinyMCE editor control instance by id.
730
+	 *
731
+	 * @param {string} editor_id Id of editor instance to remove.
732
+	 */
733
+	removeMCEControl : function(editor_id) {
734
+		var inst = tinyMCE.getInstanceById(editor_id), h, re, ot, tn, n;
735
+
736
+		if (inst) {
737
+			inst.switchSettings();
738
+
739
+			editor_id = inst.editorId;
740
+			h = tinyMCE.getContent(editor_id);
741
+
742
+			this.removeInstance(inst);
743
+
744
+			tinyMCE.selectedElement = null;
745
+			tinyMCE.selectedInstance = null;
746
+
747
+			tinyMCE.selectedElement = null;
748
+			tinyMCE.selectedInstance = null;
749
+
750
+			// Try finding an instance
751
+			for (n in tinyMCE.instances) {
752
+				if (!tinyMCE.isInstance(tinyMCE.instances[n]))
753
+					continue;
754
+
755
+				tinyMCE.selectedInstance = tinyMCE.instances[n];
756
+				break;
757
+			}
758
+
759
+			// Remove element
760
+			re = document.getElementById(editor_id + "_parent");
761
+			ot = inst.oldTargetElement;
762
+			tn = ot.nodeName.toLowerCase();
763
+
764
+			if (tn == "textarea" || tn == "input") {
765
+				re.parentNode.removeChild(re);
766
+				ot.style.display = "inline";
767
+				ot.value = h;
768
+			} else {
769
+				ot.innerHTML = h;
770
+				ot.style.display = 'block';
771
+				re.parentNode.insertBefore(ot, re);
772
+				re.parentNode.removeChild(re);
773
+			}
774
+		}
775
+	},
776
+
777
+	/**
778
+	 * Moves the contents from a TinyMCE editor control instance to the hidden textarea
779
+	 * that got replaced with TinyMCE. This is executed automaticly on for example form submit.
780
+	 *
781
+	 * @param {boolean} skip_cleanup Optional Skip cleanup, simply move the contents as fast as possible.
782
+	 * @param {boolean} skip_callback Optional Skip callback, don't call the save_callback function.
783
+	 */
784
+	triggerSave : function(skip_cleanup, skip_callback) {
785
+		var inst, n;
786
+
787
+		// Default to false
788
+		if (typeof(skip_cleanup) == "undefined")
789
+			skip_cleanup = false;
790
+
791
+		// Default to false
792
+		if (typeof(skip_callback) == "undefined")
793
+			skip_callback = false;
794
+
795
+		// Cleanup and set all form fields
796
+		for (n in tinyMCE.instances) {
797
+			inst = tinyMCE.instances[n];
798
+
799
+			if (!tinyMCE.isInstance(inst))
800
+				continue;
801
+
802
+			inst.triggerSave(skip_cleanup, skip_callback);
803
+		}
804
+	},
805
+
806
+	/**
807
+	 * Resets a forms TinyMCE instances based on form index.
808
+	 *
809
+	 * @param {int} form_index Form index to reset.
810
+	 */
811
+	resetForm : function(form_index) {
812
+		var i, inst, n, formObj = document.forms[form_index];
813
+
814
+		for (n in tinyMCE.instances) {
815
+			inst = tinyMCE.instances[n];
816
+
817
+			if (!tinyMCE.isInstance(inst))
818
+				continue;
819
+
820
+			inst.switchSettings();
821
+
822
+			for (i=0; i<formObj.elements.length; i++) {
823
+				if (inst.formTargetElementId == formObj.elements[i].name)
824
+					inst.getBody().innerHTML = inst.startContent;
825
+			}
826
+		}
827
+	},
828
+
829
+	/**
830
+	 * Executes a command on a specific editor instance by id.
831
+	 *
832
+	 * @param {string} editor_id TinyMCE editor control instance id to perform comman on.
833
+	 * @param {string} command Command name to execute, for example mceLink or Bold.
834
+	 * @param {boolean} user_interface True/false state if a UI (dialog) should be presented or not.
835
+	 * @param {object} value Optional command value, this can be anything.
836
+	 * @param {boolean} focus True/false if the editor instance should be focused first.
837
+	 */
838
+	execInstanceCommand : function(editor_id, command, user_interface, value, focus) {
839
+		var inst = tinyMCE.getInstanceById(editor_id), r;
840
+
841
+		if (inst) {
842
+			r = inst.selection.getRng();
843
+
844
+			if (typeof(focus) == "undefined")
845
+				focus = true;
846
+
847
+			// IE bug lost focus on images in absolute divs Bug #1534575
848
+			if (focus && (!r || !r.item))
849
+				inst.contentWindow.focus();
850
+
851
+			// Reset design mode if lost
852
+			inst.autoResetDesignMode();
853
+
854
+			this.selectedElement = inst.getFocusElement();
855
+			inst.select();
856
+			tinyMCE.execCommand(command, user_interface, value);
857
+
858
+			// Cancel event so it doesn't call onbeforeonunlaod
859
+			if (tinyMCE.isIE && window.event != null)
860
+				tinyMCE.cancelEvent(window.event);
861
+		}
862
+	},
863
+
864
+	/**
865
+	 * Executes a command on the selected or last selected TinyMCE editor control instance. This function also handles
866
+	 * some non instance specific commands like mceAddControl, mceRemoveControl, mceHelp or mceFocus.
867
+	 *
868
+	 * @param {string} command Command name to execute, for example mceLink or Bold.
869
+	 * @param {boolean} user_interface True/false state if a UI (dialog) should be presented or not.
870
+	 * @param {object} value Optional command value, this can be anything.
871
+	 */
872
+	execCommand : function(command, user_interface, value) {
873
+		var inst = tinyMCE.selectedInstance, n, pe, te;
874
+
875
+		// Default input
876
+		user_interface = user_interface ? user_interface : false;
877
+		value = value ? value : null;
878
+
879
+		if (inst)
880
+			inst.switchSettings();
881
+
882
+		switch (command) {
883
+			case "Undo":
884
+				if (this.getParam('custom_undo_redo_global')) {
885
+					if (this.undoIndex > 0) {
886
+						tinyMCE.nextUndoRedoAction = 'Undo';
887
+						inst = this.undoLevels[--this.undoIndex];
888
+						inst.select();
889
+
890
+						if (!tinyMCE.nextUndoRedoInstanceId)
891
+							inst.execCommand('Undo');
892
+					}
893
+				} else
894
+					inst.execCommand('Undo');
895
+				return true;
896
+
897
+			case "Redo":
898
+				if (this.getParam('custom_undo_redo_global')) {
899
+					if (this.undoIndex <= this.undoLevels.length - 1) {
900
+						tinyMCE.nextUndoRedoAction = 'Redo';
901
+						inst = this.undoLevels[this.undoIndex++];
902
+						inst.select();
903
+
904
+						if (!tinyMCE.nextUndoRedoInstanceId)
905
+							inst.execCommand('Redo');
906
+					}
907
+				} else
908
+					inst.execCommand('Redo');
909
+
910
+				return true;
911
+
912
+			case 'mceFocus':
913
+				inst = tinyMCE.getInstanceById(value);
914
+
915
+				if (inst)
916
+					inst.getWin().focus();
917
+			return;
918
+
919
+			case "mceAddControl":
920
+			case "mceAddEditor":
921
+				tinyMCE.addMCEControl(tinyMCE._getElementById(value), value);
922
+				return;
923
+
924
+			case "mceAddFrameControl":
925
+				tinyMCE.addMCEControl(tinyMCE._getElementById(value.element, value.document), value.element, value.document);
926
+				return;
927
+
928
+			case "mceRemoveControl":
929
+			case "mceRemoveEditor":
930
+				tinyMCE.removeMCEControl(value);
931
+				return;
932
+
933
+			case "mceToggleEditor":
934
+				inst = tinyMCE.getInstanceById(value);
935
+
936
+				if (inst) {
937
+					pe = document.getElementById(inst.editorId + '_parent');
938
+					te = inst.oldTargetElement;
939
+
940
+					if (typeof(inst.enabled) == 'undefined')
941
+						inst.enabled = true;
942
+
943
+					inst.enabled = !inst.enabled;
944
+
945
+					if (!inst.enabled) {
946
+						pe.style.display = 'none';
947
+
948
+						if (te.nodeName == 'TEXTAREA' || te.nodeName == 'INPUT')
949
+							te.value = inst.getHTML();
950
+						else
951
+							te.innerHTML = inst.getHTML();
952
+
953
+						te.style.display = inst.oldTargetDisplay;
954
+						tinyMCE.dispatchCallback(inst, 'hide_instance_callback', 'hideInstance', inst);
955
+					} else {
956
+						pe.style.display = 'block';
957
+						te.style.display = 'none';
958
+
959
+						if (te.nodeName == 'TEXTAREA' || te.nodeName == 'INPUT')
960
+							inst.setHTML(te.value);
961
+						else
962
+							inst.setHTML(te.innerHTML);
963
+
964
+						inst.useCSS = false;
965
+						tinyMCE.dispatchCallback(inst, 'show_instance_callback', 'showInstance', inst);
966
+					}
967
+				} else
968
+					tinyMCE.addMCEControl(tinyMCE._getElementById(value), value);
969
+
970
+				return;
971
+
972
+			case "mceResetDesignMode":
973
+				// Resets the designmode state of the editors in Gecko
974
+				if (tinyMCE.isGecko) {
975
+					for (n in tinyMCE.instances) {
976
+						if (!tinyMCE.isInstance(tinyMCE.instances[n]))
977
+							continue;
978
+
979
+						try {
980
+							tinyMCE.instances[n].getDoc().designMode = "off";
981
+							tinyMCE.instances[n].getDoc().designMode = "on";
982
+							tinyMCE.instances[n].useCSS = false;
983
+						} catch (e) {
984
+							// Ignore any errors
985
+						}
986
+					}
987
+				}
988
+
989
+				return;
990
+		}
991
+
992
+		if (inst) {
993
+			inst.execCommand(command, user_interface, value);
994
+		} else if (tinyMCE.settings.focus_alert)
995
+			alert(tinyMCELang.lang_focus_alert);
996
+	},
997
+
998
+	/**
999
+	 * Creates a iframe editor container for the specified element.
1000
+	 *
1001
+	 * @param {HTMLElement} replace_element Element to replace with iframe element.
1002
+	 * @param {DOMDocument} doc Optional document to use with iframe replacement.
1003
+	 * @param {DOMWindow} win Optional window to use with iframe replacement.
1004
+	 * @private
1005
+	 */
1006
+	_createIFrame : function(replace_element, doc, win) {
1007
+		var iframe, id = replace_element.getAttribute("id");
1008
+		var aw, ah;
1009
+
1010
+		if (typeof(doc) == "undefined")
1011
+			doc = document;
1012
+
1013
+		if (typeof(win) == "undefined")
1014
+			win = window;
1015
+
1016
+		iframe = doc.createElement("iframe");
1017
+
1018
+		aw = "" + tinyMCE.settings.area_width;
1019
+		ah = "" + tinyMCE.settings.area_height;
1020
+
1021
+		if (aw.indexOf('%') == -1) {
1022
+			aw = parseInt(aw);
1023
+			aw = (isNaN(aw) || aw < 0) ? 300 : aw;
1024
+			aw = aw + "px";
1025
+		}
1026
+
1027
+		if (ah.indexOf('%') == -1) {
1028
+			ah = parseInt(ah);
1029
+			ah = (isNaN(ah) || ah < 0) ? 240 : ah;
1030
+			ah = ah + "px";
1031
+		}
1032
+
1033
+		iframe.setAttribute("id", id);
1034
+		iframe.setAttribute("name", id);
1035
+		iframe.setAttribute("class", "mceEditorIframe");
1036
+		iframe.setAttribute("border", "0");
1037
+		iframe.setAttribute("frameBorder", "0");
1038
+		iframe.setAttribute("marginWidth", "0");
1039
+		iframe.setAttribute("marginHeight", "0");
1040
+		iframe.setAttribute("leftMargin", "0");
1041
+		iframe.setAttribute("topMargin", "0");
1042
+		iframe.setAttribute("width", aw);
1043
+		iframe.setAttribute("height", ah);
1044
+		iframe.setAttribute("allowtransparency", "true");
1045
+		iframe.className = 'mceEditorIframe';
1046
+
1047
+		if (tinyMCE.settings.auto_resize)
1048
+			iframe.setAttribute("scrolling", "no");
1049
+
1050
+		// Must have a src element in MSIE HTTPs breaks aswell as absoute URLs
1051
+		if (tinyMCE.isRealIE)
1052
+			iframe.setAttribute("src", this.settings.default_document);
1053
+
1054
+		iframe.style.width = aw;
1055
+		iframe.style.height = ah;
1056
+
1057
+		// Ugly hack for Gecko problem in strict mode
1058
+		if (tinyMCE.settings.strict_loading_mode)
1059
+			iframe.style.marginBottom = '-5px';
1060
+
1061
+		// MSIE 5.0 issue
1062
+		if (tinyMCE.isRealIE)
1063
+			replace_element.outerHTML = iframe.outerHTML;
1064
+		else
1065
+			replace_element.parentNode.replaceChild(iframe, replace_element);
1066
+
1067
+		if (tinyMCE.isRealIE)
1068
+			return win.frames[id];
1069
+		else
1070
+			return iframe;
1071
+	},
1072
+
1073
+	/**
1074
+	 * Setups the contents of TinyMCE editor instance and fills it with contents.
1075
+	 *
1076
+	 * @param {string} editor_id TinyMCE editor instance control id to fill.
1077
+	 */
1078
+	setupContent : function(editor_id) {
1079
+		var inst = tinyMCE.instances[editor_id], i, doc = inst.getDoc(), head = doc.getElementsByTagName('head').item(0);
1080
+		var content = inst.startContent, contentElement, body;
1081
+
1082
+		// HTML values get XML encoded in strict mode
1083
+		if (tinyMCE.settings.strict_loading_mode) {
1084
+			content = content.replace(/&lt;/g, '<');
1085
+			content = content.replace(/&gt;/g, '>');
1086
+			content = content.replace(/&quot;/g, '"');
1087
+			content = content.replace(/&amp;/g, '&');
1088
+		}
1089
+
1090
+		tinyMCE.selectedInstance = inst;
1091
+		inst.switchSettings();
1092
+
1093
+		// Not loaded correctly hit it again, Mozilla bug #997860
1094
+		if (!tinyMCE.isIE && tinyMCE.getParam("setupcontent_reload", false) && doc.title != "blank_page") {
1095
+			// This part will remove the designMode status
1096
+			// Failes first time in Firefox 1.5b2 on Mac
1097
+			try {doc.location.href = tinyMCE.baseURL + "/blank.htm";} catch (ex) {}
1098
+			window.setTimeout("tinyMCE.setupContent('" + editor_id + "');", 1000);
1099
+			return;
1100
+		}
1101
+
1102
+		// Wait for it to load
1103
+		if (!head || !doc.body) {
1104
+			window.setTimeout("tinyMCE.setupContent('" + editor_id + "');", 10);
1105
+			return;
1106
+		}
1107
+
1108
+		// Import theme specific content CSS the user specific
1109
+		tinyMCE.importCSS(inst.getDoc(), tinyMCE.baseURL + "/themes/" + inst.settings.theme + "/css/editor_content.css");
1110
+		tinyMCE.importCSS(inst.getDoc(), inst.settings.content_css);
1111
+		tinyMCE.dispatchCallback(inst, 'init_instance_callback', 'initInstance', inst);
1112
+
1113
+		// Setup keyboard shortcuts
1114
+		if (tinyMCE.getParam('custom_undo_redo_keyboard_shortcuts')) {
1115
+			inst.addShortcut('ctrl', 'z', 'lang_undo_desc', 'Undo');
1116
+			inst.addShortcut('ctrl', 'y', 'lang_redo_desc', 'Redo');
1117
+		}
1118
+
1119
+		// BlockFormat shortcuts keys
1120
+		for (i=1; i<=6; i++)
1121
+			inst.addShortcut('ctrl', '' + i, '', 'FormatBlock', false, '<h' + i + '>');
1122
+
1123
+		inst.addShortcut('ctrl', '7', '', 'FormatBlock', false, '<p>');
1124
+		inst.addShortcut('ctrl', '8', '', 'FormatBlock', false, '<div>');
1125
+		inst.addShortcut('ctrl', '9', '', 'FormatBlock', false, '<address>');
1126
+
1127
+		// Add default shortcuts for gecko
1128
+		if (tinyMCE.isGecko) {
1129
+			inst.addShortcut('ctrl', 'b', 'lang_bold_desc', 'Bold');
1130
+			inst.addShortcut('ctrl', 'i', 'lang_italic_desc', 'Italic');
1131
+			inst.addShortcut('ctrl', 'u', 'lang_underline_desc', 'Underline');
1132
+		}
1133
+
1134
+		// Setup span styles
1135
+		if (tinyMCE.getParam("convert_fonts_to_spans"))
1136
+			inst.getBody().setAttribute('id', 'mceSpanFonts');
1137
+
1138
+		if (tinyMCE.settings.nowrap)
1139
+			doc.body.style.whiteSpace = "nowrap";
1140
+
1141
+		doc.body.dir = this.settings.directionality;
1142
+		doc.editorId = editor_id;
1143
+
1144
+		// Add on document element in Mozilla
1145
+		if (!tinyMCE.isIE)
1146
+			doc.documentElement.editorId = editor_id;
1147
+
1148
+		inst.setBaseHREF(tinyMCE.settings.base_href);
1149
+
1150
+		// Replace new line characters to BRs
1151
+		if (tinyMCE.settings.convert_newlines_to_brs) {
1152
+			content = tinyMCE.regexpReplace(content, "\r\n", "<br />", "gi");
1153
+			content = tinyMCE.regexpReplace(content, "\r", "<br />", "gi");
1154
+			content = tinyMCE.regexpReplace(content, "\n", "<br />", "gi");
1155
+		}
1156
+
1157
+		// Open closed anchors
1158
+	//	content = content.replace(new RegExp('<a(.*?)/>', 'gi'), '<a$1></a>');
1159
+
1160
+		// Call custom cleanup code
1161
+		content = tinyMCE.storeAwayURLs(content);
1162
+		content = tinyMCE._customCleanup(inst, "insert_to_editor", content);
1163
+
1164
+		if (tinyMCE.isIE) {
1165
+			// Ugly!!!
1166
+			window.setInterval('try{tinyMCE.getCSSClasses(tinyMCE.instances["' + editor_id + '"].getDoc(), "' + editor_id + '");}catch(e){}', 500);
1167
+
1168
+			if (tinyMCE.settings.force_br_newlines)
1169
+				doc.styleSheets[0].addRule("p", "margin: 0;");
1170
+
1171
+			body = inst.getBody();
1172
+			body.editorId = editor_id;
1173
+		}
1174
+
1175
+		content = tinyMCE.cleanupHTMLCode(content);
1176
+
1177
+		// Fix for bug #958637
1178
+		if (!tinyMCE.isIE) {
1179
+			contentElement = inst.getDoc().createElement("body");
1180
+			doc = inst.getDoc();
1181
+
1182
+			contentElement.innerHTML = content;
1183
+
1184
+			if (tinyMCE.settings.cleanup_on_startup)
1185
+				tinyMCE.setInnerHTML(inst.getBody(), tinyMCE._cleanupHTML(inst, doc, this.settings, contentElement));
1186
+			else
1187
+				tinyMCE.setInnerHTML(inst.getBody(), content);
1188
+
1189
+			tinyMCE.convertAllRelativeURLs(inst.getBody());
1190
+		} else {
1191
+			if (tinyMCE.settings.cleanup_on_startup) {
1192
+				tinyMCE._setHTML(inst.getDoc(), content);
1193
+
1194
+				// Produces permission denied error in MSIE 5.5
1195
+				try {
1196
+					tinyMCE.setInnerHTML(inst.getBody(), tinyMCE._cleanupHTML(inst, inst.contentDocument, this.settings, inst.getBody()));
1197
+				} catch(e) {
1198
+					// Ignore
1199
+				}
1200
+			} else
1201
+				tinyMCE._setHTML(inst.getDoc(), content);
1202
+		}
1203
+
1204
+		// Fix for bug #957681
1205
+		//inst.getDoc().designMode = inst.getDoc().designMode;
1206
+
1207
+		tinyMCE.handleVisualAid(inst.getBody(), true, tinyMCE.settings.visual, inst);
1208
+		tinyMCE.dispatchCallback(inst, 'setupcontent_callback', 'setupContent', editor_id, inst.getBody(), inst.getDoc());
1209
+
1210
+		// Re-add design mode on mozilla
1211
+		if (!tinyMCE.isIE)
1212
+			tinyMCE.addEventHandlers(inst);
1213
+
1214
+		// Add blur handler
1215
+		if (tinyMCE.isIE) {
1216
+			tinyMCE.addEvent(inst.getBody(), "blur", TinyMCE_Engine.prototype._eventPatch);
1217
+			tinyMCE.addEvent(inst.getBody(), "beforedeactivate", TinyMCE_Engine.prototype._eventPatch); // Bug #1439953
1218
+
1219
+			// Workaround for drag drop/copy paste base href bug
1220
+			if (!tinyMCE.isOpera) {
1221
+				tinyMCE.addEvent(doc.body, "mousemove", TinyMCE_Engine.prototype.onMouseMove);
1222
+				tinyMCE.addEvent(doc.body, "beforepaste", TinyMCE_Engine.prototype._eventPatch);
1223
+				tinyMCE.addEvent(doc.body, "drop", TinyMCE_Engine.prototype._eventPatch);
1224
+			}
1225
+		}
1226
+
1227
+		// Trigger node change, this call locks buttons for tables and so forth
1228
+		inst.select();
1229
+		tinyMCE.selectedElement = inst.contentWindow.document.body;
1230
+
1231
+		// Call custom DOM cleanup
1232
+		tinyMCE._customCleanup(inst, "insert_to_editor_dom", inst.getBody());
1233
+		tinyMCE._customCleanup(inst, "setup_content_dom", inst.getBody());
1234
+		tinyMCE._setEventsEnabled(inst.getBody(), false);
1235
+		tinyMCE.cleanupAnchors(inst.getDoc());
1236
+
1237
+		if (tinyMCE.getParam("convert_fonts_to_spans"))
1238
+			tinyMCE.convertSpansToFonts(inst.getDoc());
1239
+
1240
+		inst.startContent = tinyMCE.trim(inst.getBody().innerHTML);
1241
+		inst.undoRedo.add({ content : inst.startContent });
1242
+
1243
+		// Cleanup any mess left from storyAwayURLs
1244
+		if (tinyMCE.isGecko) {
1245
+			// Remove mce_src from textnodes and comments
1246
+			tinyMCE.selectNodes(inst.getBody(), function(n) {
1247
+				if (n.nodeType == 3 || n.nodeType == 8)
1248
+					n.nodeValue = n.nodeValue.replace(new RegExp('\\s(mce_src|mce_href)=\"[^\"]*\"', 'gi'), "");
1249
+
1250
+				return false;
1251
+			});
1252
+		}
1253
+
1254
+		// Remove Gecko spellchecking
1255
+		if (tinyMCE.isGecko)
1256
+			inst.getBody().spellcheck = tinyMCE.getParam("gecko_spellcheck");
1257
+
1258
+		// Cleanup any mess left from storyAwayURLs
1259
+		tinyMCE._removeInternal(inst.getBody());
1260
+
1261
+		inst.select();
1262
+		tinyMCE.triggerNodeChange(false, true);
1263
+	},
1264
+
1265
+	/**
1266
+	 * Stores away the src and href attribute values in separate mce_src and mce_href attributes.
1267
+	 * This is needed since both MSIE and Gecko messes with these attributes. The old
1268
+	 * src and href will be intact, this simply adds them to a separate attribute.
1269
+	 *
1270
+	 * @param {string} s HTML string to replace src and href attributes in.
1271
+	 * @return HTML string with replaced src and href attributes.
1272
+	 * @type string
1273
+	 */
1274
+	storeAwayURLs : function(s) {
1275
+		// Remove all mce_src, mce_href and replace them with new ones
1276
+		// s = s.replace(new RegExp('mce_src\\s*=\\s*\"[^ >\"]*\"', 'gi'), '');
1277
+		// s = s.replace(new RegExp('mce_href\\s*=\\s*\"[^ >\"]*\"', 'gi'), '');
1278
+
1279
+		if (!s.match(/(mce_src|mce_href)/gi, s)) {
1280
+			s = s.replace(new RegExp('src\\s*=\\s*\"([^ >\"]*)\"', 'gi'), 'src="$1" mce_src="$1"');
1281
+			s = s.replace(new RegExp('href\\s*=\\s*\"([^ >\"]*)\"', 'gi'), 'href="$1" mce_href="$1"');
1282
+		}
1283
+
1284
+		return s;
1285
+	},
1286
+
1287
+	/**
1288
+	 * Removes any internal content inserted by regexps.
1289
+	 *
1290
+	 * @param {DOMNode} n Node to remove internal content from.
1291
+	 */
1292
+	_removeInternal : function(n) {
1293
+		if (tinyMCE.isGecko) {
1294
+			// Remove mce_src from textnodes and comments
1295
+			tinyMCE.selectNodes(n, function(n) {
1296
+				if (n.nodeType == 3 || n.nodeType == 8)
1297
+					n.nodeValue = n.nodeValue.replace(new RegExp('\\s(mce_src|mce_href)=\"[^\"]*\"', 'gi'), "");
1298
+
1299
+				return false;
1300
+			});
1301
+		}
1302
+	},
1303
+
1304
+	/**
1305
+	 * Removes/disables TinyMCE built in form elements such as select boxes for font sizes etc.
1306
+	 * These are disabled when the user submits a form so they don't get picked up by the backend script
1307
+	 * that intercepts the contents.
1308
+	 *
1309
+	 * @param {HTMLElement} form_obj Form object to loop through for TinyMCE specific form elements.
1310
+	 */
1311
+	removeTinyMCEFormElements : function(form_obj) {
1312
+		var i, elementId;
1313
+
1314
+		// Skip form element removal
1315
+		if (!tinyMCE.getParam('hide_selects_on_submit'))
1316
+			return;
1317
+
1318
+		// Check if form is valid
1319
+		if (typeof(form_obj) == "undefined" || form_obj == null)
1320
+			return;
1321
+
1322
+		// If not a form, find the form
1323
+		if (form_obj.nodeName != "FORM") {
1324
+			if (form_obj.form)
1325
+				form_obj = form_obj.form;
1326
+			else
1327
+				form_obj = tinyMCE.getParentElement(form_obj, "form");
1328
+		}
1329
+
1330
+		// Still nothing
1331
+		if (form_obj == null)
1332
+			return;
1333
+
1334
+		// Disable all UI form elements that TinyMCE created
1335
+		for (i=0; i<form_obj.elements.length; i++) {
1336
+			elementId = form_obj.elements[i].name ? form_obj.elements[i].name : form_obj.elements[i].id;
1337
+
1338
+			if (elementId.indexOf('mce_editor_') == 0)
1339
+				form_obj.elements[i].disabled = true;
1340
+		}
1341
+	},
1342
+
1343
+	/**
1344
+	 * Event handler function that gets executed each time a event occurs in a TinyMCE editor control instance.
1345
+	 * Todo: Fix the return statements so they return true or false.
1346
+	 *
1347
+	 * @param {DOMEvent} e DOM event object reference.
1348
+	 * @return true - if the event is to be chained, false - if the event chain is to be canceled.
1349
+	 * @type boolean
1350
+	 */
1351
+	handleEvent : function(e) {
1352
+		var inst = tinyMCE.selectedInstance, i, elm, keys;
1353
+
1354
+		// Remove odd, error
1355
+		if (typeof(tinyMCE) == "undefined")
1356
+			return true;
1357
+
1358
+		//tinyMCE.debug(e.type + " " + e.target.nodeName + " " + (e.relatedTarget ? e.relatedTarget.nodeName : ""));
1359
+
1360
+		if (tinyMCE.executeCallback(tinyMCE.selectedInstance, 'handle_event_callback', 'handleEvent', e))
1361
+			return false;
1362
+
1363
+		switch (e.type) {
1364
+			case "beforedeactivate": // Was added due to bug #1439953
1365
+			case "blur":
1366
+				if (tinyMCE.selectedInstance)
1367
+					tinyMCE.selectedInstance.execCommand('mceEndTyping');
1368
+
1369
+				tinyMCE.hideMenus();
1370
+
1371
+				return;
1372
+
1373
+			// Workaround for drag drop/copy paste base href bug
1374
+			case "drop":
1375
+			case "beforepaste":
1376
+				if (tinyMCE.selectedInstance)
1377
+					tinyMCE.selectedInstance.setBaseHREF(null);
1378
+
1379
+				// Fixes odd MSIE bug where drag/droping elements in a iframe with height 100% breaks
1380
+				// This logic forces the width/height to be in pixels while the user is drag/dropping
1381
+				if (tinyMCE.isRealIE) {
1382
+					var ife = tinyMCE.selectedInstance.iframeElement;
1383
+
1384
+					/*if (ife.style.width.indexOf('%') != -1) {
1385
+						ife._oldWidth = ife.width.height;
1386
+						ife.style.width = ife.clientWidth;
1387
+					}*/
1388
+
1389
+					if (ife.style.height.indexOf('%') != -1) {
1390
+						ife._oldHeight = ife.style.height;
1391
+						ife.style.height = ife.clientHeight;
1392
+					}
1393
+				}
1394
+
1395
+				window.setTimeout("tinyMCE.selectedInstance.setBaseHREF(tinyMCE.settings.base_href);tinyMCE._resetIframeHeight();", 1);
1396
+				return;
1397
+
1398
+			case "submit":
1399
+				tinyMCE.formSubmit(tinyMCE.isMSIE ? window.event.srcElement : e.target);
1400
+				return;
1401
+
1402
+			case "reset":
1403
+				var formObj = tinyMCE.isIE ? window.event.srcElement : e.target;
1404
+
1405
+				for (i=0; i<document.forms.length; i++) {
1406
+					if (document.forms[i] == formObj)
1407
+						window.setTimeout('tinyMCE.resetForm(' + i + ');', 10);
1408
+				}
1409
+
1410
+				return;
1411
+
1412
+			case "keypress":
1413
+				if (inst && inst.handleShortcut(e))
1414
+					return false;
1415
+
1416
+				if (e.target.editorId) {
1417
+					tinyMCE.instances[e.target.editorId].select();
1418
+				} else {
1419
+					if (e.target.ownerDocument.editorId)
1420
+						tinyMCE.instances[e.target.ownerDocument.editorId].select();
1421
+				}
1422
+
1423
+				if (tinyMCE.selectedInstance)
1424
+					tinyMCE.selectedInstance.switchSettings();
1425
+
1426
+				// Insert P element
1427
+				if ((tinyMCE.isGecko || tinyMCE.isOpera || tinyMCE.isSafari) && tinyMCE.settings.force_p_newlines && e.keyCode == 13 && !e.shiftKey) {
1428
+					// Insert P element instead of BR
1429
+					if (TinyMCE_ForceParagraphs._insertPara(tinyMCE.selectedInstance, e)) {
1430
+						// Cancel event
1431
+						tinyMCE.execCommand("mceAddUndoLevel");
1432
+						return tinyMCE.cancelEvent(e);
1433
+					}
1434
+				}
1435
+
1436
+				// Handle backspace
1437
+				if ((tinyMCE.isGecko && !tinyMCE.isSafari) && tinyMCE.settings.force_p_newlines && (e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey) {
1438
+					// Insert P element instead of BR
1439
+					if (TinyMCE_ForceParagraphs._handleBackSpace(tinyMCE.selectedInstance, e.type)) {
1440
+						// Cancel event
1441
+						tinyMCE.execCommand("mceAddUndoLevel");
1442
+						return tinyMCE.cancelEvent(e);
1443
+					}
1444
+				}
1445
+
1446
+				// Return key pressed
1447
+				if (tinyMCE.isIE && tinyMCE.settings.force_br_newlines && e.keyCode == 13) {
1448
+					if (e.target.editorId)
1449
+						tinyMCE.instances[e.target.editorId].select();
1450
+
1451
+					if (tinyMCE.selectedInstance) {
1452
+						var sel = tinyMCE.selectedInstance.getDoc().selection;
1453
+						var rng = sel.createRange();
1454
+
1455
+						if (tinyMCE.getParentElement(rng.parentElement(), "li") != null)
1456
+							return false;
1457
+
1458
+						// Cancel event
1459
+						e.returnValue = false;
1460
+						e.cancelBubble = true;
1461
+
1462
+						// Insert BR element
1463
+						rng.pasteHTML("<br />");
1464
+						rng.collapse(false);
1465
+						rng.select();
1466
+
1467
+						tinyMCE.execCommand("mceAddUndoLevel");
1468
+						tinyMCE.triggerNodeChange(false);
1469
+						return false;
1470
+					}
1471
+				}
1472
+
1473
+				// Backspace or delete
1474
+				if (e.keyCode == 8 || e.keyCode == 46) {
1475
+					tinyMCE.selectedElement = e.target;
1476
+					tinyMCE.linkElement = tinyMCE.getParentElement(e.target, "a");
1477
+					tinyMCE.imgElement = tinyMCE.getParentElement(e.target, "img");
1478
+					tinyMCE.triggerNodeChange(false);
1479
+				}
1480
+
1481
+				return false;
1482
+
1483
+			case "keyup":
1484
+			case "keydown":
1485
+				tinyMCE.hideMenus();
1486
+				tinyMCE.hasMouseMoved = false;
1487
+
1488
+				if (inst && inst.handleShortcut(e))
1489
+					return false;
1490
+
1491
+				inst._fixRootBlocks();
1492
+
1493
+				if (inst.settings.remove_trailing_nbsp)
1494
+					inst._fixTrailingNbsp();
1495
+
1496
+				if (e.target.editorId)
1497
+					tinyMCE.instances[e.target.editorId].select();
1498
+
1499
+				if (tinyMCE.selectedInstance)
1500
+					tinyMCE.selectedInstance.switchSettings();
1501
+
1502
+				inst = tinyMCE.selectedInstance;
1503
+
1504
+				// Handle backspace
1505
+				if (tinyMCE.isGecko && tinyMCE.settings.force_p_newlines && (e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey) {
1506
+					// Insert P element instead of BR
1507
+					if (TinyMCE_ForceParagraphs._handleBackSpace(tinyMCE.selectedInstance, e.type)) {
1508
+						// Cancel event
1509
+						tinyMCE.execCommand("mceAddUndoLevel");
1510
+						e.preventDefault();
1511
+						return false;
1512
+					}
1513
+				}
1514
+
1515
+				tinyMCE.selectedElement = null;
1516
+				tinyMCE.selectedNode = null;
1517
+				elm = tinyMCE.selectedInstance.getFocusElement();
1518
+				tinyMCE.linkElement = tinyMCE.getParentElement(elm, "a");
1519
+				tinyMCE.imgElement = tinyMCE.getParentElement(elm, "img");
1520
+				tinyMCE.selectedElement = elm;
1521
+
1522
+				// Update visualaids on tabs
1523
+				if (tinyMCE.isGecko && e.type == "keyup" && e.keyCode == 9)
1524
+					tinyMCE.handleVisualAid(tinyMCE.selectedInstance.getBody(), true, tinyMCE.settings.visual, tinyMCE.selectedInstance);
1525
+
1526
+				// Fix empty elements on return/enter, check where enter occured
1527
+				if (tinyMCE.isIE && e.type == "keydown" && e.keyCode == 13)
1528
+					tinyMCE.enterKeyElement = tinyMCE.selectedInstance.getFocusElement();
1529
+
1530
+				// Fix empty elements on return/enter
1531
+				if (tinyMCE.isIE && e.type == "keyup" && e.keyCode == 13) {
1532
+					elm = tinyMCE.enterKeyElement;
1533
+					if (elm) {
1534
+						var re = new RegExp('^HR|IMG|BR$','g'); // Skip these
1535
+						var dre = new RegExp('^H[1-6]$','g'); // Add double on these
1536
+
1537
+						if (!elm.hasChildNodes() && !re.test(elm.nodeName)) {
1538
+							if (dre.test(elm.nodeName))
1539
+								elm.innerHTML = "&nbsp;&nbsp;";
1540
+							else
1541
+								elm.innerHTML = "&nbsp;";
1542
+						}
1543
+					}
1544
+				}
1545
+
1546
+				// Check if it's a position key
1547
+				keys = tinyMCE.posKeyCodes;
1548
+				var posKey = false;
1549
+				for (i=0; i<keys.length; i++) {
1550
+					if (keys[i] == e.keyCode) {
1551
+						posKey = true;
1552
+						break;
1553
+					}
1554
+				}
1555
+
1556
+				// MSIE custom key handling
1557
+				if (tinyMCE.isIE && tinyMCE.settings.custom_undo_redo) {
1558
+					keys = [8, 46]; // Backspace,Delete
1559
+
1560
+					for (i=0; i<keys.length; i++) {
1561
+						if (keys[i] == e.keyCode) {
1562
+							if (e.type == "keyup")
1563
+								tinyMCE.triggerNodeChange(false);
1564
+						}
1565
+					}
1566
+				}
1567
+
1568
+				// If Ctrl key
1569
+				if (e.keyCode == 17)
1570
+					return true;
1571
+
1572
+				// Handle Undo/Redo when typing content
1573
+
1574
+				if (tinyMCE.isGecko) {
1575
+					// Start typing (not a position key or ctrl key, but ctrl+x and ctrl+p is ok)
1576
+					if (!posKey && e.type == "keyup" && !e.ctrlKey || (e.ctrlKey && (e.keyCode == 86 || e.keyCode == 88)))
1577
+						tinyMCE.execCommand("mceStartTyping");
1578
+				} else {
1579
+					// IE seems to be working better with this setting
1580
+					if (!posKey && e.type == "keyup")
1581
+						tinyMCE.execCommand("mceStartTyping");
1582
+				}
1583
+
1584
+				// Store undo bookmark
1585
+				if (e.type == "keydown" && (posKey || e.ctrlKey) && inst)
1586
+					inst.undoBookmark = inst.selection.getBookmark();
1587
+
1588
+				// End typing (position key) or some Ctrl event
1589
+				if (e.type == "keyup" && (posKey || e.ctrlKey))
1590
+					tinyMCE.execCommand("mceEndTyping");
1591
+
1592
+				if (posKey && e.type == "keyup")
1593
+					tinyMCE.triggerNodeChange(false);
1594
+
1595
+				if (tinyMCE.isIE && e.ctrlKey)
1596
+					window.setTimeout('tinyMCE.triggerNodeChange(false);', 1);
1597
+			break;
1598
+
1599
+			case "mousedown":
1600
+			case "mouseup":
1601
+			case "click":
1602
+			case "dblclick":
1603
+			case "focus":
1604
+				tinyMCE.hideMenus();
1605
+
1606
+				if (tinyMCE.selectedInstance) {
1607
+					tinyMCE.selectedInstance.switchSettings();
1608
+					tinyMCE.selectedInstance.isFocused = true;
1609
+				}
1610
+
1611
+				// Check instance event trigged on
1612
+				var targetBody = tinyMCE.getParentElement(e.target, "html");
1613
+				for (var instanceName in tinyMCE.instances) {
1614
+					if (!tinyMCE.isInstance(tinyMCE.instances[instanceName]))
1615
+						continue;
1616
+
1617
+					inst = tinyMCE.instances[instanceName];
1618
+
1619
+					// Reset design mode if lost (on everything just in case)
1620
+					inst.autoResetDesignMode();
1621
+
1622
+					// Use HTML element since users might click outside of body element
1623
+					if (inst.getBody().parentNode == targetBody) {
1624
+						inst.select();
1625
+						tinyMCE.selectedElement = e.target;
1626
+						tinyMCE.linkElement = tinyMCE.getParentElement(tinyMCE.selectedElement, "a");
1627
+						tinyMCE.imgElement = tinyMCE.getParentElement(tinyMCE.selectedElement, "img");
1628
+						break;
1629
+					}
1630
+				}
1631
+
1632
+				// Add first bookmark location
1633
+				if (!tinyMCE.selectedInstance.undoRedo.undoLevels[0].bookmark && (e.type == "mouseup" || e.type == "dblclick"))
1634
+					tinyMCE.selectedInstance.undoRedo.undoLevels[0].bookmark = tinyMCE.selectedInstance.selection.getBookmark();
1635
+
1636
+				// Reset selected node
1637
+				if (e.type != "focus")
1638
+					tinyMCE.selectedNode = null;
1639
+
1640
+				tinyMCE.triggerNodeChange(false);
1641
+				tinyMCE.execCommand("mceEndTyping");
1642
+
1643
+				if (e.type == "mouseup")
1644
+					tinyMCE.execCommand("mceAddUndoLevel");
1645
+
1646
+				// Just in case
1647
+				if (!tinyMCE.selectedInstance && e.target.editorId)
1648
+					tinyMCE.instances[e.target.editorId].select();
1649
+
1650
+				return false;
1651
+		}
1652
+	},
1653
+
1654
+	/**
1655
+	 * Returns the HTML code for a normal button control.
1656
+	 *
1657
+	 * @param {string} id Button control id, this will be the suffix for the element id, the prefix is the editor id.
1658
+	 * @param {string} lang Language variable key name to insert as the title/alt of the button image.
1659
+	 * @param {string} img Image URL to insert, {$themeurl} and {$pluginurl} will be replaced.
1660
+	 * @param {string} cmd Command to execute when the user clicks the button.
1661
+	 * @param {string} ui Optional user interface boolean for command.
1662
+	 * @param {string} val Optional value for command.
1663
+	 * @return HTML code for a normal button based in input information.
1664
+	 * @type string
1665
+	 */
1666
+	getButtonHTML : function(id, lang, img, cmd, ui, val) {
1667
+		var h = '', m, x, io = '';
1668
+
1669
+		cmd = 'tinyMCE.execInstanceCommand(\'{$editor_id}\',\'' + cmd + '\'';
1670
+
1671
+		if (typeof(ui) != "undefined" && ui != null)
1672
+			cmd += ',' + ui;
1673
+
1674
+		if (typeof(val) != "undefined" && val != null)
1675
+			cmd += ",'" + val + "'";
1676
+
1677
+		cmd += ');';
1678
+
1679
+		// Patch for IE7 bug with hover out not restoring correctly
1680
+		if (tinyMCE.isRealIE)
1681
+			io = 'onmouseover="tinyMCE.lastHover = this;"';
1682
+
1683
+		// Use tilemaps when enabled and found and never in MSIE since it loads the tile each time from cache if cahce is disabled
1684
+		if (tinyMCE.getParam('button_tile_map') && (!tinyMCE.isIE || tinyMCE.isOpera) && (m = this.buttonMap[id]) != null && (tinyMCE.getParam("language") == "en" || img.indexOf('$lang') == -1)) {
1685
+			// Tiled button
1686
+			x = 0 - (m * 20) == 0 ? '0' : 0 - (m * 20);
1687
+			h += '<a id="{$editor_id}_' + id + '" href="javascript:' + cmd + '" onclick="' + cmd + 'return false;" onmousedown="return false;" ' + io + ' class="mceTiledButton mceButtonNormal" target="_self">';
1688
+			h += '<img src="{$themeurl}/images/spacer.gif" style="background-position: ' + x + 'px 0" alt="{$'+lang+'}" title="{$' + lang + '}" />';
1689
+			h += '</a>';
1690
+		} else {
1691
+			// Normal button
1692
+			h += '<a id="{$editor_id}_' + id + '" href="javascript:' + cmd + '" onclick="' + cmd + 'return false;" onmousedown="return false;" ' + io + ' class="mceButtonNormal" target="_self">';
1693
+			h += '<img src="' + img + '" alt="{$'+lang+'}" title="{$' + lang + '}" />';
1694
+			h += '</a>';
1695
+		}
1696
+
1697
+		return h;
1698
+	},
1699
+
1700
+	/**
1701
+	 * Returns the HTML code for a normal button control.
1702
+	 *
1703
+	 * @param {string} id Button control id, this will be the suffix for the element id, the prefix is the editor id.
1704
+	 * @param {string} lang Language variable key name to insert as the title/alt of the button image.
1705
+	 * @param {string} img Image URL to insert, {$themeurl} and {$pluginurl} will be replaced.
1706
+	 * @param {string} mcmd Command to execute when the user clicks the menu arrow button.
1707
+	 * @param {string} cmd Command to execute when the user clicks the main button.
1708
+	 * @param {string} ui Optional user interface boolean for command.
1709
+	 * @param {string} val Optional value for command.
1710
+	 * @return HTML code for a normal button based in input information.
1711
+	 * @type string
1712
+	 */
1713
+	getMenuButtonHTML : function(id, lang, img, mcmd, cmd, ui, val) {
1714
+		var h = '', m, x;
1715
+
1716
+		mcmd = 'tinyMCE.execInstanceCommand(\'{$editor_id}\',\'' + mcmd + '\');';
1717
+		cmd = 'tinyMCE.execInstanceCommand(\'{$editor_id}\',\'' + cmd + '\'';
1718
+
1719
+		if (typeof(ui) != "undefined" && ui != null)
1720
+			cmd += ',' + ui;
1721
+
1722
+		if (typeof(val) != "undefined" && val != null)
1723
+			cmd += ",'" + val + "'";
1724
+
1725
+		cmd += ');';
1726
+
1727
+		// Use tilemaps when enabled and found and never in MSIE since it loads the tile each time from cache if cahce is disabled
1728
+		if (tinyMCE.getParam('button_tile_map') && (!tinyMCE.isIE || tinyMCE.isOpera) && (m = tinyMCE.buttonMap[id]) != null && (tinyMCE.getParam("language") == "en" || img.indexOf('$lang') == -1)) {
1729
+			x = 0 - (m * 20) == 0 ? '0' : 0 - (m * 20);
1730
+
1731
+			if (tinyMCE.isRealIE)
1732
+				h += '<span id="{$editor_id}_' + id + '" class="mceMenuButton" onmouseover="tinyMCE._menuButtonEvent(\'over\',this);tinyMCE.lastHover = this;" onmouseout="tinyMCE._menuButtonEvent(\'out\',this);">';
1733
+			else
1734
+				h += '<span id="{$editor_id}_' + id + '" class="mceMenuButton">';
1735
+
1736
+			h += '<a href="javascript:' + cmd + '" onclick="' + cmd + 'return false;" onmousedown="return false;" class="mceTiledButton mceMenuButtonNormal" target="_self">';
1737
+			h += '<img src="{$themeurl}/images/spacer.gif" style="width: 20px; height: 20px; background-position: ' + x + 'px 0" title="{$' + lang + '}" /></a>';
1738
+			h += '<a href="javascript:' + mcmd + '" onclick="' + mcmd + 'return false;" onmousedown="return false;"><img src="{$themeurl}/images/button_menu.gif" title="{$' + lang + '}" class="mceMenuButton" />';
1739
+			h += '</a></span>';
1740
+		} else {
1741
+			if (tinyMCE.isRealIE)
1742
+				h += '<span id="{$editor_id}_' + id + '" dir="ltr" class="mceMenuButton" onmouseover="tinyMCE._menuButtonEvent(\'over\',this);tinyMCE.lastHover = this;" onmouseout="tinyMCE._menuButtonEvent(\'out\',this);">';
1743
+			else
1744
+				h += '<span id="{$editor_id}_' + id + '" dir="ltr" class="mceMenuButton">';
1745
+
1746
+			h += '<a href="javascript:' + cmd + '" onclick="' + cmd + 'return false;" onmousedown="return false;" class="mceMenuButtonNormal" target="_self">';
1747
+			h += '<img src="' + img + '" title="{$' + lang + '}" /></a>';
1748
+			h += '<a href="javascript:' + mcmd + '" onclick="' + mcmd + 'return false;" onmousedown="return false;"><img src="{$themeurl}/images/button_menu.gif" title="{$' + lang + '}" class="mceMenuButton" />';
1749
+			h += '</a></span>';
1750
+		}
1751
+
1752
+		return h;
1753
+	},
1754
+
1755
+	/**
1756
+	 * Switched classes on menu elements in MSIE.
1757
+	 *
1758
+	 * @param {string} e Event name	out, over.
1759
+	 * @param {HTMLElement} o HTML element to set class on.
1760
+	 */
1761
+	_menuButtonEvent : function(e, o) {
1762
+		if (o.className == 'mceMenuButtonFocus')
1763
+			return;
1764
+
1765
+		if (e == 'over')
1766
+			o.className = o.className + ' mceMenuHover';
1767
+		else
1768
+			o.className = o.className.replace(/\s.*$/, '');
1769
+	},
1770
+
1771
+	/**
1772
+	 * Adds a list of buttons available in the tiled button image used by the button_tile_map option.
1773
+	 *
1774
+	 * @param {string} m Comma separated list of buttons that are available in tiled image.
1775
+	 */
1776
+	addButtonMap : function(m) {
1777
+		var i, a = m.replace(/\s+/, '').split(',');
1778
+
1779
+		for (i=0; i<a.length; i++)
1780
+			this.buttonMap[a[i]] = i;
1781
+	},
1782
+
1783
+	/**
1784
+	 * Gets called when a form is submited with a f.submit call or when a submit button is pressed.
1785
+	 *
1786
+	 * @param {HTMLForm} f Form element that got submitted.
1787
+	 * @param {bool} p Is it a f.submit pathed call.
1788
+	 */
1789
+	formSubmit : function(f, p) {
1790
+		var n, inst, found = false;
1791
+
1792
+		if (f.form)
1793
+			f = f.form;
1794
+
1795
+		// Is it a form that has a TinyMCE instance
1796
+		if (tinyMCE.getParam('save_on_tinymce_forms')) {
1797
+			for (n in tinyMCE.instances) {
1798
+				inst = tinyMCE.instances[n];
1799
+
1800
+				if (!tinyMCE.isInstance(inst))
1801
+					continue;
1802
+
1803
+				if (inst.formElement) {
1804
+					if (f == inst.formElement.form) {
1805
+						found = true;
1806
+						inst.isNotDirty = true;
1807
+					}
1808
+				}
1809
+			}
1810
+		} else
1811
+			found  = true;
1812
+
1813
+		// Is valid
1814
+		if (found) {
1815
+			tinyMCE.removeTinyMCEFormElements(f);
1816
+			tinyMCE.triggerSave();
1817
+		}
1818
+
1819
+		// Is it patched
1820
+		if (f.mceOldSubmit && p)
1821
+			f.mceOldSubmit();
1822
+	},
1823
+
1824
+	/**
1825
+	 * Piggyback onsubmit event handler function, this will remove/hide the TinyMCE specific form elements
1826
+	 * call triggerSave to fill the textarea with the correct contents then call the old piggy backed event handler.
1827
+	 */
1828
+	submitPatch : function() {
1829
+		tinyMCE.formSubmit(this, true);
1830
+	},
1831
+
1832
+	/**
1833
+	 * Gets executed when the page loads or get intitialized. This function will then convert all textareas/divs that
1834
+	 * is to be converted into TinyMCE editor controls.
1835
+	 *
1836
+	 * @return true - if the event is to be chained, false - if the event chain is to be canceled.
1837
+	 * @type boolean
1838
+	 */
1839
+	onLoad : function() {
1840
+		var r, i, c, mode, trigger, elements, element, settings, elementId, elm;
1841
+		var selector, deselector, elementRefAr, form;
1842
+
1843
+		// Wait for everything to be loaded first
1844
+		if (tinyMCE.settings.strict_loading_mode && this.loadingIndex != -1) {
1845
+			window.setTimeout('tinyMCE.onLoad();', 1);
1846
+			return;
1847
+		}
1848
+
1849
+		if (tinyMCE.isRealIE && window.event.type == "readystatechange" && document.readyState != "complete")
1850
+			return true;
1851
+
1852
+		if (tinyMCE.isLoaded)
1853
+			return true;
1854
+
1855
+		tinyMCE.isLoaded = true;
1856
+
1857
+		// IE produces JS error if TinyMCE is placed in a frame
1858
+		// It seems to have something to do with the selection not beeing
1859
+		// correctly initialized in IE so this hack solves the problem
1860
+		if (tinyMCE.isRealIE && document.body && window.location.href != window.top.location.href) {
1861
+			r = document.body.createTextRange();
1862
+			r.collapse(true);
1863
+			r.select();
1864
+		}
1865
+
1866
+		tinyMCE.dispatchCallback(null, 'onpageload', 'onPageLoad');
1867
+
1868
+		for (c=0; c<tinyMCE.configs.length; c++) {
1869
+			tinyMCE.settings = tinyMCE.configs[c];
1870
+
1871
+			selector = tinyMCE.getParam("editor_selector");
1872
+			deselector = tinyMCE.getParam("editor_deselector");
1873
+			elementRefAr = [];
1874
+
1875
+			// Add submit triggers
1876
+			if (document.forms && tinyMCE.settings.add_form_submit_trigger && !tinyMCE.submitTriggers) {
1877
+				for (i=0; i<document.forms.length; i++) {
1878
+					form = document.forms[i];
1879
+
1880
+					tinyMCE.addEvent(form, "submit", TinyMCE_Engine.prototype.handleEvent);
1881
+					tinyMCE.addEvent(form, "reset", TinyMCE_Engine.prototype.handleEvent);
1882
+					tinyMCE.submitTriggers = true; // Do it only once
1883
+
1884
+					// Patch the form.submit function
1885
+					if (tinyMCE.settings.submit_patch) {
1886
+						try {
1887
+							form.mceOldSubmit = form.submit;
1888
+							form.submit = TinyMCE_Engine.prototype.submitPatch;
1889
+						} catch (e) {
1890
+							// Do nothing
1891
+						}
1892
+					}
1893
+				}
1894
+			}
1895
+
1896
+			// Add editor instances based on mode
1897
+			mode = tinyMCE.settings.mode;
1898
+			switch (mode) {
1899
+				case "exact":
1900
+					elements = tinyMCE.getParam('elements', '', true, ',');
1901
+
1902
+					for (i=0; i<elements.length; i++) {
1903
+						element = tinyMCE._getElementById(elements[i]);
1904
+						trigger = element ? element.getAttribute(tinyMCE.settings.textarea_trigger) : "";
1905
+
1906
+						if (new RegExp('\\b' + deselector + '\\b').test(tinyMCE.getAttrib(element, "class")))
1907
+							continue;
1908
+
1909
+						if (trigger == "false")
1910
+							continue;
1911
+
1912
+						if ((tinyMCE.settings.ask || tinyMCE.settings.convert_on_click) && element) {
1913
+							elementRefAr[elementRefAr.length] = element;
1914
+							continue;
1915
+						}
1916
+
1917
+						if (element)
1918
+							tinyMCE.addMCEControl(element, elements[i]);
1919
+					}
1920
+				break;
1921
+
1922
+				case "specific_textareas":
1923
+				case "textareas":
1924
+					elements = document.getElementsByTagName("textarea");
1925
+
1926
+					for (i=0; i<elements.length; i++) {
1927
+						elm = elements.item(i);
1928
+						trigger = elm.getAttribute(tinyMCE.settings.textarea_trigger);
1929
+
1930
+						if (selector !== '' && !new RegExp('\\b' + selector + '\\b').test(tinyMCE.getAttrib(elm, "class")))
1931
+							continue;
1932
+
1933
+						if (selector !== '')
1934
+							trigger = selector !== '' ? "true" : "";
1935
+
1936
+						if (new RegExp('\\b' + deselector + '\\b').test(tinyMCE.getAttrib(elm, "class")))
1937
+							continue;
1938
+
1939
+						if ((mode == "specific_textareas" && trigger == "true") || (mode == "textareas" && trigger != "false"))
1940
+							elementRefAr[elementRefAr.length] = elm;
1941
+					}
1942
+				break;
1943
+			}
1944
+
1945
+			for (i=0; i<elementRefAr.length; i++) {
1946
+				element = elementRefAr[i];
1947
+				elementId = element.name ? element.name : element.id;
1948
+
1949
+				if (tinyMCE.settings.ask || tinyMCE.settings.convert_on_click) {
1950
+					// Focus breaks in Mozilla
1951
+					if (tinyMCE.isGecko) {
1952
+						settings = tinyMCE.settings;
1953
+
1954
+						tinyMCE.addEvent(element, "focus", function (e) {window.setTimeout(function() {TinyMCE_Engine.prototype.confirmAdd(e, settings);}, 10);});
1955
+
1956
+						if (element.nodeName != "TEXTAREA" && element.nodeName != "INPUT")
1957
+							tinyMCE.addEvent(element, "click", function (e) {window.setTimeout(function() {TinyMCE_Engine.prototype.confirmAdd(e, settings);}, 10);});
1958
+						// tinyMCE.addEvent(element, "mouseover", function (e) {window.setTimeout(function() {TinyMCE_Engine.prototype.confirmAdd(e, settings);}, 10);});
1959
+					} else {
1960
+						settings = tinyMCE.settings;
1961
+
1962
+						tinyMCE.addEvent(element, "focus", function () { TinyMCE_Engine.prototype.confirmAdd(null, settings); });
1963
+						tinyMCE.addEvent(element, "click", function () { TinyMCE_Engine.prototype.confirmAdd(null, settings); });
1964
+						// tinyMCE.addEvent(element, "mouseenter", function () { TinyMCE_Engine.prototype.confirmAdd(null, settings); });
1965
+					}
1966
+				} else
1967
+					tinyMCE.addMCEControl(element, elementId);
1968
+			}
1969
+
1970
+			// Handle auto focus
1971
+			if (tinyMCE.settings.auto_focus) {
1972
+				window.setTimeout(function () {
1973
+					var inst = tinyMCE.getInstanceById(tinyMCE.settings.auto_focus);
1974
+					inst.selection.selectNode(inst.getBody(), true, true);
1975
+					inst.contentWindow.focus();
1976
+				}, 100);
1977
+			}
1978
+
1979
+			tinyMCE.dispatchCallback(null, 'oninit', 'onInit');
1980
+		}
1981
+	},
1982
+
1983
+	/**
1984
+	 * Returns true/false if a specific object is a TinyMCE_Control instance or not.
1985
+	 *
1986
+	 * @param {object} o Object to check.
1987
+	 * @return true/false if it's a control or not.
1988
+	 * @type boolean
1989
+	 */
1990
+	isInstance : function(o) {
1991
+		return o != null && typeof(o) == "object" && o.isTinyMCE_Control;
1992
+	},
1993
+
1994
+	/**
1995
+	 * Returns a specific configuration setting or the default value if it wasn't found.
1996
+	 *
1997
+	 * @param {string} name Configuration setting to get.
1998
+	 * @param {string} default_value Default value to return if it wasn't found.
1999
+	 * @param {boolean} strip_whitespace Optional remove all whitespace.
2000
+	 * @param {string} split_chr Split char/regex/string.
2001
+	 * @return Number, string or other object based in parameter and default_value.
2002
+	 * @type object
2003
+	 */
2004
+	getParam : function(name, default_value, strip_whitespace, split_chr) {
2005
+		var i, outArray, value = (typeof(this.settings[name]) == "undefined") ? default_value : this.settings[name];
2006
+
2007
+		// Fix bool values
2008
+		if (value == "true" || value == "false")
2009
+			return (value == "true");
2010
+
2011
+		if (strip_whitespace)
2012
+			value = tinyMCE.regexpReplace(value, "[ \t\r\n]", "");
2013
+
2014
+		if (typeof(split_chr) != "undefined" && split_chr != null) {
2015
+			value = value.split(split_chr);
2016
+			outArray = [];
2017
+
2018
+			for (i=0; i<value.length; i++) {
2019
+				if (value[i] && value[i] !== '')
2020
+					outArray[outArray.length] = value[i];
2021
+			}
2022
+
2023
+			value = outArray;
2024
+		}
2025
+
2026
+		return value;
2027
+	},
2028
+
2029
+	/**
2030
+	 * Returns a language variable value from the language packs.
2031
+	 *
2032
+	 * @param {string} name Name of the key to retrive.
2033
+	 * @param {string} default_value Optional default value to return if it wasn't found.
2034
+	 * @param {boolean} parse_entities Is HTML entities to be resolved or not.
2035
+	 * @param {Array} va Optional name/value array of variables to replace in language string.	 
2036
+	 * @return Language string value could be a number if it's a relative dimenstion.
2037
+	 * @type object
2038
+	 */
2039
+	getLang : function(name, default_value, parse_entities, va) {
2040
+		var v = (typeof(tinyMCELang[name]) == "undefined") ? default_value : tinyMCELang[name], n;
2041
+
2042
+		if (parse_entities)
2043
+			v = tinyMCE.entityDecode(v);
2044
+
2045
+		if (va) {
2046
+			for (n in va)
2047
+				v = this.replaceVar(v, n, va[n]);
2048
+		}
2049
+
2050
+		return v;
2051
+	},
2052
+
2053
+	/**
2054
+	 * HTML entity decode a string, replaces &lt; with <.
2055
+	 *
2056
+	 * @param {string} s Entity string to decode into normal string.
2057
+	 * @return Entity decoded string.
2058
+	 * @type string
2059
+	 */
2060
+	entityDecode : function(s) {
2061
+		var e = document.createElement("div");
2062
+
2063
+		e.innerHTML = s;
2064
+
2065
+		return !e.firstChild ? s : e.firstChild.nodeValue;
2066
+	},
2067
+
2068
+	/**
2069
+	 * Adds language items to the global language array.
2070
+	 *
2071
+	 * @param {string} prefix Prefix string to add infront of every array item before adding it.
2072
+	 * @param {Array} ar Language item array to add to global language array.
2073
+	 */
2074
+	addToLang : function(prefix, ar) {
2075
+		var k;
2076
+
2077
+		for (k in ar) {
2078
+			if (typeof(ar[k]) == 'function')
2079
+				continue;
2080
+
2081
+			tinyMCELang[(k.indexOf('lang_') == -1 ? 'lang_' : '') + (prefix !== '' ? (prefix + "_") : '') + k] = ar[k];
2082
+		}
2083
+
2084
+		this.loadNextScript();
2085
+	},
2086
+
2087
+	/**
2088
+	 * Triggers a nodeChange event to every theme and plugin. This will be executed when the cursor moves or
2089
+	 * when a command that modifies the editor contents is executed.
2090
+	 *
2091
+	 * @param {boolean} focus Optional state if the last selected editor instance is to be focused or not.
2092
+	 * @param {boolean} setup_content Optional state if it's called from setup content function or not.
2093
+	 */
2094
+	triggerNodeChange : function(focus, setup_content) {
2095
+		var elm, inst, editorId, undoIndex = -1, undoLevels = -1, doc, anySelection = false, st;
2096
+
2097
+		if (tinyMCE.selectedInstance) {
2098
+			inst = tinyMCE.selectedInstance;
2099
+			elm = (typeof(setup_content) != "undefined" && setup_content) ? tinyMCE.selectedElement : inst.getFocusElement();
2100
+
2101
+/*			if (elm == inst.lastTriggerEl)
2102
+				return;
2103
+
2104
+			inst.lastTriggerEl = elm;*/
2105
+
2106
+			editorId = inst.editorId;
2107
+			st = inst.selection.getSelectedText();
2108
+
2109
+			if (tinyMCE.settings.auto_resize)
2110
+				inst.resizeToContent();
2111
+
2112
+			if (setup_content && tinyMCE.isGecko && inst.isHidden())
2113
+				elm = inst.getBody();
2114
+
2115
+			inst.switchSettings();
2116
+
2117
+			if (tinyMCE.selectedElement)
2118
+				anySelection = (tinyMCE.selectedElement.nodeName.toLowerCase() == "img") || (st && st.length > 0);
2119
+
2120
+			if (tinyMCE.settings.custom_undo_redo) {
2121
+				undoIndex = inst.undoRedo.undoIndex;
2122
+				undoLevels = inst.undoRedo.undoLevels.length;
2123
+			}
2124
+
2125
+			tinyMCE.dispatchCallback(inst, 'handle_node_change_callback', 'handleNodeChange', editorId, elm, undoIndex, undoLevels, inst.visualAid, anySelection, setup_content);
2126
+		}
2127
+
2128
+		if (this.selectedInstance && (typeof(focus) == "undefined" || focus))
2129
+			this.selectedInstance.contentWindow.focus();
2130
+	},
2131
+
2132
+	/**
2133
+	 * Executes the custom cleanup functions on the specified content.
2134
+	 *
2135
+	 * @param {TinyMCE_Control} inst TinyMCE editor control instance.
2136
+	 * @param {string} type Event type to call.
2137
+	 * @param {object} content DOM element or string to pass to handlers depending on type.
2138
+	 * @return string or DOM element depending on type.
2139
+	 * @private
2140
+	 */
2141
+	_customCleanup : function(inst, type, content) {
2142
+		var pl, po, i, customCleanup;
2143
+
2144
+		// Call custom cleanup
2145
+		customCleanup = tinyMCE.settings.cleanup_callback;
2146
+		if (customCleanup != '')
2147
+			content = tinyMCE.resolveDots(tinyMCE.settings.cleanup_callback, window)(type, content, inst);
2148
+
2149
+		// Trigger theme cleanup
2150
+		po = tinyMCE.themes[tinyMCE.settings.theme];
2151
+		if (po && po.cleanup)
2152
+			content = po.cleanup(type, content, inst);
2153
+
2154
+		// Trigger plugin cleanups
2155
+		pl = inst.plugins;
2156
+		for (i=0; i<pl.length; i++) {
2157
+			po = tinyMCE.plugins[pl[i]];
2158
+
2159
+			if (po && po.cleanup)
2160
+				content = po.cleanup(type, content, inst);
2161
+		}
2162
+
2163
+		return content;
2164
+	},
2165
+
2166
+	/**
2167
+	 * Sets the HTML contents of the selected editor instance.
2168
+	 *
2169
+	 * @param {string} h HTML contents to set in the selected instance.
2170
+	 * @deprecated
2171
+	 */
2172
+	setContent : function(h) {
2173
+		if (tinyMCE.selectedInstance) {
2174
+			tinyMCE.selectedInstance.execCommand('mceSetContent', false, h);
2175
+			tinyMCE.selectedInstance.repaint();
2176
+		}
2177
+	},
2178
+
2179
+	/**
2180
+	 * Loads a theme specific language pack.
2181
+	 *
2182
+	 * @param {string} name Optional name of the theme to load language pack from.
2183
+	 */
2184
+	importThemeLanguagePack : function(name) {
2185
+		if (typeof(name) == "undefined")
2186
+			name = tinyMCE.settings.theme;
2187
+
2188
+		tinyMCE.loadScript(tinyMCE.baseURL + '/themes/' + name + '/langs/' + tinyMCE.settings.language + '.js');
2189
+	},
2190
+
2191
+	/**
2192
+	 * Loads a plugin specific language pack.
2193
+	 *
2194
+	 * @param {string} name Plugin name/id to load language pack for.
2195
+	 */
2196
+	importPluginLanguagePack : function(name) {
2197
+		var b = tinyMCE.baseURL + '/plugins/' + name;
2198
+
2199
+		if (this.plugins[name])
2200
+			b = this.plugins[name].baseURL;
2201
+
2202
+		tinyMCE.loadScript(b + '/langs/' + tinyMCE.settings.language +  '.js');
2203
+	},
2204
+
2205
+	/**
2206
+	 * Replaces language, args and settings variables in a HTML string.
2207
+	 *
2208
+	 * @param {string} h HTML string to replace language variables in.
2209
+	 * @param {Array} ag Optional arguments array to take variables from.
2210
+	 * @return HTML string with replaced varliables.
2211
+	 * @type string
2212
+	 */
2213
+	applyTemplate : function(h, ag) {
2214
+		return h.replace(new RegExp('\\{\\$([a-z0-9_]+)\\}', 'gi'), function(m, s) {
2215
+			if (s.indexOf('lang_') == 0 && tinyMCELang[s])
2216
+				return tinyMCELang[s];
2217
+
2218
+			if (ag && ag[s])
2219
+				return ag[s];
2220
+
2221
+			if (tinyMCE.settings[s])
2222
+				return tinyMCE.settings[s];
2223
+
2224
+			if (m == 'themeurl')
2225
+				return tinyMCE.themeURL;
2226
+
2227
+			return m;
2228
+		});
2229
+	},
2230
+
2231
+	/**
2232
+	 * Replaces a specific variable in the string with a nother string.
2233
+	 *
2234
+	 * @param {string} h String to search in for the variable.
2235
+	 * @param {string} r Variable name to search for.
2236
+	 * @param {string} v Value to insert where a variable is found.
2237
+	 * @return String with replaced variable.
2238
+	 * @type string
2239
+	 */
2240
+	replaceVar : function(h, r, v) {
2241
+		return h.replace(new RegExp('{\\\$' + r + '}', 'g'), v);
2242
+	},
2243
+
2244
+	/**
2245
+	 * Opens a popup window based in the specified input data. This function
2246
+	 * is used for all popup windows in TinyMCE.
2247
+	 *
2248
+	 * These are the current template keys: file, width, height, close_previous.
2249
+	 *
2250
+	 * @param {Array} template Popup template data such as with, height etc.
2251
+	 * @param {Array} args Popup arguments that is to be passed to the popup such as custom data.
2252
+	 */
2253
+	openWindow : function(template, args) {
2254
+		var html, width, height, x, y, resizable, scrollbars, url, name, win, modal, features;
2255
+
2256
+		args = !args ? {} : args;
2257
+
2258
+		args.mce_template_file = template.file;
2259
+		args.mce_width = template.width;
2260
+		args.mce_height = template.height;
2261
+		tinyMCE.windowArgs = args;
2262
+
2263
+		html = template.html;
2264
+		if (!(width = parseInt(template.width)))
2265
+			width = 320;
2266
+
2267
+		if (!(height = parseInt(template.height)))
2268
+			height = 200;
2269
+
2270
+		// Add to height in M$ due to SP2 WHY DON'T YOU GUYS IMPLEMENT innerWidth of windows!!
2271
+		if (tinyMCE.isIE)
2272
+			height += 40;
2273
+		else
2274
+			height += 20;
2275
+
2276
+		x = parseInt(screen.width / 2.0) - (width / 2.0);
2277
+		y = parseInt(screen.height / 2.0) - (height / 2.0);
2278
+
2279
+		resizable = (args && args.resizable) ? args.resizable : "no";
2280
+		scrollbars = (args && args.scrollbars) ? args.scrollbars : "no";
2281
+
2282
+		if (template.file.charAt(0) != '/' && template.file.indexOf('://') == -1)
2283
+			url = tinyMCE.baseURL + "/themes/" + tinyMCE.getParam("theme") + "/" + template.file;
2284
+		else
2285
+			url = template.file;
2286
+
2287
+		// Replace all args as variables in URL
2288
+		for (name in args) {
2289
+			if (typeof(args[name]) == 'function')
2290
+				continue;
2291
+
2292
+			url = tinyMCE.replaceVar(url, name, escape(args[name]));
2293
+		}
2294
+
2295
+		if (html) {
2296
+			html = tinyMCE.replaceVar(html, "css", this.settings.popups_css);
2297
+			html = tinyMCE.applyTemplate(html, args);
2298
+
2299
+			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);
2300
+			if (win == null) {
2301
+				alert(tinyMCELang.lang_popup_blocked);
2302
+				return;
2303
+			}
2304
+
2305
+			win.document.write(html);
2306
+			win.document.close();
2307
+			win.resizeTo(width, height);
2308
+			win.focus();
2309
+		} else {
2310
+			if ((tinyMCE.isRealIE) && resizable != 'yes' && tinyMCE.settings.dialog_type == "modal") {
2311
+				height += 10;
2312
+
2313
+				features = "resizable:" + resizable + ";scroll:" + scrollbars + ";status:yes;center:yes;help:no;dialogWidth:" + width + "px;dialogHeight:" + height + "px;";
2314
+
2315
+				window.showModalDialog(url, window, features);
2316
+			} else {
2317
+				modal = (resizable == "yes") ? "no" : "yes";
2318
+
2319
+				if (tinyMCE.isGecko && tinyMCE.isMac)
2320
+					modal = "no";
2321
+
2322
+				if (template.close_previous != "no")
2323
+					try {tinyMCE.lastWindow.close();} catch (ex) {}
2324
+
2325
+				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);
2326
+				if (win == null) {
2327
+					alert(tinyMCELang.lang_popup_blocked);
2328
+					return;
2329
+				}
2330
+
2331
+				if (template.close_previous != "no")
2332
+					tinyMCE.lastWindow = win;
2333
+
2334
+				try {
2335
+					win.resizeTo(width, height);
2336
+				} catch(e) {
2337
+					// Ignore
2338
+				}
2339
+
2340
+				// Make it bigger if statusbar is forced
2341
+				if (tinyMCE.isGecko) {
2342
+					if (win.document.defaultView.statusbar.visible)
2343
+						win.resizeBy(0, tinyMCE.isMac ? 10 : 24);
2344
+				}
2345
+
2346
+				win.focus();
2347
+			}
2348
+		}
2349
+	},
2350
+
2351
+	/**
2352
+	 * Closes the specified window. This function is deprecated and should be replaced with
2353
+	 * tinyMCEPopup.close();.
2354
+	 *
2355
+	 * @param {DOMWindow} win Window reference to close.
2356
+	 * @deprecated
2357
+	 */
2358
+	closeWindow : function(win) {
2359
+		win.close();
2360
+	},
2361
+
2362
+	/**
2363
+	 * Returns the visual aid class string, this will add/remove the visual aid class.
2364
+	 *
2365
+	 * @param {string} class_name Class name value to add/remove visual aid classes from.
2366
+	 * @param {boolean} state true/false if the classes should be added or removed.
2367
+	 * @return New class value containing the visual aid classes or not.
2368
+	 * @type string
2369
+	 */
2370
+	getVisualAidClass : function(class_name, state) {
2371
+		var i, classNames, ar, className, aidClass = tinyMCE.settings.visual_table_class;
2372
+
2373
+		if (typeof(state) == "undefined")
2374
+			state = tinyMCE.settings.visual;
2375
+
2376
+		// Split
2377
+		classNames = [];
2378
+		ar = class_name.split(' ');
2379
+		for (i=0; i<ar.length; i++) {
2380
+			if (ar[i] == aidClass)
2381
+				ar[i] = "";
2382
+
2383
+			if (ar[i] !== '')
2384
+				classNames[classNames.length] = ar[i];
2385
+		}
2386
+
2387
+		if (state)
2388
+			classNames[classNames.length] = aidClass;
2389
+
2390
+		// Glue
2391
+		className = "";
2392
+		for (i=0; i<classNames.length; i++) {
2393
+			if (i > 0)
2394
+				className += " ";
2395
+
2396
+			className += classNames[i];
2397
+		}
2398
+
2399
+		return className;
2400
+	},
2401
+
2402
+	/**
2403
+	 * Adds visual aid classes to all elements that need them recursive in the DOM tree.
2404
+	 *
2405
+	 * @param {HTMLElement} el HTML element to add visual aid classes to.
2406
+	 * @param {boolean} deep Should they be added to all children aswell.
2407
+	 * @param {boolean} state Should they be added or removed.
2408
+	 * @param {TinyMCE_Control} inst TinyMCE editor control instance to add/remove them to/from.
2409
+	 */
2410
+	handleVisualAid : function(el, deep, state, inst, skip_dispatch) {
2411
+		var i, x, y, tableElement, anchorName, oldW, oldH, bo, cn;
2412
+
2413
+		if (!el)
2414
+			return;
2415
+
2416
+		if (!skip_dispatch)
2417
+			tinyMCE.dispatchCallback(inst, 'handle_visual_aid_callback', 'handleVisualAid', el, deep, state, inst);
2418
+
2419
+		tableElement = null;
2420
+
2421
+		switch (el.nodeName) {
2422
+			case "TABLE":
2423
+				oldW = el.style.width;
2424
+				oldH = el.style.height;
2425
+				bo = tinyMCE.getAttrib(el, "border");
2426
+
2427
+				bo = bo == '' || bo == "0" ? true : false;
2428
+
2429
+				tinyMCE.setAttrib(el, "class", tinyMCE.getVisualAidClass(tinyMCE.getAttrib(el, "class"), state && bo));
2430
+
2431
+				el.style.width = oldW;
2432
+				el.style.height = oldH;
2433
+
2434
+				for (y=0; y<el.rows.length; y++) {
2435
+					for (x=0; x<el.rows[y].cells.length; x++) {
2436
+						cn = tinyMCE.getVisualAidClass(tinyMCE.getAttrib(el.rows[y].cells[x], "class"), state && bo);
2437
+						tinyMCE.setAttrib(el.rows[y].cells[x], "class", cn);
2438
+					}
2439
+				}
2440
+
2441
+				break;
2442
+
2443
+			case "A":
2444
+				anchorName = tinyMCE.getAttrib(el, "name");
2445
+
2446
+				if (anchorName !== '' && state) {
2447
+					el.title = anchorName;
2448
+					tinyMCE.addCSSClass(el, 'mceItemAnchor');
2449
+				} else if (anchorName !== '' && !state)
2450
+					el.className = '';
2451
+
2452
+				break;
2453
+		}
2454
+
2455
+		if (deep && el.hasChildNodes()) {
2456
+			for (i=0; i<el.childNodes.length; i++)
2457
+				tinyMCE.handleVisualAid(el.childNodes[i], deep, state, inst, true);
2458
+		}
2459
+	},
2460
+
2461
+	/**
2462
+	 * Fixes a Gecko specific bug where href, src attribute values gets converted incorrectly
2463
+	 * when inserted into editor. This function will replace all src, href with mce_tsrc and mce_thref
2464
+	 * to keep the values from chaging when they get inserted.
2465
+	 *
2466
+	 * @param {boolean} m Mode state, true is to replace the src, href attributes to mce_tsrc and mce_thref.
2467
+	 * @param {HTMLElement} e HTML element to replace them in. (Will be used if m is 0)
2468
+	 * @param {string} h HTML code to replace them in. (Will be used if m is 1)
2469
+	 * @return Converted string or the specified HTML value depending on mode.
2470
+	 * @type string
2471
+	 */
2472
+	fixGeckoBaseHREFBug : function(m, e, h) {
2473
+		var xsrc, xhref;
2474
+
2475
+		if (tinyMCE.isGecko) {
2476
+			if (m == 1) {
2477
+				h = h.replace(/\ssrc=/gi, " mce_tsrc=");
2478
+				h = h.replace(/\shref=/gi, " mce_thref=");
2479
+
2480
+				return h;
2481
+			} else {
2482
+				// Why bother if there is no src or href broken
2483
+				if (!new RegExp('(src|href)=', 'g').test(h))
2484
+					return h;
2485
+
2486
+				// Restore src and href that gets messed up by Gecko
2487
+				tinyMCE.selectElements(e, 'A,IMG,SELECT,AREA,IFRAME,BASE,INPUT,SCRIPT,EMBED,OBJECT,LINK', function (n) {
2488
+					xsrc = tinyMCE.getAttrib(n, "mce_tsrc");
2489
+					xhref = tinyMCE.getAttrib(n, "mce_thref");
2490
+
2491
+					if (xsrc !== '') {
2492
+						try {
2493
+							n.src = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings.base_href, xsrc);
2494
+						} catch (e) {
2495
+							// Ignore, Firefox cast exception if local file wasn't found
2496
+						}
2497
+
2498
+						n.removeAttribute("mce_tsrc");
2499
+					}
2500
+
2501
+					if (xhref !== '') {
2502
+						try {
2503
+							n.href = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings.base_href, xhref);
2504
+						} catch (e) {
2505
+							// Ignore, Firefox cast exception if local file wasn't found
2506
+						}
2507
+
2508
+						n.removeAttribute("mce_thref");
2509
+					}
2510
+
2511
+					return false;
2512
+				});
2513
+
2514
+				// Restore text/comment nodes
2515
+				tinyMCE.selectNodes(e, function(n) {
2516
+					if (n.nodeType == 3 || n.nodeType == 8) {
2517
+						n.nodeValue = n.nodeValue.replace(/\smce_tsrc=/gi, " src=");
2518
+						n.nodeValue = n.nodeValue.replace(/\smce_thref=/gi, " href=");
2519
+					}
2520
+
2521
+					return false;
2522
+				});
2523
+			}
2524
+		}
2525
+
2526
+		return h;
2527
+	},
2528
+
2529
+	/**
2530
+	 * Sets the HTML code of a specific document.
2531
+	 * Todo: Try to merge/remove this one.
2532
+	 *
2533
+	 * @param {DOMDocument} doc DOM document to set the HTML code in.
2534
+	 * @param {string} html_content HTML contents to set in DOM document.
2535
+	 * @private
2536
+	 */
2537
+	_setHTML : function(doc, html_content) {
2538
+		var i, html, paras, node;
2539
+
2540
+		// Force closed anchors open
2541
+		//html_content = html_content.replace(new RegExp('<a(.*?)/>', 'gi'), '<a$1></a>');
2542
+
2543
+		html_content = tinyMCE.cleanupHTMLCode(html_content);
2544
+
2545
+		// Try innerHTML if it fails use pasteHTML in MSIE
2546
+		try {
2547
+			tinyMCE.setInnerHTML(doc.body, html_content);
2548
+		} catch (e) {
2549
+			if (this.isMSIE)
2550
+				doc.body.createTextRange().pasteHTML(html_content);
2551
+		}
2552
+
2553
+		// Content duplication bug fix
2554
+		if (tinyMCE.isIE && tinyMCE.settings.fix_content_duplication) {
2555
+			// Remove P elements in P elements
2556
+			paras = doc.getElementsByTagName("P");
2557
+			for (i=0; i<paras.length; i++) {
2558
+				node = paras[i];
2559
+
2560
+				while ((node = node.parentNode) != null) {
2561
+					if (node.nodeName == "P")
2562
+						node.outerHTML = node.innerHTML;
2563
+				}
2564
+			}
2565
+
2566
+			// Content duplication bug fix (Seems to be word crap)
2567
+			html = doc.body.innerHTML;
2568
+
2569
+			// Always set the htmlText output
2570
+			tinyMCE.setInnerHTML(doc.body, html);
2571
+		}
2572
+
2573
+		tinyMCE.cleanupAnchors(doc);
2574
+
2575
+		if (tinyMCE.getParam("convert_fonts_to_spans"))
2576
+			tinyMCE.convertSpansToFonts(doc);
2577
+	},
2578
+
2579
+	/**
2580
+	 * Returns the editor instance id of a specific form element.
2581
+	 *
2582
+	 * @param {string} form_element Form element name to get instance id for.
2583
+	 * @return TinyMCE editor instance id or null if it wasn't found.
2584
+	 * @type string
2585
+	 */
2586
+	getEditorId : function(form_element) {
2587
+		var inst = this.getInstanceById(form_element);
2588
+
2589
+		if (!inst)
2590
+			return null;
2591
+
2592
+		return inst.editorId;
2593
+	},
2594
+
2595
+	/**
2596
+	 * Returns a TinyMCE editor instance by the specified editor id or null if it wasn't found.
2597
+	 *
2598
+	 * @param {string} editor_id Editor id to get instance for.
2599
+	 * @return TinyMCE editor control instance or null if it wasn't found.
2600
+	 * @type TinyMCE_Control
2601
+	 */
2602
+	getInstanceById : function(editor_id) {
2603
+		var inst = this.instances[editor_id], n;
2604
+
2605
+		if (!inst) {
2606
+			for (n in tinyMCE.instances) {
2607
+				inst = tinyMCE.instances[n];
2608
+
2609
+				if (!tinyMCE.isInstance(inst))
2610
+					continue;
2611
+
2612
+				if (inst.formTargetElementId == editor_id)
2613
+					return inst;
2614
+			}
2615
+		} else
2616
+			return inst;
2617
+
2618
+		return null;
2619
+	},
2620
+
2621
+	/**
2622
+	 * Queries a command value for a specific command on a specific editor instance.
2623
+	 *
2624
+	 * @param {string} editor_id Editor id to query command value on.
2625
+	 * @param {string} command Command to query for.
2626
+	 * @return Command value passed from browser.
2627
+	 * @type object
2628
+	 */
2629
+	queryInstanceCommandValue : function(editor_id, command) {
2630
+		var inst = tinyMCE.getInstanceById(editor_id);
2631
+
2632
+		if (inst)
2633
+			return inst.queryCommandValue(command);
2634
+
2635
+		return false;
2636
+	},
2637
+
2638
+	/**
2639
+	 * Queries a command state for a specific command on a specific editor instance.
2640
+	 *
2641
+	 * @param {string} editor_id Editor id to query command state on.
2642
+	 * @param {string} command Command to query for.
2643
+	 * @return Command state passed from browser.
2644
+	 * @type boolean
2645
+	 */
2646
+	queryInstanceCommandState : function(editor_id, command) {
2647
+		var inst = tinyMCE.getInstanceById(editor_id);
2648
+
2649
+		if (inst)
2650
+			return inst.queryCommandState(command);
2651
+
2652
+		return null;
2653
+	},
2654
+
2655
+	/**
2656
+	 * Sets the window argument to be passed to TinyMCE popup.
2657
+	 *
2658
+	 * @param {string} n Window argument name.
2659
+	 * @param {string} v Window argument value.
2660
+	 */
2661
+	setWindowArg : function(n, v) {
2662
+		this.windowArgs[n] = v;
2663
+	},
2664
+
2665
+	/**
2666
+	 * Returns the window argument to be passed to TinyMCE popup.
2667
+	 * Use: tinyMCEPopup.getWindowArg instead.
2668
+	 *
2669
+	 * @param {string} n Window argument name.
2670
+	 * @return Argument value or default value if it wasn't found.
2671
+	 * @deprecated
2672
+	 */
2673
+	getWindowArg : function(n, d) {
2674
+		return (typeof(this.windowArgs[n]) == "undefined") ? d : this.windowArgs[n];
2675
+	},
2676
+
2677
+	/**
2678
+	 * Returns a array of CSS classes that is available in a document.
2679
+	 * Todo: Fix this one, it's so ugly. :)
2680
+	 *
2681
+	 * @param {string} editor_id Editor id to get CSS classes from.
2682
+	 * @param {DOMDocument} doc DOM document to get the CSS classes from.
2683
+	 * @return Array of CSS classes that is available in a document.
2684
+	 * @type Array
2685
+	 */
2686
+	getCSSClasses : function(editor_id, doc) {
2687
+		var i, c, x, rule, styles, rules, csses, selectorText, inst = tinyMCE.getInstanceById(editor_id);
2688
+		var cssClass, addClass, p;
2689
+
2690
+		if (!inst)
2691
+			inst = tinyMCE.selectedInstance;
2692
+
2693
+		if (!inst)
2694
+			return [];
2695
+
2696
+		if (!doc)
2697
+			doc = inst.getDoc();
2698
+
2699
+		// Is cached, use that
2700
+		if (inst && inst.cssClasses.length > 0)
2701
+			return inst.cssClasses;
2702
+
2703
+		if (!doc)
2704
+			return;
2705
+
2706
+		styles = doc.styleSheets;
2707
+
2708
+		if (styles && styles.length > 0) {
2709
+			for (x=0; x<styles.length; x++) {
2710
+				csses = null;
2711
+
2712
+				try {
2713
+					csses = tinyMCE.isIE ? doc.styleSheets(x).rules : styles[x].cssRules;
2714
+				} catch(e) {
2715
+					// Just ignore any errors I know this is ugly!!
2716
+				}
2717
+	
2718
+				if (!csses)
2719
+					return [];
2720
+
2721
+				for (i=0; i<csses.length; i++) {
2722
+					selectorText = csses[i].selectorText;
2723
+
2724
+					// Can be multiple rules per selector
2725
+					if (selectorText) {
2726
+						rules = selectorText.split(',');
2727
+						for (c=0; c<rules.length; c++) {
2728
+							rule = rules[c];
2729
+
2730
+							// Strip spaces between selectors
2731
+							while (rule.indexOf(' ') == 0)
2732
+								rule = rule.substring(1);
2733
+
2734
+							// Invalid rule
2735
+							if (rule.indexOf(' ') != -1 || rule.indexOf(':') != -1 || rule.indexOf('mceItem') != -1)
2736
+								continue;
2737
+
2738
+							if (rule.indexOf(tinyMCE.settings.visual_table_class) != -1 || rule.indexOf('mceEditable') != -1 || rule.indexOf('mceNonEditable') != -1)
2739
+								continue;
2740
+
2741
+							// Is class rule
2742
+							if (rule.indexOf('.') != -1) {
2743
+								cssClass = rule.substring(rule.indexOf('.') + 1);
2744
+								addClass = true;
2745
+
2746
+								for (p=0; p<inst.cssClasses.length && addClass; p++) {
2747
+									if (inst.cssClasses[p] == cssClass)
2748
+										addClass = false;
2749
+								}
2750
+
2751
+								if (addClass)
2752
+									inst.cssClasses[inst.cssClasses.length] = cssClass;
2753
+							}
2754
+						}
2755
+					}
2756
+				}
2757
+			}
2758
+		}
2759
+
2760
+		return inst.cssClasses;
2761
+	},
2762
+
2763
+	/**
2764
+	 * Regexp replaces the contents of a string. Use normal replace instead.
2765
+	 *
2766
+	 * @param {string} in_str String to replace in.
2767
+	 * @param {string} reg_exp Regexp to replace.
2768
+	 * @param {string} replace_str String to replace with.
2769
+	 * @param {string} in_str Optional regexp options like "gi".
2770
+	 * @return Replaced string value.
2771
+	 * @type string
2772
+	 * @deprecated
2773
+	 */
2774
+	regexpReplace : function(in_str, reg_exp, replace_str, opts) {
2775
+		var re;
2776
+
2777
+		if (in_str == null)
2778
+			return in_str;
2779
+
2780
+		if (typeof(opts) == "undefined")
2781
+			opts = 'g';
2782
+
2783
+		re = new RegExp(reg_exp, opts);
2784
+
2785
+		return in_str.replace(re, replace_str);
2786
+	},
2787
+
2788
+	/**
2789
+	 * Removes all prefix, suffix whitespace of a string.
2790
+	 *
2791
+	 * @param {string} s String to replace whitespace in.
2792
+	 * @return Replaced string value.
2793
+	 * @type string
2794
+	 */
2795
+	trim : function(s) {
2796
+		return s.replace(/^\s*|\s*$/g, "");
2797
+	},
2798
+
2799
+	/**
2800
+	 * Removes MSIE 5.5 specific event wrapper function form a event string.
2801
+	 * This will also remove the event blocker if it's added in front of the event.
2802
+	 *
2803
+	 * @param {string} s String to replace event data in.
2804
+	 * @return Replaced string value.
2805
+	 * @type string
2806
+	 */
2807
+	cleanupEventStr : function(s) {
2808
+		s = "" + s;
2809
+		s = s.replace('function anonymous()\n{\n', '');
2810
+		s = s.replace('\n}', '');
2811
+		s = s.replace(/^return true;/gi, ''); // Remove event blocker
2812
+
2813
+		return s;
2814
+	},
2815
+
2816
+	/**
2817
+	 * Returns the HTML for the specified control this will loop through
2818
+	 * the theme and all plugins inorder to find the control. The callback for each
2819
+	 * theme and plugin is called getControlHTML.
2820
+	 *
2821
+	 * @param {string} c Control name/id to get HTML code for.
2822
+	 * @return HTML code for the specified control or empty string if it wasn't found.
2823
+	 * @type string
2824
+	 */
2825
+	getControlHTML : function(c) {
2826
+		var i, l, n, o, v, rtl = tinyMCE.getLang('lang_dir') == 'rtl';
2827
+
2828
+		l = tinyMCE.plugins;
2829
+		for (n in l) {
2830
+			o = l[n];
2831
+
2832
+			if (o.getControlHTML && (v = o.getControlHTML(c)) !== '') {
2833
+				if (rtl)
2834
+					return '<span dir="rtl">' + tinyMCE.replaceVar(v, "pluginurl", o.baseURL) + '</span>';
2835
+
2836
+				return tinyMCE.replaceVar(v, "pluginurl", o.baseURL);
2837
+			}
2838
+		}
2839
+
2840
+		o = tinyMCE.themes[tinyMCE.settings.theme];
2841
+		if (o.getControlHTML && (v = o.getControlHTML(c)) !== '') {
2842
+			if (rtl)
2843
+				return '<span dir="rtl">' + v + '</span>';
2844
+
2845
+			return v;
2846
+		}
2847
+
2848
+		return '';
2849
+	},
2850
+
2851
+	/**
2852
+	 * Evaluates the specified function and uses the array of arguments.
2853
+	 *
2854
+	 * @param {string} f Function reference to execute.
2855
+	 * @param {int} idx Index in array to start grabbing arguments from.
2856
+	 * @param {Array} a Array of function arguments.
2857
+	 * @param {Object} o Optional object reference to call function on.
2858
+	 * @return Value returned from the evaluated function.
2859
+	 * @type object
2860
+	 */
2861
+	evalFunc : function(f, idx, a, o) {
2862
+		o = !o ? window : o;
2863
+		f = typeof(f) == 'function' ? f : o[f];
2864
+
2865
+		return f.apply(o, Array.prototype.slice.call(a, idx));
2866
+	},
2867
+
2868
+	/**
2869
+	 * Dispatches the specified callback on all options, plugins and themes. This will not
2870
+	 * chain them, so all functions callbacks will be executed regardless if the return true/false.
2871
+	 *
2872
+	 * @param {TinyMCE_Control} i TinyMCE editor control instance to execute callback on.
2873
+	 * @param {string} p TinyMCE callback parameter to execute.
2874
+	 * @param {string} n Function name to execute.
2875
+	 * @return true/false if they where dispatched.
2876
+	 */
2877
+	dispatchCallback : function(i, p, n) {
2878
+		return this.callFunc(i, p, n, 0, this.dispatchCallback.arguments);
2879
+	},
2880
+
2881
+	/**
2882
+	 * Executes the specified callback on all options, plugins and themes. This will
2883
+	 * chain them, so callback chain will be broken if one function returns false.
2884
+	 *
2885
+	 * @param {TinyMCE_Control} i TinyMCE editor control instance to execute callback on.
2886
+	 * @param {string} p TinyMCE callback parameter to execute.
2887
+	 * @param {string} n Function name to execute.
2888
+	 * @return true/false if a callback was executed.
2889
+	 */
2890
+	executeCallback : function(i, p, n) {
2891
+		return this.callFunc(i, p, n, 1, this.executeCallback.arguments);
2892
+	},
2893
+
2894
+	/**
2895
+	 * Executes the specified execcommand callback on all options, plugins and themes. This will
2896
+	 * chain them, so callback chain will be broken if one function returns true.
2897
+	 *
2898
+	 * @param {TinyMCE_Control} i TinyMCE editor control instance to execute callback on.
2899
+	 * @param {string} p TinyMCE callback parameter to execute.
2900
+	 * @param {string} n Function name to execute.
2901
+	 * @return true/false if a callback was executed.
2902
+	 */
2903
+	execCommandCallback : function(i, p, n) {
2904
+		return this.callFunc(i, p, n, 2, this.execCommandCallback.arguments);
2905
+	},
2906
+
2907
+	/**
2908
+	 * Executes callback chain. Callback order: Option, Plugins, Themes.
2909
+	 *
2910
+	 * @param {TinyMCE_Control} ins TinyMCE editor control instance to execute callback on.
2911
+	 * @param {string} p TinyMCE callback parameter name.
2912
+	 * @param {string} n Function name to execute.
2913
+	 * @param {int} m Execution mode value, 0 = no chain, 1 = event chain, 2 = execcommand chain.
2914
+	 * @param {Array} a Array with function arguments.
2915
+	 * @return true - if the callback was executed, false if it wasn't.
2916
+	 * @type boolean
2917
+	 */
2918
+	callFunc : function(ins, p, n, m, a) {
2919
+		var l, i, on, o, s, v;
2920
+
2921
+		s = m == 2;
2922
+
2923
+		l = tinyMCE.getParam(p, '');
2924
+
2925
+		if (l !== '' && (v = tinyMCE.evalFunc(l, 3, a)) == s && m > 0)
2926
+			return true;
2927
+
2928
+		if (ins != null) {
2929
+			for (i=0, l = ins.plugins; i<l.length; i++) {
2930
+				o = tinyMCE.plugins[l[i]];
2931
+
2932
+				if (o[n] && (v = tinyMCE.evalFunc(n, 3, a, o)) == s && m > 0)
2933
+					return true;
2934
+			}
2935
+		}
2936
+
2937
+		l = tinyMCE.themes;
2938
+		for (on in l) {
2939
+			o = l[on];
2940
+
2941
+			if (o[n] && (v = tinyMCE.evalFunc(n, 3, a, o)) == s && m > 0)
2942
+				return true;
2943
+		}
2944
+
2945
+		return false;
2946
+	},
2947
+
2948
+	/**
2949
+	 * Resolves a x.x.x string into a reference for objects in object in objects.
2950
+	 *
2951
+	 * @param {string} s Dot notation string.
2952
+	 * @param {object} o Object tree.
2953
+	 * @return {object} Reference based on dots.
2954
+	 */
2955
+	resolveDots : function(s, o) {
2956
+		var i;
2957
+
2958
+		if (typeof(s) == 'string') {
2959
+			for (i=0, s=s.split('.'); i<s.length; i++)
2960
+				o = o[s[i]];
2961
+		} else
2962
+			o = s;
2963
+
2964
+		return o;
2965
+	},
2966
+
2967
+	/**
2968
+	 * Encodes the string to raw XML entities. This will only convert the most common ones.
2969
+	 * For real entity encoding use the xmlEncode method of the Cleanup class.
2970
+	 *
2971
+	 * @param {string} s String to encode.
2972
+	 * @return XML Encoded string.
2973
+	 * @type string
2974
+	 */
2975
+	xmlEncode : function(s) {
2976
+		return s ? ('' + s).replace(this.xmlEncodeRe, function (c, b) {
2977
+			switch (c) {
2978
+				case '&':
2979
+					return '&amp;';
2980
+
2981
+				case '"':
2982
+					return '&quot;';
2983
+
2984
+				case '<':
2985
+					return '&lt;';
2986
+
2987
+				case '>':
2988
+					return '&gt;';
2989
+			}
2990
+
2991
+			return c;
2992
+		}) : s;
2993
+	},
2994
+
2995
+	/**
2996
+	 * Add methods to existing class.
2997
+	 *
2998
+	 * @param {Object} c Function/Class to add methods to.
2999
+	 * @param {Object} m List of methods to add. Name/Value collection.
3000
+	 */
3001
+	add : function(c, m) {
3002
+		var n;
3003
+
3004
+		for (n in m) {
3005
+			if (m.hasOwnProperty(n))
3006
+				c.prototype[n] = m[n];
3007
+		}
3008
+	},
3009
+
3010
+	/**
3011
+	 * Extends the specified prototype with new methods.
3012
+	 *
3013
+	 * @param {Object} p Prototype to extend with new methods.
3014
+	 * @param {Object} np New prototype to extend the other one with.
3015
+	 * @return Extended prototype array.
3016
+	 * @type Object
3017
+	 */
3018
+	extend : function(p, np) {
3019
+		var o = {}, n;
3020
+
3021
+		o.parent = p;
3022
+
3023
+		for (n in p) {
3024
+			if (p.hasOwnProperty(n))
3025
+				o[n] = p[n];
3026
+		}
3027
+
3028
+		for (n in np) {
3029
+			if (np.hasOwnProperty(n))
3030
+				o[n] = np[n];
3031
+		}
3032
+
3033
+		return o;
3034
+	},
3035
+
3036
+	/**
3037
+	 * Hides any visible menu layers.
3038
+	 *
3039
+	 * @private
3040
+	 */
3041
+	hideMenus : function() {
3042
+		var e = tinyMCE.lastSelectedMenuBtn;
3043
+
3044
+		if (tinyMCE.lastMenu) {
3045
+			tinyMCE.lastMenu.hide();
3046
+			tinyMCE.lastMenu = null;
3047
+		}
3048
+
3049
+		if (e) {
3050
+			tinyMCE.switchClass(e, tinyMCE.lastMenuBtnClass);
3051
+			tinyMCE.lastSelectedMenuBtn = null;
3052
+		}
3053
+	}
3054
+
3055
+	/**#@-*/
3056
+};
3057
+
3058
+// Global instances
3059
+var TinyMCE = TinyMCE_Engine; // Compatiblity with gzip compressors
3060
+var tinyMCE = new TinyMCE_Engine();
3061
+var tinyMCELang = {};