2 /* file:jscripts/tiny_mce/classes/tinymce.js */
7 releaseDate : '2008-08-18',
10 var t = this, d = document, w = window, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
13 t.isOpera = w.opera && opera.buildNumber;
14 t.isWebKit = /WebKit/.test(ua);
15 t.isOldWebKit = t.isWebKit && !w.getSelection().getRangeAt;
16 t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName);
17 t.isIE6 = t.isIE && /MSIE [56]/.test(ua);
18 t.isGecko = !t.isWebKit && /Gecko/.test(ua);
19 t.isMac = ua.indexOf('Mac') != -1;
21 // TinyMCE .NET webcontrol might be setting the values for TinyMCE
22 if (w.tinyMCEPreInit) {
23 t.suffix = tinyMCEPreInit.suffix;
24 t.baseURL = tinyMCEPreInit.base;
25 t.query = tinyMCEPreInit.query;
29 // Get suffix and base
32 // If base element found, add that infront of baseURL
33 nl = d.getElementsByTagName('base');
34 for (i=0; i<nl.length; i++) {
36 // Host only value like http://site.com or http://site.com:8008
37 if (/^https?:\/\/[^\/]+$/.test(v))
40 base = v ? v.match(/.*\//)[0] : ''; // Get only directory
45 if (n.src && /tiny_mce(|_dev|_src|_gzip|_jquery|_prototype).js/.test(n.src)) {
46 if (/_(src|dev)\.js/g.test(n.src))
49 if ((p = n.src.indexOf('?')) != -1)
50 t.query = n.src.substring(p + 1);
52 t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));
54 // If path to script is relative and a base href was found add that one infront
55 if (base && t.baseURL.indexOf('://') == -1)
56 t.baseURL = base + t.baseURL;
65 nl = d.getElementsByTagName('script');
66 for (i=0; i<nl.length; i++) {
72 n = d.getElementsByTagName('head')[0];
74 nl = n.getElementsByTagName('script');
75 for (i=0; i<nl.length; i++) {
88 return n != 'undefined';
90 if (t == 'array' && (o instanceof Array))
98 each : function(o, cb, s) {
106 if (typeof(o.length) != 'undefined') {
107 // Indexed arrays, needed for Safari
108 for (n=0, l = o.length; n<l; n++) {
109 if (cb.call(s, o[n], n, o) === false)
115 if (o.hasOwnProperty(n)) {
116 if (cb.call(s, o[n], n, o) === false)
125 map : function(a, f) {
128 tinymce.each(a, function(v) {
135 grep : function(a, f) {
138 tinymce.each(a, function(v) {
146 inArray : function(a, v) {
150 for (i = 0, l = a.length; i < l; i++) {
159 extend : function(o, e) {
160 var i, a = arguments;
162 for (i=1; i<a.length; i++) {
165 tinymce.each(e, function(v, n) {
166 if (typeof(v) !== 'undefined')
175 return (s ? '' + s : '').replace(/^\s*|\s*$/g, '');
180 create : function(s, p) {
181 var t = this, sp, ns, cn, scn, c, de = 0;
183 // Parse : <prefix> <class>:<super class>
184 s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);
185 cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name
187 // Create namespace for new class
188 ns = t.createNS(s[3].replace(/\.\w+$/, ''));
190 // Class already exists
194 // Make pure static class
195 if (s[2] == 'static') {
199 this.onCreate(s[2], s[3], ns[cn]);
204 // Create default constructor
206 p[cn] = function() {};
210 // Add constructor and methods
212 t.extend(ns[cn].prototype, p);
216 sp = t.resolve(s[5]).prototype;
217 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name
219 // Extend constructor
222 // Add passthrough constructor
223 ns[cn] = function() {
224 return sp[scn].apply(this, arguments);
227 // Add inherit constructor
228 ns[cn] = function() {
229 this.parent = sp[scn];
230 return c.apply(this, arguments);
233 ns[cn].prototype[cn] = ns[cn];
236 t.each(sp, function(f, n) {
237 ns[cn].prototype[n] = sp[n];
240 // Add overridden methods
241 t.each(p, function(f, n) {
242 // Extend methods if needed
244 ns[cn].prototype[n] = function() {
246 return f.apply(this, arguments);
250 ns[cn].prototype[n] = f;
255 // Add static methods
256 t.each(p['static'], function(f, n) {
261 this.onCreate(s[2], s[3], ns[cn].prototype);
264 walk : function(o, f, n, s) {
271 tinymce.each(o, function(o, i) {
272 if (f.call(s, o, i, n) === false)
275 tinymce.walk(o, f, n, s);
280 createNS : function(n, o) {
286 for (i=0; i<n.length; i++) {
298 resolve : function(n, o) {
304 for (i=0, l = n.length; i<l; i++) {
314 addUnload : function(f, s) {
315 var t = this, w = window;
317 f = {func : f, scope : s || this};
321 var li = t.unloads, o, n;
324 // Call unload handlers
329 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy
332 // Detach unload function
334 w.detachEvent('onbeforeunload', fakeUnload);
335 w.detachEvent('onunload', unload);
336 } else if (w.removeEventListener)
337 w.removeEventListener('unload', unload, false);
339 // Destroy references
340 t.unloads = o = li = w = unload = null;
342 // Run garbarge collector on IE
343 if (window.CollectGarbage)
344 window.CollectGarbage();
348 function fakeUnload() {
351 // Is there things still loading, then do some magic
352 if (d.readyState == 'interactive') {
354 // Prevent memory leak
355 d.detachEvent('onstop', stop);
357 // Call unload handler
363 // Fire unload when the currently loading page is stopped
364 d.attachEvent('onstop', stop);
366 // Remove onstop listener after a while to prevent the unload function
367 // to execute if the user presses cancel in an onbeforeunload
368 // confirm dialog and then presses the browser stop button
369 window.setTimeout(function() {
370 d.detachEvent('onstop', stop);
375 // Attach unload handler
377 w.attachEvent('onunload', unload);
378 w.attachEvent('onbeforeunload', fakeUnload);
379 } else if (w.addEventListener)
380 w.addEventListener('unload', unload, false);
382 // Setup initial unload handler array
390 removeUnload : function(f) {
391 var u = this.unloads, r = null;
393 tinymce.each(u, function(o, i) {
394 if (o && o.func == f) {
404 explode : function(s, d) {
405 return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;
408 _addVer : function(u) {
414 v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;
416 if (u.indexOf('#') == -1)
419 return u.replace('#', v + '#');
424 // Required for GZip AJAX loading
425 window.tinymce = tinymce;
427 // Initialize the API
430 /* file:jscripts/tiny_mce/classes/adapter/jquery/adapter.js */
433 /* file:jscripts/tiny_mce/classes/adapter/prototype/adapter.js */
436 /* file:jscripts/tiny_mce/classes/util/Dispatcher.js */
438 tinymce
.create('tinymce.util.Dispatcher', {
442 Dispatcher : function(s
) {
443 this.scope
= s
|| this;
447 add : function(cb
, s
) {
448 this.listeners
.push({cb
: cb
, scope
: s
|| this.scope
});
453 addToTop : function(cb
, s
) {
454 this.listeners
.unshift({cb
: cb
, scope
: s
|| this.scope
});
459 remove : function(cb
) {
460 var l
= this.listeners
, o
= null;
462 tinymce
.each(l
, function(c
, i
) {
473 dispatch : function() {
474 var s
, a
= arguments
, i
, li
= this.listeners
, c
;
476 // Needs to be a real loop since the listener count might change while looping
477 // And this is also more efficient
478 for (i
= 0; i
<li
.length
; i
++) {
480 s
= c
.cb
.apply(c
.scope
, a
);
491 /* file:jscripts/tiny_mce/classes/util/URI.js */
494 var each
= tinymce
.each
;
496 tinymce
.create('tinymce.util.URI', {
497 URI : function(u
, s
) {
498 var t
= this, o
, a
, b
;
501 s
= t
.settings
= s
|| {};
503 // Strange app protocol or local anchor
504 if (/^(mailto|news|javascript|about):/i.test(u
) || /^\s*#/.test(u
)) {
509 // Absolute path with no host, fake host and protocol
510 if (u
.indexOf('/') === 0 && u
.indexOf('//') !== 0)
511 u
= (s
.base_uri
? s
.base_uri
.protocol
|| 'http' : 'http') + '://mce_host' + u
;
514 if (u
.indexOf('://') === -1 && u
.indexOf('//') !== 0)
515 u
= (s
.base_uri
.protocol
|| 'http') + '://mce_host' + t
.toAbsPath(s
.base_uri
.path
, u
);
517 // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
518 u
= u
.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
519 u
= /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u
);
520 each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v
, i
) {
523 // Zope 3 workaround, they use @@something
525 s
= s
.replace(/\(mce_at\)/g, '@@');
530 if (b
= s
.base_uri
) {
532 t
.protocol
= b
.protocol
;
535 t
.userInfo
= b
.userInfo
;
537 if (!t
.port
&& t
.host
== 'mce_host')
540 if (!t
.host
|| t
.host
== 'mce_host')
546 //t.path = t.path || '/';
549 setPath : function(p
) {
552 p
= /^(.*?)\/?(\w+)?$/.exec(p
);
564 toRelative : function(u
) {
567 u
= new tinymce
.util
.URI(u
, {base_uri
: t
});
569 // Not on same domain/port or protocol
570 if ((u
.host
!= 'mce_host' && t
.host
!= u
.host
&& u
.host
) || t
.port
!= u
.port
|| t
.protocol
!= u
.protocol
)
573 o
= t
.toRelPath(t
.path
, u
.path
);
586 toAbsolute : function(u
, nh
) {
587 var u
= new tinymce
.util
.URI(u
, {base_uri
: this});
589 return u
.getURI(this.host
== u
.host
? nh
: 0);
592 toRelPath : function(base
, path
) {
593 var items
, bp
= 0, out
= '', i
;
596 base
= base
.substring(0, base
.lastIndexOf('/'));
597 base
= base
.split('/');
598 items
= path
.split('/');
600 if (base
.length
>= items
.length
) {
601 for (i
= 0; i
< base
.length
; i
++) {
602 if (i
>= items
.length
|| base
[i
] != items
[i
]) {
609 if (base
.length
< items
.length
) {
610 for (i
= 0; i
< items
.length
; i
++) {
611 if (i
>= base
.length
|| base
[i
] != items
[i
]) {
621 for (i
= 0; i
< base
.length
- (bp
- 1); i
++)
624 for (i
= bp
- 1; i
< items
.length
; i
++) {
626 out
+= "/" + items
[i
];
634 toAbsPath : function(base
, path
) {
635 var i
, nb
= 0, o
= [];
638 base
= base
.split('/');
639 path
= path
.split('/');
641 // Remove empty chunks
642 each(base
, function(k
) {
649 // Merge relURLParts chunks
650 for (i
= path
.length
- 1, o
= []; i
>= 0; i
--) {
652 if (path
[i
].length
== 0 || path
[i
] == ".")
656 if (path
[i
] == '..') {
670 i
= base
.length
- nb
;
674 return '/' + o
.reverse().join('/');
676 return '/' + base
.slice(0, i
).join('/') + '/' + o
.reverse().join('/');
679 getURI : function(nh
) {
683 if (!t
.source
|| nh
) {
688 s
+= t
.protocol
+ '://';
691 s
+= t
.userInfo
+ '@';
718 /* file:jscripts/tiny_mce/classes/util/Cookie.js */
721 var each
= tinymce
.each
;
723 tinymce
.create('static tinymce.util.Cookie', {
724 getHash : function(n
) {
725 var v
= this.get(n
), h
;
728 each(v
.split('&'), function(v
) {
731 h
[unescape(v
[0])] = unescape(v
[1]);
738 setHash : function(n
, v
, e
, p
, d
, s
) {
741 each(v
, function(v
, k
) {
742 o
+= (!o
? '' : '&') + escape(k
) + '=' + escape(v
);
745 this.set(n
, o
, e
, p
, d
, s
);
749 var c
= document
.cookie
, e
, p
= n
+ "=", b
;
755 b
= c
.indexOf("; " + p
);
765 e
= c
.indexOf(";", b
);
770 return unescape(c
.substring(b
+ p
.length
, e
));
773 set : function(n
, v
, e
, p
, d
, s
) {
774 document
.cookie
= n
+ "=" + escape(v
) +
775 ((e
) ? "; expires=" + e
.toGMTString() : "") +
776 ((p
) ? "; path=" + escape(p
) : "") +
777 ((d
) ? "; domain=" + d
: "") +
778 ((s
) ? "; secure" : "");
781 remove : function(n
, p
) {
784 d
.setTime(d
.getTime() - 1000);
786 this.set(n
, '', d
, p
, d
);
792 /* file:jscripts/tiny_mce/classes/util/JSON.js */
794 tinymce
.create('static tinymce.util.JSON', {
795 serialize : function(o
) {
796 var i
, v
, s
= tinymce
.util
.JSON
.serialize
, t
;
804 v
= '\bb\tt\nn\ff\rr\""\'\'\\\\';
806 return '"' + o
.replace(/([\u0080-\uFFFF\x00-\x1f\"\'])/g, function(a
, b
) {
810 return '\\' + v
.charAt(i
+ 1);
812 a
= b
.charCodeAt().toString(16);
814 return '\\u' + '0000'.substring(a
.length
) + a
;
819 if (o
instanceof Array
) {
820 for (i
=0, v
= '['; i
<o
.length
; i
++)
821 v
+= (i
> 0 ? ',' : '') + s(o
[i
]);
829 v
+= typeof o
[i
] != 'function' ? (v
.length
> 1 ? ',"' : '"') + i
+ '":' + s(o
[i
]) : '';
837 parse : function(s
) {
839 return eval('(' + s
+ ')');
847 /* file:jscripts/tiny_mce/classes/util/XHR.js */
849 tinymce
.create('static tinymce.util.XHR', {
851 var x
, t
, w
= window
, c
= 0;
854 o
.scope
= o
.scope
|| this;
855 o
.success_scope
= o
.success_scope
|| o
.scope
;
856 o
.error_scope
= o
.error_scope
|| o
.scope
;
857 o
.async
= o
.async
=== false ? false : true;
858 o
.data
= o
.data
|| '';
864 x
= new ActiveXObject(s
);
871 x
= w
.XMLHttpRequest
? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');
874 if (x
.overrideMimeType
)
875 x
.overrideMimeType(o
.content_type
);
877 x
.open(o
.type
|| (o
.data
? 'POST' : 'GET'), o
.url
, o
.async
);
880 x
.setRequestHeader('Content-Type', o
.content_type
);
885 if (!o
.async
|| x
.readyState
== 4 || c
++ > 10000) {
886 if (o
.success
&& c
< 10000 && x
.status
== 200)
887 o
.success
.call(o
.success_scope
, '' + x
.responseText
, x
, o
);
889 o
.error
.call(o
.error_scope
, c
> 10000 ? 'TIMED_OUT' : 'GENERAL', x
, o
);
893 w
.setTimeout(ready
, 10);
896 // Syncronous request
900 // Wait for response, onReadyStateChange can not be used since it leaks memory in IE
901 t
= w
.setTimeout(ready
, 10);
907 /* file:jscripts/tiny_mce/classes/util/JSONRequest.js */
910 var extend
= tinymce
.extend
, JSON
= tinymce
.util
.JSON
, XHR
= tinymce
.util
.XHR
;
912 tinymce
.create('tinymce.util.JSONRequest', {
913 JSONRequest : function(s
) {
914 this.settings
= extend({
920 var ecb
= o
.error
, scb
= o
.success
;
922 o
= extend(this.settings
, o
);
924 o
.success = function(c
, x
) {
927 if (typeof(c
) == 'undefined') {
929 error
: 'JSON Parse error.'
934 ecb
.call(o
.error_scope
|| o
.scope
, c
.error
, x
);
936 scb
.call(o
.success_scope
|| o
.scope
, c
.result
);
939 o
.error = function(ty
, x
) {
940 ecb
.call(o
.error_scope
|| o
.scope
, ty
, x
);
943 o
.data
= JSON
.serialize({
944 id
: o
.id
|| 'c' + (this.count
++),
949 // JSON content type for Ruby on rails. Bug: #1883287
950 o
.content_type
= 'application/json';
956 sendRPC : function(o
) {
957 return new tinymce
.util
.JSONRequest().send(o
);
963 /* file:jscripts/tiny_mce/classes/dom/DOMUtils.js */
967 var each
= tinymce
.each
, is
= tinymce
.is
;
968 var isWebKit
= tinymce
.isWebKit
, isIE
= tinymce
.isIE
;
970 tinymce
.create('tinymce.dom.DOMUtils', {
975 pixelStyles
: /^(top|left|bottom|right|width|height|borderWidth)$/,
977 idPattern
: /^#[\w]+$/,
978 elmPattern
: /^[\w_*]+$/,
979 elmClassPattern
: /^([\w_]*)\.([\w_]+)$/,
982 "class" : "className",
983 className
: "className",
985 disabled
: "disabled",
986 maxlength
: "maxLength",
987 readonly
: "readOnly",
988 selected
: "selected",
992 DOMUtils : function(d
, s
) {
998 t
.cssFlicker
= false;
1000 t
.boxModel
= !tinymce
.isIE
|| d
.compatMode
== "CSS1Compat";
1001 t
.stdMode
= d
.documentMode
=== 8;
1003 this.settings
= s
= tinymce
.extend({
1004 keep_values
: false,
1009 // Fix IE6SP2 flicker and check it failed for pre SP2
1010 if (tinymce
.isIE6
) {
1012 d
.execCommand('BackgroundImageCache', false, true);
1014 t
.cssFlicker
= true;
1018 tinymce
.addUnload(t
.destroy
, t
);
1021 getRoot : function() {
1022 var t
= this, s
= t
.settings
;
1024 return (s
&& t
.get(s
.root_element
)) || t
.doc
.body
;
1027 getViewPort : function(w
) {
1030 w
= !w
? this.win
: w
;
1032 b
= this.boxModel
? d
.documentElement
: d
.body
;
1034 // Returns viewport size excluding scrollbars
1036 x
: w
.pageXOffset
|| b
.scrollLeft
,
1037 y
: w
.pageYOffset
|| b
.scrollTop
,
1038 w
: w
.innerWidth
|| b
.clientWidth
,
1039 h
: w
.innerHeight
|| b
.clientHeight
1043 getRect : function(e
) {
1044 var p
, t
= this, sr
;
1058 getSize : function(e
) {
1062 w
= t
.getStyle(e
, 'width');
1063 h
= t
.getStyle(e
, 'height');
1065 // Non pixel value, then force offset/clientWidth
1066 if (w
.indexOf('px') === -1)
1069 // Non pixel value, then force offset/clientWidth
1070 if (h
.indexOf('px') === -1)
1074 w
: parseInt(w
) || e
.offsetWidth
|| e
.clientWidth
,
1075 h
: parseInt(h
) || e
.offsetHeight
|| e
.clientHeight
1079 getParent : function(n
, f
, r
) {
1080 var na
, se
= this.settings
;
1085 r
= r
|| this.getRoot();
1087 // Wrap node name as func
1088 if (is(f
, 'string')) {
1089 na
= f
.toUpperCase();
1095 if (n
.nodeType
== 1 && na
=== '*') {
1100 each(na
.split(','), function(v
) {
1101 if (n
.nodeType
== 1 && ((se
.strict
&& n
.nodeName
.toUpperCase() == v
) || n
.nodeName
.toUpperCase() == v
)) {
1103 return false; // Break loop
1127 if (e
&& this.doc
&& typeof(e
) == 'string') {
1129 e
= this.doc
.getElementById(e
);
1131 // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick
1132 if (e
&& e
.id
!== n
)
1133 return this.doc
.getElementsByName(n
)[1];
1141 select : function(pa
, s
) {
1142 var t
= this, cs
, c
, pl
, o
= [], x
, i
, l
, n
;
1144 s
= t
.get(s
) || t
.doc
;
1146 // Look for native support and use that if it's found
1147 if (s
.querySelectorAll
) {
1148 // Element scope then use temp id
1149 // We need to do this to be compatible with other implementations
1150 // See bug report: http://bugs.webkit.org/show_bug.cgi?id=17461
1154 pa
= '#_mc_tmp ' + pa
;
1158 l
= tinymce
.grep(s
.querySelectorAll(pa
));
1166 if (t
.settings
.strict
) {
1167 function get(s
, n
) {
1168 return s
.getElementsByTagName(n
.toLowerCase());
1171 function get(s
, n
) {
1172 return s
.getElementsByTagName(n
);
1176 // Simple element pattern. For example: "p" or "*"
1177 if (t
.elmPattern
.test(pa
)) {
1180 for (i
= 0, l
= x
.length
; i
<l
; i
++)
1186 // Simple class pattern. For example: "p.class" or ".class"
1187 if (t
.elmClassPattern
.test(pa
)) {
1188 pl
= t
.elmClassPattern
.exec(pa
);
1189 x
= get(s
, pl
[1] || '*');
1190 c
= ' ' + pl
[2] + ' ';
1192 for (i
= 0, l
= x
.length
; i
<l
; i
++) {
1195 if (n
.className
&& (' ' + n
.className
+ ' ').indexOf(c
) !== -1)
1202 function collect(n
) {
1209 function collectIE(n
) {
1210 if (!n
.getAttribute('mce_save')) {
1211 n
.setAttribute('mce_save', '1');
1216 function find(n
, f
, r
) {
1217 var i
, l
, nl
= get(r
, n
);
1219 for (i
= 0, l
= nl
.length
; i
< l
; i
++)
1223 each(pa
.split(','), function(v
, i
) {
1224 v
= tinymce
.trim(v
);
1226 // Simple element pattern, most common in TinyMCE
1227 if (t
.elmPattern
.test(v
)) {
1228 each(get(s
, v
), function(n
) {
1235 // Simple element pattern with class, fairly common in TinyMCE
1236 if (t
.elmClassPattern
.test(v
)) {
1237 x
= t
.elmClassPattern
.exec(v
);
1239 each(get(s
, x
[1]), function(n
) {
1240 if (t
.hasClass(n
, x
[2]))
1247 if (!(cs
= t
.cache
[pa
])) {
1248 cs
= 'x=(function(cf, s) {';
1251 each(pl
, function(v
) {
1252 var p
= /^([\w\\*]+)?(?:#([\w\\]+))?(?:\.([\w\\\.]+))?(?:\[\@([\w\\]+)([\^\$\*!]?=)([\w\\]+)\])?(?:\:([\w\\]+))?/i.exec(v
);
1256 cs
+= 'find("' + p
[1] + '", function(n) {';
1260 cs
+= 'if (n.id !== "' + p
[2] + '") return;';
1264 cs
+= 'var c = " " + n.className + " ";';
1267 each(p
[3].split('.'), function(v
) {
1269 c
+= (c
? '||' : '') + 'c.indexOf(" ' + v
+ ' ") === -1';
1271 cs
+= c
+ ') return;';
1277 for (i
= pl
.length
- 1; i
>= 0; i
--)
1278 cs
+= '}, ' + (i
? 'n' : 's') + ');';
1282 // Compile CSS pattern function
1283 t
.cache
[pa
] = cs
= eval(cs
);
1286 // Run selector function
1287 cs(isIE
? collectIE
: collect
, s
);
1291 each(o
, function(n
) {
1293 n
.removeAttribute('mce_save');
1303 add : function(p
, n
, a
, h
, c
) {
1306 return this.run(p
, function(p
) {
1309 e
= is(n
, 'string') ? t
.doc
.createElement(n
) : n
;
1313 if (a
.hasOwnProperty(k
) && !is(a
[k
], 'object'))
1314 t
.setAttrib(e
, k
, '' + a
[k
]);
1317 if (a
.style
&& !is(a
.style
, 'string')) {
1318 each(a
.style
, function(v
, n
) {
1319 t
.setStyle(e
, n
, v
);
1331 return !c
? p
.appendChild(e
) : e
;
1335 create : function(n
, a
, h
) {
1336 return this.add(this.doc
.createElement(n
), n
, a
, h
, 1);
1339 createHTML : function(n
, a
, h
) {
1340 var o
= '', t
= this, k
;
1345 if (a
.hasOwnProperty(k
))
1346 o
+= ' ' + k
+ '="' + t
.encode(a
[k
]) + '"';
1350 return o
+ '>' + h
+ '</' + n
+ '>';
1355 remove : function(n
, k
) {
1356 return this.run(n
, function(n
) {
1365 each (n
.childNodes
, function(c
) {
1366 p
.insertBefore(c
.cloneNode(true), n
);
1370 // Fix IE psuedo leak
1372 p = n.cloneNode(true);
1378 return p
.removeChild(n
);
1384 setStyle : function(n
, na
, v
) {
1387 return t
.run(n
, function(e
) {
1392 // Camelcase it, if needed
1393 na
= na
.replace(/-(\D)/g, function(a
, b
){
1394 return b
.toUpperCase();
1397 // Default px suffix on these
1398 if (t
.pixelStyles
.test(na
) && (tinymce
.is(v
, 'number') || /^[\-0-9\.]+$/.test(v
)))
1403 // IE specific opacity
1405 s
.filter
= v
=== '' ? '' : "alpha(opacity=" + (v
* 100) + ")";
1407 if (!n
.currentStyle
|| !n
.currentStyle
.hasLayout
)
1408 s
.display
= 'inline-block';
1411 // Fix for older browsers
1412 s
[na
] = s
['-moz-opacity'] = s
['-khtml-opacity'] = v
|| '';
1416 isIE
? s
.styleFloat
= v
: s
.cssFloat
= v
;
1423 // Force update of the style data
1424 if (t
.settings
.update_styles
)
1425 t
.setAttrib(e
, 'mce_style');
1429 getStyle : function(n
, na
, c
) {
1436 if (this.doc
.defaultView
&& c
) {
1438 na
= na
.replace(/[A-Z]/g, function(a
){
1443 return this.doc
.defaultView
.getComputedStyle(n
, null).getPropertyValue(na
);
1445 // Old safari might fail
1450 // Camelcase it, if needed
1451 na
= na
.replace(/-(\D)/g, function(a
, b
){
1452 return b
.toUpperCase();
1456 na
= isIE
? 'styleFloat' : 'cssFloat';
1459 if (n
.currentStyle
&& c
)
1460 return n
.currentStyle
[na
];
1465 setStyles : function(e
, o
) {
1466 var t
= this, s
= t
.settings
, ol
;
1468 ol
= s
.update_styles
;
1469 s
.update_styles
= 0;
1471 each(o
, function(v
, n
) {
1472 t
.setStyle(e
, n
, v
);
1475 // Update style info
1476 s
.update_styles
= ol
;
1477 if (s
.update_styles
)
1478 t
.setAttrib(e
, s
.cssText
);
1481 setAttrib : function(e
, n
, v
) {
1489 if (t
.settings
.strict
)
1490 n
= n
.toLowerCase();
1492 return this.run(e
, function(e
) {
1497 // No mce_style for elements with these since they might get resized by the user
1498 if (s
.keep_values
) {
1499 if (v
&& !t
._isRes(v
))
1500 e
.setAttribute('mce_style', v
, 2);
1502 e
.removeAttribute('mce_style', 2);
1505 e
.style
.cssText
= v
;
1509 e
.className
= v
|| ''; // Fix IE null bug
1514 if (s
.keep_values
) {
1515 if (s
.url_converter
)
1516 v
= s
.url_converter
.call(s
.url_converter_scope
|| t
, v
, n
, e
);
1518 t
.setAttrib(e
, 'mce_' + n
, v
, 2);
1524 e
.setAttribute('mce_style', v
);
1528 if (is(v
) && v
!== null && v
.length
!== 0)
1529 e
.setAttribute(n
, '' + v
, 2);
1531 e
.removeAttribute(n
, 2);
1535 setAttribs : function(e
, o
) {
1538 return this.run(e
, function(e
) {
1539 each(o
, function(v
, n
) {
1540 t
.setAttrib(e
, n
, v
);
1547 getAttrib : function(e
, n
, dv
) {
1552 if (!e
|| e
.nodeType
!== 1)
1558 // Try the mce variant for these
1559 if (/^(src|href|style|coords|shape)$/.test(n
)) {
1560 v
= e
.getAttribute("mce_" + n
);
1566 if (isIE
&& t
.props
[n
]) {
1568 v
= v
&& v
.nodeValue
? v
.nodeValue
: v
;
1572 v
= e
.getAttribute(n
, 2);
1574 if (n
=== 'style') {
1575 v
= v
|| e
.style
.cssText
;
1578 v
= t
.serializeStyle(t
.parseStyle(v
));
1580 if (t
.settings
.keep_values
&& !t
._isRes(v
))
1581 e
.setAttribute('mce_style', v
);
1585 // Remove Apple and WebKit stuff
1586 if (isWebKit
&& n
=== "class" && v
)
1587 v
= v
.replace(/(apple|webkit)\-[a-z\-]+/gi, '');
1594 // IE returns 1 as default value
1601 // IE returns +0 as default value for size
1602 if (v
=== '+0' || v
=== 20)
1616 // IE returns -1 as default value
1624 // IE returns default value
1625 if (v
=== 32768 || v
=== 2147483647)
1638 v
= v
.toLowerCase();
1642 // IE has odd anonymous function for event attributes
1643 if (n
.indexOf('on') === 0 && v
)
1644 v
= ('' + v
).replace(/^function\s+anonymous\(\)\s+\{\s+(.*)\s+\}$/, '$1');
1648 return (v
!== undefined && v
!== null && v
!== '') ? '' + v
: dv
;
1651 getPos : function(n
) {
1652 var t
= this, x
= 0, y
= 0, e
, d
= t
.doc
, r
;
1656 // Use getBoundingClientRect on IE, Opera has it but it's not perfect
1658 n
= n
.getBoundingClientRect();
1659 e
= t
.boxModel
? d
.documentElement
: d
.body
;
1660 x
= t
.getStyle(t
.select('html')[0], 'borderWidth'); // Remove border
1661 x
= (x
== 'medium' || t
.boxModel
&& !t
.isIE6
) && 2 || x
;
1662 n
.top
+= t
.win
.self
!= t
.win
.top
? 2 : 0; // IE adds some strange extra cord if used in a frameset
1664 return {x
: n
.left
+ e
.scrollLeft
- x
, y
: n
.top
+ e
.scrollTop
- x
};
1669 x
+= r
.offsetLeft
|| 0;
1670 y
+= r
.offsetTop
|| 0;
1676 // Opera 9.25 bug fix, fixed in 9.50
1677 if (!/^table-row|inline.*/i.test(t
.getStyle(r
, "display", 1))) {
1678 x
-= r
.scrollLeft
|| 0;
1679 y
-= r
.scrollTop
|| 0;
1688 return {x
: x
, y
: y
};
1691 parseStyle : function(st
) {
1692 var t
= this, s
= t
.settings
, o
= {};
1697 function compress(p
, s
, ot
) {
1700 // Get values and check it it needs compressing
1701 t
= o
[p
+ '-top' + s
];
1705 r
= o
[p
+ '-right' + s
];
1709 b
= o
[p
+ '-bottom' + s
];
1713 l
= o
[p
+ '-left' + s
];
1719 delete o
[p
+ '-top' + s
];
1720 delete o
[p
+ '-right' + s
];
1721 delete o
[p
+ '-bottom' + s
];
1722 delete o
[p
+ '-left' + s
];
1725 function compress2(ta
, a
, b
, c
) {
1741 o
[ta
] = o
[a
] + ' ' + o
[b
] + ' ' + o
[c
];
1747 st
= st
.replace(/&(#?[a-z0-9]+);/g, '&$1_MCE_SEMI_'); // Protect entities
1749 each(st
.split(';'), function(v
) {
1753 v
= v
.replace(/_MCE_SEMI_/g, ';'); // Restore entities
1754 v
= v
.replace(/url\([^\)]+\)/g, function(v
) {ur
.push(v
);return 'url(' + ur
.length
+ ')';});
1756 sv
= tinymce
.trim(v
[1]);
1757 sv
= sv
.replace(/url\(([^\)]+)\)/g, function(a
, b
) {return ur
[parseInt(b
) - 1];});
1759 sv
= sv
.replace(/rgb\([^\)]+\)/g, function(v
) {
1763 if (s
.url_converter
) {
1764 sv
= sv
.replace(/url\([\'\"]?([^\)\'\"]+)[\'\"]?\)/g, function(x
, c
) {
1765 return 'url(' + s
.url_converter
.call(s
.url_converter_scope
|| t
, t
.decode(c
), 'style', null) + ')';
1769 o
[tinymce
.trim(v
[0]).toLowerCase()] = sv
;
1773 compress("border", "", "border");
1774 compress("border", "-width", "border-width");
1775 compress("border", "-color", "border-color");
1776 compress("border", "-style", "border-style");
1777 compress("padding", "", "padding");
1778 compress("margin", "", "margin");
1779 compress2('border', 'border-width', 'border-style', 'border-color');
1782 // Remove pointless border
1783 if (o
.border
== 'medium none')
1790 serializeStyle : function(o
) {
1793 each(o
, function(v
, k
) {
1795 if (tinymce
.isGecko
&& k
.indexOf('-moz-') === 0)
1800 case 'background-color':
1801 v
= v
.toLowerCase();
1805 s
+= (s
? ' ' : '') + k
+ ': ' + v
+ ';';
1812 loadCSS : function(u
) {
1813 var t
= this, d
= t
.doc
;
1818 each(u
.split(','), function(u
) {
1823 t
.add(t
.select('head')[0], 'link', {rel
: 'stylesheet', href
: tinymce
._addVer(u
)});
1829 addClass : function(e
, c
) {
1830 return this.run(e
, function(e
) {
1836 if (this.hasClass(e
, c
))
1839 o
= this.removeClass(e
, c
);
1841 return e
.className
= (o
!= '' ? (o
+ ' ') : '') + c
;
1845 removeClass : function(e
, c
) {
1848 return t
.run(e
, function(e
) {
1851 if (t
.hasClass(e
, c
)) {
1853 re
= new RegExp("(^|\\s+)" + c
+ "(\\s+|$)", "g");
1855 v
= e
.className
.replace(re
, ' ');
1857 return e
.className
= tinymce
.trim(v
!= ' ' ? v
: '');
1864 hasClass : function(n
, c
) {
1870 return (' ' + n
.className
+ ' ').indexOf(' ' + c
+ ' ') !== -1;
1873 show : function(e
) {
1874 return this.setStyle(e
, 'display', 'block');
1877 hide : function(e
) {
1878 return this.setStyle(e
, 'display', 'none');
1881 isHidden : function(e
) {
1884 return e
.style
.display
== 'none' || this.getStyle(e
, 'display') == 'none';
1889 uniqueId : function(p
) {
1890 return (!p
? 'mce_' : p
) + (this.counter
++);
1893 setHTML : function(e
, h
) {
1896 return this.run(e
, function(e
) {
1897 var x
, i
, nl
, n
, p
, x
;
1899 h
= t
.processHTML(h
);
1904 // IE will remove comments from the beginning
1905 // unless you padd the contents with something
1906 e
.innerHTML
= '<br />' + h
;
1907 e
.removeChild(e
.firstChild
);
1909 // IE sometimes produces an unknown runtime error on innerHTML if it's an block element within a block element for example a div inside a p
1910 // This seems to fix this problem
1912 // Remove all child nodes
1913 while (e
.firstChild
)
1914 e
.firstChild
.removeNode();
1916 // Create new div with HTML contents and a BR infront to keep comments
1917 x
= t
.create('div');
1918 x
.innerHTML
= '<br />' + h
;
1920 // Add all children from div to target
1921 each (x
.childNodes
, function(n
, i
) {
1929 // IE has a serious bug when it comes to paragraphs it can produce an invalid
1930 // DOM tree if contents like this <p><ul><li>Item 1</li></ul></p> is inserted
1931 // It seems to be that IE doesn't like a root block element placed inside another root block element
1932 if (t
.settings
.fix_ie_paragraphs
)
1933 h
= h
.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi, '<p$1 mce_keep="true"> </p>');
1937 if (t
.settings
.fix_ie_paragraphs
) {
1938 // Check for odd paragraphs this is a sign of a broken DOM
1939 nl
= e
.getElementsByTagName("p");
1940 for (i
= nl
.length
- 1, x
= 0; i
>= 0; i
--) {
1943 if (!n
.hasChildNodes()) {
1949 n
.removeAttribute('mce_keep');
1954 // Time to fix the madness IE left us
1956 // So if we replace the p elements with divs and mark them and then replace them back to paragraphs
1957 // after we use innerHTML we can fix the DOM tree
1958 h
= h
.replace(/<p([^>]+)>|<p>/g, '<div$1 mce_tmp="1">');
1959 h
= h
.replace(/<\/p>/g, '</div>');
1961 // Set the new HTML with DIVs
1964 // Replace all DIV elements with he mce_tmp attibute back to paragraphs
1965 // This is needed since IE has a annoying bug see above for details
1966 // This is a slow process but it has to be done. :(
1967 if (t
.settings
.fix_ie_paragraphs
) {
1968 nl
= e
.getElementsByTagName("DIV");
1969 for (i
= nl
.length
- 1; i
>= 0; i
--) {
1974 // Create new paragraph
1975 p
= t
.doc
.createElement('p');
1977 // Copy all attributes
1978 n
.cloneNode(false).outerHTML
.replace(/([a-z0-9\-_]+)=/gi, function(a
, b
) {
1981 if (b
!== 'mce_tmp') {
1982 v
= n
.getAttribute(b
);
1984 if (!v
&& b
=== 'class')
1987 p
.setAttribute(b
, v
);
1991 // Append all children to new paragraph
1992 for (x
= 0; x
<n
.childNodes
.length
; x
++)
1993 p
.appendChild(n
.childNodes
[x
].cloneNode(true));
1995 // Replace div with new paragraph
2008 processHTML : function(h
) {
2009 var t
= this, s
= t
.settings
;
2011 if (!s
.process_html
)
2014 // Convert strong and em to b and i in FF since it can't handle them
2015 if (tinymce
.isGecko
) {
2016 h
= h
.replace(/<(\/?)strong>|<strong( [^>]+)>/gi, '<$1b$2>');
2017 h
= h
.replace(/<(\/?)em>|<em( [^>]+)>/gi, '<$1i$2>');
2019 h
= h
.replace(/'/g, '''); // IE can't handle apos
2022 h
= h
.replace(/<a( )([^>]+)\/>|<a\/>/gi, '<a$1$2></a>'); // Force open
2024 // Store away src and href in mce_src and mce_href since browsers mess them up
2025 if (s
.keep_values
) {
2026 h
= h
.replace(/<!\[CDATA\[([\s\S]+)\]\]>/g, '<!--[CDATA[$1]]-->');
2028 // Wrap scripts and styles in comments for serialization purposes
2029 if (/<script|style/.test(h
)) {
2031 // Remove prefix and suffix code for element
2032 s
= s
.replace(/^[\r\n]*|[\r\n]*$/g, '');
2033 s
= s
.replace(/^\s*(\/\/\s*<!--|\/\/\s*<\[CDATA\[|<!--|<\[CDATA\[)[\r\n]*/g, '');
2034 s
= s
.replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->)\s*$/g, '');
2039 // Preserve script elements
2040 h
= h
.replace(/<script([^>]+|)>([\s\S]*?)<\/script>/g, function(v
, a
, b
) {
2041 // Remove prefix and suffix code for script element
2044 // Force type attribute
2046 a
= ' type="text/javascript"';
2048 // Wrap contents in a comment
2050 b
= '<!--\n' + b
+ '\n// -->';
2052 // Output fake element
2053 return '<mce:script' + a
+ '>' + b
+ '</mce:script>';
2056 // Preserve style elements
2057 h
= h
.replace(/<style([^>]+|)>([\s\S]*?)<\/style>/g, function(v
, a
, b
) {
2059 return '<mce:style' + a
+ '><!--\n' + b
+ '\n--></mce:style><style' + a
+ ' mce_bogus="1">' + b
+ '</style>';
2063 // Process all tags with src, href or style
2064 h
= h
.replace(/<([\w:]+) [^>]*(src|href|style|shape|coords)[^>]*>/gi, function(a
, n
) {
2065 function handle(m
, b
, c
) {
2068 // Tag already got a mce_ version
2069 if (a
.indexOf('mce_' + b
) != -1)
2073 // Why did I need this one?
2075 // u = t.serializeStyle(t.parseStyle(u));
2077 // No mce_style for elements with these since they might get resized by the user
2082 u
= u
.replace(/rgb\([^\)]+\)/g, function(v
) {
2087 if (s
.url_converter
) {
2088 u
= u
.replace(/url\([\'\"]?([^\)\'\"]+)\)/g, function(x
, c
) {
2089 return 'url(' + t
.encode(s
.url_converter
.call(s
.url_converter_scope
|| t
, t
.decode(c
), b
, n
)) + ')';
2092 } else if (b
!= 'coords' && b
!= 'shape') {
2093 if (s
.url_converter
)
2094 u
= t
.encode(s
.url_converter
.call(s
.url_converter_scope
|| t
, t
.decode(c
), b
, n
));
2097 return ' ' + b
+ '="' + c
+ '" mce_' + b
+ '="' + u
+ '"';
2100 a
= a
.replace(/ (src
|href
|style
|coords
|shape
)=[\"]([^\"]+)[\"]/gi, handle); // W3C
2101 a
= a
.replace(/ (src
|href
|style
|coords
|shape
)=[\']([^\']+)[\']/gi, handle); // W3C
2103 return a
.replace(/ (src
|href
|style
|coords
|shape
)=([^\s
\"\'>]+)/gi, handle); // IE
2110 getOuterHTML : function(e
) {
2121 d
= (e
.ownerDocument
|| this.doc
).createElement("body");
2122 d
.appendChild(e
.cloneNode(true));
2127 setOuterHTML : function(e
, h
, d
) {
2130 return this.run(e
, function(e
) {
2134 d
= d
|| e
.ownerDocument
|| t
.doc
;
2136 if (isIE
&& e
.nodeType
== 1)
2139 tp
= d
.createElement("body");
2144 t
.insertAfter(n
.cloneNode(true), e
);
2145 n
= n
.previousSibling
;
2153 decode : function(s
) {
2156 // Look for entities to decode
2157 if (/&[^;]+;/.test(s
)) {
2158 // Decode the entities using a div element not super efficient but less code
2159 e
= this.doc
.createElement("div");
2162 return !e
.firstChild
? s
: e
.firstChild
.nodeValue
;
2168 encode : function(s
) {
2169 return s
? ('' + s
).replace(/[<>&\"]/g, function (c
, b
) {
2190 insertAfter : function(n
, r
) {
2195 return this.run(n
, function(n
) {
2202 p
.insertBefore(n
, ns
);
2212 isBlock : function(n
) {
2213 if (n
.nodeType
&& n
.nodeType
!== 1)
2216 n
= n
.nodeName
|| n
;
2218 return /^(H[1-6]|HR|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP)$/.test(n
);
2223 replace : function(n
, o
, k
) {
2225 n
= n
.cloneNode(true);
2227 return this.run(o
, function(o
) {
2229 each(o
.childNodes
, function(c
) {
2230 n
.appendChild(c
.cloneNode(true));
2234 // Fix IE psuedo leak for elements since replacing elements if fairly common
2235 // Will break parentNode for some unknown reason
2236 /* if (isIE && o.nodeType === 1) {
2237 o.parentNode.insertBefore(n, o);
2242 return o
.parentNode
.replaceChild(n
, o
);
2248 toHex : function(s
) {
2249 var c
= /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s
);
2252 s
= parseInt(s
).toString(16);
2254 return s
.length
> 1 ? s
: '0' + s
; // 0 -> 00
2258 s
= '#' + hex(c
[1]) + hex(c
[2]) + hex(c
[3]);
2266 getClasses : function() {
2267 var t
= this, cl
= [], i
, lo
= {}, f
= t
.settings
.class_filter
, ov
;
2272 function addClasses(s
) {
2274 each(s
.imports
, function(r
) {
2278 each(s
.cssRules
|| s
.rules
, function(r
) {
2279 // Real type or fake it on IE
2280 switch (r
.type
|| 1) {
2283 if (r
.selectorText
) {
2284 each(r
.selectorText
.split(','), function(v
) {
2285 v
= v
.replace(/^\s*|\s*$|^\s\./g, "");
2287 // Is internal or it doesn't contain a class
2288 if (/\.mce/.test(v
) || !/\.[\w\-]+$/.test(v
))
2291 // Remove everything but class name
2293 v
= v
.replace(/.*\.([a-z0-9_\-]+).*/i, '$1');
2296 if (f
&& !(v
= f(v
, ov
)))
2300 cl
.push({'class' : v
});
2309 addClasses(r
.styleSheet
);
2316 each(t
.doc
.styleSheets
, addClasses
);
2327 run : function(e
, f
, s
) {
2330 if (t
.doc
&& typeof(e
) === 'string')
2331 e
= t
.doc
.getElementById(e
);
2337 if (!e
.nodeType
&& (e
.length
|| e
.length
=== 0)) {
2340 each(e
, function(e
, i
) {
2342 if (typeof(e
) == 'string')
2343 e
= t
.doc
.getElementById(e
);
2345 o
.push(f
.call(s
, e
, i
));
2352 return f
.call(s
, e
);
2355 getAttribs : function(n
) {
2366 // Object will throw exception in IE
2367 if (n
.nodeName
== 'OBJECT')
2368 return n
.attributes
;
2370 // It's crazy that this is faster in IE but it's because it returns all attributes all the time
2371 n
.cloneNode(false).outerHTML
.replace(/([a-z0-9\:\-_]+)=/gi, function(a
, b
) {
2372 o
.push({specified
: 1, nodeName
: b
});
2378 return n
.attributes
;
2381 destroy : function(s
) {
2384 t
.win
= t
.doc
= t
.root
= null;
2386 // Manual destroy then remove unload handler
2388 tinymce
.removeUnload(t
.destroy
);
2391 _isRes : function(c
) {
2392 // Is live resizble element
2393 return /^(top|left|bottom|right|width|height)/i.test(c
) || /;\s*(top|left|bottom|right|width|height)/i.test(c
);
2397 walk : function(n, f, s) {
2398 var d = this.doc, w;
2400 if (d.createTreeWalker) {
2401 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
2403 while ((n = w.nextNode()) != null)
2404 f.call(s || this, n);
2406 tinymce.walk(n, f, 'childNodes', s);
2411 toRGB : function(s) {
2412 var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);
2419 return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";
2429 tinymce
.DOM
= new tinymce
.dom
.DOMUtils(document
, {process_html
: 0});
2432 /* file:jscripts/tiny_mce/classes/dom/Event.js */
2436 var each
= tinymce
.each
, DOM
= tinymce
.DOM
, isIE
= tinymce
.isIE
, isWebKit
= tinymce
.isWebKit
, Event
;
2438 tinymce
.create('static tinymce.dom.Event', {
2444 add : function(o
, n
, f
, s
) {
2445 var cb
, t
= this, el
= t
.events
, r
;
2448 if (o
&& o
instanceof Array
) {
2451 each(o
, function(o
) {
2453 r
.push(t
.add(o
, n
, f
, s
));
2464 // Setup event callback
2466 e
= e
|| window
.event
;
2468 // Patch in target in IE it's W3C valid
2469 if (e
&& !e
.target
&& isIE
)
2470 e
.target
= e
.srcElement
;
2475 return f
.call(s
, e
);
2478 if (n
== 'unload') {
2479 tinymce
.unloads
.unshift({func
: cb
});
2492 // Store away listener reference
2506 remove : function(o
, n
, f
) {
2507 var t
= this, a
= t
.events
, s
= false, r
;
2510 if (o
&& o
instanceof Array
) {
2513 each(o
, function(o
) {
2515 r
.push(t
.remove(o
, n
, f
));
2523 each(a
, function(e
, i
) {
2524 if (e
.obj
== o
&& e
.name
== n
&& (!f
|| (e
.func
== f
|| e
.cfunc
== f
))) {
2526 t
._remove(o
, n
, e
.cfunc
);
2535 clear : function(o
) {
2536 var t
= this, a
= t
.events
, i
, e
;
2541 for (i
= a
.length
- 1; i
>= 0; i
--) {
2545 t
._remove(e
.obj
, e
.name
, e
.cfunc
);
2546 e
.obj
= e
.cfunc
= null;
2555 cancel : function(e
) {
2560 return this.prevent(e
);
2563 stop : function(e
) {
2564 if (e
.stopPropagation
)
2565 e
.stopPropagation();
2567 e
.cancelBubble
= true;
2572 prevent : function(e
) {
2573 if (e
.preventDefault
)
2576 e
.returnValue
= false;
2581 _unload : function() {
2584 each(t
.events
, function(e
, i
) {
2585 t
._remove(e
.obj
, e
.name
, e
.cfunc
);
2586 e
.obj
= e
.cfunc
= null;
2593 _add : function(o
, n
, f
) {
2595 o
.attachEvent('on' + n
, f
);
2596 else if (o
.addEventListener
)
2597 o
.addEventListener(n
, f
, false);
2602 _remove : function(o
, n
, f
) {
2606 o
.detachEvent('on' + n
, f
);
2607 else if (o
.removeEventListener
)
2608 o
.removeEventListener(n
, f
, false);
2612 // Might fail with permission denined on IE so we just ignore that
2617 _pageInit : function() {
2620 e
._remove(window
, 'DOMContentLoaded', e
._pageInit
);
2623 each(e
.inits
, function(c
) {
2630 _wait : function() {
2633 // No need since the document is already loaded
2634 if (window
.tinyMCE_GZ
&& tinyMCE_GZ
.loaded
) {
2635 Event
.domLoaded
= 1;
2639 if (isIE
&& document
.location
.protocol
!= 'https:') {
2640 // Fake DOMContentLoaded on IE
2641 document
.write('<script id=__ie_onload defer src=\'javascript:""\';><\/script>');
2642 DOM
.get("__ie_onload").onreadystatechange = function() {
2643 if (this.readyState
== "complete") {
2645 DOM
.get("__ie_onload").onreadystatechange
= null; // Prevent leak
2649 Event
._add(window
, 'DOMContentLoaded', Event
._pageInit
, Event
);
2651 if (isIE
|| isWebKit
) {
2652 t
= setInterval(function() {
2653 if (/loaded|complete/.test(document
.readyState
)) {
2665 Event
= tinymce
.dom
.Event
;
2667 // Dispatch DOM content loaded event for IE and Safari
2669 tinymce
.addUnload(Event
._unload
);
2672 /* file:jscripts/tiny_mce/classes/dom/Element.js */
2675 var each
= tinymce
.each
;
2677 tinymce
.create('tinymce.dom.Element', {
2678 Element : function(id
, s
) {
2679 var t
= this, dom
, el
;
2683 t
.dom
= dom
= s
.dom
|| tinymce
.DOM
;
2686 // Only IE leaks DOM references, this is a lot faster
2688 el
= t
.dom
.get(t
.id
);
2714 var a
= arguments
, o
;
2717 if (tinymce
.isOpera
) {
2720 each(arguments
, function(v
) {
2724 Array
.prototype.unshift
.call(a
, el
|| id
);
2726 o
= dom
[k
].apply(dom
, a
);
2734 on : function(n
, f
, s
) {
2735 return tinymce
.dom
.Event
.add(this.id
, n
, f
, s
);
2738 getXY : function() {
2740 x
: parseInt(this.getStyle('left')),
2741 y
: parseInt(this.getStyle('top'))
2745 getSize : function() {
2746 var n
= this.dom
.get(this.id
);
2749 w
: parseInt(this.getStyle('width') || n
.clientWidth
),
2750 h
: parseInt(this.getStyle('height') || n
.clientHeight
)
2754 moveTo : function(x
, y
) {
2755 this.setStyles({left
: x
, top
: y
});
2758 moveBy : function(x
, y
) {
2759 var p
= this.getXY();
2761 this.moveTo(p
.x
+ x
, p
.y
+ y
);
2764 resizeTo : function(w
, h
) {
2765 this.setStyles({width
: w
, height
: h
});
2768 resizeBy : function(w
, h
) {
2769 var s
= this.getSize();
2771 this.resizeTo(s
.w
+ w
, s
.h
+ h
);
2774 update : function(k
) {
2775 var t
= this, b
, dom
= t
.dom
;
2777 if (tinymce
.isIE6
&& t
.settings
.blocker
) {
2781 if (k
.indexOf('get') === 0 || k
.indexOf('has') === 0 || k
.indexOf('is') === 0)
2784 // Remove blocker on remove
2785 if (k
== 'remove') {
2786 dom
.remove(t
.blocker
);
2791 t
.blocker
= dom
.uniqueId();
2792 b
= dom
.add(t
.settings
.container
|| dom
.getRoot(), 'iframe', {id
: t
.blocker
, style
: 'position:absolute;', frameBorder
: 0, src
: 'javascript:""'});
2793 dom
.setStyle(b
, 'opacity', 0);
2795 b
= dom
.get(t
.blocker
);
2797 dom
.setStyle(b
, 'left', t
.getStyle('left', 1));
2798 dom
.setStyle(b
, 'top', t
.getStyle('top', 1));
2799 dom
.setStyle(b
, 'width', t
.getStyle('width', 1));
2800 dom
.setStyle(b
, 'height', t
.getStyle('height', 1));
2801 dom
.setStyle(b
, 'display', t
.getStyle('display', 1));
2802 dom
.setStyle(b
, 'zIndex', parseInt(t
.getStyle('zIndex', 1) || 0) - 1);
2809 /* file:jscripts/tiny_mce/classes/dom/Selection.js */
2812 function trimNl(s
) {
2813 return s
.replace(/[\n\r]+/g, '');
2817 var is
= tinymce
.is
, isIE
= tinymce
.isIE
, each
= tinymce
.each
;
2819 tinymce
.create('tinymce.dom.Selection', {
2820 Selection : function(dom
, win
, serializer
) {
2825 t
.serializer
= serializer
;
2828 tinymce
.addUnload(t
.destroy
, t
);
2831 getContent : function(s
) {
2832 var t
= this, r
= t
.getRng(), e
= t
.dom
.create("body"), se
= t
.getSel(), wb
, wa
, n
;
2837 s
.format
= s
.format
|| 'html';
2839 if (s
.format
== 'text')
2840 return t
.isCollapsed() ? '' : (r
.text
|| (se
.toString
? se
.toString() : ''));
2842 if (r
.cloneContents
) {
2843 n
= r
.cloneContents();
2847 } else if (is(r
.item
) || is(r
.htmlText
))
2848 e
.innerHTML
= r
.item
? r
.item(0).outerHTML
: r
.htmlText
;
2850 e
.innerHTML
= r
.toString();
2852 // Keep whitespace before and after
2853 if (/^\s/.test(e
.innerHTML
))
2856 if (/\s+$/.test(e
.innerHTML
))
2861 return t
.isCollapsed() ? '' : wb
+ t
.serializer
.serialize(e
, s
) + wa
;
2864 setContent : function(h
, s
) {
2865 var t
= this, r
= t
.getRng(), c
, d
= t
.win
.document
;
2867 s
= s
|| {format
: 'html'};
2869 h
= t
.dom
.processHTML(h
);
2872 // Make caret marker since insertNode places the caret in the beginning of text after insert
2873 h
+= '<span id="__caret">_</span>';
2875 // Delete and insert new node
2877 r
.insertNode(t
.getRng().createContextualFragment(h
));
2879 // Move to caret marker
2880 c
= t
.dom
.get('__caret');
2882 // Make sure we wrap it compleatly, Opera fails with a simple select call
2883 r
= d
.createRange();
2884 r
.setStartBefore(c
);
2888 // Delete the marker, and hopefully the caret gets placed in the right location
2889 d
.execCommand('Delete', false, null);
2891 // In case it's still there
2892 t
.dom
.remove('__caret');
2895 // Delete content and get caret text selection
2896 d
.execCommand('Delete', false, null);
2904 getStart : function() {
2905 var t
= this, r
= t
.getRng(), e
;
2913 e
= r
.parentElement();
2915 if (e
&& e
.nodeName
== 'BODY')
2916 return e
.firstChild
;
2920 e
= r
.startContainer
;
2922 if (e
.nodeName
== 'BODY')
2923 return e
.firstChild
;
2925 return t
.dom
.getParent(e
, function(n
) {return n
.nodeType
== 1;});
2929 getEnd : function() {
2930 var t
= this, r
= t
.getRng(), e
;
2938 e
= r
.parentElement();
2940 if (e
&& e
.nodeName
== 'BODY')
2947 if (e
.nodeName
== 'BODY')
2950 return t
.dom
.getParent(e
, function(n
) {return n
.nodeType
== 1;});
2954 getBookmark : function(si
) {
2955 var t
= this, r
= t
.getRng(), tr
, sx
, sy
, vp
= t
.dom
.getViewPort(t
.win
), e
, sp
, bp
, le
, c
= -0xFFFFFF, s
, ro
= t
.dom
.getRoot(), wb
= 0, wa
= 0, nv
;
2959 // Simple bookmark fast but not as persistent
2961 return {rng
: r
, scrollX
: sx
, scrollY
: sy
};
2965 // Control selection
2969 each(t
.dom
.select(e
.nodeName
), function(n
, i
) {
2985 tr
= t
.dom
.doc
.body
.createTextRange();
2986 tr
.moveToElementText(ro
);
2988 bp
= Math
.abs(tr
.move('character', c
));
2992 sp
= Math
.abs(tr
.move('character', c
));
2996 le
= Math
.abs(tr
.move('character', c
)) - sp
;
3014 if (e
&& e
.nodeName
== 'IMG') {
3023 function getPos(r
, sn
, en
) {
3024 var w
= t
.dom
.doc
.createTreeWalker(r
, NodeFilter
.SHOW_TEXT
, null, false), n
, p
= 0, d
= {};
3026 while ((n
= w
.nextNode()) != null) {
3035 p
+= trimNl(n
.nodeValue
|| '').length
;
3041 // Caret or selection
3042 if (s
.anchorNode
== s
.focusNode
&& s
.anchorOffset
== s
.focusOffset
) {
3043 e
= getPos(ro
, s
.anchorNode
, s
.focusNode
);
3046 return {scrollX
: sx
, scrollY
: sy
};
3048 // Count whitespace before
3049 trimNl(s
.anchorNode
.nodeValue
|| '').replace(/^\s+/, function(a
) {wb
= a
.length
;});
3052 start
: Math
.max(e
.start
+ s
.anchorOffset
- wb
, 0),
3053 end
: Math
.max(e
.end
+ s
.focusOffset
- wb
, 0),
3056 beg
: s
.anchorOffset
- wb
== 0
3059 e
= getPos(ro
, r
.startContainer
, r
.endContainer
);
3061 // Count whitespace before start and end container
3062 //(r.startContainer.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;});
3063 //(r.endContainer.nodeValue || '').replace(/^\s+/, function(a) {wa = a.length;});
3066 return {scrollX
: sx
, scrollY
: sy
};
3069 start
: Math
.max(e
.start
+ r
.startOffset
- wb
, 0),
3070 end
: Math
.max(e
.end
+ r
.endOffset
- wa
, 0),
3073 beg
: r
.startOffset
- wb
== 0
3078 moveToBookmark : function(b
) {
3079 var t
= this, r
= t
.getRng(), s
= t
.getSel(), ro
= t
.dom
.getRoot(), sd
, nvl
, nv
;
3081 function getPos(r
, sp
, ep
) {
3082 var w
= t
.dom
.doc
.createTreeWalker(r
, NodeFilter
.SHOW_TEXT
, null, false), n
, p
= 0, d
= {}, o
, v
, wa
, wb
;
3084 while ((n
= w
.nextNode()) != null) {
3087 nv
= n
.nodeValue
|| '';
3088 //nv.replace(/^\s+[^\s]/, function(a) {wb = a.length - 1;});
3089 //nv.replace(/[^\s]\s+$/, function(a) {wa = a.length - 1;});
3091 nvl
= trimNl(nv
).length
;
3094 if (p
>= sp
&& !d
.startNode
) {
3097 // Fix for odd quirk in FF
3098 if (b
.beg
&& o
>= nvl
)
3102 d
.startOffset
= o
+ wb
;
3107 d
.endOffset
= ep
- (p
- nvl
) + wb
;
3118 t
.win
.scrollTo(b
.scrollX
, b
.scrollY
);
3135 // Handle control bookmark
3137 r
= ro
.createControlRange();
3139 each(t
.dom
.select(b
.tag
), function(n
, i
) {
3144 // Try/catch needed since this operation breaks when TinyMCE is placed in hidden divs/tabs
3146 // Incorrect bookmark
3150 r
= s
.createRange();
3151 r
.moveToElementText(ro
);
3153 r
.moveStart('character', b
.start
);
3154 r
.moveEnd('character', b
.length
);
3163 // Needed for some odd IE bug #1843306
3175 s
.removeAllRanges();
3178 if (is(b
.start
) && is(b
.end
)) {
3180 sd
= getPos(ro
, b
.start
, b
.end
);
3183 r
= t
.dom
.doc
.createRange();
3184 r
.setStart(sd
.startNode
, sd
.startOffset
);
3185 r
.setEnd(sd
.endNode
, sd
.endOffset
);
3186 s
.removeAllRanges();
3190 if (!tinymce
.isOpera
)
3199 select : function(n
, c
) {
3200 var t
= this, r
= t
.getRng(), s
= t
.getSel(), b
, fn
, ln
, d
= t
.win
.document
;
3203 return n
? d
.createTreeWalker(n
, NodeFilter
.SHOW_TEXT
, null, false).nextNode() : null;
3212 w
= d
.createTreeWalker(n
, NodeFilter
.SHOW_TEXT
, null, false);
3213 while (c
= w
.nextNode())
3223 if (/^(IMG|TABLE)$/.test(n
.nodeName
)) {
3224 r
= b
.createControlRange();
3227 r
= b
.createTextRange();
3228 r
.moveToElementText(n
);
3233 // Throws illigal agrument in IE some times
3241 //console.debug(fn, ln);
3242 r
= d
.createRange();
3244 r
.setEnd(ln
, ln
.nodeValue
.length
);
3256 isCollapsed : function() {
3257 var t
= this, r
= t
.getRng(), s
= t
.getSel();
3262 return !s
|| r
.boundingWidth
== 0 || r
.collapsed
;
3265 collapse : function(b
) {
3266 var t
= this, r
= t
.getRng(), n
;
3268 // Control range on IE
3271 r
= this.win
.document
.body
.createTextRange();
3272 r
.moveToElementText(n
);
3279 getSel : function() {
3280 var t
= this, w
= this.win
;
3282 return w
.getSelection
? w
.getSelection() : w
.document
.selection
;
3285 getRng : function() {
3286 var t
= this, s
= t
.getSel(), r
;
3290 r
= s
.rangeCount
> 0 ? s
.getRangeAt(0) : (s
.createRange
? s
.createRange() : t
.win
.document
.createRange());
3292 // IE throws unspecified error here if TinyMCE is placed in a frame/iframe
3295 // No range found then create an empty one
3296 // This can occur when the editor is placed in a hidden container element on Gecko
3297 // Or on IE when there was an exception
3299 r
= isIE
? t
.win
.document
.body
.createTextRange() : t
.win
.document
.createRange();
3304 setRng : function(r
) {
3311 s
.removeAllRanges();
3318 // Needed for some odd IE bug #1843306
3323 setNode : function(n
) {
3326 t
.setContent(t
.dom
.getOuterHTML(n
));
3331 getNode : function() {
3332 var t
= this, r
= t
.getRng(), s
= t
.getSel(), e
;
3335 // Range maybe lost after the editor is made visible again
3337 return t
.dom
.getRoot();
3339 e
= r
.commonAncestorContainer
;
3341 // Handle selection a image or other control like element such as anchors
3343 // If the anchor node is a element instead of a text node then return this element
3344 if (tinymce
.isWebKit
&& s
.anchorNode
&& s
.anchorNode
.nodeType
== 1)
3345 return s
.anchorNode
.childNodes
[s
.anchorOffset
];
3347 if (r
.startContainer
== r
.endContainer
) {
3348 if (r
.startOffset
- r
.endOffset
< 2) {
3349 if (r
.startContainer
.hasChildNodes())
3350 e
= r
.startContainer
.childNodes
[r
.startOffset
];
3355 return t
.dom
.getParent(e
, function(n
) {
3356 return n
.nodeType
== 1;
3360 return r
.item
? r
.item(0) : r
.parentElement();
3363 destroy : function(s
) {
3368 // Manual destroy then remove unload handler
3370 tinymce
.removeUnload(t
.destroy
);
3376 /* file:jscripts/tiny_mce/classes/dom/XMLWriter.js */
3379 tinymce
.create('tinymce.dom.XMLWriter', {
3382 XMLWriter : function(s
) {
3385 var i
= document
.implementation
;
3387 if (!i
|| !i
.createDocument
) {
3389 try {return new ActiveXObject('MSXML2.DOMDocument');} catch (ex
) {}
3390 try {return new ActiveXObject('Microsoft.XmlDom');} catch (ex
) {}
3392 return i
.createDocument('', '', null);
3395 this.doc
= getXML();
3397 // Since Opera and WebKit doesn't escape > into > we need to do it our self to normalize the output for all browsers
3398 this.valid
= tinymce
.isOpera
|| tinymce
.isWebKit
;
3403 reset : function() {
3404 var t
= this, d
= t
.doc
;
3407 d
.removeChild(d
.firstChild
);
3409 t
.node
= d
.appendChild(d
.createElement("html"));
3412 writeStartElement : function(n
) {
3415 t
.node
= t
.node
.appendChild(t
.doc
.createElement(n
));
3418 writeAttribute : function(n
, v
) {
3420 v
= v
.replace(/>/g
, '%MCGT%');
3422 this.node
.setAttribute(n
, v
);
3425 writeEndElement : function() {
3426 this.node
= this.node
.parentNode
;
3429 writeFullEndElement : function() {
3430 var t
= this, n
= t
.node
;
3432 n
.appendChild(t
.doc
.createTextNode(""));
3433 t
.node
= n
.parentNode
;
3436 writeText : function(v
) {
3438 v
= v
.replace(/>/g
, '%MCGT%');
3440 this.node
.appendChild(this.doc
.createTextNode(v
));
3443 writeCDATA : function(v
) {
3444 this.node
.appendChild(this.doc
.createCDATA(v
));
3447 writeComment : function(v
) {
3448 this.node
.appendChild(this.doc
.createComment(v
.replace(/\-\-/g, ' ')));
3451 getContent : function() {
3454 h
= this.doc
.xml
|| new XMLSerializer().serializeToString(this.doc
);
3455 h
= h
.replace(/<\?[^?]+\?>|<html>|<\/html>|<html\/>|<!DOCTYPE[^>]+>/g, '');
3456 h
= h
.replace(/ ?\/>/g, ' />');
3459 h = h.replace(/\%MCGT%/g, '>
;');
3467 /* file:jscripts/tiny_mce/classes/dom/StringWriter.js */
3470 tinymce.create('tinymce
.dom
.StringWriter
', {
3477 StringWriter : function(s) {
3478 this.settings = tinymce.extend({
3486 reset : function() {
3493 writeStartElement : function(n) {
3494 this._writeAttributesEnd();
3495 this.writeRaw('<' + n);
3499 this.elementCount = this.count;
3502 writeAttribute : function(n, v) {
3505 t.writeRaw(" " + t.encode(n) + '="' + t.encode(v) + '"');
3508 writeEndElement : function() {
3511 if (this.tags.length > 0) {
3512 n = this.tags.pop();
3514 if (this._writeAttributesEnd(1))
3515 this.writeRaw('</' + n + '>');
3517 if (this.settings.indentation > 0)
3518 this.writeRaw('\n');
3522 writeFullEndElement : function() {
3523 if (this.tags.length > 0) {
3524 this._writeAttributesEnd();
3525 this.writeRaw('</' + this.tags.pop() + '>');
3527 if (this.settings.indentation > 0)
3528 this.writeRaw('\n');
3532 writeText : function(v) {
3533 this._writeAttributesEnd();
3534 this.writeRaw(this.encode(v));
3538 writeCDATA : function(v) {
3539 this._writeAttributesEnd();
3540 this.writeRaw('<![CDATA
[' + v + ']]>');
3544 writeComment : function(v) {
3545 this._writeAttributesEnd();
3546 this.writeRaw('<!-- ' + v + '-->');
3550 writeRaw : function(v) {
3554 encode : function(s) {
3555 return s.replace(/[<>&"]/g, function(v) {
3574 getContent : function() {
3578 _writeAttributesEnd : function(s) {
3582 this.inAttr = false;
3584 if (s && this.elementCount == this.count) {
3585 this.writeRaw(' />');
3597 /* file:jscripts/tiny_mce/classes/dom/Serializer.js */
3601 var extend = tinymce.extend, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, isIE = tinymce.isIE, isGecko = tinymce.isGecko;
3603 // Returns only attribites that have values not all attributes in IE
3604 function getIEAtts(n) {
3607 // Object will throw exception in IE
3608 if (n.nodeName == 'OBJECT')
3609 return n.attributes;
3611 n.cloneNode(false).outerHTML.replace(/([a-z0-9\:\-_]+)=/gi, function(a, b) {
3612 o.push({specified : 1, nodeName : b});
3618 function wildcardToRE(s) {
3619 return s.replace(/([?+*])/g, '.$1');
3622 tinymce.create('tinymce.dom.Serializer', {
3623 Serializer : function(s) {
3627 t.onPreProcess = new Dispatcher(t);
3628 t.onPostProcess = new Dispatcher(t);
3630 if (tinymce.relaxedDomain && tinymce.isGecko) {
3631 // Gecko has a bug where we can't create a new XML document if domain relaxing is used
3632 t.writer = new tinymce.dom.StringWriter();
3635 t.writer = new tinymce.dom.XMLWriter();
3637 // IE might throw exception if ActiveX is disabled so we then switch to the slightly slower StringWriter
3638 t.writer = new tinymce.dom.StringWriter();
3643 t.settings = s = extend({
3648 invalid_attrs : /^(mce_|_moz_)/,
3649 closed : /(br|hr|input|meta|img|link|param)/,
3650 entity_encoding : 'named',
3651 entities : '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,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',
3652 valid_elements : '*[*]',
3653 extended_valid_elements : 0,
3654 valid_child_elements : 0,
3655 invalid_elements : 0,
3656 fix_table_elements : 0,
3657 fix_list_elements : true,
3658 fix_content_duplication : true,
3659 convert_fonts_to_spans : false,
3660 font_size_classes : 0,
3661 font_size_style_values : 0,
3662 apply_source_formatting : 0,
3663 indent_mode : 'simple',
3666 remove_linebreaks : 1,
3667 remove_redundant_brs : 1
3672 if (s.remove_redundant_brs) {
3673 t.onPostProcess.add(function(se, o) {
3674 // Remove BR elements at end of list elements since they get rendered in IE
3675 o.content = o.content.replace(/<br \/>(\s*<\/li>)/g, '$1');
3679 if (s.fix_list_elements) {
3680 t.onPreProcess.add(function(se, o) {
3681 var nl, x, a = ['ol', 'ul'], i, n, p, r = /^(OL|UL)$/, np;
3683 function prevNode(e, n) {
3684 var a = n.split(','), i;
3686 while ((e = e.previousSibling) != null) {
3687 for (i=0; i<a.length; i++) {
3688 if (e.nodeName == a[i])
3696 for (x=0; x<a.length; x++) {
3697 nl = t.dom.select(a[x], o.node);
3699 for (i=0; i<nl.length; i++) {
3703 if (r.test(p.nodeName)) {
3704 np = prevNode(n, 'LI');
3707 np = t.dom.create('li');
3708 np.innerHTML = ' ';
3710 p.insertBefore(np, p.firstChild);
3719 if (s.fix_table_elements) {
3720 t.onPreProcess.add(function(se, o) {
3721 each(t.dom.select('table', o.node), function(e) {
3722 var pa = t.dom.getParent(e, 'H1,H2,H3,H4,H5,H6,P'), pa2, n, tm, pl = [], i, ns;
3725 pa2 = pa.cloneNode(false);
3728 for (n = e; n = n.parentNode;) {
3736 for (i = pl.length - 1; i >= 0; i--) {
3737 if (i == pl.length - 1) {
3738 while (ns = pl[i - 1].nextSibling)
3739 tm.appendChild(ns.parentNode.removeChild(ns));
3741 n = pl[i].cloneNode(false);
3744 while (ns = pl[i - 1].nextSibling)
3745 n.appendChild(ns.parentNode.removeChild(ns));
3748 tm = tm.appendChild(n);
3752 e = t.dom.insertAfter(e.parentNode.removeChild(e), pa);
3753 t.dom.insertAfter(e, pa);
3754 t.dom.insertAfter(pa2, e);
3761 setEntities : function(s) {
3762 var t = this, a, i, l = {}, re = '', v;
3764 // No need to setup more than once
3768 // Build regex and lookup array
3770 for (i = 0; i < a.length; i += 2) {
3773 // Don't add default & " etc.
3774 if (v == 34 || v == 38 || v == 60 || v == 62)
3777 l[String.fromCharCode(a[i])] = a[i + 1];
3779 v = parseInt(a[i]).toString(16);
3780 re += '\\u' + '0000'.substring(v.length) + v;
3784 t.settings.entity_encoding = 'raw';
3788 t.entitiesRE = new RegExp('[' + re + ']', 'g');
3792 setValidChildRules : function(s) {
3793 this.childRules = null;
3794 this.addValidChildRules(s);
3797 addValidChildRules : function(s) {
3798 var t = this, inst, intr, bloc;
3803 inst = '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';
3804 intr = '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';
3805 bloc = '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';
3807 each(s.split(','), function(s) {
3808 var p = s.split(/\[|\]/), re;
3811 each(p[1].split('|'), function(v) {
3821 v = intr.substring(2);
3829 v = inst.substring(2);
3843 re = new RegExp('^(' + s.toLowerCase() + ')$', 'i');
3845 each(p[0].split('/'), function(s) {
3846 t.childRules = t.childRules || {};
3847 t.childRules[s] = re;
3853 each(t.childRules, function(v, k) {
3860 t.parentElementsRE = new RegExp('^(' + s.toLowerCase() + ')$', 'i');
3862 /*console.debug(t.parentElementsRE.toString());
3863 each(t.childRules, function(v) {
3864 console.debug(v.toString());
3868 setRules : function(s) {
3874 t.validElements = {};
3876 return t.addRules(s);
3879 addRules : function(s) {
3887 each(s.split(','), function(s) {
3888 var p = s.split(/\[|\]/), tn = p[0].split('/'), ra, at, wat, va = [];
3890 // Extend with default rules
3892 at = tinymce.extend([], dr.attribs);
3896 each(p[1].split('|'), function(s) {
3901 // Parse attribute rule
3902 s = s.replace(/::/g, '~');
3903 s = /^([!\-])?([\w*.?~_\-]+|)([=:<])?(.+)?$/.exec(s);
3904 s[2] = s[2].replace(/~/g, ':');
3906 // Add required attributes
3912 // Remove inherited attributes
3914 for (i = 0; i <at.length; i++) {
3915 if (at[i].name == s[2]) {
3923 // Add default attrib values
3925 ar.defaultVal = s[4] || '';
3928 // Add forced attrib values
3930 ar.forcedVal = s[4];
3933 // Add validation values
3935 ar.validVals = s[4].split('?');
3939 if (/[*.?]/.test(s[2])) {
3941 ar.nameRE = new RegExp('^' + wildcardToRE(s[2]) + '$');
3952 // Handle element names
3953 each(tn, function(s, i) {
3954 var pr = s.charAt(0), x = 1, ru = {};
3956 // Extend with default rule data
3959 ru.noEmpty = dr.noEmpty;
3962 ru.fullEnd = dr.fullEnd;
3986 tn[i] = s = s.substring(x);
3987 t.validElements[s] = 1;
3989 // Add element name or element regex
3990 if (/[*.?]/.test(tn[0])) {
3991 ru.nameRE = new RegExp('^' + wildcardToRE(tn[0]) + '$');
3992 t.wildRules = t.wildRules || {};
3993 t.wildRules.push(ru);
3997 // Store away default rule
4007 ru.requiredAttribs = ra;
4010 // Build valid attributes regexp
4012 each(va, function(v) {
4016 s += '(' + wildcardToRE(v) + ')';
4018 ru.validAttribsRE = new RegExp('^' + s.toLowerCase() + '$');
4019 ru.wildAttribs = wat;
4024 // Build valid elements regexp
4026 each(t.validElements, function(v, k) {
4033 t.validElementsRE = new RegExp('^(' + wildcardToRE(s.toLowerCase()) + ')$');
4035 //console.debug(t.validElementsRE.toString());
4036 //console.dir(t.rules);
4037 //console.dir(t.wildRules);
4040 findRule : function(n) {
4041 var t = this, rl = t.rules, i, r;
4052 for (i = 0; i < rl.length; i++) {
4053 if (rl[i].nameRE.test(n))
4060 findAttribRule : function(ru, n) {
4061 var i, wa = ru.wildAttribs;
4063 for (i = 0; i < wa.length; i++) {
4064 if (wa[i].nameRE.test(n))
4071 serialize : function(n, o) {
4076 o.format = o.format || 'html';
4078 n = n.cloneNode(true);
4079 t.key = '' + (parseInt(t.key) + 1);
4084 t.onPreProcess.dispatch(t, o);
4087 // Serialize HTML DOM into a string
4089 t._serializeNode(n, o.getInner);
4092 o.content = t.writer.getContent();
4095 t.onPostProcess.dispatch(t, o);
4100 return tinymce.trim(o.content);
4103 // Internal functions
4105 _postProcess : function(o) {
4106 var t = this, s = t.settings, h = o.content, sc = [], p;
4108 if (o.format == 'html') {
4109 // Protect some elements
4113 {pattern : /(<script[^>]*>)(.*?)(<\/script>)/g},
4114 {pattern : /(<style[^>]*>)(.*?)(<\/style>)/g},
4115 {pattern : /(<pre[^>]*>)(.*?)(<\/pre>)/g, encode : 1},
4116 {pattern : /(<!--\[CDATA\[)(.*?)(\]\]-->)/g}
4123 if (s.entity_encoding !== 'raw')
4126 // Use BR instead of padded P elements inside editor and use <p> </p> outside editor
4128 h = h.replace(/<p>\s+( | |\u00a0|<br \/>)\s+<\/p>/g, '<p><br /></p>');
4130 h = h.replace(/<p>\s+( | |\u00a0|<br \/>)\s+<\/p>/g, '<p>$1</p>');*/
4132 // Since Gecko and Safari keeps whitespace in the DOM we need to
4133 // remove it inorder to match other browsers. But I think Gecko and Safari is right.
4134 // This process is only done when getting contents out from the editor.
4136 // We need to replace paragraph whitespace with an nbsp before indentation to keep the \u00a0 char
4137 h = h.replace(/<p>\s+<\/p>|<p([^>]+)>\s+<\/p>/g, s.entity_encoding == 'numeric' ? '<p$1> </p>' : '<p$1> </p>');
4139 if (s.remove_linebreaks) {
4140 h = h.replace(/\r?\n|\r/g, ' ');
4141 h = h.replace(/(<[^>]+>)\s+/g, '$1 ');
4142 h = h.replace(/\s+(<\/[^>]+>)/g, ' $1');
4143 h = h.replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object) ([^>]+)>\s+/g, '<$1 $2>'); // Trim block start
4144 h = h.replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>\s+/g, '<$1>'); // Trim block start
4145 h = h.replace(/\s+<\/(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>/g, '</$1>'); // Trim block end
4148 // Simple indentation
4149 if (s.apply_source_formatting && s.indent_mode == 'simple') {
4150 // Add line breaks before and after block elements
4151 h = h.replace(/<(\/?)(ul|hr|table|meta|link|tbody|tr|object|body|head|html|map)(|[^>]+)>\s*/g, '\n<$1$2$3>\n');
4152 h = h.replace(/\s*<(p|h[1-6]|blockquote|div|title|style|pre|script|td|li|area)(|[^>]+)>/g, '\n<$1$2>');
4153 h = h.replace(/<\/(p|h[1-6]|blockquote|div|title|style|pre|script|td|li)>\s*/g, '</$1>\n');
4154 h = h.replace(/\n\n/g, '\n');
4158 h = t._unprotect(h, p);
4160 // Restore CDATA sections
4161 h = h.replace(/<!--\[CDATA\[([\s\S]+)\]\]-->/g, '<![CDATA[$1]]>');
4163 // Restore the \u00a0 character if raw mode is enabled
4164 if (s.entity_encoding == 'raw')
4165 h = h.replace(/<p> <\/p>|<p([^>]+)> <\/p>/g, '<p$1>\u00a0</p>');
4171 _serializeNode : function(n, inn) {
4172 var t = this, s = t.settings, w = t.writer, hc, el, cn, i, l, a, at, no, v, nn, ru, ar, iv;
4174 if (!s.node_filter || s.node_filter(n)) {
4175 switch (n.nodeType) {
4177 if (n.hasAttribute ? n.hasAttribute('mce_bogus') : n.getAttribute('mce_bogus'))
4181 hc = n.hasChildNodes();
4182 nn = n.getAttribute('mce_name') || n.nodeName.toLowerCase();
4184 // Add correct prefix on IE
4186 if (n.scopeName !== 'HTML' && n.scopeName !== 'html')
4187 nn = n.scopeName + ':' + nn;
4190 // Remove mce prefix on IE needed for the abbr element
4191 if (nn.indexOf('mce:') === 0)
4192 nn = nn.substring(4);
4195 if (!t.validElementsRE.test(nn) || (t.invalidElementsRE && t.invalidElementsRE.test(nn)) || inn) {
4201 // Fix IE content duplication (DOM can have multiple copies of the same node)
4202 if (s.fix_content_duplication) {
4203 if (n.mce_serialized == t.key)
4206 n.mce_serialized = t.key;
4209 // IE sometimes adds a / infront of the node name
4210 if (nn.charAt(0) == '/')
4211 nn = nn.substring(1);
4212 } else if (isGecko) {
4213 // Ignore br elements
4214 if (n.nodeName === 'BR' && n.getAttribute('type') == '_moz')
4218 // Check if valid child
4220 if (t.parentElementsRE.test(t.elementName)) {
4221 if (!t.childRules[t.elementName].test(nn)) {
4230 ru = t.findRule(nn);
4233 // Skip empty nodes or empty node name in IE
4234 if ((!hc && ru.noEmpty) || (isIE && !nn)) {
4240 if (ru.requiredAttribs) {
4241 a = ru.requiredAttribs;
4243 for (i = a.length - 1; i >= 0; i--) {
4244 if (this.dom.getAttrib(n, a[i]) !== '')
4248 // None of the required was there
4255 w.writeStartElement(nn);
4257 // Add ordered attributes
4259 for (i=0, at = ru.attribs, l = at.length; i<l; i++) {
4261 v = t._getAttrib(n, a);
4264 w.writeAttribute(a.name, v);
4268 // Add wild attributes
4269 if (ru.validAttribsRE) {
4270 at = isIE ? getIEAtts(n) : n.attributes;
4271 for (i=at.length-1; i>-1; i--) {
4275 a = no.nodeName.toLowerCase();
4277 if (s.invalid_attrs.test(a) || !ru.validAttribsRE.test(a))
4280 ar = t.findAttribRule(ru, a);
4281 v = t._getAttrib(n, ar, a);
4284 w.writeAttribute(a, v);
4289 // Padd empty nodes with a
4291 w.writeText('\u00a0');
4296 // Check if valid child
4297 if (t.childRules && t.parentElementsRE.test(t.elementName)) {
4298 if (!t.childRules[t.elementName].test(n.nodeName))
4302 return w.writeText(n.nodeValue);
4305 return w.writeCDATA(n.nodeValue);
4308 return w.writeComment(n.nodeValue);
4310 } else if (n.nodeType == 1)
4311 hc = n.hasChildNodes();
4317 t._serializeNode(cn);
4319 cn = cn.nextSibling;
4323 // Write element end
4325 if (hc || !s.closed.test(nn))
4326 w.writeFullEndElement();
4328 w.writeEndElement();
4332 _protect : function(o) {
4335 o.items = o.items || [];
4338 return s.replace(/[\r\n\\]/g, function(c) {
4341 else if (c === '\\')
4349 return s.replace(/\\[\\rn]/g, function(c) {
4352 else if (c === '\\\\')
4359 each(o.patterns, function(p) {
4360 o.content = dec(enc(o.content).replace(p.pattern, function(x, a, b, c) {
4367 return a + '<!--mce:' + (o.items.length - 1) + '-->' + c;
4374 _unprotect : function(h, o) {
4375 h = h.replace(/\<!--mce:([0-9]+)--\>/g, function(a, b) {
4376 return o.items[parseInt(b)];
4384 _encode : function(h) {
4385 var t = this, s = t.settings, l;
4388 if (s.entity_encoding !== 'raw') {
4389 if (s.entity_encoding.indexOf('named') != -1) {
4390 t.setEntities(s.entities);
4393 h = h.replace(t.entitiesRE, function(a) {
4403 if (s.entity_encoding.indexOf('numeric') != -1) {
4404 h = h.replace(/[\u007E-\uFFFF]/g, function(a) {
4405 return '&#' + a.charCodeAt(0) + ';';
4413 _setup : function() {
4414 var t = this, s = this.settings;
4421 t.setRules(s.valid_elements);
4422 t.addRules(s.extended_valid_elements);
4423 t.addValidChildRules(s.valid_child_elements);
4425 if (s.invalid_elements)
4426 t.invalidElementsRE = new RegExp('^(' + wildcardToRE(s.invalid_elements.replace(/,/g, '|').toLowerCase()) + ')$');
4428 if (s.attrib_value_filter)
4429 t.attribValueFilter = s.attribValueFilter;
4432 _getAttrib : function(n, a, na) {
4437 if (a.forcedVal && (v = a.forcedVal)) {
4439 return this.dom.uniqueId();
4444 v = this.dom.getAttrib(n, na);
4449 // Whats the point? Remove usless attribute value
4456 if (this.attribValueFilter)
4457 v = this.attribValueFilter(na, v, n);
4460 for (i = a.validVals.length - 1; i >= 0; i--) {
4461 if (v == a.validVals[i])
4469 if (v === '' && typeof(a.defaultVal) != 'undefined') {
4473 return this.dom.uniqueId();
4477 // Remove internal mceItemXX classes when content is extracted from editor
4478 if (na == 'class' && this.processObj.get)
4479 v = v.replace(/\s?mceItem\w+\s?/g, '');
4492 /* file:jscripts/tiny_mce/classes/dom/ScriptLoader.js */
4495 var each = tinymce.each;
4497 tinymce.create('tinymce.dom.ScriptLoader', {
4498 ScriptLoader : function(s) {
4499 this.settings = s || {};
4504 isDone : function(u) {
4505 return this.lookup[u] ? this.lookup[u].state == 2 : 0;
4508 markDone : function(u) {
4509 this.lookup[u] = {state : 2, url : u};
4512 add : function(u, cb, s, pr) {
4513 var t = this, lo = t.lookup, o;
4516 // Is loaded fire callback
4517 if (cb && o.state == 2)
4523 o = {state : 0, url : u, func : cb, scope : s || this};
4535 load : function(u, cb, s) {
4538 if (o = t.lookup[u]) {
4539 // Is loaded fire callback
4540 if (cb && o.state == 2)
4546 function loadScript(u) {
4547 if (tinymce.dom.Event.domLoaded || t.settings.strict_mode) {
4548 tinymce.util.XHR.send({
4549 url : tinymce._addVer(u),
4550 error : t.settings.error,
4552 success : function(co) {
4557 document.write('<script type="text
/javascript" src="' + tinymce._addVer(u) + '"></script>');
4560 if (!tinymce.is(u, 'string
')) {
4561 each(u, function(u) {
4575 loadQueue : function(cb, s) {
4578 if (!t.queueLoading) {
4580 t.queueCallbacks = [];
4582 t.loadScripts(t.queue, function() {
4588 each(t.queueCallbacks, function(o) {
4589 o.func.call(o.scope);
4593 t.queueCallbacks.push({func : cb, scope : s || t});
4596 eval : function(co) {
4600 if (!w.execScript) {
4604 eval(co, w); // Firefox 3.0a8
4607 w.execScript(co); // IE
4610 loadScripts : function(sc, cb, s) {
4611 var t = this, lo = t.lookup;
4614 o.state = 2; // Has been loaded
4618 o.func.call(o.scope || t);
4621 function allDone() {
4624 // Check if all files are loaded
4626 each(sc, function(o) {
4629 if (o.state === 2) {// It has finished loading
4636 // They are all loaded
4637 if (l === 0 && cb) {
4647 o.state = 1; // Is loading
4649 tinymce.util.XHR.send({
4651 error : t.settings.error,
4652 success : function(co) {
4660 each(sc, function(o) {
4663 // Add to queue if needed
4670 // Is already loading or has been loaded
4674 if (!tinymce.dom.Event.domLoaded && !t.settings.strict_mode) {
4677 // Add onload events
4679 o.state = 1; // Is loading
4681 ix = tinymce.dom.ScriptLoader._addOnLoad(function() {
4687 ol = ' onreadystatechange
="';
4691 ol += 'tinymce
.dom
.ScriptLoader
._onLoad(this,\'' + u + '\',' + ix + ');"';
4694 document.write('<script type="text
/javascript" src="' + tinymce._addVer(u) + '"' + ol + '></script>');
4707 _addOnLoad : function(f) {
4710 t._funcs = t._funcs || [];
4713 return t._funcs.length - 1;
4716 _onLoad : function(e, u, ix) {
4717 if (!tinymce.isIE || e.readyState == 'complete
')
4718 this._funcs[ix].call(this);
4724 // Global script loader
4725 tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();
4728 /* file:jscripts/tiny_mce/classes/ui/Control.js */
4731 // Shorten class names
4732 var DOM = tinymce.DOM, is = tinymce.is;
4734 tinymce.create('tinymce
.ui
.Control
', {
4735 Control : function(id, s) {
4737 this.settings = s = s || {};
4738 this.rendered = false;
4739 this.onRender = new tinymce.util.Dispatcher(this);
4740 this.classPrefix = '';
4741 this.scope = s.scope || this;
4746 setDisabled : function(s) {
4749 if (s != this.disabled) {
4750 e = DOM.get(this.id);
4752 // Add accessibility title for unavailable actions
4753 if (e && this.settings.unavailable_prefix) {
4755 this.prevTitle = e.title;
4756 e.title = this.settings.unavailable_prefix + ": " + e.title;
4758 e.title = this.prevTitle;
4761 this.setState('Disabled
', s);
4762 this.setState('Enabled
', !s);
4767 isDisabled : function() {
4768 return this.disabled;
4771 setActive : function(s) {
4772 if (s != this.active) {
4773 this.setState('Active
', s);
4778 isActive : function() {
4782 setState : function(c, s) {
4783 var n = DOM.get(this.id);
4785 c = this.classPrefix + c;
4790 DOM.removeClass(n, c);
4793 isRendered : function() {
4794 return this.rendered;
4797 renderHTML : function() {
4800 renderTo : function(n) {
4801 DOM.setHTML(n, this.renderHTML());
4804 postRender : function() {
4807 // Set pending states
4808 if (is(t.disabled)) {
4821 remove : function() {
4822 DOM.remove(this.id);
4826 destroy : function() {
4827 tinymce.dom.Event.clear(this.id);
4832 /* file:jscripts/tiny_mce/classes/ui/Container.js */
4834 tinymce.create('tinymce
.ui
.Container
:tinymce
.ui
.Control
', {
4835 Container : function(id, s) {
4842 this.lookup[c.id] = c;
4843 this.controls.push(c);
4849 return this.lookup[n];
4855 /* file:jscripts/tiny_mce/classes/ui/Separator.js */
4857 tinymce.create('tinymce
.ui
.Separator
:tinymce
.ui
.Control
', {
4858 Separator : function(id, s) {
4860 this.classPrefix = 'mceSeparator
';
4863 renderHTML : function() {
4864 return tinymce.DOM.createHTML('span
', {'class' : this.classPrefix});
4869 /* file:jscripts/tiny_mce/classes/ui/MenuItem.js */
4872 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;
4874 tinymce.create('tinymce
.ui
.MenuItem
:tinymce
.ui
.Control
', {
4875 MenuItem : function(id, s) {
4877 this.classPrefix = 'mceMenuItem
';
4880 setSelected : function(s) {
4881 this.setState('Selected
', s);
4885 isSelected : function() {
4886 return this.selected;
4889 postRender : function() {
4894 // Set pending state
4896 t.setSelected(t.selected);
4902 /* file:jscripts/tiny_mce/classes/ui/Menu.js */
4905 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;
4907 tinymce.create('tinymce
.ui
.Menu
:tinymce
.ui
.MenuItem
', {
4908 Menu : function(id, s) {
4913 t.collapsed = false;
4915 t.onAddItem = new tinymce.util.Dispatcher(this);
4918 expand : function(d) {
4922 walk(t, function(o) {
4928 t.collapsed = false;
4931 collapse : function(d) {
4935 walk(t, function(o) {
4944 isCollapsed : function() {
4945 return this.collapsed;
4950 o = new tinymce.ui.MenuItem(o.id || DOM.uniqueId(), o);
4952 this.onAddItem.dispatch(this, o);
4954 return this.items[o.id] = o;
4957 addSeparator : function() {
4958 return this.add({separator : true});
4961 addMenu : function(o) {
4963 o = this.createMenu(o);
4970 hasMenus : function() {
4971 return this.menuCount !== 0;
4974 remove : function(o) {
4975 delete this.items[o.id];
4978 removeAll : function() {
4981 walk(t, function(o) {
4993 createMenu : function(o) {
4994 var m = new tinymce.ui.Menu(o.id || DOM.uniqueId(), o);
4996 m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem);
5003 /* file:jscripts/tiny_mce/classes/ui/DropMenu.js */
5006 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element;
5008 tinymce.create('tinymce
.ui
.DropMenu
:tinymce
.ui
.Menu
', {
5009 DropMenu : function(id, s) {
5011 s.container = s.container || DOM.doc.body;
5012 s.offset_x = s.offset_x || 0;
5013 s.offset_y = s.offset_y || 0;
5014 s.vp_offset_x = s.vp_offset_x || 0;
5015 s.vp_offset_y = s.vp_offset_y || 0;
5017 if (is(s.icons) && !s.icons)
5018 s['class'] += ' mceNoIcons
';
5021 this.onShowMenu = new tinymce.util.Dispatcher(this);
5022 this.onHideMenu = new tinymce.util.Dispatcher(this);
5023 this.classPrefix = 'mceMenu
';
5026 createMenu : function(s) {
5027 var t = this, cs = t.settings, m;
5029 s.container = s.container || cs.container;
5031 s.constrain = s.constrain || cs.constrain;
5032 s['class'] = s['class'] || cs['class'];
5033 s.vp_offset_x = s.vp_offset_x || cs.vp_offset_x;
5034 s.vp_offset_y = s.vp_offset_y || cs.vp_offset_y;
5035 m = new tinymce.ui.DropMenu(s.id || DOM.uniqueId(), s);
5037 m.onAddItem.add(t.onAddItem.dispatch, t.onAddItem);
5042 update : function() {
5043 var t = this, s = t.settings, tb = DOM.get('menu_
' + t.id + '_tbl
'), co = DOM.get('menu_
' + t.id + '_co
'), tw, th;
5045 tw = s.max_width ? Math.min(tb.clientWidth, s.max_width) : tb.clientWidth;
5046 th = s.max_height ? Math.min(tb.clientHeight, s.max_height) : tb.clientHeight;
5049 t.element.setStyles({width : tw + 2, height : th + 2});
5051 t.element.setStyles({width : tw, height : th});
5054 DOM.setStyle(co, 'width
', tw);
5057 DOM.setStyle(co, 'height
', th);
5059 if (tb.clientHeight < s.max_height)
5060 DOM.setStyle(co, 'overflow
', 'hidden
');
5064 showMenu : function(x, y, px) {
5065 var t = this, s = t.settings, co, vp = DOM.getViewPort(), w, h, mx, my, ot = 2, dm, tb, cp = t.classPrefix;
5069 if (t.isMenuVisible)
5073 co = DOM.add(t.settings.container, t.renderNode());
5075 each(t.items, function(o) {
5079 t.element = new Element('menu_
' + t.id, {blocker : 1, container : s.container});
5081 co = DOM.get('menu_
' + t.id);
5083 // Move layer out of sight unless it's Opera since it scrolls to top
of page due to an bug
5084 if (!tinymce
.isOpera
)
5085 DOM
.setStyles(co
, {left
: -0xFFFF , top
: -0xFFFF});
5090 x
+= s
.offset_x
|| 0;
5091 y
+= s
.offset_y
|| 0;
5095 // Move inside viewport if not submenu
5097 w
= co
.clientWidth
- ot
;
5098 h
= co
.clientHeight
- ot
;
5102 if ((x
+ s
.vp_offset_x
+ w
) > mx
)
5103 x
= px
? px
- w
: Math
.max(0, (mx
- s
.vp_offset_x
) - w
);
5105 if ((y
+ s
.vp_offset_y
+ h
) > my
)
5106 y
= Math
.max(0, (my
- s
.vp_offset_y
) - h
);
5109 DOM
.setStyles(co
, {left
: x
, top
: y
});
5112 t
.isMenuVisible
= 1;
5113 t
.mouseClickFunc
= Event
.add(co
, 'click', function(e
) {
5118 if (e
&& (e
= DOM
.getParent(e
, 'TR')) && !DOM
.hasClass(e
, cp
+ 'ItemSub')) {
5130 dm
= dm
.settings
.parent
;
5133 if (m
.settings
.onclick
)
5134 m
.settings
.onclick(e
);
5136 return Event
.cancel(e
); // Cancel to fix onbeforeunload problem
5141 t
.mouseOverFunc
= Event
.add(co
, 'mouseover', function(e
) {
5145 if (e
&& (e
= DOM
.getParent(e
, 'TR'))) {
5149 t
.lastMenu
.collapse(1);
5154 if (e
&& DOM
.hasClass(e
, cp
+ 'ItemSub')) {
5155 //p = DOM.getPos(s.container);
5157 m
.showMenu((r
.x
+ r
.w
- ot
), r
.y
- ot
, r
.x
);
5159 DOM
.addClass(DOM
.get(m
.id
).firstChild
, cp
+ 'ItemActive');
5165 t
.onShowMenu
.dispatch(t
);
5167 if (s
.keyboard_focus
) {
5168 Event
.add(co
, 'keydown', t
._keyHandler
, t
);
5169 DOM
.select('a', 'menu_' + t
.id
)[0].focus(); // Select first link
5174 hideMenu : function(c
) {
5175 var t
= this, co
= DOM
.get('menu_' + t
.id
), e
;
5177 if (!t
.isMenuVisible
)
5180 Event
.remove(co
, 'mouseover', t
.mouseOverFunc
);
5181 Event
.remove(co
, 'click', t
.mouseClickFunc
);
5182 Event
.remove(co
, 'keydown', t
._keyHandler
);
5184 t
.isMenuVisible
= 0;
5192 if (e
= DOM
.get(t
.id
))
5193 DOM
.removeClass(e
.firstChild
, t
.classPrefix
+ 'ItemActive');
5195 t
.onHideMenu
.dispatch(t
);
5203 if (t
.isRendered
&& (co
= DOM
.get('menu_' + t
.id
)))
5204 t
._add(DOM
.select('tbody', co
)[0], o
);
5209 collapse : function(d
) {
5214 remove : function(o
) {
5218 return this.parent(o
);
5221 destroy : function() {
5222 var t
= this, co
= DOM
.get('menu_' + t
.id
);
5224 Event
.remove(co
, 'mouseover', t
.mouseOverFunc
);
5225 Event
.remove(co
, 'click', t
.mouseClickFunc
);
5233 renderNode : function() {
5234 var t
= this, s
= t
.settings
, n
, tb
, co
, w
;
5236 w
= DOM
.create('div', {id
: 'menu_' + t
.id
, 'class' : s
['class'], 'style' : 'position:absolute;left:0;top:0;z-index:200000'});
5237 co
= DOM
.add(w
, 'div', {id
: 'menu_' + t
.id
+ '_co', 'class' : t
.classPrefix
+ (s
['class'] ? ' ' + s
['class'] : '')});
5238 t
.element
= new Element('menu_' + t
.id
, {blocker
: 1, container
: s
.container
});
5241 DOM
.add(co
, 'span', {'class' : t
.classPrefix
+ 'Line'});
5243 // n = DOM.add(co, 'div', {id : 'menu_' + t.id + '_co', 'class' : 'mceMenuContainer'});
5244 n
= DOM
.add(co
, 'table', {id
: 'menu_' + t
.id
+ '_tbl', border
: 0, cellPadding
: 0, cellSpacing
: 0});
5245 tb
= DOM
.add(n
, 'tbody');
5247 each(t
.items
, function(o
) {
5256 // Internal functions
5258 _keyHandler : function(e
) {
5259 var t
= this, kc
= e
.keyCode
;
5262 var i
= t
._focusIdx
+ d
, e
= DOM
.select('a', 'menu_' + t
.id
)[i
];
5272 focus(-1); // Select first link
5283 return this.hideMenu();
5287 _add : function(tb
, o
) {
5288 var n
, s
= o
.settings
, a
, ro
, it
, cp
= this.classPrefix
;
5291 ro
= DOM
.add(tb
, 'tr', {id
: o
.id
, 'class' : cp
+ 'ItemSeparator'});
5292 DOM
.add(ro
, 'td', {'class' : cp
+ 'ItemSeparator'});
5294 if (n
= ro
.previousSibling
)
5295 DOM
.addClass(n
, 'mceLast');
5300 n
= ro
= DOM
.add(tb
, 'tr', {id
: o
.id
, 'class' : cp
+ 'Item ' + cp
+ 'ItemEnabled'});
5301 n
= it
= DOM
.add(n
, 'td');
5302 n
= a
= DOM
.add(n
, 'a', {href
: 'javascript:;', onclick
: "return false;", onmousedown
: 'return false;'});
5304 DOM
.addClass(it
, s
['class']);
5305 // n = DOM.add(n, 'span', {'class' : 'item'});
5306 DOM
.add(n
, 'span', {'class' : 'mceIcon' + (s
.icon
? ' mce_' + s
.icon
: '')});
5307 n
= DOM
.add(n
, s
.element
|| 'span', {'class' : 'mceText', title
: o
.settings
.title
}, o
.settings
.title
);
5309 if (o
.settings
.style
)
5310 DOM
.setAttrib(n
, 'style', o
.settings
.style
);
5312 if (tb
.childNodes
.length
== 1)
5313 DOM
.addClass(ro
, 'mceFirst');
5315 if ((n
= ro
.previousSibling
) && DOM
.hasClass(n
, cp
+ 'ItemSeparator'))
5316 DOM
.addClass(ro
, 'mceFirst');
5319 DOM
.addClass(ro
, cp
+ 'ItemSub');
5321 if (n
= ro
.previousSibling
)
5322 DOM
.removeClass(n
, 'mceLast');
5324 DOM
.addClass(ro
, 'mceLast');
5329 /* file:jscripts/tiny_mce/classes/ui/Button.js */
5332 var DOM
= tinymce
.DOM
;
5334 tinymce
.create('tinymce.ui.Button:tinymce.ui.Control', {
5335 Button : function(id
, s
) {
5337 this.classPrefix
= 'mceButton';
5340 renderHTML : function() {
5341 var cp
= this.classPrefix
, s
= this.settings
, h
, l
;
5343 l
= DOM
.encode(s
.label
|| '');
5344 h
= '<a id="' + this.id
+ '" href="javascript:;" class="' + cp
+ ' ' + cp
+ 'Enabled ' + s
['class'] + (l
? ' ' + cp
+ 'Labeled' : '') +'" onmousedown="return false;" onclick="return false;" title="' + DOM
.encode(s
.title
) + '">';
5347 h
+= '<img class="mceIcon" src="' + s
.image
+ '" />' + l
+ '</a>';
5349 h
+= '<span class="mceIcon ' + s
['class'] + '"></span>' + (l
? '<span class="' + cp
+ 'Label">' + l
+ '</span>' : '') + '</a>';
5354 postRender : function() {
5355 var t
= this, s
= t
.settings
;
5357 tinymce
.dom
.Event
.add(t
.id
, 'click', function(e
) {
5358 if (!t
.isDisabled())
5359 return s
.onclick
.call(s
.scope
, e
);
5366 /* file:jscripts/tiny_mce/classes/ui/ListBox.js */
5369 var DOM
= tinymce
.DOM
, Event
= tinymce
.dom
.Event
, each
= tinymce
.each
, Dispatcher
= tinymce
.util
.Dispatcher
;
5371 tinymce
.create('tinymce.ui.ListBox:tinymce.ui.Control', {
5372 ListBox : function(id
, s
) {
5377 t
.onChange
= new Dispatcher(t
);
5378 t
.onPostRender
= new Dispatcher(t
);
5379 t
.onAdd
= new Dispatcher(t
);
5380 t
.onRenderMenu
= new tinymce
.util
.Dispatcher(this);
5381 t
.classPrefix
= 'mceListBox';
5384 select : function(v
) {
5385 var t
= this, e
, fv
;
5387 // Do we need to do something?
5388 if (v
!= t
.selectedValue
) {
5389 e
= DOM
.get(t
.id
+ '_text');
5390 t
.selectedValue
= v
;
5393 each(t
.items
, function(o
) {
5395 DOM
.setHTML(e
, DOM
.encode(o
.title
));
5401 // If no item was found then present title
5403 DOM
.setHTML(e
, DOM
.encode(t
.settings
.title
));
5404 DOM
.addClass(e
, 'mceTitle');
5408 DOM
.removeClass(e
, 'mceTitle');
5414 add : function(n
, v
, o
) {
5418 o
= tinymce
.extend(o
, {
5424 t
.onAdd
.dispatch(t
, o
);
5427 getLength : function() {
5428 return this.items
.length
;
5431 renderHTML : function() {
5432 var h
= '', t
= this, s
= t
.settings
, cp
= t
.classPrefix
;
5434 h
= '<table id="' + t
.id
+ '" cellpadding="0" cellspacing="0" class="' + cp
+ ' ' + cp
+ 'Enabled' + (s
['class'] ? (' ' + s
['class']) : '') + '"><tbody><tr>';
5435 h
+= '<td>' + DOM
.createHTML('a', {id
: t
.id
+ '_text', href
: 'javascript:;', 'class' : 'mceText', onclick
: "return false;", onmousedown
: 'return false;'}, DOM
.encode(t
.settings
.title
)) + '</td>';
5436 h
+= '<td>' + DOM
.createHTML('a', {id
: t
.id
+ '_open', tabindex
: -1, href
: 'javascript:;', 'class' : 'mceOpen', onclick
: "return false;", onmousedown
: 'return false;'}, '<span></span>') + '</td>';
5437 h
+= '</tr></tbody></table>';
5442 showMenu : function() {
5443 var t
= this, p1
, p2
, e
= DOM
.get(this.id
), m
;
5445 if (t
.isDisabled() || t
.items
.length
== 0)
5448 if (t
.menu
&& t
.menu
.isMenuVisible
)
5449 return t
.hideMenu();
5451 if (!t
.isMenuRendered
) {
5453 t
.isMenuRendered
= true;
5456 p1
= DOM
.getPos(this.settings
.menu_container
);
5460 m
.settings
.offset_x
= p2
.x
;
5461 m
.settings
.offset_y
= p2
.y
;
5462 m
.settings
.keyboard_focus
= !tinymce
.isOpera
; // Opera is buggy when it comes to auto focus
5466 m
.items
[t
.oldID
].setSelected(0);
5468 each(t
.items
, function(o
) {
5469 if (o
.value
=== t
.selectedValue
) {
5470 m
.items
[o
.id
].setSelected(1);
5475 m
.showMenu(0, e
.clientHeight
);
5477 Event
.add(DOM
.doc
, 'mousedown', t
.hideMenu
, t
);
5478 DOM
.addClass(t
.id
, t
.classPrefix
+ 'Selected');
5480 //DOM.get(t.id + '_text').focus();
5483 hideMenu : function(e
) {
5486 // Prevent double toogles by canceling the mouse click event to the button
5487 if (e
&& e
.type
== "mousedown" && (e
.target
.id
== t
.id
+ '_text' || e
.target
.id
== t
.id
+ '_open'))
5490 if (!e
|| !DOM
.getParent(e
.target
, function(n
) {return DOM
.hasClass(n
, 'mceMenu');})) {
5491 DOM
.removeClass(t
.id
, t
.classPrefix
+ 'Selected');
5492 Event
.remove(DOM
.doc
, 'mousedown', t
.hideMenu
, t
);
5499 renderMenu : function() {
5502 m
= t
.settings
.control_manager
.createDropMenu(t
.id
+ '_menu', {
5504 'class' : t
.classPrefix
+ 'Menu mceNoIcons',
5509 m
.onHideMenu
.add(t
.hideMenu
, t
);
5512 title
: t
.settings
.title
,
5513 'class' : 'mceMenuItemTitle',
5514 onclick : function() {
5515 if (t
.settings
.onselect('') !== false)
5516 t
.select(''); // Must be runned after
5520 each(t
.items
, function(o
) {
5521 o
.id
= DOM
.uniqueId();
5522 o
.onclick = function() {
5523 if (t
.settings
.onselect(o
.value
) !== false)
5524 t
.select(o
.value
); // Must be runned after
5530 t
.onRenderMenu
.dispatch(t
, m
);
5534 postRender : function() {
5535 var t
= this, cp
= t
.classPrefix
;
5537 Event
.add(t
.id
, 'click', t
.showMenu
, t
);
5538 Event
.add(t
.id
+ '_text', 'focus', function(e
) {
5540 t
.keyDownHandler
= Event
.add(t
.id
+ '_text', 'keydown', function(e
) {
5541 var idx
= -1, v
, kc
= e
.keyCode
;
5543 // Find current index
5544 each(t
.items
, function(v
, i
) {
5545 if (t
.selectedValue
== v
.value
)
5551 v
= t
.items
[idx
- 1];
5553 v
= t
.items
[idx
+ 1];
5554 else if (kc
== 13) {
5555 // Fake select on enter
5556 v
= t
.selectedValue
;
5557 t
.selectedValue
= null; // Needs to be null to fake change
5558 t
.settings
.onselect(v
);
5559 return Event
.cancel(e
);
5571 Event
.add(t
.id
+ '_text', 'blur', function() {Event
.remove(t
.id
+ '_text', 'keydown', t
.keyDownHandler
); t
._focused
= 0;});
5573 // Old IE doesn't have hover on all elements
5574 if (tinymce
.isIE6
|| !DOM
.boxModel
) {
5575 Event
.add(t
.id
, 'mouseover', function() {
5576 if (!DOM
.hasClass(t
.id
, cp
+ 'Disabled'))
5577 DOM
.addClass(t
.id
, cp
+ 'Hover');
5580 Event
.add(t
.id
, 'mouseout', function() {
5581 if (!DOM
.hasClass(t
.id
, cp
+ 'Disabled'))
5582 DOM
.removeClass(t
.id
, cp
+ 'Hover');
5586 t
.onPostRender
.dispatch(t
, DOM
.get(t
.id
));
5589 destroy : function() {
5592 Event
.clear(this.id
+ '_text');
5597 /* file:jscripts/tiny_mce/classes/ui/NativeListBox.js */
5600 var DOM
= tinymce
.DOM
, Event
= tinymce
.dom
.Event
, each
= tinymce
.each
, Dispatcher
= tinymce
.util
.Dispatcher
;
5602 tinymce
.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {
5603 NativeListBox : function(id
, s
) {
5605 this.classPrefix
= 'mceNativeListBox';
5608 setDisabled : function(s
) {
5609 DOM
.get(this.id
).disabled
= s
;
5612 isDisabled : function() {
5613 return DOM
.get(this.id
).disabled
;
5616 select : function(v
) {
5617 var e
= DOM
.get(this.id
), ol
= e
.options
;
5621 e
.selectedIndex
= 0;
5622 each(ol
, function(o
, i
) {
5624 e
.selectedIndex
= i
;
5630 add : function(n
, v
, a
) {
5637 DOM
.add(DOM
.get(this.id
), 'option', a
, n
);
5646 t
.onAdd
.dispatch(t
, o
);
5649 getLength : function() {
5650 return DOM
.get(this.id
).options
.length
- 1;
5653 renderHTML : function() {
5656 h
= DOM
.createHTML('option', {value
: ''}, '-- ' + t
.settings
.title
+ ' --');
5658 each(t
.items
, function(it
) {
5659 h
+= DOM
.createHTML('option', {value
: it
.value
}, it
.title
);
5662 h
= DOM
.createHTML('select', {id
: t
.id
, 'class' : 'mceNativeListBox'}, h
);
5667 postRender : function() {
5672 function onChange(e
) {
5673 var v
= e
.target
.options
[e
.target
.selectedIndex
].value
;
5675 t
.onChange
.dispatch(t
, v
);
5677 if (t
.settings
.onselect
)
5678 t
.settings
.onselect(v
);
5681 Event
.add(t
.id
, 'change', onChange
);
5683 // Accessibility keyhandler
5684 Event
.add(t
.id
, 'keydown', function(e
) {
5687 Event
.remove(t
.id
, 'change', ch
);
5689 bf
= Event
.add(t
.id
, 'blur', function() {
5690 Event
.add(t
.id
, 'change', onChange
);
5691 Event
.remove(t
.id
, 'blur', bf
);
5694 if (e
.keyCode
== 13 || e
.keyCode
== 32) {
5696 return Event
.cancel(e
);
5700 t
.onPostRender
.dispatch(t
, DOM
.get(t
.id
));
5705 /* file:jscripts/tiny_mce/classes/ui/MenuButton.js */
5708 var DOM
= tinymce
.DOM
, Event
= tinymce
.dom
.Event
, each
= tinymce
.each
;
5710 tinymce
.create('tinymce.ui.MenuButton:tinymce.ui.Button', {
5711 MenuButton : function(id
, s
) {
5713 this.onRenderMenu
= new tinymce
.util
.Dispatcher(this);
5714 s
.menu_container
= s
.menu_container
|| DOM
.doc
.body
;
5717 showMenu : function() {
5718 var t
= this, p1
, p2
, e
= DOM
.get(t
.id
), m
;
5723 if (!t
.isMenuRendered
) {
5725 t
.isMenuRendered
= true;
5728 if (t
.isMenuVisible
)
5729 return t
.hideMenu();
5731 p1
= DOM
.getPos(t
.settings
.menu_container
);
5735 m
.settings
.offset_x
= p2
.x
;
5736 m
.settings
.offset_y
= p2
.y
;
5737 m
.settings
.vp_offset_x
= p2
.x
;
5738 m
.settings
.vp_offset_y
= p2
.y
;
5739 m
.settings
.keyboard_focus
= t
._focused
;
5740 m
.showMenu(0, e
.clientHeight
);
5742 Event
.add(DOM
.doc
, 'mousedown', t
.hideMenu
, t
);
5743 t
.setState('Selected', 1);
5745 t
.isMenuVisible
= 1;
5748 renderMenu : function() {
5751 m
= t
.settings
.control_manager
.createDropMenu(t
.id
+ '_menu', {
5753 'class' : this.classPrefix
+ 'Menu',
5754 icons
: t
.settings
.icons
5757 m
.onHideMenu
.add(t
.hideMenu
, t
);
5759 t
.onRenderMenu
.dispatch(t
, m
);
5763 hideMenu : function(e
) {
5766 // Prevent double toogles by canceling the mouse click event to the button
5767 if (e
&& e
.type
== "mousedown" && DOM
.getParent(e
.target
, function(e
) {return e
.id
=== t
.id
|| e
.id
=== t
.id
+ '_open';}))
5770 if (!e
|| !DOM
.getParent(e
.target
, function(n
) {return DOM
.hasClass(n
, 'mceMenu');})) {
5771 t
.setState('Selected', 0);
5772 Event
.remove(DOM
.doc
, 'mousedown', t
.hideMenu
, t
);
5777 t
.isMenuVisible
= 0;
5780 postRender : function() {
5781 var t
= this, s
= t
.settings
;
5783 Event
.add(t
.id
, 'click', function() {
5784 if (!t
.isDisabled()) {
5796 /* file:jscripts/tiny_mce/classes/ui/SplitButton.js */
5799 var DOM
= tinymce
.DOM
, Event
= tinymce
.dom
.Event
, each
= tinymce
.each
;
5801 tinymce
.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', {
5802 SplitButton : function(id
, s
) {
5804 this.classPrefix
= 'mceSplitButton';
5807 renderHTML : function() {
5808 var h
, t
= this, s
= t
.settings
, h1
;
5813 h1
= DOM
.createHTML('img ', {src
: s
.image
, 'class' : 'mceAction ' + s
['class']});
5815 h1
= DOM
.createHTML('span', {'class' : 'mceAction ' + s
['class']}, '');
5817 h
+= '<td>' + DOM
.createHTML('a', {id
: t
.id
+ '_action', href
: 'javascript:;', 'class' : 'mceAction ' + s
['class'], onclick
: "return false;", onmousedown
: 'return false;', title
: s
.title
}, h1
) + '</td>';
5819 h1
= DOM
.createHTML('span', {'class' : 'mceOpen ' + s
['class']});
5820 h
+= '<td>' + DOM
.createHTML('a', {id
: t
.id
+ '_open', href
: 'javascript:;', 'class' : 'mceOpen ' + s
['class'], onclick
: "return false;", onmousedown
: 'return false;', title
: s
.title
}, h1
) + '</td>';
5822 h
+= '</tr></tbody>';
5824 return DOM
.createHTML('table', {id
: t
.id
, 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s
['class'], cellpadding
: '0', cellspacing
: '0', onmousedown
: 'return false;', title
: s
.title
}, h
);
5827 postRender : function() {
5828 var t
= this, s
= t
.settings
;
5831 Event
.add(t
.id
+ '_action', 'click', function() {
5832 if (!t
.isDisabled())
5837 Event
.add(t
.id
+ '_open', 'click', t
.showMenu
, t
);
5838 Event
.add(t
.id
+ '_open', 'focus', function() {t
._focused
= 1;});
5839 Event
.add(t
.id
+ '_open', 'blur', function() {t
._focused
= 0;});
5841 // Old IE doesn't have hover on all elements
5842 if (tinymce
.isIE6
|| !DOM
.boxModel
) {
5843 Event
.add(t
.id
, 'mouseover', function() {
5844 if (!DOM
.hasClass(t
.id
, 'mceSplitButtonDisabled'))
5845 DOM
.addClass(t
.id
, 'mceSplitButtonHover');
5848 Event
.add(t
.id
, 'mouseout', function() {
5849 if (!DOM
.hasClass(t
.id
, 'mceSplitButtonDisabled'))
5850 DOM
.removeClass(t
.id
, 'mceSplitButtonHover');
5855 destroy : function() {
5858 Event
.clear(this.id
+ '_action');
5859 Event
.clear(this.id
+ '_open');
5865 /* file:jscripts/tiny_mce/classes/ui/ColorSplitButton.js */
5868 var DOM
= tinymce
.DOM
, Event
= tinymce
.dom
.Event
, is
= tinymce
.is
, each
= tinymce
.each
;
5870 tinymce
.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', {
5871 ColorSplitButton : function(id
, s
) {
5876 t
.settings
= s
= tinymce
.extend({
5877 colors
: '000000,993300,333300,003300,003366,000080,333399,333333,800000,FF6600,808000,008000,008080,0000FF,666699,808080,FF0000,FF9900,99CC00,339966,33CCCC,3366FF,800080,999999,FF00FF,FFCC00,FFFF00,00FF00,00FFFF,00CCFF,993366,C0C0C0,FF99CC,FFCC99,FFFF99,CCFFCC,CCFFFF,99CCFF,CC99FF,FFFFFF',
5879 default_color
: '#888888'
5882 t
.onShowMenu
= new tinymce
.util
.Dispatcher(t
);
5883 t
.onHideMenu
= new tinymce
.util
.Dispatcher(t
);
5885 t
.value
= s
.default_color
;
5888 showMenu : function() {
5889 var t
= this, r
, p
, e
, p2
;
5894 if (!t
.isMenuRendered
) {
5896 t
.isMenuRendered
= true;
5899 if (t
.isMenuVisible
)
5900 return t
.hideMenu();
5903 DOM
.show(t
.id
+ '_menu');
5904 DOM
.addClass(e
, 'mceSplitButtonSelected');
5906 DOM
.setStyles(t
.id
+ '_menu', {
5908 top
: p2
.y
+ e
.clientHeight
,
5913 Event
.add(DOM
.doc
, 'mousedown', t
.hideMenu
, t
);
5916 t
._keyHandler
= Event
.add(t
.id
+ '_menu', 'keydown', function(e
) {
5917 if (e
.keyCode
== 27)
5921 DOM
.select('a', t
.id
+ '_menu')[0].focus(); // Select first link
5924 t
.onShowMenu
.dispatch(t
);
5926 t
.isMenuVisible
= 1;
5929 hideMenu : function(e
) {
5932 // Prevent double toogles by canceling the mouse click event to the button
5933 if (e
&& e
.type
== "mousedown" && DOM
.getParent(e
.target
, function(e
) {return e
.id
=== t
.id
+ '_open';}))
5936 if (!e
|| !DOM
.getParent(e
.target
, function(n
) {return DOM
.hasClass(n
, 'mceSplitButtonMenu');})) {
5937 DOM
.removeClass(t
.id
, 'mceSplitButtonSelected');
5938 Event
.remove(DOM
.doc
, 'mousedown', t
.hideMenu
, t
);
5939 Event
.remove(t
.id
+ '_menu', 'keydown', t
._keyHandler
);
5940 DOM
.hide(t
.id
+ '_menu');
5943 t
.onHideMenu
.dispatch(t
);
5945 t
.isMenuVisible
= 0;
5948 renderMenu : function() {
5949 var t
= this, m
, i
= 0, s
= t
.settings
, n
, tb
, tr
, w
;
5951 w
= DOM
.add(s
.menu_container
, 'div', {id
: t
.id
+ '_menu', 'class' : s
['menu_class'] + ' ' + s
['class'], style
: 'position:absolute;left:0;top:-1000px;'});
5952 m
= DOM
.add(w
, 'div', {'class' : s
['class'] + ' mceSplitButtonMenu'});
5953 DOM
.add(m
, 'span', {'class' : 'mceMenuLine'});
5955 n
= DOM
.add(m
, 'table', {'class' : 'mceColorSplitMenu'});
5956 tb
= DOM
.add(n
, 'tbody');
5958 // Generate color grid
5960 each(is(s
.colors
, 'array') ? s
.colors
: s
.colors
.split(','), function(c
) {
5961 c
= c
.replace(/^#/, '');
5964 tr
= DOM
.add(tb
, 'tr');
5965 i
= s
.grid_width
- 1;
5968 n
= DOM
.add(tr
, 'td');
5970 n
= DOM
.add(n
, 'a', {
5971 href
: 'javascript:;',
5973 backgroundColor
: '#' + c
5979 if (s
.more_colors_func
) {
5980 n
= DOM
.add(tb
, 'tr');
5981 n
= DOM
.add(n
, 'td', {colspan
: s
.grid_width
, 'class' : 'mceMoreColors'});
5982 n
= DOM
.add(n
, 'a', {id
: t
.id
+ '_more', href
: 'javascript:;', onclick
: 'return false;', 'class' : 'mceMoreColors'}, s
.more_colors_title
);
5984 Event
.add(n
, 'click', function(e
) {
5985 s
.more_colors_func
.call(s
.more_colors_scope
|| this);
5986 return Event
.cancel(e
); // Cancel to fix onbeforeunload problem
5990 DOM
.addClass(m
, 'mceColorSplitMenu');
5992 Event
.add(t
.id
+ '_menu', 'click', function(e
) {
5997 if (e
.nodeName
== 'A' && (c
= e
.getAttribute('mce_color')))
6000 return Event
.cancel(e
); // Prevent IE auto save warning
6006 setColor : function(c
) {
6009 DOM
.setStyle(t
.id
+ '_preview', 'backgroundColor', c
);
6013 t
.settings
.onselect(c
);
6016 postRender : function() {
6017 var t
= this, id
= t
.id
;
6020 DOM
.add(id
+ '_action', 'div', {id
: id
+ '_preview', 'class' : 'mceColorPreview'});
6023 destroy : function() {
6026 Event
.clear(this.id
+ '_menu');
6027 Event
.clear(this.id
+ '_more');
6028 DOM
.remove(this.id
+ '_menu');
6034 /* file:jscripts/tiny_mce/classes/ui/Toolbar.js */
6036 tinymce
.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
6037 renderHTML : function() {
6038 var t
= this, h
= '', c
, co
, dom
= tinymce
.DOM
, s
= t
.settings
, i
, pr
, nx
, cl
;
6041 for (i
=0; i
<cl
.length
; i
++) {
6042 // Get current control, prev control, next control and if the control is a list box or not
6047 // Add toolbar start
6049 c
= 'mceToolbarStart';
6052 c
+= ' mceToolbarStartButton';
6053 else if (co
.SplitButton
)
6054 c
+= ' mceToolbarStartSplitButton';
6055 else if (co
.ListBox
)
6056 c
+= ' mceToolbarStartListBox';
6058 h
+= dom
.createHTML('td', {'class' : c
}, dom
.createHTML('span', null, '<!-- IE -->'));
6061 // Add toolbar end before list box and after the previous button
6062 // This is to fix the o2k7 editor skins
6063 if (pr
&& co
.ListBox
) {
6064 if (pr
.Button
|| pr
.SplitButton
)
6065 h
+= dom
.createHTML('td', {'class' : 'mceToolbarEnd'}, dom
.createHTML('span', null, '<!-- IE -->'));
6068 // Render control HTML
6070 // IE 8 quick fix, needed to propertly generate a hit area for anchors
6072 h
+= '<td style="position: relative">' + co
.renderHTML() + '</td>';
6074 h
+= '<td>' + co
.renderHTML() + '</td>';
6076 // Add toolbar start after list box and before the next button
6077 // This is to fix the o2k7 editor skins
6078 if (nx
&& co
.ListBox
) {
6079 if (nx
.Button
|| nx
.SplitButton
)
6080 h
+= dom
.createHTML('td', {'class' : 'mceToolbarStart'}, dom
.createHTML('span', null, '<!-- IE -->'));
6084 c
= 'mceToolbarEnd';
6087 c
+= ' mceToolbarEndButton';
6088 else if (co
.SplitButton
)
6089 c
+= ' mceToolbarEndSplitButton';
6090 else if (co
.ListBox
)
6091 c
+= ' mceToolbarEndListBox';
6093 h
+= dom
.createHTML('td', {'class' : c
}, dom
.createHTML('span', null, '<!-- IE -->'));
6095 return dom
.createHTML('table', {id
: t
.id
, 'class' : 'mceToolbar' + (s
['class'] ? ' ' + s
['class'] : ''), cellpadding
: '0', cellspacing
: '0', align
: t
.settings
.align
|| ''}, '<tbody><tr>' + h
+ '</tr></tbody>');
6100 /* file:jscripts/tiny_mce/classes/AddOnManager.js */
6103 var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;
6105 tinymce.create('tinymce.AddOnManager', {
6109 onAdd : new Dispatcher(this),
6112 return this.lookup[n];
6115 requireLangPack : function(n) {
6116 var u, s = tinymce.EditorManager.settings;
6118 if (s && s.language) {
6119 u = this.urls[n] + '/langs/' + s.language + '.js';
6121 if (!tinymce.dom.Event.domLoaded && !s.strict_mode)
6122 tinymce.ScriptLoader.load(u);
6124 tinymce.ScriptLoader.add(u);
6128 add : function(id, o) {
6130 this.lookup[id] = o;
6131 this.onAdd.dispatch(this, id, o);
6136 load : function(n, u, cb, s) {
6142 if (u.indexOf('/') != 0 && u.indexOf('://') == -1)
6143 u = tinymce.baseURL + '/' + u;
6145 t.urls[n] = u.substring(0, u.lastIndexOf('/'));
6146 tinymce.ScriptLoader.add(u, cb, s);
6151 // Create plugin and theme managers
6152 tinymce.PluginManager = new tinymce.AddOnManager();
6153 tinymce.ThemeManager = new tinymce.AddOnManager();
6155 /* file:jscripts/tiny_mce/classes/EditorManager.js */
6159 var each = tinymce.each, extend = tinymce.extend, DOM = tinymce.DOM, Event = tinymce.dom.Event, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, explode = tinymce.explode;
6161 tinymce.create('static tinymce.EditorManager', {
6164 activeEditor : null,
6166 preInit : function() {
6167 var t = this, lo = window.location;
6169 // Setup some URLs where the editor API is located and where the document is
6170 tinymce.documentBaseURL = lo.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
6171 if (!/[\/\\]$/.test(tinymce.documentBaseURL))
6172 tinymce.documentBaseURL += '/';
6174 tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL);
6175 tinymce.EditorManager.baseURI = new tinymce.util.URI(tinymce.baseURL);
6177 // User already specified a document.domain value
6178 if (document.domain && lo.hostname != document.domain)
6179 tinymce.relaxedDomain = document.domain;
6181 // Setup document domain if tinymce is loaded from other domain
6182 if (!tinymce.relaxedDomain && tinymce.EditorManager.baseURI.host != lo.hostname && lo.hostname)
6183 document.domain = tinymce.relaxedDomain = lo.hostname.replace(/.*\.(.+\..+)$/, '$1');
6185 // Add before unload listener
6186 // This was required since IE was leaking memory if you added and removed beforeunload listeners
6187 // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event
6188 t.onBeforeUnload = new tinymce.util.Dispatcher(t);
6190 // Must be on window or IE will leak if the editor is placed in frame or iframe
6191 Event.add(window, 'beforeunload', function(e) {
6192 t.onBeforeUnload.dispatch(t, e);
6196 init : function(s) {
6197 var t = this, pl, sl = tinymce.ScriptLoader, c, e, el = [], ed;
6199 function execCallback(se, n, s) {
6205 if (tinymce.is(f, 'string')) {
6206 s = f.replace(/\.\w+$/, '');
6207 s = s ? tinymce.resolve(s) : 0;
6208 f = tinymce.resolve(f);
6211 return f.apply(s || this, Array.prototype.slice.call(arguments, 2));
6217 strict_loading_mode : document.contentType == 'application/xhtml+xml'
6222 // If page not loaded and strict mode isn't enabled then load them
6223 if (!Event.domLoaded && !s.strict_loading_mode) {
6226 sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');
6229 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
6230 ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');
6234 pl = explode(s.plugins);
6236 // Load compat2x first
6237 if (tinymce.inArray(pl, 'compat2x') != -1)
6238 PluginManager.load('compat2x', 'plugins/compat2x/editor_plugin' + tinymce.suffix + '.js');
6240 // Load rest if plugins
6241 each(pl, function(v) {
6242 if (v && v.charAt(0) != '-' && !PluginManager.urls[v]) {
6243 // Skip safari plugin for other browsers
6244 if (!tinymce.isWebKit && v == 'safari')
6247 PluginManager.load(v, 'plugins/' + v + '/editor_plugin' + tinymce.suffix + '.js');
6256 Event.add(document, 'init', function() {
6259 execCallback(s, 'onpageload');
6261 // Verify that it's a valid browser
6265 each(explode(s.browsers), function(v) {
6274 if (tinymce.isGecko)
6280 if (tinymce.isWebKit)
6285 if (tinymce.isOpera)
6299 l = s.elements || '';
6302 each(explode(l), function(v) {
6304 ed = new tinymce.Editor(v, s);
6310 each(document.forms, function(f) {
6311 each(f.elements, function(e) {
6313 v = 'mce_editor_' + c;
6314 DOM.setAttrib(e, 'id', v);
6316 ed = new tinymce.Editor(v, s);
6328 case "specific_textareas":
6329 function hasClass(n, c) {
6330 return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);
6333 each(DOM.select('textarea'), function(v) {
6334 if (s.editor_deselector && hasClass(v, s.editor_deselector))
6337 if (!s.editor_selector || hasClass(v, s.editor_selector)) {
6338 // Can we use the name
6339 e = DOM.get(v.name);
6343 // Generate unique name if missing or already exists
6344 if (!v.id || t.get(v.id))
6345 v.id = DOM.uniqueId();
6347 ed = new tinymce.Editor(v.id, s);
6355 // Call onInit when all editors are initialized
6359 each (el, function(ed) {
6362 if (!ed.initialized) {
6364 ed.onInit.add(function() {
6369 execCallback(s, 'oninit');
6376 execCallback(s, 'oninit');
6382 get : function(id) {
6383 return this.editors[id];
6386 getInstanceById : function(id) {
6387 return this.get(id);
6391 this.editors[e.id] = e;
6397 remove : function(e) {
6400 // Not in the collection
6401 if (!t.editors[e.id])
6404 delete t.editors[e.id];
6406 // Select another editor since the active one was removed
6407 if (t.activeEditor == e) {
6408 each(t.editors, function(e) {
6410 return false; // Break
6419 execCommand : function(c, u, v) {
6420 var t = this, ed = t.get(v), w;
6428 case "mceAddEditor":
6429 case "mceAddControl":
6431 new tinymce.Editor(v, t.settings).render();
6435 case "mceAddFrameControl":
6438 // Add tinyMCE global instance and tinymce namespace to specified window
6439 w.tinyMCE = tinyMCE;
6440 w.tinymce = tinymce;
6442 tinymce.DOM.doc = w.document;
6443 tinymce.DOM.win = w;
6445 ed = new tinymce.Editor(v.element_id, v);
6448 // Fix IE memory leaks
6452 w.detachEvent('onunload', clr);
6453 w = w.tinyMCE = w.tinymce = null; // IE leak
6456 w.attachEvent('onunload', clr);
6459 v.page_window = null;
6463 case "mceRemoveEditor":
6464 case "mceRemoveControl":
6468 case 'mceToggleEditor':
6470 t.execCommand('mceAddControl', 0, v);
6482 // Run command on active editor
6484 return t.activeEditor.execCommand(c, u, v);
6489 execInstanceCommand : function(id, c, u, v) {
6490 var ed = this.get(id);
6493 return ed.execCommand(c, u, v);
6498 triggerSave : function() {
6499 each(this.editors, function(e) {
6504 addI18n : function(p, o) {
6505 var lo, i18n = this.i18n;
6507 if (!tinymce.is(p, 'string')) {
6508 each(p, function(o, lc) {
6509 each(o, function(o, g) {
6510 each(o, function(o, k) {
6512 i18n[lc + '.' + k] = o;
6514 i18n[lc + '.' + g + '.' + k] = o;
6519 each(o, function(o, k) {
6520 i18n[p + '.' + k] = o;
6527 _setActive : function(e) {
6528 this.selectedInstance = this.activeEditor = e;
6533 tinymce.EditorManager.preInit();
6536 // Short for editor manager window.tinyMCE is needed when TinyMCE gets loaded though a XHR call
6537 var tinyMCE = window.tinyMCE = tinymce.EditorManager;
6539 /* file:jscripts/tiny_mce/classes/Editor.js */
6542 var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend, Dispatcher = tinymce.util.Dispatcher;
6543 var each = tinymce.each, isGecko = tinymce.isGecko, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit;
6544 var is = tinymce.is, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, EditorManager = tinymce.EditorManager;
6545 var inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;
6547 tinymce.create('tinymce.Editor', {
6548 Editor : function(id, s) {
6551 t.id = t.editorId = id;
6552 t.execCommands = {};
6553 t.queryStateCommands = {};
6554 t.queryValueCommands = {};
6557 // Add events to the editor
6580 'onBeforeSetContent',
6581 'onBeforeGetContent',
6588 'onBeforeExecCommand',
6593 'onSetProgressState'
6595 t[e] = new Dispatcher(t);
6598 // Default editor config
6599 t.settings = s = extend({
6602 docs_language : 'en',
6609 document_base_url : tinymce.documentBaseURL,
6610 add_form_submit_trigger : 1,
6612 add_unload_trigger : 1,
6615 remove_script_host : 1,
6616 table_inline_editing : 0,
6617 object_resizing : 1,
6619 accessibility_focus : 1,
6620 custom_shortcuts : 1,
6621 custom_undo_redo_keyboard_shortcuts : 1,
6622 custom_undo_redo_restore_selection : 1,
6623 custom_undo_redo : 1,
6624 doctype : '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">',
6625 visual_table_class : 'mceItemTable',
6627 inline_styles : true,
6628 convert_fonts_to_spans : true,
6629 font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',
6630 apply_source_formatting : 1,
6631 directionality : 'ltr',
6632 forced_root_block : 'p',
6633 valid_elements : '@[id|class|style|title|dir<ltr?rtl|lang|xml::lang|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],a[rel|rev|charset|hreflang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur],strong/b,em/i,strike,u,#p[align],-ol[type|compact],-ul[type|compact],-li,br,img[longdesc|usemap|src|border|alt=|title|hspace|vspace|width|height|align],-sub,-sup,-blockquote[cite],-table[border=0|cellspacing|cellpadding|width|frame|rules|height|align|summary|bgcolor|background|bordercolor],-tr[rowspan|width|height|align|valign|bgcolor|background|bordercolor],tbody,thead,tfoot,#td[colspan|rowspan|width|height|align|valign|bgcolor|background|bordercolor|scope],#th[colspan|rowspan|width|height|align|valign|scope],caption,-div,-span,-code,-pre,address,-h1,-h2,-h3,-h4,-h5,-h6,hr[size|noshade],-font[face|size|color],dd,dl,dt,cite,abbr,acronym,del[datetime|cite],ins[datetime|cite],object[classid|width|height|codebase|*],param[name|value],embed[type|width|height|src|*],script[src|type],map[name],area[shape|coords|href|alt|target],bdo,button,col[align|char|charoff|span|valign|width],colgroup[align|char|charoff|span|valign|width],dfn,fieldset,form[action|accept|accept-charset|enctype|method],input[accept|alt|checked|disabled|maxlength|name|readonly|size|src|type|value|tabindex|accesskey],kbd,label[for],legend,noscript,optgroup[label|disabled],option[disabled|label|selected|value],q[cite],samp,select[disabled|multiple|name|size],small,textarea[cols|rows|disabled|name|readonly],tt,var,big',
6635 padd_empty_editor : 1,
6638 force_p_newlines : 1,
6639 indentation : '30px'
6643 t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {
6644 base_uri : tinyMCE.baseURI
6646 t.baseURI = EditorManager.baseURI;
6649 t.execCallback('setup', t);
6652 render : function(nst) {
6653 var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader;
6655 // Page is not loaded yet, wait for it
6656 if (!Event.domLoaded) {
6657 Event.add(document, 'init', function() {
6663 // Force strict loading mode if render us called by user and not internally
6665 s.strict_loading_mode = 1;
6666 tinyMCE.settings = s;
6669 // Element not found, then skip initialization
6670 if (!t.getElement())
6673 if (s.strict_loading_mode) {
6674 sl.settings.strict_mode = s.strict_loading_mode;
6675 tinymce.DOM.settings.strict = 1;
6678 // Add hidden input for non input elements inside form elements
6679 if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))
6680 DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);
6682 t.windowManager = new tinymce.WindowManager(t);
6684 if (s.encoding == 'xml') {
6685 t.onGetContent.add(function(ed, o) {
6687 o.content = DOM.encode(o.content);
6691 if (s.add_form_submit_trigger) {
6692 t.onSubmit.addToTop(function() {
6693 if (t.initialized) {
6700 if (s.add_unload_trigger && !s.ask) {
6701 t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {
6702 if (t.initialized && !t.destroyed && !t.isHidden())
6703 t.save({format : 'raw', no_events : true});
6707 tinymce.addUnload(t.destroy, t);
6709 if (s.submit_patch) {
6710 t.onBeforeRenderUI.add(function() {
6711 var n = t.getElement().form;
6717 if (n._mceOldSubmit)
6720 // Check page uses id="submit" or name="submit" for it's submit button
6721 if (!n.submit.nodeType && !n.submit.length) {
6723 n._mceOldSubmit = n.submit;
6724 n.submit = function() {
6725 // Save all instances
6726 EditorManager.triggerSave();
6729 return this._mceOldSubmit(this);
6738 function loadScripts() {
6740 sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');
6742 if (s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
6743 ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');
6745 each(explode(s.plugins), function(p) {
6746 if (p && p.charAt(0) != '-' && !PluginManager.urls[p]) {
6747 // Skip safari plugin for other browsers
6748 if (!isWebKit && p == 'safari')
6751 PluginManager.load(p, 'plugins/' + p + '/editor_plugin' + tinymce.suffix + '.js');
6755 // Init when que is loaded
6756 sl.loadQueue(function() {
6759 // Yield for awhile to avoid focus bug on FF 3 when cancel is pressed
6760 window.setTimeout(function() {
6761 Event.remove(t.id, 'focus', ask);
6763 t.windowManager.confirm(t.getLang('edit_confirm'), function(s) {
6770 Event.add(t.id, 'focus', ask);
6779 // Load compat2x first
6780 if (s.plugins.indexOf('compat2x') != -1) {
6781 PluginManager.load('compat2x', 'plugins/compat2x/editor_plugin' + tinymce.suffix + '.js');
6782 sl.loadQueue(loadScripts);
6788 var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re;
6790 EditorManager.add(t);
6793 s.theme = s.theme.replace(/-/, '');
6794 o = ThemeManager.get(s.theme);
6797 if (t.theme.init && s.init_theme)
6798 t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));
6800 // Create all plugins
6801 each(explode(s.plugins.replace(/\-/g, '')), function(p) {
6802 var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;
6814 // Setup popup CSS path(s)
6816 s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);
6818 s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");
6820 if (s.popup_css_add)
6821 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);
6823 // Setup control factory
6824 t.controlManager = new tinymce.ControlManager(t);
6825 t.undoManager = new tinymce.UndoManager(t);
6828 t.undoManager.onAdd.add(function(um, l) {
6830 return t.onChange.dispatch(t, l, um);
6833 t.undoManager.onUndo.add(function(um, l) {
6834 return t.onUndo.dispatch(t, l, um);
6837 t.undoManager.onRedo.add(function(um, l) {
6838 return t.onRedo.dispatch(t, l, um);
6841 if (s.custom_undo_redo) {
6842 t.onExecCommand.add(function(ed, cmd, ui, val, a) {
6843 if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))
6844 t.undoManager.add();
6848 t.onExecCommand.add(function(ed, c) {
6849 // Don't refresh the select lists until caret move
6850 if (!/^(FontName|FontSize)$/.test(c))
6854 // Remove ghost selections on images and tables in Gecko
6856 function repaint(a, o) {
6857 if (!o || !o.initial)
6858 t.execCommand('mceRepaint');
6861 t.onUndo.add(repaint);
6862 t.onRedo.add(repaint);
6863 t.onSetContent.add(repaint);
6866 // Enables users to override the control factory
6867 t.onBeforeRenderUI.dispatch(t, t.controlManager);
6871 w = s.width || e.style.width || e.offsetWidth;
6872 h = s.height || e.style.height || e.offsetHeight;
6873 t.orgDisplay = e.style.display;
6874 re = /^[0-9\.]+(|px)$/i;
6876 if (re.test('' + w))
6877 w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);
6879 if (re.test('' + h))
6880 h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);
6883 o = t.theme.renderUI({
6887 deltaWidth : s.delta_width,
6888 deltaHeight : s.delta_height
6891 t.editorContainer = o.editorContainer;
6896 DOM.setStyles(o.sizeContainer || o.editorContainer, {
6901 h = (o.iframeHeight || h) + ((h + '').indexOf('%') == -1 ? (o.deltaHeight || 0) : '');
6905 t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml"><base href="' + t.documentBaseURI.getURI() + '" />';
6906 t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
6908 if (tinymce.relaxedDomain)
6909 t.iframeHTML += '<script type="text/javascript">document.domain = "' + tinymce.relaxedDomain + '";</script>';
6911 bi = s.body_id || 'tinymce';
6912 if (bi.indexOf('=') != -1) {
6913 bi = t.getParam('body_id', '', 'hash');
6914 bi = bi[t.id] || bi;
6917 bc = s.body_class || '';
6918 if (bc.indexOf('=') != -1) {
6919 bc = t.getParam('body_class', '', 'hash');
6920 bc = bc[t.id] || '';
6923 t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"></body></html>';
6925 // Domain relaxing enabled, then set document domain
6926 if (tinymce.relaxedDomain) {
6927 // We need to write the contents here in IE since multiple writes messes up refresh button and back button
6928 if (isIE || (tinymce.isOpera && parseFloat(opera.version()) >= 9.5))
6929 u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()';
6930 else if (tinymce.isOpera)
6931 u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";document.close();ed.setupIframe();})()';
6935 n = DOM.add(o.iframeContainer, 'iframe', {
6937 src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7
6945 t.contentAreaContainer = o.iframeContainer;
6946 DOM.get(o.editorContainer).style.display = t.orgDisplay;
6947 DOM.get(t.id).style.display = 'none';
6949 // Safari 2.x requires us to wait for the load event and load a real HTML doc
6950 if (tinymce.isOldWebKit) {
6951 Event.add(n, 'load', t.setupIframe, t);
6952 n.src = tinymce.baseURL + '/plugins/safari/blank.htm';
6954 if (!isIE || !tinymce.relaxedDomain)
6957 e = n = o = null; // Cleanup
6961 setupIframe : function() {
6962 var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;
6964 // Setup iframe body
6965 if (!isIE || !tinymce.relaxedDomain) {
6967 d.write(t.iframeHTML);
6971 // Design mode needs to be added here Ctrl+A will fail otherwise
6975 d.designMode = 'On';
6977 // Will fail on Gecko if the editor is placed in an hidden container element
6978 // The design mode will be set ones the editor is focused
6982 // IE needs to use contentEditable or it will display non secure items for HTTPS
6984 // It will not steal focus if we hide it while setting contentEditable
6989 b.contentEditable = true;
6995 t.dom = new tinymce.DOM.DOMUtils(t.getDoc(), {
6997 url_converter : t.convertURL,
6998 url_converter_scope : t,
6999 hex_colors : s.force_hex_style_colors,
7000 class_filter : s.class_filter,
7002 fix_ie_paragraphs : 1
7005 t.serializer = new tinymce.dom.Serializer({
7006 entity_encoding : s.entity_encoding,
7007 entities : s.entities,
7008 valid_elements : s.verify_html === false ? '*[*]' : s.valid_elements,
7009 extended_valid_elements : s.extended_valid_elements,
7010 valid_child_elements : s.valid_child_elements,
7011 invalid_elements : s.invalid_elements,
7012 fix_table_elements : s.fix_table_elements,
7013 fix_list_elements : s.fix_list_elements,
7014 fix_content_duplication : s.fix_content_duplication,
7015 convert_fonts_to_spans : s.convert_fonts_to_spans,
7016 font_size_classes : s.font_size_classes,
7017 font_size_style_values : s.font_size_style_values,
7018 apply_source_formatting : s.apply_source_formatting,
7019 remove_linebreaks : s.remove_linebreaks,
7023 t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);
7024 t.forceBlocks = new tinymce.ForceBlocks(t, {
7025 forced_root_block : s.forced_root_block
7027 t.editorCommands = new tinymce.EditorCommands(t);
7030 t.serializer.onPreProcess.add(function(se, o) {
7031 return t.onPreProcess.dispatch(t, o, se);
7034 t.serializer.onPostProcess.add(function(se, o) {
7035 return t.onPostProcess.dispatch(t, o, se);
7038 t.onPreInit.dispatch(t);
7040 if (!s.gecko_spellcheck)
7041 t.getBody().spellcheck = 0;
7046 t.controlManager.onPostRender.dispatch(t, t.controlManager);
7047 t.onPostRender.dispatch(t);
7049 if (s.directionality)
7050 t.getBody().dir = s.directionality;
7053 t.getBody().style.whiteSpace = "nowrap";
7056 t.onNodeChange.add(t.resizeToContent, t);
7058 if (s.custom_elements) {
7059 function handleCustom(ed, o) {
7060 each(explode(s.custom_elements), function(v) {
7063 if (v.indexOf('~') === 0) {
7069 o.content = o.content.replace(new RegExp('<(' + v + ')([^>]*)>', 'g'), '<' + n + ' mce_name="$1"$2>');
7070 o.content = o.content.replace(new RegExp('</(' + v + ')>', 'g'), '</' + n + '>');
7074 t.onBeforeSetContent.add(handleCustom);
7075 t.onPostProcess.add(function(ed, o) {
7081 if (s.handle_node_change_callback) {
7082 t.onNodeChange.add(function(ed, cm, n) {
7083 t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());
7087 if (s.save_callback) {
7088 t.onSaveContent.add(function(ed, o) {
7089 var h = t.execCallback('save_callback', t.id, o.content, t.getBody());
7096 if (s.onchange_callback) {
7097 t.onChange.add(function(ed, l) {
7098 t.execCallback('onchange_callback', t, l);
7102 if (s.convert_newlines_to_brs) {
7103 t.onBeforeSetContent.add(function(ed, o) {
7105 o.content = o.content.replace(/\r?\n/g, '<br />');
7109 if (s.fix_nesting && isIE) {
7110 t.onBeforeSetContent.add(function(ed, o) {
7111 o.content = t._fixNesting(o.content);
7115 if (s.preformatted) {
7116 t.onPostProcess.add(function(ed, o) {
7117 o.content = o.content.replace(/^\s*<pre.*?>/, '');
7118 o.content = o.content.replace(/<\/pre>\s*$/, '');
7121 o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';
7125 if (s.verify_css_classes) {
7126 t.serializer.attribValueFilter = function(n, v) {
7130 // Build regexp for classes
7132 cl = t.dom.getClasses();
7134 if (cl.length > 0) {
7137 each (cl, function(o) {
7138 s += (s ? '|' : '') + o['class'];
7141 t.classesRE = new RegExp('(' + s + ')', 'gi');
7145 return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';
7152 if (s.convert_fonts_to_spans)
7155 if (s.inline_styles)
7156 t._convertInlineElements();
7158 if (s.cleanup_callback) {
7159 t.onBeforeSetContent.add(function(ed, o) {
7160 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
7163 t.onPreProcess.add(function(ed, o) {
7165 t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);
7168 t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);
7171 t.onPostProcess.add(function(ed, o) {
7173 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
7176 o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);
7180 if (s.save_callback) {
7181 t.onGetContent.add(function(ed, o) {
7183 o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());
7187 if (s.handle_event_callback) {
7188 t.onEvent.add(function(ed, e, o) {
7189 if (t.execCallback('handle_event_callback', e, ed, o) === false)
7194 t.onSetContent.add(function() {
7195 // Safari needs some time, it will crash the browser when a link is created otherwise
7196 // I think this crash issue is resolved in the latest 3.0.4
7197 //window.setTimeout(function() {
7198 t.addVisual(t.getBody());
7202 // Remove empty contents
7203 if (s.padd_empty_editor) {
7204 t.onPostProcess.add(function(ed, o) {
7205 o.content = o.content.replace(/^(<p>( | |\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');
7209 if (isGecko && !s.readonly) {
7211 // Design mode must be set here once again to fix a bug where
7212 // Ctrl+A/Delete/Backspace didn't work if the editor was added using mceAddControl then removed then added again
7213 d.designMode = 'Off';
7214 d.designMode = 'On';
7216 // Will fail on Gecko if the editor is placed in an hidden container element
7217 // The design mode will be set ones the editor is focused
7221 // A small timeout was needed since firefox will remove. Bug: #1838304
7222 setTimeout(function () {
7226 t.load({initial : true, format : (s.cleanup_on_startup ? 'html' : 'raw')});
7227 t.startContent = t.getContent({format : 'raw'});
7228 t.undoManager.add({initial : true});
7229 t.initialized = true;
7231 t.onInit.dispatch(t);
7232 t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());
7233 t.execCallback('init_instance_callback', t);
7235 t.nodeChanged({initial : 1});
7237 // Load specified content CSS last
7238 if (s.content_css) {
7239 tinymce.each(explode(s.content_css), function(u) {
7240 t.dom.loadCSS(t.documentBaseURI.toAbsolute(u));
7244 // Handle auto focus
7246 setTimeout(function () {
7247 var ed = EditorManager.get(s.auto_focus);
7249 ed.selection.select(ed.getBody(), 1);
7250 ed.selection.collapse(1);
7251 ed.getWin().focus();
7260 focus : function(sf) {
7261 var oed, t = this, ce = t.settings.content_editable;
7264 // Is not content editable or the selection is outside the area in IE
7265 // the IE statement is needed to avoid bluring if element selections inside layers since
7266 // the layer is like it's own document in IE
7267 if (!ce && (!isIE || t.selection.getNode().ownerDocument != t.getDoc()))
7272 if (EditorManager.activeEditor != t) {
7273 if ((oed = EditorManager.activeEditor) != null)
7274 oed.onDeactivate.dispatch(oed, t);
7276 t.onActivate.dispatch(t, oed);
7279 EditorManager._setActive(t);
7282 execCallback : function(n) {
7283 var t = this, f = t.settings[n], s;
7288 // Look through lookup
7289 if (t.callbackLookup && (s = t.callbackLookup[n])) {
7294 if (is(f, 'string')) {
7295 s = f.replace(/\.\w+$/, '');
7296 s = s ? tinymce.resolve(s) : 0;
7297 f = tinymce.resolve(f);
7298 t.callbackLookup = t.callbackLookup || {};
7299 t.callbackLookup[n] = {func : f, scope : s};
7302 return f.apply(s || t, Array.prototype.slice.call(arguments, 1));
7305 translate : function(s) {
7306 var c = this.settings.language || 'en', i18n = EditorManager.i18n;
7311 return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {
7312 return i18n[c + '.' + b] || '{#' + b + '}';
7316 getLang : function(n, dv) {
7317 return EditorManager.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}');
7320 getParam : function(n, dv, ty) {
7321 var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;
7323 if (ty === 'hash') {
7326 if (is(v, 'string')) {
7327 each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {
7331 o[tr(v[0])] = tr(v[1]);
7333 o[tr(v[0])] = tr(v);
7344 nodeChanged : function(o) {
7345 var t = this, s = t.selection, n = s.getNode() || t.getBody();
7347 // Fix for bug #1896577 it seems that this can not be fired while the editor is loading
7348 if (t.initialized) {
7349 t.onNodeChange.dispatch(
7351 o ? o.controlManager || t.controlManager : t.controlManager,
7352 isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n, // Fix for IE initial state
7359 addButton : function(n, s) {
7362 t.buttons = t.buttons || {};
7366 addCommand : function(n, f, s) {
7367 this.execCommands[n] = {func : f, scope : s || this};
7370 addQueryStateHandler : function(n, f, s) {
7371 this.queryStateCommands[n] = {func : f, scope : s || this};
7374 addQueryValueHandler : function(n, f, s) {
7375 this.queryValueCommands[n] = {func : f, scope : s || this};
7378 addShortcut : function(pa, desc, cmd_func, sc) {
7381 if (!t.settings.custom_shortcuts)
7384 t.shortcuts = t.shortcuts || {};
7386 if (is(cmd_func, 'string')) {
7389 cmd_func = function() {
7390 t.execCommand(c, false, null);
7394 if (is(cmd_func, 'object')) {
7397 cmd_func = function() {
7398 t.execCommand(c[0], c[1], c[2]);
7402 each(explode(pa), function(pa) {
7412 each(explode(pa, '+'), function(v) {
7421 o.charCode = v.charCodeAt(0);
7422 o.keyCode = v.toUpperCase().charCodeAt(0);
7426 t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;
7432 execCommand : function(cmd, ui, val, a) {
7433 var t = this, s = 0, o, st;
7435 if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))
7439 t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);
7444 if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {
7445 t.onExecCommand.dispatch(t, cmd, ui, val, a);
7449 // Registred commands
7450 if (o = t.execCommands[cmd]) {
7451 st = o.func.call(o.scope, ui, val);
7453 // Fall through on true
7455 t.onExecCommand.dispatch(t, cmd, ui, val, a);
7461 each(t.plugins, function(p) {
7462 if (p.execCommand && p.execCommand(cmd, ui, val)) {
7463 t.onExecCommand.dispatch(t, cmd, ui, val, a);
7473 if (t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {
7474 t.onExecCommand.dispatch(t, cmd, ui, val, a);
7479 if (t.editorCommands.execCommand(cmd, ui, val)) {
7480 t.onExecCommand.dispatch(t, cmd, ui, val, a);
7485 t.getDoc().execCommand(cmd, ui, val);
7486 t.onExecCommand.dispatch(t, cmd, ui, val, a);
7489 queryCommandState : function(c) {
7492 // Is hidden then return undefined
7496 // Registred commands
7497 if (o = t.queryStateCommands[c]) {
7498 s = o.func.call(o.scope);
7500 // Fall though on true
7505 // Registred commands
7506 o = t.editorCommands.queryCommandState(c);
7512 return this.getDoc().queryCommandState(c);
7514 // Fails sometimes see bug: 1896577
7518 queryCommandValue : function(c) {
7521 // Is hidden then return undefined
7525 // Registred commands
7526 if (o = t.queryValueCommands[c]) {
7527 s = o.func.call(o.scope);
7529 // Fall though on true
7534 // Registred commands
7535 o = t.editorCommands.queryCommandValue(c);
7541 return this.getDoc().queryCommandValue(c);
7543 // Fails sometimes see bug: 1896577
7550 DOM.show(t.getContainer());
7556 var t = this, d = t.getDoc();
7558 // Fixed bug where IE has a blinking cursor left from the editor
7560 d.execCommand('SelectAll');
7562 // We must save before we hide so Safari doesn't crash
7564 DOM.hide(t.getContainer());
7565 DOM.setStyle(t.id, 'display', t.orgDisplay);
7568 isHidden : function() {
7569 return !DOM.isHidden(this.id);
7572 setProgressState : function(b, ti, o) {
7573 this.onSetProgressState.dispatch(this, b, ti, o);
7578 resizeToContent : function() {
7581 DOM.setStyle(t.id + "_ifr", 'height', t.getBody().scrollHeight);
7584 load : function(o) {
7585 var t = this, e = t.getElement(), h;
7590 h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);
7594 t.onLoadContent.dispatch(t, o);
7596 o.element = e = null;
7601 save : function(o) {
7602 var t = this, e = t.getElement(), h, f;
7610 // Add undo level will trigger onchange event
7612 t.undoManager.typing = 0;
7613 t.undoManager.add();
7617 h = o.content = t.getContent(o);
7620 t.onSaveContent.dispatch(t, o);
7624 if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {
7627 // Update hidden form element
7628 if (f = DOM.getParent(t.id, 'form')) {
7629 each(f.elements, function(e) {
7630 if (e.name == t.id) {
7639 o.element = e = null;
7644 setContent : function(h, o) {
7648 o.format = o.format || 'html';
7653 t.onBeforeSetContent.dispatch(t, o);
7655 // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
7656 // It will also be impossible to place the caret in the editor unless there is a BR element present
7657 if (!tinymce.isIE && (h.length === 0 || /^\s+$/.test(h))) {
7658 o.content = t.dom.setHTML(t.getBody(), '<br mce_bogus="1" />');
7662 o.content = t.dom.setHTML(t.getBody(), tinymce.trim(o.content));
7664 if (o.format != 'raw' && t.settings.cleanup) {
7666 o.content = t.dom.setHTML(t.getBody(), t.serializer.serialize(t.getBody(), o));
7670 t.onSetContent.dispatch(t, o);
7675 getContent : function(o) {
7679 o.format = o.format || 'html';
7683 t.onBeforeGetContent.dispatch(t, o);
7685 if (o.format != 'raw' && t.settings.cleanup) {
7687 h = t.serializer.serialize(t.getBody(), o);
7689 h = t.getBody().innerHTML;
7691 h = h.replace(/^\s*|\s*$/g, '');
7695 t.onGetContent.dispatch(t, o);
7700 isDirty : function() {
7703 return tinymce.trim(t.startContent) != tinymce.trim(t.getContent({format : 'raw', no_events : 1})) && !t.isNotDirty;
7706 getContainer : function() {
7710 t.container = DOM.get(t.editorContainer || t.id + '_parent');
7715 getContentAreaContainer : function() {
7716 return this.contentAreaContainer;
7719 getElement : function() {
7720 return DOM.get(this.settings.content_element || this.id);
7723 getWin : function() {
7726 if (!t.contentWindow) {
7727 e = DOM.get(t.id + "_ifr");
7730 t.contentWindow = e.contentWindow;
7733 return t.contentWindow;
7736 getDoc : function() {
7739 if (!t.contentDocument) {
7743 t.contentDocument = w.document;
7746 return t.contentDocument;
7749 getBody : function() {
7750 return this.bodyElement || this.getDoc().body;
7753 convertURL : function(u, n, e) {
7754 var t = this, s = t.settings;
7756 // Use callback instead
7757 if (s.urlconverter_callback)
7758 return t.execCallback('urlconverter_callback', u, e, true, n);
7760 // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs
7761 if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)
7764 // Convert to relative
7765 if (s.relative_urls)
7766 return t.documentBaseURI.toRelative(u);
7768 // Convert to absolute
7769 u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);
7774 addVisual : function(e) {
7775 var t = this, s = t.settings;
7777 e = e || t.getBody();
7779 if (!is(t.hasVisual))
7780 t.hasVisual = s.visual;
7782 each(t.dom.select('table,a', e), function(e) {
7785 switch (e.nodeName) {
7787 v = t.dom.getAttrib(e, 'border');
7789 if (!v || v == '0') {
7791 t.dom.addClass(e, s.visual_table_class);
7793 t.dom.removeClass(e, s.visual_table_class);
7799 v = t.dom.getAttrib(e, 'name');
7803 t.dom.addClass(e, 'mceItemAnchor');
7805 t.dom.removeClass(e, 'mceItemAnchor');
7812 t.onVisualAid.dispatch(t, e, t.hasVisual);
7815 remove : function() {
7816 var t = this, e = t.getContainer();
7818 t.removed = 1; // Cancels post remove event execution
7821 t.execCallback('remove_instance_callback', t);
7822 t.onRemove.dispatch(t);
7824 // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command
7825 t.onExecCommand.listeners = [];
7827 EditorManager.remove(t);
7831 destroy : function(s) {
7834 // One time is enough
7839 tinymce.removeUnload(t.destroy);
7840 tinyMCE.onBeforeUnload.remove(t._beforeUnload);
7843 if (t.theme.destroy)
7846 // Destroy controls, selection and dom
7847 t.controlManager.destroy();
7848 t.selection.destroy();
7851 // Remove all events
7853 // Don't clear the window or document if content editable
7854 // is enabled since other instances might still be present
7855 if (!t.settings.content_editable) {
7856 Event.clear(t.getWin());
7857 Event.clear(t.getDoc());
7860 Event.clear(t.getBody());
7861 Event.clear(t.formElement);
7864 if (t.formElement) {
7865 t.formElement.submit = t.formElement._mceOldSubmit;
7866 t.formElement._mceOldSubmit = null;
7869 t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;
7872 t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;
7877 // Internal functions
7879 _addEvents : function() {
7880 // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset
7881 var t = this, i, s = t.settings, lo = {
7882 mouseup : 'onMouseUp',
7883 mousedown : 'onMouseDown',
7886 keydown : 'onKeyDown',
7887 keypress : 'onKeyPress',
7888 submit : 'onSubmit',
7890 contextmenu : 'onContextMenu',
7891 dblclick : 'onDblClick',
7892 paste : 'onPaste' // Doesn't work in all browsers yet
7895 function eventHandler(e, o) {
7898 // Don't fire events when it's removed
7902 // Generic event handler
7903 if (t.onEvent.dispatch(t, e, o) !== false) {
7904 // Specific event handler
7905 t[lo[e.fakeType || e.type]].dispatch(t, e, o);
7910 each(lo, function(v, k) {
7913 if (tinymce.isOpera) {
7914 // Fake contextmenu on Opera
7915 Event.add(t.getBody(), 'mousedown', function(e) {
7917 e.fakeType = 'contextmenu';
7922 Event.add(t.getBody(), k, eventHandler);
7926 Event.add(t.getBody(), k, function(e) {
7929 // Get plain text data
7930 if (e.clipboardData)
7931 tx = e.clipboardData.getData('text/plain');
7932 else if (tinymce.isIE)
7933 tx = t.getWin().clipboardData.getData('Text');
7936 /*if (tinymce.isIE) {
7937 el = DOM.add(DOM.doc.body, 'div', {style : 'visibility:hidden;overflow:hidden;position:absolute;width:1px;height:1px'});
7938 r = DOM.doc.body.createTextRange();
7939 r.moveToElementText(el);
7940 r.execCommand('Paste');
7945 eventHandler(e
, {text
: tx
, html
: h
});
7951 Event
.add(t
.getElement().form
|| DOM
.getParent(t
.id
, 'form'), k
, eventHandler
);
7955 Event
.add(s
.content_editable
? t
.getBody() : t
.getDoc(), k
, eventHandler
);
7959 Event
.add(s
.content_editable
? t
.getBody() : (isGecko
? t
.getDoc() : t
.getWin()), 'focus', function(e
) {
7964 // Fixes bug where a specified document_base_uri could result in broken images
7965 // This will also fix drag drop of images in Gecko
7966 if (tinymce
.isGecko
) {
7967 // Convert all images to absolute URLs
7968 /* t.onSetContent.add(function(ed, o) {
7969 each(ed.dom.select('img'), function(e) {
7972 if (v = e.getAttribute('mce_src'))
7973 e.src = t.documentBaseURI.toAbsolute(v);
7977 Event
.add(t
.getDoc(), 'DOMNodeInserted', function(e
) {
7982 if (e
.nodeType
=== 1 && e
.nodeName
=== 'IMG' && (v
= e
.getAttribute('mce_src')))
7983 e
.src
= t
.documentBaseURI
.toAbsolute(v
);
7987 // Set various midas options in Gecko
7989 function setOpts() {
7990 var t
= this, d
= t
.getDoc(), s
= t
.settings
;
7992 if (isGecko
&& !s
.readonly
) {
7993 if (t
._isHidden()) {
7995 if (!s
.content_editable
)
7996 d
.designMode
= 'On';
7998 // Fails if it's hidden
8003 // Try new Gecko method
8004 d
.execCommand("styleWithCSS", 0, false);
8008 try {d
.execCommand("useCSS", 0, true);} catch (ex
) {}
8011 if (!s
.table_inline_editing
)
8012 try {d
.execCommand('enableInlineTableEditing', false, false);} catch (ex
) {}
8014 if (!s
.object_resizing
)
8015 try {d
.execCommand('enableObjectResizing', false, false);} catch (ex
) {}
8019 t
.onBeforeExecCommand
.add(setOpts
);
8020 t
.onMouseDown
.add(setOpts
);
8023 // Add node change handlers
8024 t
.onMouseUp
.add(t
.nodeChanged
);
8025 t
.onClick
.add(t
.nodeChanged
);
8026 t
.onKeyUp
.add(function(ed
, e
) {
8029 if ((c
>= 33 && c
<= 36) || (c
>= 37 && c
<= 40) || c
== 13 || c
== 45 || c
== 46 || c
== 8 || (tinymce
.isMac
&& c
>= 91 && c
<= 93) || e
.ctrlKey
)
8033 // Add reset handler
8034 t
.onReset
.add(function() {
8035 t
.setContent(t
.startContent
, {format
: 'raw'});
8038 if (t
.getParam('tab_focus')) {
8039 function tabCancel(ed
, e
) {
8040 if (e
.keyCode
=== 9)
8041 return Event
.cancel(e
);
8044 function tabHandler(ed
, e
) {
8048 f
= DOM
.getParent(ed
.id
, 'form');
8052 each(el
, function(e
, i
) {
8053 if (e
.id
== ed
.id
) {
8060 for (i
= x
+ 1; i
< el
.length
; i
++) {
8061 if (el
[i
].type
!= 'hidden')
8065 for (i
= x
- 1; i
>= 0; i
--) {
8066 if (el
[i
].type
!= 'hidden')
8075 if (e
.keyCode
=== 9) {
8076 v
= explode(ed
.getParam('tab_focus'));
8078 if (v
.length
== 1) {
8083 // Find element to focus
8085 if (v
[0] == ':prev')
8090 if (v
[1] == ':next')
8097 if (ed
= EditorManager
.get(el
.id
|| el
.name
))
8100 window
.setTimeout(function() {window
.focus();el
.focus();}, 10);
8102 return Event
.cancel(e
);
8107 t
.onKeyUp
.add(tabCancel
);
8110 t
.onKeyPress
.add(tabHandler
);
8111 t
.onKeyDown
.add(tabCancel
);
8113 t
.onKeyDown
.add(tabHandler
);
8117 if (s
.custom_shortcuts
) {
8118 if (s
.custom_undo_redo_keyboard_shortcuts
) {
8119 t
.addShortcut('ctrl+z', t
.getLang('undo_desc'), 'Undo');
8120 t
.addShortcut('ctrl+y', t
.getLang('redo_desc'), 'Redo');
8123 // Add default shortcuts for gecko
8125 t
.addShortcut('ctrl+b', t
.getLang('bold_desc'), 'Bold');
8126 t
.addShortcut('ctrl+i', t
.getLang('italic_desc'), 'Italic');
8127 t
.addShortcut('ctrl+u', t
.getLang('underline_desc'), 'Underline');
8130 // BlockFormat shortcuts keys
8131 for (i
=1; i
<=6; i
++)
8132 t
.addShortcut('ctrl+' + i
, '', ['FormatBlock', false, '<h' + i
+ '>']);
8134 t
.addShortcut('ctrl+7', '', ['FormatBlock', false, '<p>']);
8135 t
.addShortcut('ctrl+8', '', ['FormatBlock', false, '<div>']);
8136 t
.addShortcut('ctrl+9', '', ['FormatBlock', false, '<address>']);
8141 if (!e
.altKey
&& !e
.ctrlKey
&& !e
.metaKey
)
8144 each(t
.shortcuts
, function(o
) {
8145 if (o
.ctrl
!= e
.ctrlKey
&& (!tinymce
.isMac
|| o
.ctrl
== e
.metaKey
))
8148 if (o
.alt
!= e
.altKey
)
8151 if (o
.shift
!= e
.shiftKey
)
8154 if (e
.keyCode
== o
.keyCode
|| (e
.charCode
&& e
.charCode
== o
.charCode
)) {
8163 t
.onKeyUp
.add(function(ed
, e
) {
8167 return Event
.cancel(e
);
8170 t
.onKeyPress
.add(function(ed
, e
) {
8174 return Event
.cancel(e
);
8177 t
.onKeyDown
.add(function(ed
, e
) {
8181 o
.func
.call(o
.scope
);
8182 return Event
.cancel(e
);
8188 // Fix so resize will only update the width and height attributes not the styles of an image
8189 // It will also block mceItemNoResize items
8190 Event
.add(t
.getDoc(), 'controlselect', function(e
) {
8191 var re
= t
.resizeInfo
, cb
;
8195 // Don't do this action for non image elements
8196 if (e
.nodeName
!== 'IMG')
8200 Event
.remove(re
.node
, re
.ev
, re
.cb
);
8202 if (!t
.dom
.hasClass(e
, 'mceItemNoResize')) {
8204 cb
= Event
.add(e
, ev
, function(e
) {
8209 if (v
= t
.dom
.getStyle(e
, 'width')) {
8210 t
.dom
.setAttrib(e
, 'width', v
.replace(/[^0-9%]+/g, ''));
8211 t
.dom
.setStyle(e
, 'width', '');
8214 if (v
= t
.dom
.getStyle(e
, 'height')) {
8215 t
.dom
.setAttrib(e
, 'height', v
.replace(/[^0-9%]+/g, ''));
8216 t
.dom
.setStyle(e
, 'height', '');
8221 cb
= Event
.add(e
, 'resizestart', Event
.cancel
, Event
);
8224 re
= t
.resizeInfo
= {
8231 t
.onKeyDown
.add(function(ed
, e
) {
8232 switch (e
.keyCode
) {
8234 // Fix IE control + backspace browser bug
8235 if (t
.selection
.getRng().item
) {
8236 t
.selection
.getRng().item(0).removeNode();
8237 return Event
.cancel(e
);
8243 if (tinymce
.isOpera
) {
8244 t
.onClick
.add(function(ed
, e
) {
8249 // Add custom undo/redo handlers
8250 if (s
.custom_undo_redo
) {
8251 function addUndo() {
8252 t
.undoManager
.typing
= 0;
8253 t
.undoManager
.add();
8256 // Add undo level on editor blur
8258 Event
.add(t
.getWin(), 'blur', function(e
) {
8261 // Check added for fullscreen bug
8263 n
= t
.selection
.getNode();
8265 // Add undo level is selection was lost to another document
8266 if (!t
.removed
&& n
.ownerDocument
&& n
.ownerDocument
!= t
.getDoc())
8271 Event
.add(t
.getDoc(), 'blur', function() {
8272 if (t
.selection
&& !t
.removed
)
8277 t
.onMouseDown
.add(addUndo
);
8279 t
.onKeyUp
.add(function(ed
, e
) {
8280 if ((e
.keyCode
>= 33 && e
.keyCode
<= 36) || (e
.keyCode
>= 37 && e
.keyCode
<= 40) || e
.keyCode
== 13 || e
.keyCode
== 45 || e
.ctrlKey
) {
8281 t
.undoManager
.typing
= 0;
8282 t
.undoManager
.add();
8286 t
.onKeyDown
.add(function(ed
, e
) {
8287 // Is caracter positon keys
8288 if ((e
.keyCode
>= 33 && e
.keyCode
<= 36) || (e
.keyCode
>= 37 && e
.keyCode
<= 40) || e
.keyCode
== 13 || e
.keyCode
== 45) {
8289 if (t
.undoManager
.typing
) {
8290 t
.undoManager
.add();
8291 t
.undoManager
.typing
= 0;
8297 if (!t
.undoManager
.typing
) {
8298 t
.undoManager
.add();
8299 t
.undoManager
.typing
= 1;
8305 _convertInlineElements : function() {
8306 var t
= this, s
= t
.settings
, dom
= t
.dom
, v
, e
, na
, st
, sp
;
8308 function convert(ed
, o
) {
8309 if (!s
.inline_styles
)
8313 each(t
.dom
.select('table,u,strike', o
.node
), function(n
) {
8314 switch (n
.nodeName
) {
8316 if (v
= dom
.getAttrib(n
, 'height')) {
8317 dom
.setStyle(n
, 'height', v
);
8318 dom
.setAttrib(n
, 'height', '');
8324 //sp = dom.create('span', {style : dom.getAttrib(n, 'style')});
8325 n
.style
.textDecoration
= n
.nodeName
== 'U' ? 'underline' : 'line-through';
8326 dom
.setAttrib(n
, 'mce_style', '');
8327 dom
.setAttrib(n
, 'mce_name', 'span');
8332 each(t
.dom
.select('table,span', o
.node
).reverse(), function(n
) {
8333 if (n
.nodeName
== 'TABLE') {
8334 if (v
= dom
.getStyle(n
, 'height'))
8335 dom
.setAttrib(n
, 'height', v
.replace(/[^0-9%]+/g, ''));
8337 // Convert spans to elements
8338 if (n
.style
.textDecoration
== 'underline')
8340 else if (n
.style
.textDecoration
== 'line-through')
8346 n
.style
.textDecoration
= '';
8347 dom
.setAttrib(n
, 'mce_style', '');
8349 e
= dom
.create(na
, {
8350 style
: dom
.getAttrib(n
, 'style')
8353 dom
.replace(e
, n
, 1);
8360 t
.onPreProcess
.add(convert
);
8362 if (!s
.cleanup_on_startup
) {
8363 t
.onSetContent
.add(function(ed
, o
) {
8365 convert(t
, {node
: t
.getBody(), set : 1});
8370 _convertFonts : function() {
8371 var t
= this, s
= t
.settings
, dom
= t
.dom
, fz
, fzn
, sl
, cl
;
8374 if (!s
.inline_styles
)
8377 // Font pt values and font size names
8378 fz
= [8, 10, 12, 14, 18, 24, 36];
8379 fzn
= ['xx-small', 'x-small','small','medium','large','x-large', 'xx-large'];
8381 if (sl
= s
.font_size_style_values
)
8384 if (cl
= s
.font_size_classes
)
8387 function convertToFonts(no
) {
8388 var n
, f
, nl
, x
, i
, v
, st
;
8390 // Convert spans to fonts on non WebKit browsers
8391 if (tinymce
.isWebKit
|| !s
.inline_styles
)
8394 nl
= t
.dom
.select('span', no
);
8395 for (x
= nl
.length
- 1; x
>= 0; x
--) {
8398 f
= dom
.create('font', {
8399 color
: dom
.toHex(dom
.getStyle(n
, 'color')),
8400 face
: dom
.getStyle(n
, 'fontFamily'),
8401 style
: dom
.getAttrib(n
, 'style'),
8402 'class' : dom
.getAttrib(n
, 'class')
8405 // Clear color and font family
8407 if (st
.color
|| st
.fontFamily
) {
8408 st
.color
= st
.fontFamily
= '';
8409 dom
.setAttrib(f
, 'mce_style', ''); // Remove cached style data
8413 i
= inArray(sl
, dom
.getStyle(n
, 'fontSize'));
8416 dom
.setAttrib(f
, 'size', '' + (i
+ 1 || 1));
8417 //f.style.fontSize = '';
8420 i
= inArray(cl
, dom
.getAttrib(n
, 'class'));
8421 v
= dom
.getStyle(n
, 'fontSize');
8423 if (i
== -1 && v
.indexOf('pt') > 0)
8424 i
= inArray(fz
, parseInt(v
));
8427 i
= inArray(fzn
, v
);
8430 dom
.setAttrib(f
, 'size', '' + (i
+ 1 || 1));
8431 f
.style
.fontSize
= '';
8435 if (f
.color
|| f
.face
|| f
.size
) {
8436 f
.style
.fontFamily
= '';
8437 dom
.setAttrib(f
, 'mce_style', '');
8438 dom
.replace(f
, n
, 1);
8446 t
.onSetContent
.add(function(ed
, o
) {
8447 convertToFonts(ed
.getBody());
8451 t
.onPreProcess
.add(function(ed
, o
) {
8454 // Keep unit tests happy
8455 if (!s
.inline_styles
)
8459 nl
= t
.dom
.select('font', o
.node
);
8460 for (x
= nl
.length
- 1; x
>= 0; x
--) {
8463 sp
= dom
.create('span', {
8464 style
: dom
.getAttrib(n
, 'style'),
8465 'class' : dom
.getAttrib(n
, 'class')
8469 fontFamily
: dom
.getAttrib(n
, 'face'),
8470 color
: dom
.getAttrib(n
, 'color'),
8471 backgroundColor
: n
.style
.backgroundColor
8476 dom
.setStyle(sp
, 'fontSize', sl
[parseInt(n
.size
) - 1]);
8478 dom
.setAttrib(sp
, 'class', cl
[parseInt(n
.size
) - 1]);
8481 dom
.setAttrib(sp
, 'mce_style', '');
8482 dom
.replace(sp
, n
, 1);
8488 _isHidden : function() {
8494 // Weird, wheres that cursor selection?
8495 s
= this.selection
.getSel();
8496 return (!s
|| !s
.rangeCount
|| s
.rangeCount
== 0);
8499 // Fix for bug #1867292
8500 _fixNesting : function(s
) {
8503 s
= s
.replace(/<(\/)?([^\s>]+)[^>]*?>/g, function(a
, b
, c
) {
8506 // Handle end element
8511 if (c
!== d
[d
.length
- 1].tag
) {
8512 for (i
=d
.length
- 1; i
>=0; i
--) {
8513 if (d
[i
].tag
=== c
) {
8523 if (d
.length
&& d
[d
.length
- 1].close
) {
8524 a
= a
+ '</' + d
[d
.length
- 1].tag
+ '>';
8530 if (/^(br|hr|input|meta|img|link|param)$/i.test(c
))
8533 // Ignore closed ones
8537 d
.push({tag
: c
}); // Push start element
8543 // End all open tags
8544 for (i
=d
.length
- 1; i
>=0; i
--)
8545 s
+= '</' + d
[i
].tag
+ '>';
8553 /* file:jscripts/tiny_mce/classes/EditorCommands.js */
8556 var each = tinymce.each, isIE = tinymce.isIE, isGecko = tinymce.isGecko, isOpera = tinymce.isOpera, isWebKit = tinymce.isWebKit;
8558 tinymce.create('tinymce.EditorCommands', {
8559 EditorCommands : function(ed) {
8563 execCommand : function(cmd, ui, val) {
8564 var t = this, ed = t.editor, f;
8571 ed.getDoc().execCommand(cmd, ui, val);
8574 ed.windowManager.confirm(ed.getLang('clipboard_msg'), function(s) {
8576 window.open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', 'mceExternal');
8579 ed.windowManager.alert(ed.getLang('clipboard_no_support'));
8585 case 'mceResetDesignMode':
8586 case 'mceBeginUndoLevel':
8594 // Bundle these together
8596 case 'JustifyCenter':
8597 case 'JustifyRight':
8599 t.mceJustify(cmd, cmd.substring(7).toLowerCase());
8602 case 'mceEndUndoLevel':
8603 case 'mceAddUndoLevel':
8604 ed.undoManager.add();
8611 f.call(this, ui, val);
8619 Indent : function() {
8620 var ed = this.editor, d = ed.dom, s = ed.selection, e, iv, iu;
8622 // Setup indent level
8623 iv = ed.settings.indentation;
8624 iu = /[a-z%]+$/i.exec(iv);
8627 if (ed.settings.inline_styles && (!this.queryStateInsertUnorderedList() && !this.queryStateInsertOrderedList())) {
8628 each(this._getSelectedBlocks(), function(e) {
8629 d.setStyle(e, 'paddingLeft', (parseInt(e.style.paddingLeft || 0) + iv) + iu);
8635 ed.getDoc().execCommand('Indent', false, null);
8638 d.getParent(s.getNode(), function(n) {
8639 if (n.nodeName == 'BLOCKQUOTE') {
8640 n.dir = n.style.cssText = '';
8646 Outdent : function() {
8647 var ed = this.editor, d = ed.dom, s = ed.selection, e, v, iv, iu;
8649 // Setup indent level
8650 iv = ed.settings.indentation;
8651 iu = /[a-z%]+$/i.exec(iv);
8654 if (ed.settings.inline_styles && (!this.queryStateInsertUnorderedList() && !this.queryStateInsertOrderedList())) {
8655 each(this._getSelectedBlocks(), function(e) {
8656 v = Math.max(0, parseInt(e.style.paddingLeft || 0) - iv);
8657 d.setStyle(e, 'paddingLeft', v ? v + iu : '');
8663 ed.getDoc().execCommand('Outdent', false, null);
8666 mceSetAttribute : function(u, v) {
8667 var ed = this.editor, d = ed.dom, e;
8669 if (e = d.getParent(ed.selection.getNode(), d.isBlock))
8670 d.setAttrib(e, v.name, v.value);
8673 mceSetContent : function(u, v) {
8674 this.editor.setContent(v);
8677 mceToggleVisualAid : function() {
8678 var ed = this.editor;
8680 ed.hasVisual = !ed.hasVisual;
8684 mceReplaceContent : function(u, v) {
8685 var s = this.editor.selection;
8687 s.setContent(v.replace(/\{\$selection\}/g, s.getContent({format : 'text'})));
8690 mceInsertLink : function(u, v) {
8691 var ed = this.editor, s = ed.selection, e = ed.dom.getParent(s.getNode(), 'A');
8693 if (tinymce.is(v, 'string'))
8697 each(v, function(v, k) {
8698 ed.dom.setAttrib(e, k, v);
8703 ed.execCommand('CreateLink', false, 'javascript:mctmp(0);');
8704 each(ed.dom.select('a'), function(e) {
8705 if (e.href == 'javascript:mctmp(0);')
8712 ed.dom.remove(e, 1);
8716 UnLink : function() {
8717 var ed = this.editor, s = ed.selection;
8719 if (s.isCollapsed())
8720 s.select(s.getNode());
8722 ed.getDoc().execCommand('unlink', false, null);
8726 FontName : function(u, v) {
8727 var t = this, ed = t.editor, s = ed.selection, e;
8730 if (s.isCollapsed())
8731 s.select(s.getNode());
8735 ed.getDoc().execCommand('FontName', false, v);
8738 FontSize : function(u, v) {
8739 var ed = this.editor, s = ed.settings, fz = tinymce.explode(s.font_size_style_values), fzc = tinymce.explode(s.font_size_classes), h, bm;
8741 // Remove style sizes
8742 each(ed.dom.select('font'), function(e) {
8743 e.style.fontSize = '';
8746 // Let the browser add new size it will remove unneded ones in some browsers
8747 ed.getDoc().execCommand('FontSize', false, v);
8750 if (s.inline_styles) {
8751 each(ed.dom.select('font'), function(e) {
8752 // Try remove redundant font elements
8753 if (e.parentNode.nodeName == 'FONT' && e.size == e.parentNode.size) {
8755 bm = ed.selection.getBookmark();
8757 ed.dom.remove(e, 1);
8761 // Setup font size based on font size value
8763 if (fzc && fzc.length > 0)
8764 ed.dom.setAttrib(e, 'class', fzc[parseInt(v) - 1]);
8766 ed.dom.setStyle(e, 'fontSize', fz[parseInt(v) - 1]);
8771 ed.selection.moveToBookmark(bm);
8774 queryCommandValue : function(c) {
8775 var f = this['queryValue' + c];
8778 return f.call(this, c);
8783 queryCommandState : function(cmd) {
8787 // Bundle these together
8789 case 'JustifyCenter':
8790 case 'JustifyRight':
8792 return this.queryStateJustify(cmd, cmd.substring(7).toLowerCase());
8795 if (f = this['queryState' + cmd])
8796 return f.call(this, cmd);
8802 _queryState : function(c) {
8804 return this.editor.getDoc().queryCommandState(c);
8810 _queryVal : function(c) {
8812 return this.editor.getDoc().queryCommandValue(c);
8818 queryValueFontSize : function() {
8819 var ed = this.editor, v = 0, p;
8821 if (isOpera || isWebKit) {
8822 if (p = ed.dom.getParent(ed.selection.getNode(), 'FONT'))
8828 return this._queryVal('FontSize');
8831 queryValueFontName : function() {
8832 var ed = this.editor, v = 0, p;
8834 if (p = ed.dom.getParent(ed.selection.getNode(), 'FONT'))
8838 v = this._queryVal('FontName');
8843 mceJustify : function(c, v) {
8844 var ed = this.editor, se = ed.selection, n = se.getNode(), nn = n.nodeName, bl, nb, dom = ed.dom, rm;
8846 if (ed.settings.inline_styles && this.queryStateJustify(c, v))
8849 bl = dom.getParent(n, ed.dom.isBlock);
8857 dom.setStyle(bl || n.parentNode, 'textAlign', '');
8859 dom.setStyle(n, 'float', '');
8864 if (v == 'center') {
8865 // Do not change table elements
8866 if (bl && /^(TD|TH)$/.test(bl.nodeName))
8869 if (!bl || bl.childNodes.length > 1) {
8870 nb = dom.create('p');
8871 nb.appendChild(n.cloneNode(false));
8874 dom.insertAfter(nb, bl);
8876 dom.insertAfter(nb, n);
8883 dom.setStyle(bl, 'textAlign', v);
8884 dom.setStyle(n, 'float', '');
8886 dom.setStyle(n, 'float', v);
8887 dom.setStyle(bl || n.parentNode, 'textAlign', '');
8894 // Handle the alignment outselfs, less quirks in all browsers
8895 if (ed.settings.inline_styles && ed.settings.forced_root_block) {
8899 each(this._getSelectedBlocks(dom.getParent(se.getStart(), dom.isBlock), dom.getParent(se.getEnd(), dom.isBlock)), function(e) {
8900 dom.setAttrib(e, 'align', '');
8901 dom.setStyle(e, 'textAlign', v == 'full' ? 'justify' : v);
8906 ed.getDoc().execCommand(c, false, null);
8908 if (ed.settings.inline_styles) {
8910 dom.getParent(ed.selection.getNode(), function(n) {
8911 if (n.style && n.style.textAlign)
8912 dom.setStyle(n, 'textAlign', '');
8918 each(dom.select('*'), function(n) {
8925 dom.setStyle(n, 'textAlign', v);
8926 dom.setAttrib(n, 'align', '');
8932 mceSetCSSClass : function(u, v) {
8933 this.mceSetStyleInfo(0, {command : 'setattrib', name : 'class', value : v});
8936 getSelectedElement : function() {
8937 var t = this, ed = t.editor, dom = ed.dom, se = ed.selection, r = se.getRng(), r1, r2, sc, ec, so, eo, e, sp, ep, re;
8939 if (se.isCollapsed() || r.item)
8940 return se.getNode();
8943 re = ed.settings.merge_styles_invalid_parents;
8944 if (tinymce.is(re, 'string'))
8945 re = new RegExp(re, 'i');
8950 sc = r1.parentElement();
8954 ec = r2.parentElement();
8957 r1.move('character', 1);
8958 sc = r1.parentElement();
8963 r1.moveToElementText(sc);
8965 if (r1.compareEndPoints('StartToStart', r) == 0 && r1.compareEndPoints('EndToEnd', r) == 0)
8966 return re && re.test(sc.nodeName) ? null : sc;
8969 function getParent(n) {
8970 return dom.getParent(n, function(n) {return n.nodeType == 1;});
8973 sc = r.startContainer;
8974 ec = r.endContainer;
8981 if (sc.hasChildNodes()) {
8982 sp = sc.childNodes[so];
8983 return re && re.test(sp.nodeName) ? null : sp;
8989 if (sc.nodeType != 3 || ec.nodeType != 3)
8995 if (sp && sp.firstChild != sc)
8999 if (so == sc.nodeValue.length) {
9002 if (e && e.nodeType == 1)
9003 sp = sc.nextSibling;
9007 e = ec.previousSibling;
9009 if (e && e.nodeType == 1)
9013 if (eo == ec.nodeValue.length) {
9016 if (ep && ep.lastChild != ec)
9022 return re && sp && re.test(sp.nodeName) ? null : sp;
9028 InsertHorizontalRule : function() {
9029 // Fix for Gecko <hr size="1" /> issue and IE bug rep(/<a.*?href=\"(.*?)\".*?>(.*?)<\/a>/gi,"[url=$1]$2[/url]");
9030 if (isGecko || isIE)
9031 this.editor.selection.setContent('<hr />');
9033 this.editor.getDoc().execCommand('InsertHorizontalRule', false, '');
9036 RemoveFormat : function() {
9037 var t = this, ed = t.editor, s = ed.selection, b;
9039 // Safari breaks tables
9041 s.setContent(s.getContent({format : 'raw'}).replace(/(<(span|b|i|strong|em|strike) [^>]+>|<(span|b|i|strong|em|strike)>|<\/(span|b|i|strong|em|strike)>|)/g, ''), {format : 'raw'});
9043 ed.getDoc().execCommand('RemoveFormat', false, null);
9045 t.mceSetStyleInfo(0, {command : 'removeformat'});
9049 mceSetStyleInfo : function(u, v) {
9050 var t = this, ed = t.editor, d = ed.getDoc(), dom = ed.dom, e, b, s = ed.selection, nn = v.wrapper || 'span', b = s.getBookmark(), re;
9052 function set(n, e) {
9053 if (n.nodeType == 1) {
9054 switch (v.command) {
9056 return dom.setAttrib(n, v.name, v.value);
9059 return dom.setStyle(n, v.name, v.value);
9061 case 'removeformat':
9062 return dom.setAttrib(n, 'class', '');
9068 re = ed.settings.merge_styles_invalid_parents;
9069 if (tinymce.is(re, 'string'))
9070 re = new RegExp(re, 'i');
9072 // Set style info on selected element
9073 if ((e = t.getSelectedElement()) && !ed.settings.force_span_wrappers)
9076 // Generate wrappers and set styles on them
9077 d.execCommand('FontName', false, '__');
9078 each(isWebKit ? dom.select('span') : dom.select('font'), function(n) {
9081 if (dom.getAttrib(n, 'face') == '__' || n.style.fontFamily === '__') {
9082 sp = dom.create(nn, {mce_new : '1'});
9086 each (n.childNodes, function(n) {
9087 sp.appendChild(n.cloneNode(true));
9095 // Remove wrappers inside new ones
9096 each(dom.select(nn).reverse(), function(n) {
9097 var p = n.parentNode;
9099 // Check if it's an old span in a new wrapper
9100 if (!dom.getAttrib(n, 'mce_new')) {
9102 p = dom.getParent(n, function(n) {
9103 return n.nodeType == 1 && dom.getAttrib(n, 'mce_new');
9111 // Merge wrappers with parent wrappers
9112 each(dom.select(nn).reverse(), function(n) {
9113 var p = n.parentNode;
9115 if (!p || !dom.getAttrib(n, 'mce_new'))
9118 if (ed.settings.force_span_wrappers && p.nodeName != 'SPAN')
9121 // Has parent of the same type and only child
9122 if (p.nodeName == nn.toUpperCase() && p.childNodes.length == 1)
9123 return dom.remove(p, 1);
9125 // Has parent that is more suitable to have the class and only child
9126 if (n.nodeType == 1 && (!re || !re.test(p.nodeName)) && p.childNodes.length == 1) {
9127 set(p); // Set style info on parent instead
9128 dom.setAttrib(n, 'class', '');
9132 // Remove empty wrappers
9133 each(dom.select(nn).reverse(), function(n) {
9134 if (dom.getAttrib(n, 'mce_new') || (dom.getAttribs(n).length <= 1 && n.className === '')) {
9135 if (!dom.getAttrib(n, 'class') && !dom.getAttrib(n, 'style'))
9136 return dom.remove(n, 1);
9138 dom.setAttrib(n, 'mce_new', ''); // Remove mce_new marker
9142 s.moveToBookmark(b);
9145 queryStateJustify : function(c, v) {
9146 var ed = this.editor, n = ed.selection.getNode(), dom = ed.dom;
9148 if (n && n.nodeName == 'IMG') {
9149 if (dom.getStyle(n, 'float') == v)
9152 return n.parentNode.style.textAlign == v;
9155 n = dom.getParent(ed.selection.getStart(), function(n) {
9156 return n.nodeType == 1 && n.style.textAlign;
9162 if (ed.settings.inline_styles)
9163 return (n && n.style.textAlign == v);
9165 return this._queryState(c);
9168 HiliteColor : function(ui, val) {
9169 var t = this, ed = t.editor, d = ed.getDoc();
9176 // Try new Gecko method
9177 d.execCommand("styleWithCSS", 0, s);
9180 d.execCommand("useCSS", 0, !s);
9184 if (isGecko || isOpera) {
9186 d.execCommand('hilitecolor', false, val);
9189 d.execCommand('BackColor', false, val);
9193 var ed = this.editor;
9195 if (ed.settings.custom_undo_redo) {
9196 ed.undoManager.undo();
9199 ed.getDoc().execCommand('Undo', false, null);
9203 var ed = this.editor;
9205 if (ed.settings.custom_undo_redo) {
9206 ed.undoManager.redo();
9209 ed.getDoc().execCommand('Redo', false, null);
9212 FormatBlock : function(ui, val) {
9213 var t = this, ed = t.editor, s = ed.selection, dom = ed.dom, bl, nb, b;
9215 function isBlock(n) {
9216 return /^(P|DIV|H[1-6]|ADDRESS|BLOCKQUOTE|PRE)$/.test(n.nodeName);
9219 bl = dom.getParent(s.getNode(), function(n) {
9223 // IE has an issue where it removes the parent div if you change format on the paragrah in <div><p>Content</p></div>
9224 // FF and Opera doesn't change parent DIV elements if you switch format
9226 if ((isIE && isBlock(bl.parentNode)) || bl.nodeName == 'DIV') {
9227 // Rename block element
9228 nb = ed.dom.create(val);
9230 each(dom.getAttribs(bl), function(v) {
9231 dom.setAttrib(nb, v.nodeName, dom.getAttrib(bl, v.nodeName));
9234 b = s.getBookmark();
9235 dom.replace(nb, bl, 1);
9236 s.moveToBookmark(b);
9242 val = ed.settings.forced_root_block ? (val || '<p>') : val;
9244 if (val.indexOf('<') == -1)
9245 val = '<' + val + '>';
9247 if (tinymce.isGecko)
9248 val = val.replace(/<(div|blockquote|code|dt|dd|dl|samp)>/gi, '$1');
9250 ed.getDoc().execCommand('FormatBlock', false, val);
9253 mceCleanup : function() {
9254 var ed = this.editor, s = ed.selection, b = s.getBookmark();
9255 ed.setContent(ed.getContent());
9256 s.moveToBookmark(b);
9259 mceRemoveNode : function(ui, val) {
9260 var ed = this.editor, s = ed.selection, b, n = val || s.getNode();
9262 // Make sure that the body node isn't removed
9263 if (n == ed.getBody())
9266 b = s.getBookmark();
9267 ed.dom.remove(n, 1);
9268 s.moveToBookmark(b);
9272 mceSelectNodeDepth : function(ui, val) {
9273 var ed = this.editor, s = ed.selection, c = 0;
9275 ed.dom.getParent(s.getNode(), function(n) {
9276 if (n.nodeType == 1 && c++ == val) {
9284 mceSelectNode : function(u, v) {
9285 this.editor.selection.select(v);
9288 mceInsertContent : function(ui, val) {
9289 this.editor.selection.setContent(val);
9292 mceInsertRawHTML : function(ui, val) {
9293 var ed = this.editor;
9295 ed.selection.setContent('tiny_mce_marker');
9296 ed.setContent(ed.getContent().replace(/tiny_mce_marker/g, val));
9299 mceRepaint : function() {
9300 var s, b, e = this.editor;
9302 if (tinymce.isGecko) {
9305 b = s.getBookmark(true);
9308 s.getSel().selectAllChildren(e.getBody());
9311 s.moveToBookmark(b);
9318 queryStateUnderline : function() {
9319 var ed = this.editor, n = ed.selection.getNode();
9321 if (n && n.nodeName == 'A')
9324 return this._queryState('Underline');
9327 queryStateOutdent : function() {
9328 var ed = this.editor, n;
9330 if (ed.settings.inline_styles) {
9331 if ((n = ed.dom.getParent(ed.selection.getStart(), ed.dom.isBlock)) && parseInt(n.style.paddingLeft) > 0)
9334 if ((n = ed.dom.getParent(ed.selection.getEnd(), ed.dom.isBlock)) && parseInt(n.style.paddingLeft) > 0)
9337 return !!ed.dom.getParent(ed.selection.getNode(), 'BLOCKQUOTE');
9339 return this.queryStateInsertUnorderedList() || this.queryStateInsertOrderedList();
9342 queryStateInsertUnorderedList : function() {
9343 return this.editor.dom.getParent(this.editor.selection.getNode(), 'UL');
9346 queryStateInsertOrderedList : function() {
9347 return this.editor.dom.getParent(this.editor.selection.getNode(), 'OL');
9350 queryStatemceBlockQuote : function() {
9351 return !!this.editor.dom.getParent(this.editor.selection.getStart(), function(n) {return n.nodeName === 'BLOCKQUOTE';});
9354 mceBlockQuote : function() {
9355 var t = this, ed = t.editor, s = ed.selection, dom = ed.dom, sb, eb, n, bm, bq, r, bq2, i, nl;
9358 return dom.getParent(e, function(n) {return n.nodeName === 'BLOCKQUOTE';});
9361 // Get start/end block
9362 sb = dom.getParent(s.getStart(), dom.isBlock);
9363 eb = dom.getParent(s.getEnd(), dom.isBlock);
9365 // Remove blockquote(s)
9366 if (bq = getBQ(sb)) {
9367 if (sb != eb || sb.childNodes.length > 1 || (sb.childNodes.length == 1 && sb.firstChild.nodeName != 'BR'))
9368 bm = s.getBookmark();
9370 // Move all elements after the end block into new bq
9372 bq2 = bq.cloneNode(false);
9374 while (n = eb.nextSibling)
9375 bq2.appendChild(n.parentNode.removeChild(n));
9380 dom.insertAfter(bq2, bq);
9382 // Move all selected blocks after the current bq
9383 nl = t._getSelectedBlocks(sb, eb);
9384 for (i = nl.length - 1; i >= 0; i--) {
9385 dom.insertAfter(nl[i], bq);
9388 // Empty bq, then remove it
9389 if (/^\s*$/.test(bq.innerHTML))
9390 dom.remove(bq, 1); // Keep children so boomark restoration works correctly
9392 // Empty bq, then remote it
9393 if (bq2 && /^\s*$/.test(bq2.innerHTML))
9394 dom.remove(bq2, 1); // Keep children so boomark restoration works correctly
9397 // Move caret inside empty block element
9399 r = ed.getDoc().createRange();
9407 // IE misses the empty block some times element so we must move back the caret
9408 if (dom.getParent(s.getStart(), dom.isBlock) != sb) {
9410 r.move('character', -1);
9415 t.editor.selection.moveToBookmark(bm);
9420 // Since IE can start with a totally empty document we need to add the first bq and paragraph
9421 if (isIE && !sb && !eb) {
9422 t.editor.getDoc().execCommand('Indent');
9423 n = getBQ(s.getNode());
9424 n.style.margin = n.dir = ''; // IE adds margin and dir to bq
9431 // If empty paragraph node then do not use bookmark
9432 if (sb != eb || sb.childNodes.length > 1 || (sb.childNodes.length == 1 && sb.firstChild.nodeName != 'BR'))
9433 bm = s.getBookmark();
9435 // Move selected block elements into a bq
9436 each(t._getSelectedBlocks(getBQ(s.getStart()), getBQ(s.getEnd())), function(e) {
9437 // Found existing BQ add to this one
9438 if (e.nodeName == 'BLOCKQUOTE' && !bq) {
9443 // No BQ found, create one
9445 bq = dom.create('blockquote');
9446 e.parentNode.insertBefore(bq, e);
9449 // Add children from existing BQ
9450 if (e.nodeName == 'BLOCKQUOTE' && bq) {
9454 bq.appendChild(n.cloneNode(true));
9462 // Add non BQ element to BQ
9463 bq.appendChild(dom.remove(e));
9467 // Move caret inside empty block element
9469 r = ed.getDoc().createRange();
9478 s.moveToBookmark(bm);
9481 _mceBlockQuote : function() {
9482 var t = this, s = t.editor.selection, b = s.getBookmark(), bq, dom = t.editor.dom;
9484 function findBQ(e) {
9485 return dom.getParent(e, function(n) {return n.nodeName === 'BLOCKQUOTE';});
9488 // Remove blockquote(s)
9489 if (findBQ(s.getStart())) {
9490 each(t._getSelectedBlocks(findBQ(s.getStart()), findBQ(s.getEnd())), function(e) {
9491 // Found BQ lets remove it
9492 if (e.nodeName == 'BLOCKQUOTE')
9496 t.editor.selection.moveToBookmark(b);
9500 each(t._getSelectedBlocks(findBQ(s.getStart()), findBQ(s.getEnd())), function(e) {
9503 // Found existing BQ add to this one
9504 if (e.nodeName == 'BLOCKQUOTE' && !bq) {
9509 // No BQ found, create one
9511 bq = dom.create('blockquote');
9512 e.parentNode.insertBefore(bq, e);
9515 // Add children from existing BQ
9516 if (e.nodeName == 'BLOCKQUOTE' && bq) {
9520 bq.appendChild(n.cloneNode(true));
9529 // Add non BQ element to BQ
9530 bq.appendChild(dom.remove(e));
9533 t.editor.selection.moveToBookmark(b);
9536 _getSelectedBlocks : function(st
, en
) {
9537 var ed
= this.editor
, dom
= ed
.dom
, s
= ed
.selection
, sb
, eb
, n
, bl
= [];
9539 sb
= dom
.getParent(st
|| s
.getStart(), dom
.isBlock
);
9540 eb
= dom
.getParent(en
|| s
.getEnd(), dom
.isBlock
);
9545 if (sb
&& eb
&& sb
!= eb
) {
9548 while ((n
= n
.nextSibling
) && n
!= eb
) {
9563 /* file:jscripts/tiny_mce/classes/UndoManager.js */
9565 tinymce.create('tinymce.UndoManager', {
9570 UndoManager : function(ed) {
9571 var t = this, Dispatcher = tinymce.util.Dispatcher;
9575 t.onAdd = new Dispatcher(this);
9576 t.onUndo = new Dispatcher(this);
9577 t.onRedo = new Dispatcher(this);
9581 var t = this, i, ed = t.editor, b, s = ed.settings, la;
9584 l.content = l.content || ed.getContent({format : 'raw', no_events : 1});
9586 // Add undo level if needed
9587 l.content = l.content.replace(/^\s*|\s*$/g, '');
9588 la = t.data[t.index > 0 && (t.index == 0 || t.index == t.data.length) ? t.index - 1 : t.index];
9589 if (!l.initial && la && l.content == la.content)
9593 if (s.custom_undo_redo_levels) {
9594 if (t.data.length > s.custom_undo_redo_levels) {
9595 for (i = 0; i < t.data.length - 1; i++)
9596 t.data[i] = t.data[i + 1];
9599 t.index = t.data.length;
9603 if (s.custom_undo_redo_restore_selection && !l.initial)
9604 l.bookmark = b = l.bookmark || ed.selection.getBookmark();
9606 if (t.index < t.data.length)
9609 // Only initial marked undo levels should be allowed as first item
9610 // This to workaround a bug with Firefox and the blur event
9611 if (t.data.length === 0 && !l.initial)
9615 t.data.length = t.index + 1;
9616 t.data[t.index++] = l;
9621 // Set initial bookmark use first real undo level
9622 if (t.data.length == 2 && t.data[0].initial)
9623 t.data[0].bookmark = b;
9625 t.onAdd.dispatch(t, l);
9628 //console.dir(t.data);
9634 var t = this, ed = t.editor, l = l, i;
9642 // If undo on last index then take snapshot
9643 if (t.index == t.data.length && t.index > 1) {
9653 l = t.data[--t.index];
9654 ed.setContent(l.content, {format : 'raw'});
9655 ed.selection.moveToBookmark(l.bookmark);
9657 t.onUndo.dispatch(t, l);
9664 var t = this, ed = t.editor, l = null;
9666 if (t.index < t.data.length - 1) {
9667 l = t.data[++t.index];
9668 ed.setContent(l.content, {format : 'raw'});
9669 ed.selection.moveToBookmark(l.bookmark);
9671 t.onRedo.dispatch(t, l);
9677 clear : function() {
9683 t.add({initial : true});
9686 hasUndo : function() {
9687 return this.index != 0 || this.typing;
9690 hasRedo : function() {
9691 return this.index < this.data.length - 1;
9695 /* file:jscripts/tiny_mce/classes/ForceBlocks.js */
9699 var Event, isIE, isGecko, isOpera, each, extend;
9701 Event = tinymce.dom.Event;
9702 isIE = tinymce.isIE;
9703 isGecko = tinymce.isGecko;
9704 isOpera = tinymce.isOpera;
9705 each = tinymce.each;
9706 extend = tinymce.extend;
9708 tinymce.create('tinymce.ForceBlocks', {
9709 ForceBlocks : function(ed) {
9710 var t = this, s = ed.settings, elm;
9714 elm = (s.forced_root_block || 'p').toLowerCase();
9715 s.element = elm.toUpperCase();
9717 ed.onPreInit.add(t.setup, t);
9719 t.reOpera = new RegExp('(\\u00a0| | )<\/' + elm + '>', 'gi');
9720 t.rePadd = new RegExp('<p( )([^>]+)><\\\/p>|<p( )([^>]+)\\\/>|<p( )([^>]+)>\\s+<\\\/p>|<p><\\\/p>|<p\\\/>|<p>\\s+<\\\/p>'.replace(/p/g, elm), 'gi');
9721 t.reNbsp2BR1 = new RegExp('<p( )([^>]+)>[\\s\\u00a0]+<\\\/p>|<p>[\\s\\u00a0]+<\\\/p>'.replace(/p/g, elm), 'gi');
9722 t.reNbsp2BR2 = new RegExp('<p( )([^>]+)>( | )<\\\/p>|<p>( | )<\\\/p>'.replace(/p/g, elm), 'gi');
9723 t.reBR2Nbsp = new RegExp('<p( )([^>]+)>\\s*<br \\\/>\\s*<\\\/p>|<p>\\s*<br \\\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi');
9724 t.reTrailBr = new RegExp('\\s*<br \\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi');
9726 function padd(ed, o) {
9728 o.content = o.content.replace(t.reOpera, '</' + elm + '>');
9730 o.content = o.content.replace(t.rePadd, '<' + elm + '$1$2$3$4$5$6>\u00a0</' + elm + '>');
9732 if (!isIE && !isOpera && o.set) {
9733 // Use instead of BR in padded paragraphs
9734 o.content = o.content.replace(t.reNbsp2BR1, '<' + elm + '$1$2><br /></' + elm + '>');
9735 o.content = o.content.replace(t.reNbsp2BR2, '<' + elm + '$1$2><br /></' + elm + '>');
9737 o.content = o.content.replace(t.reBR2Nbsp, '<' + elm + '$1$2>\u00a0</' + elm + '>');
9738 o.content = o.content.replace(t.reTrailBr, '</' + elm + '>');
9742 ed.onBeforeSetContent.add(padd);
9743 ed.onPostProcess.add(padd);
9745 if (s.forced_root_block) {
9746 ed.onInit.add(t.forceRoots, t);
9747 ed.onSetContent.add(t.forceRoots, t);
9748 ed.onBeforeGetContent.add(t.forceRoots, t);
9752 setup : function() {
9753 var t = this, ed = t.editor, s = ed.settings;
9755 // Force root blocks when typing and when getting output
9756 if (s.forced_root_block) {
9757 ed.onKeyUp.add(t.forceRoots, t);
9758 ed.onPreProcess.add(t.forceRoots, t);
9761 if (s.force_br_newlines) {
9762 // Force IE to produce BRs on enter
9764 ed.onKeyPress.add(function(ed, e) {
9765 var n, s = ed.selection;
9767 if (e.keyCode == 13 && s.getNode().nodeName != 'LI') {
9768 s.setContent('<br id="__" /> ', {format : 'raw'});
9769 n = ed.dom.get('__');
9770 n.removeAttribute('id');
9773 return Event.cancel(e);
9781 if (!isIE && s.force_p_newlines) {
9782 /* ed.onPreProcess.add(function(ed, o) {
9783 each(ed.dom.select('br', o.node), function(n) {
9784 var p = n.parentNode;
9786 // Replace <p><br /></p> with <p> </p>
9787 if (p && p.nodeName == 'p' && (p.childNodes.length == 1 || p.lastChild == n)) {
9788 p.replaceChild(ed.getDoc().createTextNode('\u00a0'), n);
9793 ed
.onKeyPress
.add(function(ed
, e
) {
9794 if (e
.keyCode
== 13 && !e
.shiftKey
) {
9795 if (!t
.insertPara(e
))
9801 ed
.onKeyDown
.add(function(ed
, e
) {
9802 if ((e
.keyCode
== 8 || e
.keyCode
== 46) && !e
.shiftKey
)
9803 t
.backspaceDelete(e
, e
.keyCode
== 8);
9808 function ren(rn
, na
) {
9809 var ne
= ed
.dom
.create(na
);
9811 each(rn
.attributes
, function(a
) {
9812 if (a
.specified
&& a
.nodeValue
)
9813 ne
.setAttribute(a
.nodeName
.toLowerCase(), a
.nodeValue
);
9816 each(rn
.childNodes
, function(n
) {
9817 ne
.appendChild(n
.cloneNode(true));
9820 rn
.parentNode
.replaceChild(ne
, rn
);
9825 // Replaces IE:s auto generated paragraphs with the specified element name
9826 if (isIE
&& s
.element
!= 'P') {
9827 ed
.onKeyPress
.add(function(ed
, e
) {
9828 t
.lastElm
= ed
.selection
.getNode().nodeName
;
9831 ed
.onKeyUp
.add(function(ed
, e
) {
9832 var bl
, sel
= ed
.selection
, n
= sel
.getNode(), b
= ed
.getBody();
9834 if (b
.childNodes
.length
=== 1 && n
.nodeName
== 'P') {
9835 n
= ren(n
, s
.element
);
9839 } else if (e
.keyCode
== 13 && !e
.shiftKey
&& t
.lastElm
!= 'P') {
9840 bl
= ed
.dom
.getParent(n
, 'P');
9851 find : function(n
, t
, s
) {
9852 var ed
= this.editor
, w
= ed
.getDoc().createTreeWalker(n
, 4, null, false), c
= -1;
9854 while (n
= w
.nextNode()) {
9858 if (t
== 0 && n
== s
)
9862 if (t
== 1 && c
== s
)
9869 forceRoots : function(ed
, e
) {
9870 var t
= this, ed
= t
.editor
, b
= ed
.getBody(), d
= ed
.getDoc(), se
= ed
.selection
, s
= se
.getSel(), r
= se
.getRng(), si
= -2, ei
, so
, eo
, tr
, c
= -0xFFFFFF;
9871 var nx
, bl
, bp
, sp
, le
, nl
= b
.childNodes
, i
;
9873 // Fix for bug #1863847
9874 if (e
&& e
.keyCode
== 13)
9877 // Wrap non blocks into blocks
9878 for (i
= nl
.length
- 1; i
>= 0; i
--) {
9881 // Is text or non block element
9882 if (nx
.nodeType
== 3 || (!t
.dom
.isBlock(nx
) && nx
.nodeType
!= 8)) {
9884 // Create new block but ignore whitespace
9885 if (nx
.nodeType
!= 3 || /[^\s]/g.test(nx
.nodeValue
)) {
9887 if (si
== -2 && r
) {
9889 // If element is inside body, might not be the case in contentEdiable mode
9890 if (ed
.dom
.getParent(r
.startContainer
, function(e
) {return e
=== b
;})) {
9893 si
= t
.find(b
, 0, r
.startContainer
);
9894 ei
= t
.find(b
, 0, r
.endContainer
);
9897 tr
= d
.body
.createTextRange();
9898 tr
.moveToElementText(b
);
9900 bp
= tr
.move('character', c
) * -1;
9904 sp
= tr
.move('character', c
) * -1;
9908 le
= (tr
.move('character', c
) * -1) - sp
;
9915 bl
= ed
.dom
.create(ed
.settings
.forced_root_block
);
9916 bl
.appendChild(nx
.cloneNode(1));
9917 nx
.parentNode
.replaceChild(bl
, nx
);
9920 if (bl
.hasChildNodes())
9921 bl
.insertBefore(nx
, bl
.firstChild
);
9926 bl
= null; // Time to create new block
9929 // Restore selection
9932 bl
= b
.getElementsByTagName(ed
.settings
.element
)[0];
9933 r
= d
.createRange();
9935 // Select last location or generated block
9937 r
.setStart(t
.find(b
, 1, si
), so
);
9941 // Select last location or generated block
9943 r
.setEnd(t
.find(b
, 1, ei
), eo
);
9948 s
.removeAllRanges();
9953 r
= s
.createRange();
9954 r
.moveToElementText(b
);
9956 r
.moveStart('character', si
);
9957 r
.moveEnd('character', ei
);
9966 getParentBlock : function(n
) {
9969 return d
.getParent(n
, d
.isBlock
);
9972 insertPara : function(e
) {
9973 var t
= this, ed
= t
.editor
, dom
= ed
.dom
, d
= ed
.getDoc(), se
= ed
.settings
, s
= ed
.selection
.getSel(), r
= s
.getRangeAt(0), b
= d
.body
;
9974 var rb
, ra
, dir
, sn
, so
, en
, eo
, sb
, eb
, bn
, bef
, aft
, sc
, ec
, n
, vp
= dom
.getViewPort(ed
.getWin()), y
, ch
;
9976 function isEmpty(n
) {
9978 n
= n
.replace(/<(img|hr|table)/gi, '-'); // Keep these convert them to - chars
9979 n
= n
.replace(/<[^>]+>/g, ''); // Remove all tags
9981 return n
.replace(/[ \t\r\n]+/g, '') == '';
9984 // If root blocks are forced then use Operas default behavior since it's really good
9985 // Removed due to bug: #1853816
9986 // if (se.forced_root_block && isOpera)
9989 // Setup before range
9990 rb
= d
.createRange();
9992 // If is before the first block element and in body, then move it into first block element
9993 rb
.setStart(s
.anchorNode
, s
.anchorOffset
);
9996 // Setup after range
9997 ra
= d
.createRange();
9999 // If is before the first block element and in body, then move it into first block element
10000 ra
.setStart(s
.focusNode
, s
.focusOffset
);
10003 // Setup start/end points
10004 dir
= rb
.compareBoundaryPoints(rb
.START_TO_END
, ra
) < 0;
10005 sn
= dir
? s
.anchorNode
: s
.focusNode
;
10006 so
= dir
? s
.anchorOffset
: s
.focusOffset
;
10007 en
= dir
? s
.focusNode
: s
.anchorNode
;
10008 eo
= dir
? s
.focusOffset
: s
.anchorOffset
;
10010 // If selection is in empty table cell
10011 if (sn
=== en
&& /^(TD|TH)$/.test(sn
.nodeName
)) {
10012 dom
.remove(sn
.firstChild
); // Remove BR
10014 // Create two new block elements
10015 ed
.dom
.add(sn
, se
.element
, null, '<br />');
10016 aft
= ed
.dom
.add(sn
, se
.element
, null, '<br />');
10018 // Move caret into the last one
10019 r
= d
.createRange();
10020 r
.selectNodeContents(aft
);
10022 ed
.selection
.setRng(r
);
10027 // If the caret is in an invalid location in FF we need to move it into the first block
10028 if (sn
== b
&& en
== b
&& b
.firstChild
&& ed
.dom
.isBlock(b
.firstChild
)) {
10029 sn
= en
= sn
.firstChild
;
10031 rb
= d
.createRange();
10032 rb
.setStart(sn
, 0);
10033 ra
= d
.createRange();
10034 ra
.setStart(en
, 0);
10037 // Never use body as start or end node
10038 sn
= sn
.nodeName
== "HTML" ? d
.body
: sn
; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes
10039 sn
= sn
.nodeName
== "BODY" ? sn
.firstChild
: sn
;
10040 en
= en
.nodeName
== "HTML" ? d
.body
: en
; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes
10041 en
= en
.nodeName
== "BODY" ? en
.firstChild
: en
;
10043 // Get start and end blocks
10044 sb
= t
.getParentBlock(sn
);
10045 eb
= t
.getParentBlock(en
);
10046 bn
= sb
? sb
.nodeName
: se
.element
; // Get block name to create
10048 // Return inside list use default browser behavior
10049 if (t
.dom
.getParent(sb
, function(n
) { return /OL|UL|PRE/.test(n
.nodeName
); }))
10052 // If caption or absolute layers then always generate new blocks within
10053 if (sb
&& (sb
.nodeName
== 'CAPTION' || /absolute|relative|static/gi.test(sb
.style
.position
))) {
10058 // If caption or absolute layers then always generate new blocks within
10059 if (eb
&& (eb
.nodeName
== 'CAPTION' || /absolute|relative|static/gi.test(eb
.style
.position
))) {
10065 if (/(TD|TABLE|TH|CAPTION)/.test(bn
) || (sb
&& bn
== "DIV" && /left|right/gi.test(sb
.style
.cssFloat
))) {
10070 // Setup new before and after blocks
10071 bef
= (sb
&& sb
.nodeName
== bn
) ? sb
.cloneNode(0) : ed
.dom
.create(bn
);
10072 aft
= (eb
&& eb
.nodeName
== bn
) ? eb
.cloneNode(0) : ed
.dom
.create(bn
);
10074 // Remove id from after clone
10075 aft
.removeAttribute('id');
10077 // Is header and cursor is at the end, then force paragraph under
10078 if (/^(H[1-6])$/.test(bn
) && sn
.nodeValue
&& so
== sn
.nodeValue
.length
)
10079 aft
= ed
.dom
.create(se
.element
);
10081 // Find start chop node
10084 if (n
== b
|| n
.nodeType
== 9 || t
.dom
.isBlock(n
) || /(TD|TABLE|TH|CAPTION)/.test(n
.nodeName
))
10088 } while ((n
= n
.previousSibling
? n
.previousSibling
: n
.parentNode
));
10090 // Find end chop node
10093 if (n
== b
|| n
.nodeType
== 9 || t
.dom
.isBlock(n
) || /(TD|TABLE|TH|CAPTION)/.test(n
.nodeName
))
10097 } while ((n
= n
.nextSibling
? n
.nextSibling
: n
.parentNode
));
10099 // Place first chop part into before block element
10100 if (sc
.nodeName
== bn
)
10101 rb
.setStart(sc
, 0);
10103 rb
.setStartBefore(sc
);
10106 bef
.appendChild(rb
.cloneContents() || d
.createTextNode('')); // Empty text node needed for Safari
10108 // Place secnd chop part within new block element
10110 ra
.setEndAfter(ec
);
10112 //console.debug(s.focusNode, s.focusOffset);
10115 ra
.setStart(en
, eo
);
10116 aft
.appendChild(ra
.cloneContents() || d
.createTextNode('')); // Empty text node needed for Safari
10118 // Create range around everything
10119 r
= d
.createRange();
10120 if (!sc
.previousSibling
&& sc
.parentNode
.nodeName
== bn
) {
10121 r
.setStartBefore(sc
.parentNode
);
10123 if (rb
.startContainer
.nodeName
== bn
&& rb
.startOffset
== 0)
10124 r
.setStartBefore(rb
.startContainer
);
10126 r
.setStart(rb
.startContainer
, rb
.startOffset
);
10129 if (!ec
.nextSibling
&& ec
.parentNode
.nodeName
== bn
)
10130 r
.setEndAfter(ec
.parentNode
);
10132 r
.setEnd(ra
.endContainer
, ra
.endOffset
);
10134 // Delete and replace it with new block elements
10135 r
.deleteContents();
10138 ed
.getWin().scrollTo(0, vp
.y
);
10140 // Never wrap blocks in blocks
10141 if (bef
.firstChild
&& bef
.firstChild
.nodeName
== bn
)
10142 bef
.innerHTML
= bef
.firstChild
.innerHTML
;
10144 if (aft
.firstChild
&& aft
.firstChild
.nodeName
== bn
)
10145 aft
.innerHTML
= aft
.firstChild
.innerHTML
;
10147 // Padd empty blocks
10149 bef
.innerHTML
= '<br />';
10152 aft
.innerHTML
= isOpera
? ' ' : '<br />'; // Extra space for Opera so that the caret can move there
10154 // Opera needs this one backwards for older versions
10155 if (isOpera
&& parseFloat(opera
.version()) < 9.5) {
10167 function first(n
) {
10168 return d
.createTreeWalker(n
, NodeFilter
.SHOW_TEXT
, null, false).nextNode() || n
;
10171 // Move cursor and scroll into view
10172 r
= d
.createRange();
10173 r
.selectNodeContents(isGecko
? first(aft
) : aft
);
10175 s
.removeAllRanges();
10178 // scrollIntoView seems to scroll the parent window in most browsers now including FF 3.0b4 so it's time to stop using it and do it our selfs
10179 y
= ed
.dom
.getPos(aft
).y
;
10180 ch
= aft
.clientHeight
;
10182 // Is element within viewport
10183 if (y
< vp
.y
|| y
+ ch
> vp
.y
+ vp
.h
) {
10184 ed
.getWin().scrollTo(0, y
< vp
.y
? y
: y
- vp
.h
+ 25); // Needs to be hardcoded to roughly one line of text if a huge text block is broken into two blocks
10185 //console.debug('SCROLL!', 'vp.y: ' + vp.y, 'y' + y, 'vp.h' + vp.h, 'clientHeight' + aft.clientHeight, 'yyy: ' + (y < vp.y ? y : y - vp.h + aft.clientHeight));
10191 backspaceDelete : function(e
, bs
) {
10192 var t
= this, ed
= t
.editor
, b
= ed
.getBody(), n
, se
= ed
.selection
, r
= se
.getRng(), sc
= r
.startContainer
, n
, w
, tn
;
10194 // The caret sometimes gets stuck in Gecko if you delete empty paragraphs
10195 // This workaround removes the element by hand and moves the caret to the previous element
10196 if (sc
&& ed
.dom
.isBlock(sc
) && !/^(TD|TH)$/.test(sc
.nodeName
) && bs
) {
10197 if (sc
.childNodes
.length
== 0 || (sc
.childNodes
.length
== 1 && sc
.firstChild
.nodeName
== 'BR')) {
10198 // Find previous block element
10200 while ((n
= n
.previousSibling
) && !ed
.dom
.isBlock(n
)) ;
10203 if (sc
!= b
.firstChild
) {
10204 // Find last text node
10205 w
= ed
.dom
.doc
.createTreeWalker(n
, NodeFilter
.SHOW_TEXT
, null, false);
10206 while (tn
= w
.nextNode())
10209 // Place caret at the end of last text node
10210 r
= ed
.getDoc().createRange();
10211 r
.setStart(n
, n
.nodeValue
? n
.nodeValue
.length
: 0);
10212 r
.setEnd(n
, n
.nodeValue
? n
.nodeValue
.length
: 0);
10215 // Remove the target container
10219 return Event
.cancel(e
);
10224 // Gecko generates BR elements here and there, we don't like those so lets remove them
10225 function handler(e
) {
10230 // A new BR was created in a block element, remove it
10231 if (e
&& e
.parentNode
&& e
.nodeName
== 'BR' && (n
= t
.getParentBlock(e
))) {
10232 pr
= e
.previousSibling
;
10234 Event
.remove(b
, 'DOMNodeInserted', handler
);
10236 // Is there whitespace at the end of the node before then we might need the pesky BR
10237 // to place the caret at a correct location see bug: #2013943
10238 if (pr
&& pr
.nodeType
== 3 && /\s+$/.test(pr
.nodeValue
))
10241 // Only remove BR elements that got inserted in the middle of the text
10242 if (e
.previousSibling
|| e
.nextSibling
)
10247 // Listen for new nodes
10248 Event
._add(b
, 'DOMNodeInserted', handler
);
10251 window
.setTimeout(function() {
10252 Event
._remove(b
, 'DOMNodeInserted', handler
);
10258 /* file:jscripts/tiny_mce/classes/ControlManager.js */
10262 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;
10264 tinymce.create('tinymce.ControlManager', {
10265 ControlManager : function(ed, s) {
10271 t.onAdd = new tinymce.util.Dispatcher(t);
10272 t.onPostRender = new tinymce.util.Dispatcher(t);
10273 t.prefix = s.prefix || ed.id + '_';
10276 t.onPostRender.add(function() {
10277 each(t.controls, function(c) {
10283 get : function(id) {
10284 return this.controls[this.prefix + id] || this.controls[id];
10287 setActive : function(id, s) {
10290 if (c = this.get(id))
10296 setDisabled : function(id, s) {
10299 if (c = this.get(id))
10305 add : function(c) {
10309 t.controls[c.id] = c;
10310 t.onAdd.dispatch(c, t);
10316 createControl : function(n) {
10317 var c, t = this, ed = t.editor;
10319 each(ed.plugins, function(p) {
10320 if (p.createControl) {
10321 c = p.createControl(n, t);
10331 return t.createSeparator();
10334 if (!c && ed.buttons && (c = ed.buttons[n]))
10335 return t.createButton(n, c);
10340 createDropMenu : function(id, s, cc) {
10341 var t = this, ed = t.editor, c, bm, v, cls;
10344 'class' : 'mceDropDown',
10345 constrain : ed.settings.constrain_menus
10348 s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin';
10349 if (v = ed.getParam('skin_variant'))
10350 s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);
10352 id = t.prefix + id;
10353 cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;
10354 c = t.controls[id] = new cls(id, s);
10355 c.onAddItem.add(function(c, o) {
10356 var s = o.settings;
10358 s.title = ed.getLang(s.title, s.title);
10361 s.onclick = function(v) {
10362 ed.execCommand(s.cmd, s.ui || false, s.value);
10367 ed.onRemove.add(function() {
10371 // Fix for bug #1897785, #1898007
10372 if (tinymce.isIE) {
10373 c.onShowMenu.add(function() {
10374 bm = ed.selection.getBookmark(1);
10377 c.onHideMenu.add(function() {
10379 ed.selection.moveToBookmark(bm);
10386 createListBox : function(id, s, cc) {
10387 var t = this, ed = t.editor, cmd, c, cls;
10392 s.title = ed.translate(s.title);
10393 s.scope = s.scope || ed;
10396 s.onselect = function(v) {
10397 ed.execCommand(s.cmd, s.ui || false, v || s.value);
10403 'class' : 'mce_' + id,
10405 control_manager : t
10408 id = t.prefix + id;
10410 if (ed.settings.use_native_selects)
10411 c = new tinymce.ui.NativeListBox(id, s);
10413 cls = cc || t._cls.listbox || tinymce.ui.ListBox;
10414 c = new cls(id, s);
10417 t.controls[id] = c;
10419 // Fix focus problem in Safari
10420 if (tinymce.isWebKit) {
10421 c.onPostRender.add(function(c, n) {
10422 // Store bookmark on mousedown
10423 Event.add(n, 'mousedown', function() {
10424 ed.bookmark = ed.selection.getBookmark('simple');
10427 // Restore on focus, since it might be lost
10428 Event.add(n, 'focus', function() {
10429 ed.selection.moveToBookmark(ed.bookmark);
10430 ed.bookmark = null;
10436 ed.onMouseDown.add(c.hideMenu, c);
10441 createButton : function(id, s, cc) {
10442 var t = this, ed = t.editor, o, c, cls;
10447 s.title = ed.translate(s.title);
10448 s.label = ed.translate(s.label);
10449 s.scope = s.scope || ed;
10451 if (!s.onclick && !s.menu_button) {
10452 s.onclick = function() {
10453 ed.execCommand(s.cmd, s.ui || false, s.value);
10459 'class' : 'mce_' + id,
10460 unavailable_prefix : ed.getLang('unavailable', ''),
10462 control_manager : t
10465 id = t.prefix + id;
10467 if (s.menu_button) {
10468 cls = cc || t._cls.menubutton || tinymce.ui.MenuButton;
10469 c = new cls(id, s);
10470 ed.onMouseDown.add(c.hideMenu, c);
10472 cls = t._cls.button || tinymce.ui.Button;
10473 c = new cls(id, s);
10479 createMenuButton : function(id, s) {
10483 return this.createButton(id, s);
10486 createSplitButton : function(id, s, cc) {
10487 var t = this, ed = t.editor, cmd, c, cls;
10492 s.title = ed.translate(s.title);
10493 s.scope = s.scope || ed;
10496 s.onclick = function(v) {
10497 ed.execCommand(s.cmd, s.ui || false, v || s.value);
10502 s.onselect = function(v) {
10503 ed.execCommand(s.cmd, s.ui || false, v || s.value);
10509 'class' : 'mce_' + id,
10511 control_manager : t
10514 id = t.prefix + id;
10515 cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton;
10516 c = t.add(new cls(id, s));
10517 ed.onMouseDown.add(c.hideMenu, c);
10522 createColorSplitButton : function(id, s, cc) {
10523 var t = this, ed = t.editor, cmd, c, cls, bm;
10528 s.title = ed.translate(s.title);
10529 s.scope = s.scope || ed;
10532 s.onclick = function(v) {
10533 ed.execCommand(s.cmd, s.ui || false, v || s.value);
10538 s.onselect = function(v) {
10539 ed.execCommand(s.cmd, s.ui || false, v || s.value);
10545 'class' : 'mce_' + id,
10546 'menu_class' : ed.getParam('skin') + 'Skin',
10548 more_colors_title : ed.getLang('more_colors')
10551 id = t.prefix + id;
10552 cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton;
10553 c = new cls(id, s);
10554 ed.onMouseDown.add(c.hideMenu, c);
10556 // Remove the menu element when the editor is removed
10557 ed.onRemove.add(function() {
10561 // Fix for bug #1897785, #1898007
10562 if (tinymce.isIE) {
10563 c.onShowMenu.add(function() {
10564 bm = ed.selection.getBookmark(1);
10567 c.onHideMenu.add(function() {
10569 ed.selection.moveToBookmark(bm);
10578 createToolbar : function(id, s, cc) {
10579 var c, t = this, cls;
10581 id = t.prefix + id;
10582 cls = cc || t._cls.toolbar || tinymce.ui.Toolbar;
10583 c = new cls(id, s);
10591 createSeparator : function(cc) {
10592 var cls = cc || this._cls.separator || tinymce.ui.Separator;
10597 setControlType : function(n, c) {
10598 return this._cls[n.toLowerCase()] = c;
10601 destroy : function() {
10602 each(this.controls, function(c) {
10606 this.controls = null;
10612 /* file:jscripts/tiny_mce/classes/WindowManager.js */
10615 var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;
10617 tinymce.create('tinymce.WindowManager', {
10618 WindowManager : function(ed) {
10622 t.onOpen = new Dispatcher(t);
10623 t.onClose = new Dispatcher(t);
10628 open : function(s, p) {
10629 var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u;
10631 // Default some options
10634 sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window
10635 sh = isOpera ? vp.h : screen.height;
10636 s.name = s.name || 'mc_' + new Date().getTime();
10637 s.width = parseInt(s.width || 320);
10638 s.height = parseInt(s.height || 240);
10639 s.resizable = true;
10640 s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0);
10641 s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0);
10643 p.mce_width = s.width;
10644 p.mce_height = s.height;
10645 p.mce_auto_focus = s.auto_focus;
10651 s.dialogWidth = s.width + 'px';
10652 s.dialogHeight = s.height + 'px';
10653 s.scroll = s.scrollbars || false;
10657 // Build features string
10658 each(s, function(v, k) {
10659 if (tinymce.is(v, 'boolean'))
10660 v = v ? 'yes' : 'no';
10662 if (!/^(name|url)$/.test(k)) {
10664 f += (f ? ';' : '') + k + ':' + v;
10666 f += (f ? ',' : '') + k + '=' + v;
10672 t.onOpen.dispatch(t, s, p);
10674 u = s.url || s.file;
10675 if (tinymce.relaxedDomain)
10676 u += (u.indexOf('?') == -1 ? '?' : '&') + 'mce_rdomain=' + tinymce.relaxedDomain;
10678 u = tinymce._addVer(u);
10683 window.showModalDialog(u, window, f);
10685 w = window.open(u, s.name, f);
10691 alert(t.editor.getLang('popup_blocked'));
10694 close : function(w) {
10696 this.onClose.dispatch(this);
10699 createInstance : function(cl, a, b, c, d, e) {
10700 var f = tinymce.resolve(cl);
10702 return new f(a, b, c, d, e);
10705 confirm : function(t, cb, s, w) {
10708 cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t))));
10711 alert : function(tx, cb, s, w) {
10715 w.alert(t._decode(t.editor.getLang(tx, tx)));
10721 // Internal functions
10723 _decode : function(s) {
10724 return tinymce.DOM.decode(s).replace(/\\n/g, '\n');