Use key_name instead of link_id
[Melange.git] / app / tiny_mce / tiny_mce_src.js
blobd1a9b1f71b780585027365adb94a985a207a0902
2 /* file:jscripts/tiny_mce/classes/tinymce.js */
4 var tinymce = {
5 majorVersion : '3',
6 minorVersion : '1.1',
7 releaseDate : '2008-08-18',
9 _init : function() {
10 var t = this, d = document, w = window, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
12 // Browser checks
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;
26 return;
29 // Get suffix and base
30 t.suffix = '';
32 // If base element found, add that infront of baseURL
33 nl = d.getElementsByTagName('base');
34 for (i=0; i<nl.length; i++) {
35 if (v = nl[i].href) {
36 // Host only value like http://site.com or http://site.com:8008
37 if (/^https?:\/\/[^\/]+$/.test(v))
38 v += '/';
40 base = v ? v.match(/.*\//)[0] : ''; // Get only directory
44 function getBase(n) {
45 if (n.src && /tiny_mce(|_dev|_src|_gzip|_jquery|_prototype).js/.test(n.src)) {
46 if (/_(src|dev)\.js/g.test(n.src))
47 t.suffix = '_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;
58 return t.baseURL;
61 return null;
64 // Check document
65 nl = d.getElementsByTagName('script');
66 for (i=0; i<nl.length; i++) {
67 if (getBase(nl[i]))
68 return;
71 // Check head
72 n = d.getElementsByTagName('head')[0];
73 if (n) {
74 nl = n.getElementsByTagName('script');
75 for (i=0; i<nl.length; i++) {
76 if (getBase(nl[i]))
77 return;
81 return;
84 is : function(o, t) {
85 var n = typeof(o);
87 if (!t)
88 return n != 'undefined';
90 if (t == 'array' && (o instanceof Array))
91 return true;
93 return n == t;
96 // #if !jquery
98 each : function(o, cb, s) {
99 var n, l;
101 if (!o)
102 return 0;
104 s = s || o;
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)
110 return 0;
112 } else {
113 // Hashtables
114 for (n in o) {
115 if (o.hasOwnProperty(n)) {
116 if (cb.call(s, o[n], n, o) === false)
117 return 0;
122 return 1;
125 map : function(a, f) {
126 var o = [];
128 tinymce.each(a, function(v) {
129 o.push(f(v));
132 return o;
135 grep : function(a, f) {
136 var o = [];
138 tinymce.each(a, function(v) {
139 if (!f || f(v))
140 o.push(v);
143 return o;
146 inArray : function(a, v) {
147 var i, l;
149 if (a) {
150 for (i = 0, l = a.length; i < l; i++) {
151 if (a[i] === v)
152 return i;
156 return -1;
159 extend : function(o, e) {
160 var i, a = arguments;
162 for (i=1; i<a.length; i++) {
163 e = a[i];
165 tinymce.each(e, function(v, n) {
166 if (typeof(v) !== 'undefined')
167 o[n] = v;
171 return o;
174 trim : function(s) {
175 return (s ? '' + s : '').replace(/^\s*|\s*$/g, '');
178 // #endif
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
191 if (ns[cn])
192 return;
194 // Make pure static class
195 if (s[2] == 'static') {
196 ns[cn] = p;
198 if (this.onCreate)
199 this.onCreate(s[2], s[3], ns[cn]);
201 return;
204 // Create default constructor
205 if (!p[cn]) {
206 p[cn] = function() {};
207 de = 1;
210 // Add constructor and methods
211 ns[cn] = p[cn];
212 t.extend(ns[cn].prototype, p);
214 // Extend
215 if (s[5]) {
216 sp = t.resolve(s[5]).prototype;
217 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name
219 // Extend constructor
220 c = ns[cn];
221 if (de) {
222 // Add passthrough constructor
223 ns[cn] = function() {
224 return sp[scn].apply(this, arguments);
226 } else {
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];
235 // Add super methods
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
243 if (sp[n]) {
244 ns[cn].prototype[n] = function() {
245 this.parent = sp[n];
246 return f.apply(this, arguments);
248 } else {
249 if (n != cn)
250 ns[cn].prototype[n] = f;
255 // Add static methods
256 t.each(p['static'], function(f, n) {
257 ns[cn][n] = f;
260 if (this.onCreate)
261 this.onCreate(s[2], s[3], ns[cn].prototype);
264 walk : function(o, f, n, s) {
265 s = s || this;
267 if (o) {
268 if (n)
269 o = o[n];
271 tinymce.each(o, function(o, i) {
272 if (f.call(s, o, i, n) === false)
273 return false;
275 tinymce.walk(o, f, n, s);
280 createNS : function(n, o) {
281 var i, v;
283 o = o || window;
285 n = n.split('.');
286 for (i=0; i<n.length; i++) {
287 v = n[i];
289 if (!o[v])
290 o[v] = {};
292 o = o[v];
295 return o;
298 resolve : function(n, o) {
299 var i, l;
301 o = o || window;
303 n = n.split('.');
304 for (i=0, l = n.length; i<l; i++) {
305 o = o[n[i]];
307 if (!o)
308 break;
311 return o;
314 addUnload : function(f, s) {
315 var t = this, w = window;
317 f = {func : f, scope : s || this};
319 if (!t.unloads) {
320 function unload() {
321 var li = t.unloads, o, n;
323 if (li) {
324 // Call unload handlers
325 for (n in li) {
326 o = li[n];
328 if (o && o.func)
329 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy
332 // Detach unload function
333 if (w.detachEvent) {
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() {
349 var d = document;
351 // Is there things still loading, then do some magic
352 if (d.readyState == 'interactive') {
353 function stop() {
354 // Prevent memory leak
355 d.detachEvent('onstop', stop);
357 // Call unload handler
358 unload();
360 d = null;
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);
371 }, 0);
375 // Attach unload handler
376 if (w.attachEvent) {
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
383 t.unloads = [f];
384 } else
385 t.unloads.push(f);
387 return f;
390 removeUnload : function(f) {
391 var u = this.unloads, r = null;
393 tinymce.each(u, function(o, i) {
394 if (o && o.func == f) {
395 u.splice(i, 1);
396 r = f;
397 return false;
401 return r;
404 explode : function(s, d) {
405 return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;
408 _addVer : function(u) {
409 var v;
411 if (!this.query)
412 return u;
414 v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;
416 if (u.indexOf('#') == -1)
417 return u + v;
419 return u.replace('#', v + '#');
424 // Required for GZip AJAX loading
425 window.tinymce = tinymce;
427 // Initialize the API
428 tinymce._init();
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', {
439 scope : null,
440 listeners : null,
442 Dispatcher : function(s) {
443 this.scope = s || this;
444 this.listeners = [];
447 add : function(cb, s) {
448 this.listeners.push({cb : cb, scope : s || this.scope});
450 return cb;
453 addToTop : function(cb, s) {
454 this.listeners.unshift({cb : cb, scope : s || this.scope});
456 return cb;
459 remove : function(cb) {
460 var l = this.listeners, o = null;
462 tinymce.each(l, function(c, i) {
463 if (cb == c.cb) {
464 o = cb;
465 l.splice(i, 1);
466 return false;
470 return o;
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++) {
479 c = li[i];
480 s = c.cb.apply(c.scope, a);
482 if (s === false)
483 break;
486 return s;
491 /* file:jscripts/tiny_mce/classes/util/URI.js */
493 (function() {
494 var each = tinymce.each;
496 tinymce.create('tinymce.util.URI', {
497 URI : function(u, s) {
498 var t = this, o, a, b;
500 // Default settings
501 s = t.settings = s || {};
503 // Strange app protocol or local anchor
504 if (/^(mailto|news|javascript|about):/i.test(u) || /^\s*#/.test(u)) {
505 t.source = u;
506 return;
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;
513 // Relative path
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) {
521 var s = u[i];
523 // Zope 3 workaround, they use @@something
524 if (s)
525 s = s.replace(/\(mce_at\)/g, '@@');
527 t[v] = s;
530 if (b = s.base_uri) {
531 if (!t.protocol)
532 t.protocol = b.protocol;
534 if (!t.userInfo)
535 t.userInfo = b.userInfo;
537 if (!t.port && t.host == 'mce_host')
538 t.port = b.port;
540 if (!t.host || t.host == 'mce_host')
541 t.host = b.host;
543 t.source = '';
546 //t.path = t.path || '/';
549 setPath : function(p) {
550 var t = this;
552 p = /^(.*?)\/?(\w+)?$/.exec(p);
554 // Update path parts
555 t.path = p[0];
556 t.directory = p[1];
557 t.file = p[2];
559 // Rebuild source
560 t.source = '';
561 t.getURI();
564 toRelative : function(u) {
565 var t = this, o;
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)
571 return u.getURI();
573 o = t.toRelPath(t.path, u.path);
575 // Add query
576 if (u.query)
577 o += '?' + u.query;
579 // Add anchor
580 if (u.anchor)
581 o += '#' + u.anchor;
583 return o;
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;
595 // Split the paths
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]) {
603 bp = i + 1;
604 break;
609 if (base.length < items.length) {
610 for (i = 0; i < items.length; i++) {
611 if (i >= base.length || base[i] != items[i]) {
612 bp = i + 1;
613 break;
618 if (bp == 1)
619 return path;
621 for (i = 0; i < base.length - (bp - 1); i++)
622 out += "../";
624 for (i = bp - 1; i < items.length; i++) {
625 if (i != bp - 1)
626 out += "/" + items[i];
627 else
628 out += items[i];
631 return out;
634 toAbsPath : function(base, path) {
635 var i, nb = 0, o = [];
637 // Split paths
638 base = base.split('/');
639 path = path.split('/');
641 // Remove empty chunks
642 each(base, function(k) {
643 if (k)
644 o.push(k);
647 base = o;
649 // Merge relURLParts chunks
650 for (i = path.length - 1, o = []; i >= 0; i--) {
651 // Ignore empty or .
652 if (path[i].length == 0 || path[i] == ".")
653 continue;
655 // Is parent
656 if (path[i] == '..') {
657 nb++;
658 continue;
661 // Move up
662 if (nb > 0) {
663 nb--;
664 continue;
667 o.push(path[i]);
670 i = base.length - nb;
672 // If /a/b/c or /
673 if (i <= 0)
674 return '/' + o.reverse().join('/');
676 return '/' + base.slice(0, i).join('/') + '/' + o.reverse().join('/');
679 getURI : function(nh) {
680 var s, t = this;
682 // Rebuild source
683 if (!t.source || nh) {
684 s = '';
686 if (!nh) {
687 if (t.protocol)
688 s += t.protocol + '://';
690 if (t.userInfo)
691 s += t.userInfo + '@';
693 if (t.host)
694 s += t.host;
696 if (t.port)
697 s += ':' + t.port;
700 if (t.path)
701 s += t.path;
703 if (t.query)
704 s += '?' + t.query;
706 if (t.anchor)
707 s += '#' + t.anchor;
709 t.source = s;
712 return t.source;
716 })();
718 /* file:jscripts/tiny_mce/classes/util/Cookie.js */
720 (function() {
721 var each = tinymce.each;
723 tinymce.create('static tinymce.util.Cookie', {
724 getHash : function(n) {
725 var v = this.get(n), h;
727 if (v) {
728 each(v.split('&'), function(v) {
729 v = v.split('=');
730 h = h || {};
731 h[unescape(v[0])] = unescape(v[1]);
735 return h;
738 setHash : function(n, v, e, p, d, s) {
739 var o = '';
741 each(v, function(v, k) {
742 o += (!o ? '' : '&') + escape(k) + '=' + escape(v);
745 this.set(n, o, e, p, d, s);
748 get : function(n) {
749 var c = document.cookie, e, p = n + "=", b;
751 // Strict mode
752 if (!c)
753 return;
755 b = c.indexOf("; " + p);
757 if (b == -1) {
758 b = c.indexOf(p);
760 if (b != 0)
761 return null;
762 } else
763 b += 2;
765 e = c.indexOf(";", b);
767 if (e == -1)
768 e = c.length;
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) {
782 var d = new Date();
784 d.setTime(d.getTime() - 1000);
786 this.set(n, '', d, p, d);
790 })();
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;
798 if (o == null)
799 return 'null';
801 t = typeof o;
803 if (t == 'string') {
804 v = '\bb\tt\nn\ff\rr\""\'\'\\\\';
806 return '"' + o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'])/g, function(a, b) {
807 i = v.indexOf(b);
809 if (i + 1)
810 return '\\' + v.charAt(i + 1);
812 a = b.charCodeAt().toString(16);
814 return '\\u' + '0000'.substring(a.length) + a;
815 }) + '"';
818 if (t == 'object') {
819 if (o instanceof Array) {
820 for (i=0, v = '['; i<o.length; i++)
821 v += (i > 0 ? ',' : '') + s(o[i]);
823 return v + ']';
826 v = '{';
828 for (i in o)
829 v += typeof o[i] != 'function' ? (v.length > 1 ? ',"' : '"') + i + '":' + s(o[i]) : '';
831 return v + '}';
834 return '' + o;
837 parse : function(s) {
838 try {
839 return eval('(' + s + ')');
840 } catch (ex) {
841 // Ignore
847 /* file:jscripts/tiny_mce/classes/util/XHR.js */
849 tinymce.create('static tinymce.util.XHR', {
850 send : function(o) {
851 var x, t, w = window, c = 0;
853 // Default settings
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 || '';
860 function get(s) {
861 x = 0;
863 try {
864 x = new ActiveXObject(s);
865 } catch (ex) {
868 return x;
871 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');
873 if (x) {
874 if (x.overrideMimeType)
875 x.overrideMimeType(o.content_type);
877 x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);
879 if (o.content_type)
880 x.setRequestHeader('Content-Type', o.content_type);
882 x.send(o.data);
884 function ready() {
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);
888 else if (o.error)
889 o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);
891 x = null;
892 } else
893 w.setTimeout(ready, 10);
896 // Syncronous request
897 if (!o.async)
898 return ready();
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 */
909 (function() {
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({
915 }, s);
916 this.count = 0;
919 send : function(o) {
920 var ecb = o.error, scb = o.success;
922 o = extend(this.settings, o);
924 o.success = function(c, x) {
925 c = JSON.parse(c);
927 if (typeof(c) == 'undefined') {
928 c = {
929 error : 'JSON Parse error.'
933 if (c.error)
934 ecb.call(o.error_scope || o.scope, c.error, x);
935 else
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++),
945 method : o.method,
946 params : o.params
949 // JSON content type for Ruby on rails. Bug: #1883287
950 o.content_type = 'application/json';
952 XHR.send(o);
955 'static' : {
956 sendRPC : function(o) {
957 return new tinymce.util.JSONRequest().send(o);
962 }());
963 /* file:jscripts/tiny_mce/classes/dom/DOMUtils.js */
965 (function() {
966 // Shorten names
967 var each = tinymce.each, is = tinymce.is;
968 var isWebKit = tinymce.isWebKit, isIE = tinymce.isIE;
970 tinymce.create('tinymce.dom.DOMUtils', {
971 doc : null,
972 root : null,
973 files : null,
974 listeners : {},
975 pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,
976 cache : {},
977 idPattern : /^#[\w]+$/,
978 elmPattern : /^[\w_*]+$/,
979 elmClassPattern : /^([\w_]*)\.([\w_]+)$/,
980 props : {
981 "for" : "htmlFor",
982 "class" : "className",
983 className : "className",
984 checked : "checked",
985 disabled : "disabled",
986 maxlength : "maxLength",
987 readonly : "readOnly",
988 selected : "selected",
989 value : "value"
992 DOMUtils : function(d, s) {
993 var t = this;
995 t.doc = d;
996 t.win = window;
997 t.files = {};
998 t.cssFlicker = false;
999 t.counter = 0;
1000 t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat";
1001 t.stdMode = d.documentMode === 8;
1003 this.settings = s = tinymce.extend({
1004 keep_values : false,
1005 hex_colors : 1,
1006 process_html : 1
1007 }, s);
1009 // Fix IE6SP2 flicker and check it failed for pre SP2
1010 if (tinymce.isIE6) {
1011 try {
1012 d.execCommand('BackgroundImageCache', false, true);
1013 } catch (e) {
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) {
1028 var d, b;
1030 w = !w ? this.win : w;
1031 d = w.document;
1032 b = this.boxModel ? d.documentElement : d.body;
1034 // Returns viewport size excluding scrollbars
1035 return {
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;
1046 e = t.get(e);
1047 p = t.getPos(e);
1048 sr = t.getSize(e);
1050 return {
1051 x : p.x,
1052 y : p.y,
1053 w : sr.w,
1054 h : sr.h
1058 getSize : function(e) {
1059 var t = this, w, h;
1061 e = t.get(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)
1067 w = 0;
1069 // Non pixel value, then force offset/clientWidth
1070 if (h.indexOf('px') === -1)
1071 h = 0;
1073 return {
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;
1082 n = this.get(n);
1084 if (se.strict_root)
1085 r = r || this.getRoot();
1087 // Wrap node name as func
1088 if (is(f, 'string')) {
1089 na = f.toUpperCase();
1091 f = function(n) {
1092 var s = false;
1094 // Any element
1095 if (n.nodeType == 1 && na === '*') {
1096 s = true;
1097 return false;
1100 each(na.split(','), function(v) {
1101 if (n.nodeType == 1 && ((se.strict && n.nodeName.toUpperCase() == v) || n.nodeName.toUpperCase() == v)) {
1102 s = true;
1103 return false; // Break loop
1107 return s;
1111 while (n) {
1112 if (n == r)
1113 return null;
1115 if (f(n))
1116 return n;
1118 n = n.parentNode;
1121 return null;
1124 get : function(e) {
1125 var n;
1127 if (e && this.doc && typeof(e) == 'string') {
1128 n = e;
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];
1136 return e;
1139 // #if !jquery
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
1151 if (s != t.doc) {
1152 i = s.id;
1153 s.id = '_mc_tmp';
1154 pa = '#_mc_tmp ' + pa;
1157 // Select elements
1158 l = tinymce.grep(s.querySelectorAll(pa));
1160 // Restore old id
1161 s.id = i;
1163 return l;
1166 if (t.settings.strict) {
1167 function get(s, n) {
1168 return s.getElementsByTagName(n.toLowerCase());
1170 } else {
1171 function get(s, n) {
1172 return s.getElementsByTagName(n);
1176 // Simple element pattern. For example: "p" or "*"
1177 if (t.elmPattern.test(pa)) {
1178 x = get(s, pa);
1180 for (i = 0, l = x.length; i<l; i++)
1181 o.push(x[i]);
1183 return o;
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++) {
1193 n = x[i];
1195 if (n.className && (' ' + n.className + ' ').indexOf(c) !== -1)
1196 o.push(n);
1199 return o;
1202 function collect(n) {
1203 if (!n.mce_save) {
1204 n.mce_save = 1;
1205 o.push(n);
1209 function collectIE(n) {
1210 if (!n.getAttribute('mce_save')) {
1211 n.setAttribute('mce_save', '1');
1212 o.push(n);
1216 function find(n, f, r) {
1217 var i, l, nl = get(r, n);
1219 for (i = 0, l = nl.length; i < l; i++)
1220 f(nl[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) {
1229 collect(n);
1232 return;
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]))
1241 collect(n);
1244 return;
1247 if (!(cs = t.cache[pa])) {
1248 cs = 'x=(function(cf, s) {';
1249 pl = v.split(' ');
1251 each(pl, function(v) {
1252 var p = /^([\w\\*]+)?(?:#([\w\\]+))?(?:\.([\w\\\.]+))?(?:\[\@([\w\\]+)([\^\$\*!]?=)([\w\\]+)\])?(?:\:([\w\\]+))?/i.exec(v);
1254 // Find elements
1255 p[1] = p[1] || '*';
1256 cs += 'find("' + p[1] + '", function(n) {';
1258 // Check id
1259 if (p[2])
1260 cs += 'if (n.id !== "' + p[2] + '") return;';
1262 // Check classes
1263 if (p[3]) {
1264 cs += 'var c = " " + n.className + " ";';
1265 cs += 'if (';
1266 c = '';
1267 each(p[3].split('.'), function(v) {
1268 if (v)
1269 c += (c ? '||' : '') + 'c.indexOf(" ' + v + ' ") === -1';
1271 cs += c + ') return;';
1275 cs += 'cf(n);';
1277 for (i = pl.length - 1; i >= 0; i--)
1278 cs += '}, ' + (i ? 'n' : 's') + ');';
1280 cs += '})';
1282 // Compile CSS pattern function
1283 t.cache[pa] = cs = eval(cs);
1286 // Run selector function
1287 cs(isIE ? collectIE : collect, s);
1290 // Cleanup
1291 each(o, function(n) {
1292 if (isIE)
1293 n.removeAttribute('mce_save');
1294 else
1295 delete n.mce_save;
1298 return o;
1301 // #endif
1303 add : function(p, n, a, h, c) {
1304 var t = this;
1306 return this.run(p, function(p) {
1307 var e, k;
1309 e = is(n, 'string') ? t.doc.createElement(n) : n;
1311 if (a) {
1312 for (k in a) {
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);
1324 if (h) {
1325 if (h.nodeType)
1326 e.appendChild(h);
1327 else
1328 t.setHTML(e, h);
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;
1342 o += '<' + n;
1344 for (k in a) {
1345 if (a.hasOwnProperty(k))
1346 o += ' ' + k + '="' + t.encode(a[k]) + '"';
1349 if (tinymce.is(h))
1350 return o + '>' + h + '</' + n + '>';
1352 return o + ' />';
1355 remove : function(n, k) {
1356 return this.run(n, function(n) {
1357 var p, g;
1359 p = n.parentNode;
1361 if (!p)
1362 return null;
1364 if (k) {
1365 each (n.childNodes, function(c) {
1366 p.insertBefore(c.cloneNode(true), n);
1370 // Fix IE psuedo leak
1371 /* if (isIE) {
1372 p = n.cloneNode(true);
1373 n.outerHTML = '';
1375 return p;
1378 return p.removeChild(n);
1382 // #if !jquery
1384 setStyle : function(n, na, v) {
1385 var t = this;
1387 return t.run(n, function(e) {
1388 var s, i;
1390 s = e.style;
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)))
1399 v += 'px';
1401 switch (na) {
1402 case 'opacity':
1403 // IE specific opacity
1404 if (isIE) {
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 || '';
1413 break;
1415 case 'float':
1416 isIE ? s.styleFloat = v : s.cssFloat = v;
1417 break;
1419 default:
1420 s[na] = 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) {
1430 n = this.get(n);
1432 if (!n)
1433 return false;
1435 // Gecko
1436 if (this.doc.defaultView && c) {
1437 // Remove camelcase
1438 na = na.replace(/[A-Z]/g, function(a){
1439 return '-' + a;
1442 try {
1443 return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);
1444 } catch (ex) {
1445 // Old safari might fail
1446 return null;
1450 // Camelcase it, if needed
1451 na = na.replace(/-(\D)/g, function(a, b){
1452 return b.toUpperCase();
1455 if (na == 'float')
1456 na = isIE ? 'styleFloat' : 'cssFloat';
1458 // IE & Opera
1459 if (n.currentStyle && c)
1460 return n.currentStyle[na];
1462 return n.style[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) {
1482 var t = this;
1484 // Whats the point
1485 if (!e || !n)
1486 return;
1488 // Strict XML mode
1489 if (t.settings.strict)
1490 n = n.toLowerCase();
1492 return this.run(e, function(e) {
1493 var s = t.settings;
1495 switch (n) {
1496 case "style":
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);
1501 else
1502 e.removeAttribute('mce_style', 2);
1505 e.style.cssText = v;
1506 break;
1508 case "class":
1509 e.className = v || ''; // Fix IE null bug
1510 break;
1512 case "src":
1513 case "href":
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);
1521 break;
1523 case "shape":
1524 e.setAttribute('mce_style', v);
1525 break;
1528 if (is(v) && v !== null && v.length !== 0)
1529 e.setAttribute(n, '' + v, 2);
1530 else
1531 e.removeAttribute(n, 2);
1535 setAttribs : function(e, o) {
1536 var t = this;
1538 return this.run(e, function(e) {
1539 each(o, function(v, n) {
1540 t.setAttrib(e, n, v);
1545 // #endif
1547 getAttrib : function(e, n, dv) {
1548 var v, t = this;
1550 e = t.get(e);
1552 if (!e || e.nodeType !== 1)
1553 return false;
1555 if (!is(dv))
1556 dv = '';
1558 // Try the mce variant for these
1559 if (/^(src|href|style|coords|shape)$/.test(n)) {
1560 v = e.getAttribute("mce_" + n);
1562 if (v)
1563 return v;
1566 if (isIE && t.props[n]) {
1567 v = e[t.props[n]];
1568 v = v && v.nodeValue ? v.nodeValue : v;
1571 if (!v)
1572 v = e.getAttribute(n, 2);
1574 if (n === 'style') {
1575 v = v || e.style.cssText;
1577 if (v) {
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, '');
1589 // Handle IE issues
1590 if (isIE) {
1591 switch (n) {
1592 case 'rowspan':
1593 case 'colspan':
1594 // IE returns 1 as default value
1595 if (v === 1)
1596 v = '';
1598 break;
1600 case 'size':
1601 // IE returns +0 as default value for size
1602 if (v === '+0' || v === 20)
1603 v = '';
1605 break;
1607 case 'width':
1608 case 'height':
1609 case 'vspace':
1610 if (v === 0)
1611 v = '';
1613 break;
1615 case 'hspace':
1616 // IE returns -1 as default value
1617 if (v === -1)
1618 v = '';
1620 break;
1622 case 'maxlength':
1623 case 'tabindex':
1624 // IE returns default value
1625 if (v === 32768 || v === 2147483647)
1626 v = '';
1628 break;
1630 case 'compact':
1631 case 'noshade':
1632 if (v === 65535)
1633 return n;
1635 return dv;
1637 case 'shape':
1638 v = v.toLowerCase();
1639 break;
1641 default:
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;
1654 n = t.get(n);
1656 // Use getBoundingClientRect on IE, Opera has it but it's not perfect
1657 if (n && isIE) {
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};
1667 r = n;
1668 while (r) {
1669 x += r.offsetLeft || 0;
1670 y += r.offsetTop || 0;
1671 r = r.offsetParent;
1674 r = n;
1675 while (r) {
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;
1682 r = r.parentNode;
1684 if (r == d.body)
1685 break;
1688 return {x : x, y : y};
1691 parseStyle : function(st) {
1692 var t = this, s = t.settings, o = {};
1694 if (!st)
1695 return o;
1697 function compress(p, s, ot) {
1698 var t, r, b, l;
1700 // Get values and check it it needs compressing
1701 t = o[p + '-top' + s];
1702 if (!t)
1703 return;
1705 r = o[p + '-right' + s];
1706 if (t != r)
1707 return;
1709 b = o[p + '-bottom' + s];
1710 if (r != b)
1711 return;
1713 l = o[p + '-left' + s];
1714 if (b != l)
1715 return;
1717 // Compress
1718 o[ot] = l;
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) {
1726 var t;
1728 t = o[a];
1729 if (!t)
1730 return;
1732 t = o[b];
1733 if (!t)
1734 return;
1736 t = o[c];
1737 if (!t)
1738 return;
1740 // Compress
1741 o[ta] = o[a] + ' ' + o[b] + ' ' + o[c];
1742 delete o[a];
1743 delete o[b];
1744 delete o[c];
1747 st = st.replace(/&(#?[a-z0-9]+);/g, '&$1_MCE_SEMI_'); // Protect entities
1749 each(st.split(';'), function(v) {
1750 var sv, ur = [];
1752 if (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 + ')';});
1755 v = v.split(':');
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) {
1760 return t.toHex(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');
1781 if (isIE) {
1782 // Remove pointless border
1783 if (o.border == 'medium none')
1784 o.border = '';
1787 return o;
1790 serializeStyle : function(o) {
1791 var s = '';
1793 each(o, function(v, k) {
1794 if (k && v) {
1795 if (tinymce.isGecko && k.indexOf('-moz-') === 0)
1796 return;
1798 switch (k) {
1799 case 'color':
1800 case 'background-color':
1801 v = v.toLowerCase();
1802 break;
1805 s += (s ? ' ' : '') + k + ': ' + v + ';';
1809 return s;
1812 loadCSS : function(u) {
1813 var t = this, d = t.doc;
1815 if (!u)
1816 u = '';
1818 each(u.split(','), function(u) {
1819 if (t.files[u])
1820 return;
1822 t.files[u] = true;
1823 t.add(t.select('head')[0], 'link', {rel : 'stylesheet', href : tinymce._addVer(u)});
1827 // #if !jquery
1829 addClass : function(e, c) {
1830 return this.run(e, function(e) {
1831 var o;
1833 if (!c)
1834 return 0;
1836 if (this.hasClass(e, c))
1837 return e.className;
1839 o = this.removeClass(e, c);
1841 return e.className = (o != '' ? (o + ' ') : '') + c;
1845 removeClass : function(e, c) {
1846 var t = this, re;
1848 return t.run(e, function(e) {
1849 var v;
1851 if (t.hasClass(e, c)) {
1852 if (!re)
1853 re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g");
1855 v = e.className.replace(re, ' ');
1857 return e.className = tinymce.trim(v != ' ' ? v : '');
1860 return e.className;
1864 hasClass : function(n, c) {
1865 n = this.get(n);
1867 if (!n || !c)
1868 return false;
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) {
1882 e = this.get(e);
1884 return e.style.display == 'none' || this.getStyle(e, 'display') == 'none';
1887 // #endif
1889 uniqueId : function(p) {
1890 return (!p ? 'mce_' : p) + (this.counter++);
1893 setHTML : function(e, h) {
1894 var t = this;
1896 return this.run(e, function(e) {
1897 var x, i, nl, n, p, x;
1899 h = t.processHTML(h);
1901 if (isIE) {
1902 function set() {
1903 try {
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);
1908 } catch (ex) {
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) {
1922 // Skip br element
1923 if (i)
1924 e.appendChild(n);
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">&nbsp;</p>');
1935 set();
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--) {
1941 n = nl[i];
1943 if (!n.hasChildNodes()) {
1944 if (!n.mce_keep) {
1945 x = 1; // Is broken
1946 break;
1949 n.removeAttribute('mce_keep');
1954 // Time to fix the madness IE left us
1955 if (x) {
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
1962 set();
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--) {
1970 n = nl[i];
1972 // Is it a temp div
1973 if (n.mce_tmp) {
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) {
1979 var v;
1981 if (b !== 'mce_tmp') {
1982 v = n.getAttribute(b);
1984 if (!v && b === 'class')
1985 v = n.className;
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
1996 n.swapNode(p);
2001 } else
2002 e.innerHTML = h;
2004 return h;
2008 processHTML : function(h) {
2009 var t = this, s = t.settings;
2011 if (!s.process_html)
2012 return h;
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>');
2018 } else if (isIE)
2019 h = h.replace(/&apos;/g, '&#39;'); // IE can't handle apos
2021 // Fix some issues
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)) {
2030 function trim(s) {
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, '');
2036 return s;
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
2042 b = trim(b);
2044 // Force type attribute
2045 if (!a)
2046 a = ' type="text/javascript"';
2048 // Wrap contents in a comment
2049 if (b)
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) {
2058 b = trim(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) {
2066 var u = c;
2068 // Tag already got a mce_ version
2069 if (a.indexOf('mce_' + b) != -1)
2070 return m;
2072 if (b == 'style') {
2073 // Why did I need this one?
2074 //if (isIE)
2075 // u = t.serializeStyle(t.parseStyle(u));
2077 // No mce_style for elements with these since they might get resized by the user
2078 if (t._isRes(c))
2079 return m;
2081 if (s.hex_colors) {
2082 u = u.replace(/rgb\([^\)]+\)/g, function(v) {
2083 return t.toHex(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
2107 return h;
2110 getOuterHTML : function(e) {
2111 var d;
2113 e = this.get(e);
2115 if (!e)
2116 return null;
2118 if (isIE)
2119 return e.outerHTML;
2121 d = (e.ownerDocument || this.doc).createElement("body");
2122 d.appendChild(e.cloneNode(true));
2124 return d.innerHTML;
2127 setOuterHTML : function(e, h, d) {
2128 var t = this;
2130 return this.run(e, function(e) {
2131 var n, tp;
2133 e = t.get(e);
2134 d = d || e.ownerDocument || t.doc;
2136 if (isIE && e.nodeType == 1)
2137 e.outerHTML = h;
2138 else {
2139 tp = d.createElement("body");
2140 tp.innerHTML = h;
2142 n = tp.lastChild;
2143 while (n) {
2144 t.insertAfter(n.cloneNode(true), e);
2145 n = n.previousSibling;
2148 t.remove(e);
2153 decode : function(s) {
2154 var e;
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");
2160 e.innerHTML = s;
2162 return !e.firstChild ? s : e.firstChild.nodeValue;
2165 return s;
2168 encode : function(s) {
2169 return s ? ('' + s).replace(/[<>&\"]/g, function (c, b) {
2170 switch (c) {
2171 case '&':
2172 return '&amp;';
2174 case '"':
2175 return '&quot;';
2177 case '<':
2178 return '&lt;';
2180 case '>':
2181 return '&gt;';
2184 return c;
2185 }) : s;
2188 // #if !jquery
2190 insertAfter : function(n, r) {
2191 var t = this;
2193 r = t.get(r);
2195 return this.run(n, function(n) {
2196 var p, ns;
2198 p = r.parentNode;
2199 ns = r.nextSibling;
2201 if (ns)
2202 p.insertBefore(n, ns);
2203 else
2204 p.appendChild(n);
2206 return n;
2210 // #endif
2212 isBlock : function(n) {
2213 if (n.nodeType && n.nodeType !== 1)
2214 return false;
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);
2221 // #if !jquery
2223 replace : function(n, o, k) {
2224 if (is(o, 'array'))
2225 n = n.cloneNode(true);
2227 return this.run(o, function(o) {
2228 if (k) {
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);
2238 o.outerHTML = '';
2239 return n;
2242 return o.parentNode.replaceChild(n, o);
2246 // #endif
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);
2251 function hex(s) {
2252 s = parseInt(s).toString(16);
2254 return s.length > 1 ? s : '0' + s; // 0 -> 00
2257 if (c) {
2258 s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);
2260 return s;
2263 return s;
2266 getClasses : function() {
2267 var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;
2269 if (t.classes)
2270 return t.classes;
2272 function addClasses(s) {
2273 // IE style imports
2274 each(s.imports, function(r) {
2275 addClasses(r);
2278 each(s.cssRules || s.rules, function(r) {
2279 // Real type or fake it on IE
2280 switch (r.type || 1) {
2281 // Rule
2282 case 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))
2289 return;
2291 // Remove everything but class name
2292 ov = v;
2293 v = v.replace(/.*\.([a-z0-9_\-]+).*/i, '$1');
2295 // Filter classes
2296 if (f && !(v = f(v, ov)))
2297 return;
2299 if (!lo[v]) {
2300 cl.push({'class' : v});
2301 lo[v] = 1;
2305 break;
2307 // Import
2308 case 3:
2309 addClasses(r.styleSheet);
2310 break;
2315 try {
2316 each(t.doc.styleSheets, addClasses);
2317 } catch (ex) {
2318 // Ignore
2321 if (cl.length > 0)
2322 t.classes = cl;
2324 return cl;
2327 run : function(e, f, s) {
2328 var t = this, o;
2330 if (t.doc && typeof(e) === 'string')
2331 e = t.doc.getElementById(e);
2333 if (!e)
2334 return false;
2336 s = s || this;
2337 if (!e.nodeType && (e.length || e.length === 0)) {
2338 o = [];
2340 each(e, function(e, i) {
2341 if (e) {
2342 if (typeof(e) == 'string')
2343 e = t.doc.getElementById(e);
2345 o.push(f.call(s, e, i));
2349 return o;
2352 return f.call(s, e);
2355 getAttribs : function(n) {
2356 var o;
2358 n = this.get(n);
2360 if (!n)
2361 return [];
2363 if (isIE) {
2364 o = [];
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});
2375 return o;
2378 return n.attributes;
2381 destroy : function(s) {
2382 var t = this;
2384 t.win = t.doc = t.root = null;
2386 // Manual destroy then remove unload handler
2387 if (!s)
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);
2405 } else
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);
2414 if (c) {
2415 // #FFF -> #FFFFFF
2416 if (!is(c[3]))
2417 c[3] = c[2] = c[1];
2419 return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";
2422 return s;
2428 // Setup page DOM
2429 tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});
2430 })();
2432 /* file:jscripts/tiny_mce/classes/dom/Event.js */
2434 (function() {
2435 // Shorten names
2436 var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event;
2438 tinymce.create('static tinymce.dom.Event', {
2439 inits : [],
2440 events : [],
2442 // #if !jquery
2444 add : function(o, n, f, s) {
2445 var cb, t = this, el = t.events, r;
2447 // Handle array
2448 if (o && o instanceof Array) {
2449 r = [];
2451 each(o, function(o) {
2452 o = DOM.get(o);
2453 r.push(t.add(o, n, f, s));
2456 return r;
2459 o = DOM.get(o);
2461 if (!o)
2462 return;
2464 // Setup event callback
2465 cb = function(e) {
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;
2472 if (!s)
2473 return f(e);
2475 return f.call(s, e);
2478 if (n == 'unload') {
2479 tinymce.unloads.unshift({func : cb});
2480 return cb;
2483 if (n == 'init') {
2484 if (t.domLoaded)
2485 cb();
2486 else
2487 t.inits.push(cb);
2489 return cb;
2492 // Store away listener reference
2493 el.push({
2494 obj : o,
2495 name : n,
2496 func : f,
2497 cfunc : cb,
2498 scope : s
2501 t._add(o, n, cb);
2503 return f;
2506 remove : function(o, n, f) {
2507 var t = this, a = t.events, s = false, r;
2509 // Handle array
2510 if (o && o instanceof Array) {
2511 r = [];
2513 each(o, function(o) {
2514 o = DOM.get(o);
2515 r.push(t.remove(o, n, f));
2518 return r;
2521 o = DOM.get(o);
2523 each(a, function(e, i) {
2524 if (e.obj == o && e.name == n && (!f || (e.func == f || e.cfunc == f))) {
2525 a.splice(i, 1);
2526 t._remove(o, n, e.cfunc);
2527 s = true;
2528 return false;
2532 return s;
2535 clear : function(o) {
2536 var t = this, a = t.events, i, e;
2538 if (o) {
2539 o = DOM.get(o);
2541 for (i = a.length - 1; i >= 0; i--) {
2542 e = a[i];
2544 if (e.obj === o) {
2545 t._remove(e.obj, e.name, e.cfunc);
2546 e.obj = e.cfunc = null;
2547 a.splice(i, 1);
2553 // #endif
2555 cancel : function(e) {
2556 if (!e)
2557 return false;
2559 this.stop(e);
2560 return this.prevent(e);
2563 stop : function(e) {
2564 if (e.stopPropagation)
2565 e.stopPropagation();
2566 else
2567 e.cancelBubble = true;
2569 return false;
2572 prevent : function(e) {
2573 if (e.preventDefault)
2574 e.preventDefault();
2575 else
2576 e.returnValue = false;
2578 return false;
2581 _unload : function() {
2582 var t = Event;
2584 each(t.events, function(e, i) {
2585 t._remove(e.obj, e.name, e.cfunc);
2586 e.obj = e.cfunc = null;
2589 t.events = [];
2590 t = null;
2593 _add : function(o, n, f) {
2594 if (o.attachEvent)
2595 o.attachEvent('on' + n, f);
2596 else if (o.addEventListener)
2597 o.addEventListener(n, f, false);
2598 else
2599 o['on' + n] = f;
2602 _remove : function(o, n, f) {
2603 if (o) {
2604 try {
2605 if (o.detachEvent)
2606 o.detachEvent('on' + n, f);
2607 else if (o.removeEventListener)
2608 o.removeEventListener(n, f, false);
2609 else
2610 o['on' + n] = null;
2611 } catch (ex) {
2612 // Might fail with permission denined on IE so we just ignore that
2617 _pageInit : function() {
2618 var e = Event;
2620 e._remove(window, 'DOMContentLoaded', e._pageInit);
2621 e.domLoaded = true;
2623 each(e.inits, function(c) {
2624 c();
2627 e.inits = [];
2630 _wait : function() {
2631 var t;
2633 // No need since the document is already loaded
2634 if (window.tinyMCE_GZ && tinyMCE_GZ.loaded) {
2635 Event.domLoaded = 1;
2636 return;
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") {
2644 Event._pageInit();
2645 DOM.get("__ie_onload").onreadystatechange = null; // Prevent leak
2648 } else {
2649 Event._add(window, 'DOMContentLoaded', Event._pageInit, Event);
2651 if (isIE || isWebKit) {
2652 t = setInterval(function() {
2653 if (/loaded|complete/.test(document.readyState)) {
2654 clearInterval(t);
2655 Event._pageInit();
2657 }, 10);
2664 // Shorten name
2665 Event = tinymce.dom.Event;
2667 // Dispatch DOM content loaded event for IE and Safari
2668 Event._wait();
2669 tinymce.addUnload(Event._unload);
2670 })();
2672 /* file:jscripts/tiny_mce/classes/dom/Element.js */
2674 (function() {
2675 var each = tinymce.each;
2677 tinymce.create('tinymce.dom.Element', {
2678 Element : function(id, s) {
2679 var t = this, dom, el;
2681 s = s || {};
2682 t.id = id;
2683 t.dom = dom = s.dom || tinymce.DOM;
2684 t.settings = s;
2686 // Only IE leaks DOM references, this is a lot faster
2687 if (!tinymce.isIE)
2688 el = t.dom.get(t.id);
2690 each([
2691 'getPos',
2692 'getRect',
2693 'getParent',
2694 'add',
2695 'setStyle',
2696 'getStyle',
2697 'setStyles',
2698 'setAttrib',
2699 'setAttribs',
2700 'getAttrib',
2701 'addClass',
2702 'removeClass',
2703 'hasClass',
2704 'getOuterHTML',
2705 'setOuterHTML',
2706 'remove',
2707 'show',
2708 'hide',
2709 'isHidden',
2710 'setHTML',
2711 'get'
2712 ], function(k) {
2713 t[k] = function() {
2714 var a = arguments, o;
2716 // Opera fails
2717 if (tinymce.isOpera) {
2718 a = [id];
2720 each(arguments, function(v) {
2721 a.push(v);
2723 } else
2724 Array.prototype.unshift.call(a, el || id);
2726 o = dom[k].apply(dom, a);
2727 t.update(k);
2729 return o;
2734 on : function(n, f, s) {
2735 return tinymce.dom.Event.add(this.id, n, f, s);
2738 getXY : function() {
2739 return {
2740 x : parseInt(this.getStyle('left')),
2741 y : parseInt(this.getStyle('top'))
2745 getSize : function() {
2746 var n = this.dom.get(this.id);
2748 return {
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) {
2778 k = k || '';
2780 // Ignore getters
2781 if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0)
2782 return;
2784 // Remove blocker on remove
2785 if (k == 'remove') {
2786 dom.remove(t.blocker);
2787 return;
2790 if (!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);
2794 } else
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);
2807 })();
2809 /* file:jscripts/tiny_mce/classes/dom/Selection.js */
2811 (function() {
2812 function trimNl(s) {
2813 return s.replace(/[\n\r]+/g, '');
2816 // Shorten names
2817 var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each;
2819 tinymce.create('tinymce.dom.Selection', {
2820 Selection : function(dom, win, serializer) {
2821 var t = this;
2823 t.dom = dom;
2824 t.win = win;
2825 t.serializer = serializer;
2827 // Prevent leaks
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;
2834 s = s || {};
2835 wb = wa = '';
2836 s.get = true;
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();
2845 if (n)
2846 e.appendChild(n);
2847 } else if (is(r.item) || is(r.htmlText))
2848 e.innerHTML = r.item ? r.item(0).outerHTML : r.htmlText;
2849 else
2850 e.innerHTML = r.toString();
2852 // Keep whitespace before and after
2853 if (/^\s/.test(e.innerHTML))
2854 wb = ' ';
2856 if (/\s+$/.test(e.innerHTML))
2857 wa = ' ';
2859 s.getInner = true;
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'};
2868 s.set = true;
2869 h = t.dom.processHTML(h);
2871 if (r.insertNode) {
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
2876 r.deleteContents();
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);
2885 r.setEndAfter(c);
2886 t.setRng(r);
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');
2893 } else {
2894 if (r.item) {
2895 // Delete content and get caret text selection
2896 d.execCommand('Delete', false, null);
2897 r = t.getRng();
2900 r.pasteHTML(h);
2904 getStart : function() {
2905 var t = this, r = t.getRng(), e;
2907 if (isIE) {
2908 if (r.item)
2909 return r.item(0);
2911 r = r.duplicate();
2912 r.collapse(1);
2913 e = r.parentElement();
2915 if (e && e.nodeName == 'BODY')
2916 return e.firstChild;
2918 return e;
2919 } else {
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;
2932 if (isIE) {
2933 if (r.item)
2934 return r.item(0);
2936 r = r.duplicate();
2937 r.collapse(0);
2938 e = r.parentElement();
2940 if (e && e.nodeName == 'BODY')
2941 return e.lastChild;
2943 return e;
2944 } else {
2945 e = r.endContainer;
2947 if (e.nodeName == 'BODY')
2948 return e.lastChild;
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;
2956 sx = vp.x;
2957 sy = vp.y;
2959 // Simple bookmark fast but not as persistent
2960 if (si == 'simple')
2961 return {rng : r, scrollX : sx, scrollY : sy};
2963 // Handle IE
2964 if (isIE) {
2965 // Control selection
2966 if (r.item) {
2967 e = r.item(0);
2969 each(t.dom.select(e.nodeName), function(n, i) {
2970 if (e == n) {
2971 sp = i;
2972 return false;
2976 return {
2977 tag : e.nodeName,
2978 index : sp,
2979 scrollX : sx,
2980 scrollY : sy
2984 // Text selection
2985 tr = t.dom.doc.body.createTextRange();
2986 tr.moveToElementText(ro);
2987 tr.collapse(true);
2988 bp = Math.abs(tr.move('character', c));
2990 tr = r.duplicate();
2991 tr.collapse(true);
2992 sp = Math.abs(tr.move('character', c));
2994 tr = r.duplicate();
2995 tr.collapse(false);
2996 le = Math.abs(tr.move('character', c)) - sp;
2998 return {
2999 start : sp - bp,
3000 length : le,
3001 scrollX : sx,
3002 scrollY : sy
3006 // Handle W3C
3007 e = t.getNode();
3008 s = t.getSel();
3010 if (!s)
3011 return null;
3013 // Image selection
3014 if (e && e.nodeName == 'IMG') {
3015 return {
3016 scrollX : sx,
3017 scrollY : sy
3021 // Text selection
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) {
3027 if (n == sn)
3028 d.start = p;
3030 if (n == en) {
3031 d.end = p;
3032 return d;
3035 p += trimNl(n.nodeValue || '').length;
3038 return null;
3041 // Caret or selection
3042 if (s.anchorNode == s.focusNode && s.anchorOffset == s.focusOffset) {
3043 e = getPos(ro, s.anchorNode, s.focusNode);
3045 if (!e)
3046 return {scrollX : sx, scrollY : sy};
3048 // Count whitespace before
3049 trimNl(s.anchorNode.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;});
3051 return {
3052 start : Math.max(e.start + s.anchorOffset - wb, 0),
3053 end : Math.max(e.end + s.focusOffset - wb, 0),
3054 scrollX : sx,
3055 scrollY : sy,
3056 beg : s.anchorOffset - wb == 0
3058 } else {
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;});
3065 if (!e)
3066 return {scrollX : sx, scrollY : sy};
3068 return {
3069 start : Math.max(e.start + r.startOffset - wb, 0),
3070 end : Math.max(e.end + r.endOffset - wa, 0),
3071 scrollX : sx,
3072 scrollY : sy,
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) {
3085 wa = wb = 0;
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;
3092 p += nvl;
3094 if (p >= sp && !d.startNode) {
3095 o = sp - (p - nvl);
3097 // Fix for odd quirk in FF
3098 if (b.beg && o >= nvl)
3099 continue;
3101 d.startNode = n;
3102 d.startOffset = o + wb;
3105 if (p >= ep) {
3106 d.endNode = n;
3107 d.endOffset = ep - (p - nvl) + wb;
3108 return d;
3112 return null;
3115 if (!b)
3116 return false;
3118 t.win.scrollTo(b.scrollX, b.scrollY);
3120 // Handle explorer
3121 if (isIE) {
3122 // Handle simple
3123 if (r = b.rng) {
3124 try {
3125 r.select();
3126 } catch (ex) {
3127 // Ignore
3130 return true;
3133 t.win.focus();
3135 // Handle control bookmark
3136 if (b.tag) {
3137 r = ro.createControlRange();
3139 each(t.dom.select(b.tag), function(n, i) {
3140 if (i == b.index)
3141 r.addElement(n);
3143 } else {
3144 // Try/catch needed since this operation breaks when TinyMCE is placed in hidden divs/tabs
3145 try {
3146 // Incorrect bookmark
3147 if (b.start < 0)
3148 return true;
3150 r = s.createRange();
3151 r.moveToElementText(ro);
3152 r.collapse(true);
3153 r.moveStart('character', b.start);
3154 r.moveEnd('character', b.length);
3155 } catch (ex2) {
3156 return true;
3160 try {
3161 r.select();
3162 } catch (ex) {
3163 // Needed for some odd IE bug #1843306
3166 return true;
3169 // Handle W3C
3170 if (!s)
3171 return false;
3173 // Handle simple
3174 if (b.rng) {
3175 s.removeAllRanges();
3176 s.addRange(b.rng);
3177 } else {
3178 if (is(b.start) && is(b.end)) {
3179 try {
3180 sd = getPos(ro, b.start, b.end);
3182 if (sd) {
3183 r = t.dom.doc.createRange();
3184 r.setStart(sd.startNode, sd.startOffset);
3185 r.setEnd(sd.endNode, sd.endOffset);
3186 s.removeAllRanges();
3187 s.addRange(r);
3190 if (!tinymce.isOpera)
3191 t.win.focus();
3192 } catch (ex) {
3193 // Ignore
3199 select : function(n, c) {
3200 var t = this, r = t.getRng(), s = t.getSel(), b, fn, ln, d = t.win.document;
3202 function first(n) {
3203 return n ? d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false).nextNode() : null;
3206 function last(n) {
3207 var c, o, w;
3209 if (!n)
3210 return null;
3212 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
3213 while (c = w.nextNode())
3214 o = c;
3216 return o;
3219 if (isIE) {
3220 try {
3221 b = d.body;
3223 if (/^(IMG|TABLE)$/.test(n.nodeName)) {
3224 r = b.createControlRange();
3225 r.addElement(n);
3226 } else {
3227 r = b.createTextRange();
3228 r.moveToElementText(n);
3231 r.select();
3232 } catch (ex) {
3233 // Throws illigal agrument in IE some times
3235 } else {
3236 if (c) {
3237 fn = first(n);
3238 ln = last(n);
3240 if (fn && ln) {
3241 //console.debug(fn, ln);
3242 r = d.createRange();
3243 r.setStart(fn, 0);
3244 r.setEnd(ln, ln.nodeValue.length);
3245 } else
3246 r.selectNode(n);
3247 } else
3248 r.selectNode(n);
3250 t.setRng(r);
3253 return n;
3256 isCollapsed : function() {
3257 var t = this, r = t.getRng(), s = t.getSel();
3259 if (!r || r.item)
3260 return false;
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
3269 if (r.item) {
3270 n = r.item(0);
3271 r = this.win.document.body.createTextRange();
3272 r.moveToElementText(n);
3275 r.collapse(!!b);
3276 t.setRng(r);
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;
3288 try {
3289 if (s)
3290 r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : t.win.document.createRange());
3291 } catch (ex) {
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
3298 if (!r)
3299 r = isIE ? t.win.document.body.createTextRange() : t.win.document.createRange();
3301 return r;
3304 setRng : function(r) {
3305 var s;
3307 if (!isIE) {
3308 s = this.getSel();
3310 if (s) {
3311 s.removeAllRanges();
3312 s.addRange(r);
3314 } else {
3315 try {
3316 r.select();
3317 } catch (ex) {
3318 // Needed for some odd IE bug #1843306
3323 setNode : function(n) {
3324 var t = this;
3326 t.setContent(t.dom.getOuterHTML(n));
3328 return n;
3331 getNode : function() {
3332 var t = this, r = t.getRng(), s = t.getSel(), e;
3334 if (!isIE) {
3335 // Range maybe lost after the editor is made visible again
3336 if (!r)
3337 return t.dom.getRoot();
3339 e = r.commonAncestorContainer;
3341 // Handle selection a image or other control like element such as anchors
3342 if (!r.collapsed) {
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) {
3364 var t = this;
3366 t.win = null;
3368 // Manual destroy then remove unload handler
3369 if (!s)
3370 tinymce.removeUnload(t.destroy);
3374 })();
3376 /* file:jscripts/tiny_mce/classes/dom/XMLWriter.js */
3378 (function() {
3379 tinymce.create('tinymce.dom.XMLWriter', {
3380 node : null,
3382 XMLWriter : function(s) {
3383 // Get XML document
3384 function getXML() {
3385 var i = document.implementation;
3387 if (!i || !i.createDocument) {
3388 // Try IE objects
3389 try {return new ActiveXObject('MSXML2.DOMDocument');} catch (ex) {}
3390 try {return new ActiveXObject('Microsoft.XmlDom');} catch (ex) {}
3391 } else
3392 return i.createDocument('', '', null);
3395 this.doc = getXML();
3397 // Since Opera and WebKit doesn't escape > into &gt; we need to do it our self to normalize the output for all browsers
3398 this.valid = tinymce.isOpera || tinymce.isWebKit;
3400 this.reset();
3403 reset : function() {
3404 var t = this, d = t.doc;
3406 if (d.firstChild)
3407 d.removeChild(d.firstChild);
3409 t.node = d.appendChild(d.createElement("html"));
3412 writeStartElement : function(n) {
3413 var t = this;
3415 t.node = t.node.appendChild(t.doc.createElement(n));
3418 writeAttribute : function(n, v) {
3419 if (this.valid)
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) {
3437 if (this.valid)
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() {
3452 var h;
3454 h = this.doc.xml || new XMLSerializer().serializeToString(this.doc);
3455 h = h.replace(/<\?[^?]+\?>|<html>|<\/html>|<html\/>|<!DOCTYPE[^>]+>/g, '');
3456 h = h.replace(/ ?\/>/g, ' />');
3458 if (this.valid)
3459 h = h.replace(/\%MCGT%/g, '&gt;');
3461 return h;
3465 })();
3467 /* file:jscripts/tiny_mce/classes/dom/StringWriter.js */
3469 (function() {
3470 tinymce.create('tinymce.dom.StringWriter', {
3471 str : null,
3472 tags : null,
3473 count : 0,
3474 settings : null,
3475 indent : null,
3477 StringWriter : function(s) {
3478 this.settings = tinymce.extend({
3479 indent_char : ' ',
3480 indentation : 1
3481 }, s);
3483 this.reset();
3486 reset : function() {
3487 this.indent = '';
3488 this.str = "";
3489 this.tags = [];
3490 this.count = 0;
3493 writeStartElement : function(n) {
3494 this._writeAttributesEnd();
3495 this.writeRaw('<' + n);
3496 this.tags.push(n);
3497 this.inAttr = true;
3498 this.count++;
3499 this.elementCount = this.count;
3502 writeAttribute : function(n, v) {
3503 var t = this;
3505 t.writeRaw(" " + t.encode(n) + '="' + t.encode(v) + '"');
3508 writeEndElement : function() {
3509 var n;
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));
3535 this.count++;
3538 writeCDATA : function(v) {
3539 this._writeAttributesEnd();
3540 this.writeRaw('<![CDATA[' + v + ']]>');
3541 this.count++;
3544 writeComment : function(v) {
3545 this._writeAttributesEnd();
3546 this.writeRaw('<!-- ' + v + '-->');
3547 this.count++;
3550 writeRaw : function(v) {
3551 this.str += v;
3554 encode : function(s) {
3555 return s.replace(/[<>&"]/g, function(v) {
3556 switch (v) {
3557 case '<':
3558 return '&lt;';
3560 case '>':
3561 return '&gt;';
3563 case '&':
3564 return '&amp;';
3566 case '"':
3567 return '&quot;';
3570 return v;
3574 getContent : function() {
3575 return this.str;
3578 _writeAttributesEnd : function(s) {
3579 if (!this.inAttr)
3580 return;
3582 this.inAttr = false;
3584 if (s && this.elementCount == this.count) {
3585 this.writeRaw(' />');
3586 return false;
3589 this.writeRaw('>');
3591 return true;
3595 })();
3597 /* file:jscripts/tiny_mce/classes/dom/Serializer.js */
3599 (function() {
3600 // Shorten names
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) {
3605 var o = [];
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});
3615 return o;
3618 function wildcardToRE(s) {
3619 return s.replace(/([?+*])/g, '.$1');
3622 tinymce.create('tinymce.dom.Serializer', {
3623 Serializer : function(s) {
3624 var t = this;
3626 t.key = 0;
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();
3633 } else {
3634 try {
3635 t.writer = new tinymce.dom.XMLWriter();
3636 } catch (ex) {
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();
3642 // Default settings
3643 t.settings = s = extend({
3644 dom : tinymce.DOM,
3645 valid_nodes : 0,
3646 node_filter : 0,
3647 attr_filter : 0,
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',
3664 indent_char : '\t',
3665 indent_levels : 1,
3666 remove_linebreaks : 1,
3667 remove_redundant_brs : 1
3668 }, s);
3670 t.dom = s.dom;
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])
3689 return e;
3693 return null;
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++) {
3700 n = nl[i];
3701 p = n.parentNode;
3703 if (r.test(p.nodeName)) {
3704 np = prevNode(n, 'LI');
3706 if (!np) {
3707 np = t.dom.create('li');
3708 np.innerHTML = '&nbsp;';
3709 np.appendChild(n);
3710 p.insertBefore(np, p.firstChild);
3711 } else
3712 np.appendChild(n);
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;
3724 if (pa) {
3725 pa2 = pa.cloneNode(false);
3727 pl.push(e);
3728 for (n = e; n = n.parentNode;) {
3729 pl.push(n);
3731 if (n == pa)
3732 break;
3735 tm = pa2;
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));
3740 } else {
3741 n = pl[i].cloneNode(false);
3743 if (i != 0) {
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
3765 if (t.entityLookup)
3766 return;
3768 // Build regex and lookup array
3769 a = s.split(',');
3770 for (i = 0; i < a.length; i += 2) {
3771 v = a[i];
3773 // Don't add default &amp; &quot; etc.
3774 if (v == 34 || v == 38 || v == 60 || v == 62)
3775 continue;
3777 l[String.fromCharCode(a[i])] = a[i + 1];
3779 v = parseInt(a[i]).toString(16);
3780 re += '\\u' + '0000'.substring(v.length) + v;
3783 if (!re) {
3784 t.settings.entity_encoding = 'raw';
3785 return;
3788 t.entitiesRE = new RegExp('[' + re + ']', 'g');
3789 t.entityLookup = l;
3792 setValidChildRules : function(s) {
3793 this.childRules = null;
3794 this.addValidChildRules(s);
3797 addValidChildRules : function(s) {
3798 var t = this, inst, intr, bloc;
3800 if (!s)
3801 return;
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;
3810 s = '';
3811 each(p[1].split('|'), function(v) {
3812 if (s)
3813 s += '|';
3815 switch (v) {
3816 case '%itrans':
3817 v = intr;
3818 break;
3820 case '%itrans_na':
3821 v = intr.substring(2);
3822 break;
3824 case '%istrict':
3825 v = inst;
3826 break;
3828 case '%istrict_na':
3829 v = inst.substring(2);
3830 break;
3832 case '%btrans':
3833 v = bloc;
3834 break;
3836 case '%bstrict':
3837 v = bloc;
3838 break;
3841 s += v;
3843 re = new RegExp('^(' + s.toLowerCase() + ')$', 'i');
3845 each(p[0].split('/'), function(s) {
3846 t.childRules = t.childRules || {};
3847 t.childRules[s] = re;
3851 // Build regex
3852 s = '';
3853 each(t.childRules, function(v, k) {
3854 if (s)
3855 s += '|';
3857 s += 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());
3865 });*/
3868 setRules : function(s) {
3869 var t = this;
3871 t._setup();
3872 t.rules = {};
3873 t.wildRules = [];
3874 t.validElements = {};
3876 return t.addRules(s);
3879 addRules : function(s) {
3880 var t = this, dr;
3882 if (!s)
3883 return;
3885 t._setup();
3887 each(s.split(','), function(s) {
3888 var p = s.split(/\[|\]/), tn = p[0].split('/'), ra, at, wat, va = [];
3890 // Extend with default rules
3891 if (dr)
3892 at = tinymce.extend([], dr.attribs);
3894 // Parse attributes
3895 if (p.length > 1) {
3896 each(p[1].split('|'), function(s) {
3897 var ar = {}, i;
3899 at = at || [];
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
3907 if (s[1] == '!') {
3908 ra = ra || [];
3909 ra.push(s[2]);
3912 // Remove inherited attributes
3913 if (s[1] == '-') {
3914 for (i = 0; i <at.length; i++) {
3915 if (at[i].name == s[2]) {
3916 at.splice(i, 1);
3917 return;
3922 switch (s[3]) {
3923 // Add default attrib values
3924 case '=':
3925 ar.defaultVal = s[4] || '';
3926 break;
3928 // Add forced attrib values
3929 case ':':
3930 ar.forcedVal = s[4];
3931 break;
3933 // Add validation values
3934 case '<':
3935 ar.validVals = s[4].split('?');
3936 break;
3939 if (/[*.?]/.test(s[2])) {
3940 wat = wat || [];
3941 ar.nameRE = new RegExp('^' + wildcardToRE(s[2]) + '$');
3942 wat.push(ar);
3943 } else {
3944 ar.name = s[2];
3945 at.push(ar);
3948 va.push(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
3957 if (dr) {
3958 if (dr.noEmpty)
3959 ru.noEmpty = dr.noEmpty;
3961 if (dr.fullEnd)
3962 ru.fullEnd = dr.fullEnd;
3964 if (dr.padd)
3965 ru.padd = dr.padd;
3968 // Handle prefixes
3969 switch (pr) {
3970 case '-':
3971 ru.noEmpty = true;
3972 break;
3974 case '+':
3975 ru.fullEnd = true;
3976 break;
3978 case '#':
3979 ru.padd = true;
3980 break;
3982 default:
3983 x = 0;
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);
3994 } else {
3995 ru.name = tn[0];
3997 // Store away default rule
3998 if (tn[0] == '@')
3999 dr = ru;
4001 t.rules[s] = ru;
4004 ru.attribs = at;
4006 if (ra)
4007 ru.requiredAttribs = ra;
4009 if (wat) {
4010 // Build valid attributes regexp
4011 s = '';
4012 each(va, function(v) {
4013 if (s)
4014 s += '|';
4016 s += '(' + wildcardToRE(v) + ')';
4018 ru.validAttribsRE = new RegExp('^' + s.toLowerCase() + '$');
4019 ru.wildAttribs = wat;
4024 // Build valid elements regexp
4025 s = '';
4026 each(t.validElements, function(v, k) {
4027 if (s)
4028 s += '|';
4030 if (k != '@')
4031 s += 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;
4043 t._setup();
4045 // Exact match
4046 r = rl[n];
4047 if (r)
4048 return r;
4050 // Try wildcards
4051 rl = t.wildRules;
4052 for (i = 0; i < rl.length; i++) {
4053 if (rl[i].nameRE.test(n))
4054 return rl[i];
4057 return null;
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))
4065 return wa[i];
4068 return null;
4071 serialize : function(n, o) {
4072 var h, t = this;
4074 t._setup();
4075 o = o || {};
4076 o.format = o.format || 'html';
4077 t.processObj = o;
4078 n = n.cloneNode(true);
4079 t.key = '' + (parseInt(t.key) + 1);
4081 // Pre process
4082 if (!o.no_events) {
4083 o.node = n;
4084 t.onPreProcess.dispatch(t, o);
4087 // Serialize HTML DOM into a string
4088 t.writer.reset();
4089 t._serializeNode(n, o.getInner);
4091 // Post process
4092 o.content = t.writer.getContent();
4094 if (!o.no_events)
4095 t.onPostProcess.dispatch(t, o);
4097 t._postProcess(o);
4098 o.node = null;
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
4110 p = t._protect({
4111 content : h,
4112 patterns : [
4113 {pattern : /(<script[^>]*>)(.*?)(<\/script>)/g},
4114 {pattern : /(<style[^>]*>)(.*?)(<\/style>)/g},
4115 {pattern : /(<pre[^>]*>)(.*?)(<\/pre>)/g, encode : 1},
4116 {pattern : /(<!--\[CDATA\[)(.*?)(\]\]-->)/g}
4120 h = p.content;
4122 // Entity encode
4123 if (s.entity_encoding !== 'raw')
4124 h = t._encode(h);
4126 // Use BR instead of &nbsp; padded P elements inside editor and use <p>&nbsp;</p> outside editor
4127 /* if (o.set)
4128 h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p><br /></p>');
4129 else
4130 h = h.replace(/<p>\s+(&nbsp;|&#160;|\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.
4135 if (!o.set) {
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>&#160;</p>' : '<p$1>&nbsp;</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>&nbsp;<\/p>|<p([^>]+)>&nbsp;<\/p>/g, '<p$1>\u00a0</p>');
4168 o.content = h;
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) {
4176 case 1: // Element
4177 if (n.hasAttribute ? n.hasAttribute('mce_bogus') : n.getAttribute('mce_bogus'))
4178 return;
4180 iv = false;
4181 hc = n.hasChildNodes();
4182 nn = n.getAttribute('mce_name') || n.nodeName.toLowerCase();
4184 // Add correct prefix on IE
4185 if (isIE) {
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);
4194 // Check if valid
4195 if (!t.validElementsRE.test(nn) || (t.invalidElementsRE && t.invalidElementsRE.test(nn)) || inn) {
4196 iv = true;
4197 break;
4200 if (isIE) {
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)
4204 return;
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')
4215 return;
4218 // Check if valid child
4219 if (t.childRules) {
4220 if (t.parentElementsRE.test(t.elementName)) {
4221 if (!t.childRules[t.elementName].test(nn)) {
4222 iv = true;
4223 break;
4227 t.elementName = nn;
4230 ru = t.findRule(nn);
4231 nn = ru.name || nn;
4233 // Skip empty nodes or empty node name in IE
4234 if ((!hc && ru.noEmpty) || (isIE && !nn)) {
4235 iv = true;
4236 break;
4239 // Check required
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]) !== '')
4245 break;
4248 // None of the required was there
4249 if (i == -1) {
4250 iv = true;
4251 break;
4255 w.writeStartElement(nn);
4257 // Add ordered attributes
4258 if (ru.attribs) {
4259 for (i=0, at = ru.attribs, l = at.length; i<l; i++) {
4260 a = at[i];
4261 v = t._getAttrib(n, a);
4263 if (v !== null)
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--) {
4272 no = at[i];
4274 if (no.specified) {
4275 a = no.nodeName.toLowerCase();
4277 if (s.invalid_attrs.test(a) || !ru.validAttribsRE.test(a))
4278 continue;
4280 ar = t.findAttribRule(ru, a);
4281 v = t._getAttrib(n, ar, a);
4283 if (v !== null)
4284 w.writeAttribute(a, v);
4289 // Padd empty nodes with a &nbsp;
4290 if (!hc && ru.padd)
4291 w.writeText('\u00a0');
4293 break;
4295 case 3: // Text
4296 // Check if valid child
4297 if (t.childRules && t.parentElementsRE.test(t.elementName)) {
4298 if (!t.childRules[t.elementName].test(n.nodeName))
4299 return;
4302 return w.writeText(n.nodeValue);
4304 case 4: // CDATA
4305 return w.writeCDATA(n.nodeValue);
4307 case 8: // Comment
4308 return w.writeComment(n.nodeValue);
4310 } else if (n.nodeType == 1)
4311 hc = n.hasChildNodes();
4313 if (hc) {
4314 cn = n.firstChild;
4316 while (cn) {
4317 t._serializeNode(cn);
4318 t.elementName = nn;
4319 cn = cn.nextSibling;
4323 // Write element end
4324 if (!iv) {
4325 if (hc || !s.closed.test(nn))
4326 w.writeFullEndElement();
4327 else
4328 w.writeEndElement();
4332 _protect : function(o) {
4333 var t = this;
4335 o.items = o.items || [];
4337 function enc(s) {
4338 return s.replace(/[\r\n\\]/g, function(c) {
4339 if (c === '\n')
4340 return '\\n';
4341 else if (c === '\\')
4342 return '\\\\';
4344 return '\\r';
4348 function dec(s) {
4349 return s.replace(/\\[\\rn]/g, function(c) {
4350 if (c === '\\n')
4351 return '\n';
4352 else if (c === '\\\\')
4353 return '\\';
4355 return '\r';
4359 each(o.patterns, function(p) {
4360 o.content = dec(enc(o.content).replace(p.pattern, function(x, a, b, c) {
4361 b = dec(b);
4363 if (p.encode)
4364 b = t._encode(b);
4366 o.items.push(b);
4367 return a + '<!--mce:' + (o.items.length - 1) + '-->' + c;
4368 }));
4371 return o;
4374 _unprotect : function(h, o) {
4375 h = h.replace(/\<!--mce:([0-9]+)--\>/g, function(a, b) {
4376 return o.items[parseInt(b)];
4379 o.items = [];
4381 return h;
4384 _encode : function(h) {
4385 var t = this, s = t.settings, l;
4387 // Entity encode
4388 if (s.entity_encoding !== 'raw') {
4389 if (s.entity_encoding.indexOf('named') != -1) {
4390 t.setEntities(s.entities);
4391 l = t.entityLookup;
4393 h = h.replace(t.entitiesRE, function(a) {
4394 var v;
4396 if (v = l[a])
4397 a = '&' + v + ';';
4399 return a;
4403 if (s.entity_encoding.indexOf('numeric') != -1) {
4404 h = h.replace(/[\u007E-\uFFFF]/g, function(a) {
4405 return '&#' + a.charCodeAt(0) + ';';
4410 return h;
4413 _setup : function() {
4414 var t = this, s = this.settings;
4416 if (t.done)
4417 return;
4419 t.done = 1;
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) {
4433 var i, v;
4435 na = na || a.name;
4437 if (a.forcedVal && (v = a.forcedVal)) {
4438 if (v === '{$uid}')
4439 return this.dom.uniqueId();
4441 return v;
4444 v = this.dom.getAttrib(n, na);
4446 switch (na) {
4447 case 'rowspan':
4448 case 'colspan':
4449 // Whats the point? Remove usless attribute value
4450 if (v == '1')
4451 v = '';
4453 break;
4456 if (this.attribValueFilter)
4457 v = this.attribValueFilter(na, v, n);
4459 if (a.validVals) {
4460 for (i = a.validVals.length - 1; i >= 0; i--) {
4461 if (v == a.validVals[i])
4462 break;
4465 if (i == -1)
4466 return null;
4469 if (v === '' && typeof(a.defaultVal) != 'undefined') {
4470 v = a.defaultVal;
4472 if (v === '{$uid}')
4473 return this.dom.uniqueId();
4475 return v;
4476 } else {
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, '');
4482 if (v === '')
4483 return null;
4486 return v;
4490 })();
4492 /* file:jscripts/tiny_mce/classes/dom/ScriptLoader.js */
4494 (function() {
4495 var each = tinymce.each;
4497 tinymce.create('tinymce.dom.ScriptLoader', {
4498 ScriptLoader : function(s) {
4499 this.settings = s || {};
4500 this.queue = [];
4501 this.lookup = {};
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;
4515 if (o = lo[u]) {
4516 // Is loaded fire callback
4517 if (cb && o.state == 2)
4518 cb.call(s || this);
4520 return o;
4523 o = {state : 0, url : u, func : cb, scope : s || this};
4525 if (pr)
4526 t.queue.unshift(o);
4527 else
4528 t.queue.push(o);
4530 lo[u] = o;
4532 return o;
4535 load : function(u, cb, s) {
4536 var t = this, o;
4538 if (o = t.lookup[u]) {
4539 // Is loaded fire callback
4540 if (cb && o.state == 2)
4541 cb.call(s || t);
4543 return o;
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,
4551 async : false,
4552 success : function(co) {
4553 t.eval(co);
4556 } else
4557 document.write('<script type="text/javascript" src="' + tinymce._addVer(u) + '"></script>');
4560 if (!tinymce.is(u, 'string')) {
4561 each(u, function(u) {
4562 loadScript(u);
4565 if (cb)
4566 cb.call(s || t);
4567 } else {
4568 loadScript(u);
4570 if (cb)
4571 cb.call(s || t);
4575 loadQueue : function(cb, s) {
4576 var t = this;
4578 if (!t.queueLoading) {
4579 t.queueLoading = 1;
4580 t.queueCallbacks = [];
4582 t.loadScripts(t.queue, function() {
4583 t.queueLoading = 0;
4585 if (cb)
4586 cb.call(s || t);
4588 each(t.queueCallbacks, function(o) {
4589 o.func.call(o.scope);
4592 } else if (cb)
4593 t.queueCallbacks.push({func : cb, scope : s || t});
4596 eval : function(co) {
4597 var w = window;
4599 // Evaluate script
4600 if (!w.execScript) {
4601 try {
4602 eval.call(w, co);
4603 } catch (ex) {
4604 eval(co, w); // Firefox 3.0a8
4606 } else
4607 w.execScript(co); // IE
4610 loadScripts : function(sc, cb, s) {
4611 var t = this, lo = t.lookup;
4613 function done(o) {
4614 o.state = 2; // Has been loaded
4616 // Run callback
4617 if (o.func)
4618 o.func.call(o.scope || t);
4621 function allDone() {
4622 var l;
4624 // Check if all files are loaded
4625 l = sc.length;
4626 each(sc, function(o) {
4627 o = lo[o.url];
4629 if (o.state === 2) {// It has finished loading
4630 done(o);
4631 l--;
4632 } else
4633 load(o);
4636 // They are all loaded
4637 if (l === 0 && cb) {
4638 cb.call(s || t);
4639 cb = 0;
4643 function load(o) {
4644 if (o.state > 0)
4645 return;
4647 o.state = 1; // Is loading
4649 tinymce.util.XHR.send({
4650 url : o.url,
4651 error : t.settings.error,
4652 success : function(co) {
4653 t.eval(co);
4654 done(o);
4655 allDone();
4660 each(sc, function(o) {
4661 var u = o.url;
4663 // Add to queue if needed
4664 if (!lo[u]) {
4665 lo[u] = o;
4666 t.queue.push(o);
4667 } else
4668 o = lo[u];
4670 // Is already loading or has been loaded
4671 if (o.state > 0)
4672 return;
4674 if (!tinymce.dom.Event.domLoaded && !t.settings.strict_mode) {
4675 var ix, ol = '';
4677 // Add onload events
4678 if (cb || o.func) {
4679 o.state = 1; // Is loading
4681 ix = tinymce.dom.ScriptLoader._addOnLoad(function() {
4682 done(o);
4683 allDone();
4686 if (tinymce.isIE)
4687 ol = ' onreadystatechange="';
4688 else
4689 ol = ' onload="';
4691 ol += 'tinymce.dom.ScriptLoader._onLoad(this,\'' + u + '\',' + ix + ');"';
4694 document.write('<script type="text/javascript" src="' + tinymce._addVer(u) + '"' + ol + '></script>');
4696 if (!o.func)
4697 done(o);
4698 } else
4699 load(o);
4702 allDone();
4705 // Static methods
4706 'static' : {
4707 _addOnLoad : function(f) {
4708 var t = this;
4710 t._funcs = t._funcs || [];
4711 t._funcs.push(f);
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();
4726 })();
4728 /* file:jscripts/tiny_mce/classes/ui/Control.js */
4730 (function() {
4731 // Shorten class names
4732 var DOM = tinymce.DOM, is = tinymce.is;
4734 tinymce.create('tinymce.ui.Control', {
4735 Control : function(id, s) {
4736 this.id = id;
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;
4742 this.disabled = 0;
4743 this.active = 0;
4746 setDisabled : function(s) {
4747 var e;
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) {
4754 if (s) {
4755 this.prevTitle = e.title;
4756 e.title = this.settings.unavailable_prefix + ": " + e.title;
4757 } else
4758 e.title = this.prevTitle;
4761 this.setState('Disabled', s);
4762 this.setState('Enabled', !s);
4763 this.disabled = s;
4767 isDisabled : function() {
4768 return this.disabled;
4771 setActive : function(s) {
4772 if (s != this.active) {
4773 this.setState('Active', s);
4774 this.active = s;
4778 isActive : function() {
4779 return this.active;
4782 setState : function(c, s) {
4783 var n = DOM.get(this.id);
4785 c = this.classPrefix + c;
4787 if (s)
4788 DOM.addClass(n, c);
4789 else
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() {
4805 var t = this, b;
4807 // Set pending states
4808 if (is(t.disabled)) {
4809 b = t.disabled;
4810 t.disabled = -1;
4811 t.setDisabled(b);
4814 if (is(t.active)) {
4815 b = t.active;
4816 t.active = -1;
4817 t.setActive(b);
4821 remove : function() {
4822 DOM.remove(this.id);
4823 this.destroy();
4826 destroy : function() {
4827 tinymce.dom.Event.clear(this.id);
4831 })();
4832 /* file:jscripts/tiny_mce/classes/ui/Container.js */
4834 tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {
4835 Container : function(id, s) {
4836 this.parent(id, s);
4837 this.controls = [];
4838 this.lookup = {};
4841 add : function(c) {
4842 this.lookup[c.id] = c;
4843 this.controls.push(c);
4845 return c;
4848 get : function(n) {
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) {
4859 this.parent(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 */
4871 (function() {
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) {
4876 this.parent(id, s);
4877 this.classPrefix = 'mceMenuItem';
4880 setSelected : function(s) {
4881 this.setState('Selected', s);
4882 this.selected = s;
4885 isSelected : function() {
4886 return this.selected;
4889 postRender : function() {
4890 var t = this;
4892 t.parent();
4894 // Set pending state
4895 if (is(t.selected))
4896 t.setSelected(t.selected);
4900 })();
4902 /* file:jscripts/tiny_mce/classes/ui/Menu.js */
4904 (function() {
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) {
4909 var t = this;
4911 t.parent(id, s);
4912 t.items = {};
4913 t.collapsed = false;
4914 t.menuCount = 0;
4915 t.onAddItem = new tinymce.util.Dispatcher(this);
4918 expand : function(d) {
4919 var t = this;
4921 if (d) {
4922 walk(t, function(o) {
4923 if (o.expand)
4924 o.expand();
4925 }, 'items', t);
4928 t.collapsed = false;
4931 collapse : function(d) {
4932 var t = this;
4934 if (d) {
4935 walk(t, function(o) {
4936 if (o.collapse)
4937 o.collapse();
4938 }, 'items', t);
4941 t.collapsed = true;
4944 isCollapsed : function() {
4945 return this.collapsed;
4948 add : function(o) {
4949 if (!o.settings)
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) {
4962 if (!o.collapse)
4963 o = this.createMenu(o);
4965 this.menuCount++;
4967 return this.add(o);
4970 hasMenus : function() {
4971 return this.menuCount !== 0;
4974 remove : function(o) {
4975 delete this.items[o.id];
4978 removeAll : function() {
4979 var t = this;
4981 walk(t, function(o) {
4982 if (o.removeAll)
4983 o.removeAll();
4984 else
4985 o.remove();
4987 o.destroy();
4988 }, 'items', t);
4990 t.items = {};
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);
4998 return m;
5002 })();
5003 /* file:jscripts/tiny_mce/classes/ui/DropMenu.js */
5005 (function() {
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) {
5010 s = 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';
5020 this.parent(id, s);
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;
5030 s.parent = t;
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);
5039 return m;
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;
5048 if (!DOM.boxModel)
5049 t.element.setStyles({width : tw + 2, height : th + 2});
5050 else
5051 t.element.setStyles({width : tw, height : th});
5053 if (s.max_width)
5054 DOM.setStyle(co, 'width', tw);
5056 if (s.max_height) {
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;
5067 t.collapse(1);
5069 if (t.isMenuVisible)
5070 return;
5072 if (!t.rendered) {
5073 co = DOM.add(t.settings.container, t.renderNode());
5075 each(t.items, function(o) {
5076 o.postRender();
5079 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});
5080 } else
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});
5087 DOM.show(co);
5088 t.update();
5090 x += s.offset_x || 0;
5091 y += s.offset_y || 0;
5092 vp.w -= 4;
5093 vp.h -= 4;
5095 // Move inside viewport if not submenu
5096 if (s.constrain) {
5097 w = co.clientWidth - ot;
5098 h = co.clientHeight - ot;
5099 mx = vp.x + vp.w;
5100 my = vp.y + vp.h;
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});
5110 t.element.update();
5112 t.isMenuVisible = 1;
5113 t.mouseClickFunc = Event.add(co, 'click', function(e) {
5114 var m;
5116 e = e.target;
5118 if (e && (e = DOM.getParent(e, 'TR')) && !DOM.hasClass(e, cp + 'ItemSub')) {
5119 m = t.items[e.id];
5121 if (m.isDisabled())
5122 return;
5124 dm = t;
5126 while (dm) {
5127 if (dm.hideMenu)
5128 dm.hideMenu();
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
5140 if (t.hasMenus()) {
5141 t.mouseOverFunc = Event.add(co, 'mouseover', function(e) {
5142 var m, r, mi;
5144 e = e.target;
5145 if (e && (e = DOM.getParent(e, 'TR'))) {
5146 m = t.items[e.id];
5148 if (t.lastMenu)
5149 t.lastMenu.collapse(1);
5151 if (m.isDisabled())
5152 return;
5154 if (e && DOM.hasClass(e, cp + 'ItemSub')) {
5155 //p = DOM.getPos(s.container);
5156 r = DOM.getRect(e);
5157 m.showMenu((r.x + r.w - ot), r.y - ot, r.x);
5158 t.lastMenu = m;
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
5170 t._focusIdx = 0;
5174 hideMenu : function(c) {
5175 var t = this, co = DOM.get('menu_' + t.id), e;
5177 if (!t.isMenuVisible)
5178 return;
5180 Event.remove(co, 'mouseover', t.mouseOverFunc);
5181 Event.remove(co, 'click', t.mouseClickFunc);
5182 Event.remove(co, 'keydown', t._keyHandler);
5183 DOM.hide(co);
5184 t.isMenuVisible = 0;
5186 if (!c)
5187 t.collapse(1);
5189 if (t.element)
5190 t.element.hide();
5192 if (e = DOM.get(t.id))
5193 DOM.removeClass(e.firstChild, t.classPrefix + 'ItemActive');
5195 t.onHideMenu.dispatch(t);
5198 add : function(o) {
5199 var t = this, co;
5201 o = t.parent(o);
5203 if (t.isRendered && (co = DOM.get('menu_' + t.id)))
5204 t._add(DOM.select('tbody', co)[0], o);
5206 return o;
5209 collapse : function(d) {
5210 this.parent(d);
5211 this.hideMenu(1);
5214 remove : function(o) {
5215 DOM.remove(o.id);
5216 this.destroy();
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);
5227 if (t.element)
5228 t.element.remove();
5230 DOM.remove(co);
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});
5240 if (s.menu_line)
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) {
5248 t._add(tb, o);
5251 t.rendered = true;
5253 return w;
5256 // Internal functions
5258 _keyHandler : function(e) {
5259 var t = this, kc = e.keyCode;
5261 function focus(d) {
5262 var i = t._focusIdx + d, e = DOM.select('a', 'menu_' + t.id)[i];
5264 if (e) {
5265 t._focusIdx = i;
5266 e.focus();
5270 switch (kc) {
5271 case 38:
5272 focus(-1); // Select first link
5273 return;
5275 case 40:
5276 focus(1);
5277 return;
5279 case 13:
5280 return;
5282 case 27:
5283 return this.hideMenu();
5287 _add : function(tb, o) {
5288 var n, s = o.settings, a, ro, it, cp = this.classPrefix;
5290 if (s.separator) {
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');
5297 return;
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');
5318 if (o.collapse)
5319 DOM.addClass(ro, cp + 'ItemSub');
5321 if (n = ro.previousSibling)
5322 DOM.removeClass(n, 'mceLast');
5324 DOM.addClass(ro, 'mceLast');
5328 })();
5329 /* file:jscripts/tiny_mce/classes/ui/Button.js */
5331 (function() {
5332 var DOM = tinymce.DOM;
5334 tinymce.create('tinymce.ui.Button:tinymce.ui.Control', {
5335 Button : function(id, s) {
5336 this.parent(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) + '">';
5346 if (s.image)
5347 h += '<img class="mceIcon" src="' + s.image + '" />' + l + '</a>';
5348 else
5349 h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '') + '</a>';
5351 return h;
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);
5364 })();
5366 /* file:jscripts/tiny_mce/classes/ui/ListBox.js */
5368 (function() {
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) {
5373 var t = this;
5375 t.parent(id, s);
5376 t.items = [];
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;
5392 // Find item
5393 each(t.items, function(o) {
5394 if (o.value == v) {
5395 DOM.setHTML(e, DOM.encode(o.title));
5396 fv = 1;
5397 return false;
5401 // If no item was found then present title
5402 if (!fv) {
5403 DOM.setHTML(e, DOM.encode(t.settings.title));
5404 DOM.addClass(e, 'mceTitle');
5405 e = 0;
5406 return;
5407 } else
5408 DOM.removeClass(e, 'mceTitle');
5411 e = 0;
5414 add : function(n, v, o) {
5415 var t = this;
5417 o = o || {};
5418 o = tinymce.extend(o, {
5419 title : n,
5420 value : v
5423 t.items.push(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>';
5439 return h;
5442 showMenu : function() {
5443 var t = this, p1, p2, e = DOM.get(this.id), m;
5445 if (t.isDisabled() || t.items.length == 0)
5446 return;
5448 if (t.menu && t.menu.isMenuVisible)
5449 return t.hideMenu();
5451 if (!t.isMenuRendered) {
5452 t.renderMenu();
5453 t.isMenuRendered = true;
5456 p1 = DOM.getPos(this.settings.menu_container);
5457 p2 = DOM.getPos(e);
5459 m = t.menu;
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
5464 // Select in menu
5465 if (t.oldID)
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);
5471 t.oldID = o.id;
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) {
5484 var t = this;
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'))
5488 return;
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);
5494 if (t.menu)
5495 t.menu.hideMenu();
5499 renderMenu : function() {
5500 var t = this, m;
5502 m = t.settings.control_manager.createDropMenu(t.id + '_menu', {
5503 menu_line : 1,
5504 'class' : t.classPrefix + 'Menu mceNoIcons',
5505 max_width : 150,
5506 max_height : 150
5509 m.onHideMenu.add(t.hideMenu, t);
5511 m.add({
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
5527 m.add(o);
5530 t.onRenderMenu.dispatch(t, m);
5531 t.menu = 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) {
5539 if (!t._focused) {
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)
5546 idx = i;
5549 // Move up/down
5550 if (kc == 38)
5551 v = t.items[idx - 1];
5552 else if (kc == 40)
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);
5562 if (v) {
5563 t.hideMenu();
5564 t.select(v.value);
5569 t._focused = 1;
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() {
5590 this.parent();
5592 Event.clear(this.id + '_text');
5596 })();
5597 /* file:jscripts/tiny_mce/classes/ui/NativeListBox.js */
5599 (function() {
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) {
5604 this.parent(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;
5619 v = '' + (v || '');
5621 e.selectedIndex = 0;
5622 each(ol, function(o, i) {
5623 if (o.value == v) {
5624 e.selectedIndex = i;
5625 return false;
5630 add : function(n, v, a) {
5631 var o, t = this;
5633 a = a || {};
5634 a.value = v;
5636 if (t.isRendered())
5637 DOM.add(DOM.get(this.id), 'option', a, n);
5639 o = {
5640 title : n,
5641 value : v,
5642 attribs : a
5645 t.items.push(o);
5646 t.onAdd.dispatch(t, o);
5649 getLength : function() {
5650 return DOM.get(this.id).options.length - 1;
5653 renderHTML : function() {
5654 var h, t = this;
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);
5664 return h;
5667 postRender : function() {
5668 var t = this, ch;
5670 t.rendered = true;
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) {
5685 var bf;
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) {
5695 onChange(e);
5696 return Event.cancel(e);
5700 t.onPostRender.dispatch(t, DOM.get(t.id));
5704 })();
5705 /* file:jscripts/tiny_mce/classes/ui/MenuButton.js */
5707 (function() {
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) {
5712 this.parent(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;
5720 if (t.isDisabled())
5721 return;
5723 if (!t.isMenuRendered) {
5724 t.renderMenu();
5725 t.isMenuRendered = true;
5728 if (t.isMenuVisible)
5729 return t.hideMenu();
5731 p1 = DOM.getPos(t.settings.menu_container);
5732 p2 = DOM.getPos(e);
5734 m = t.menu;
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() {
5749 var t = this, m;
5751 m = t.settings.control_manager.createDropMenu(t.id + '_menu', {
5752 menu_line : 1,
5753 'class' : this.classPrefix + 'Menu',
5754 icons : t.settings.icons
5757 m.onHideMenu.add(t.hideMenu, t);
5759 t.onRenderMenu.dispatch(t, m);
5760 t.menu = m;
5763 hideMenu : function(e) {
5764 var t = this;
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';}))
5768 return;
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);
5773 if (t.menu)
5774 t.menu.hideMenu();
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()) {
5785 if (s.onclick)
5786 s.onclick(t.value);
5788 t.showMenu();
5794 })();
5796 /* file:jscripts/tiny_mce/classes/ui/SplitButton.js */
5798 (function() {
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) {
5803 this.parent(id, s);
5804 this.classPrefix = 'mceSplitButton';
5807 renderHTML : function() {
5808 var h, t = this, s = t.settings, h1;
5810 h = '<tbody><tr>';
5812 if (s.image)
5813 h1 = DOM.createHTML('img ', {src : s.image, 'class' : 'mceAction ' + s['class']});
5814 else
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;
5830 if (s.onclick) {
5831 Event.add(t.id + '_action', 'click', function() {
5832 if (!t.isDisabled())
5833 s.onclick(t.value);
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() {
5856 this.parent();
5858 Event.clear(this.id + '_action');
5859 Event.clear(this.id + '_open');
5863 })();
5865 /* file:jscripts/tiny_mce/classes/ui/ColorSplitButton.js */
5867 (function() {
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) {
5872 var t = this;
5874 t.parent(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',
5878 grid_width : 8,
5879 default_color : '#888888'
5880 }, t.settings);
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;
5891 if (t.isDisabled())
5892 return;
5894 if (!t.isMenuRendered) {
5895 t.renderMenu();
5896 t.isMenuRendered = true;
5899 if (t.isMenuVisible)
5900 return t.hideMenu();
5902 e = DOM.get(t.id);
5903 DOM.show(t.id + '_menu');
5904 DOM.addClass(e, 'mceSplitButtonSelected');
5905 p2 = DOM.getPos(e);
5906 DOM.setStyles(t.id + '_menu', {
5907 left : p2.x,
5908 top : p2.y + e.clientHeight,
5909 zIndex : 200000
5911 e = 0;
5913 Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
5915 if (t._focused) {
5916 t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) {
5917 if (e.keyCode == 27)
5918 t.hideMenu();
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) {
5930 var t = this;
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';}))
5934 return;
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
5959 i = 0;
5960 each(is(s.colors, 'array') ? s.colors : s.colors.split(','), function(c) {
5961 c = c.replace(/^#/, '');
5963 if (!i--) {
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:;',
5972 style : {
5973 backgroundColor : '#' + c
5975 mce_color : '#' + 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) {
5993 var c;
5995 e = e.target;
5997 if (e.nodeName == 'A' && (c = e.getAttribute('mce_color')))
5998 t.setColor(c);
6000 return Event.cancel(e); // Prevent IE auto save warning
6003 return w;
6006 setColor : function(c) {
6007 var t = this;
6009 DOM.setStyle(t.id + '_preview', 'backgroundColor', c);
6011 t.value = c;
6012 t.hideMenu();
6013 t.settings.onselect(c);
6016 postRender : function() {
6017 var t = this, id = t.id;
6019 t.parent();
6020 DOM.add(id + '_action', 'div', {id : id + '_preview', 'class' : 'mceColorPreview'});
6023 destroy : function() {
6024 this.parent();
6026 Event.clear(this.id + '_menu');
6027 Event.clear(this.id + '_more');
6028 DOM.remove(this.id + '_menu');
6032 })();
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;
6040 cl = t.controls;
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
6043 co = cl[i];
6044 pr = cl[i - 1];
6045 nx = cl[i + 1];
6047 // Add toolbar start
6048 if (i === 0) {
6049 c = 'mceToolbarStart';
6051 if (co.Button)
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
6071 if (dom.stdMode)
6072 h += '<td style="position: relative">' + co.renderHTML() + '</td>';
6073 else
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';
6086 if (co.Button)
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 */
6102 (function() {
6103 var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;
6105 tinymce.create('tinymce.AddOnManager', {
6106 items : [],
6107 urls : {},
6108 lookup : {},
6109 onAdd : new Dispatcher(this),
6111 get : function(n) {
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);
6123 else
6124 tinymce.ScriptLoader.add(u);
6128 add : function(id, o) {
6129 this.items.push(o);
6130 this.lookup[id] = o;
6131 this.onAdd.dispatch(this, id, o);
6133 return o;
6136 load : function(n, u, cb, s) {
6137 var t = this;
6139 if (t.urls[n])
6140 return;
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();
6154 }());
6155 /* file:jscripts/tiny_mce/classes/EditorManager.js */
6157 (function() {
6158 // Shorten names
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', {
6162 editors : {},
6163 i18n : {},
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) {
6200 var f = se[n];
6202 if (!f)
6203 return;
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));
6214 s = extend({
6215 theme : "simple",
6216 language : "en",
6217 strict_loading_mode : document.contentType == 'application/xhtml+xml'
6218 }, s);
6220 t.settings = s;
6222 // If page not loaded and strict mode isn't enabled then load them
6223 if (!Event.domLoaded && !s.strict_loading_mode) {
6224 // Load language
6225 if (s.language)
6226 sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');
6228 // Load theme
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');
6232 // Load plugins
6233 if (s.plugins) {
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')
6245 return;
6247 PluginManager.load(v, 'plugins/' + v + '/editor_plugin' + tinymce.suffix + '.js');
6252 sl.loadQueue();
6255 // Legacy call
6256 Event.add(document, 'init', function() {
6257 var l, co;
6259 execCallback(s, 'onpageload');
6261 // Verify that it's a valid browser
6262 if (s.browsers) {
6263 l = false;
6265 each(explode(s.browsers), function(v) {
6266 switch (v) {
6267 case 'ie':
6268 case 'msie':
6269 if (tinymce.isIE)
6270 l = true;
6271 break;
6273 case 'gecko':
6274 if (tinymce.isGecko)
6275 l = true;
6276 break;
6278 case 'safari':
6279 case 'webkit':
6280 if (tinymce.isWebKit)
6281 l = true;
6282 break;
6284 case 'opera':
6285 if (tinymce.isOpera)
6286 l = true;
6288 break;
6292 // Not a valid one
6293 if (!l)
6294 return;
6297 switch (s.mode) {
6298 case "exact":
6299 l = s.elements || '';
6301 if(l.length > 0) {
6302 each(explode(l), function(v) {
6303 if (DOM.get(v)) {
6304 ed = new tinymce.Editor(v, s);
6305 el.push(ed);
6306 ed.render(1);
6307 } else {
6308 c = 0;
6310 each(document.forms, function(f) {
6311 each(f.elements, function(e) {
6312 if (e.name === v) {
6313 v = 'mce_editor_' + c;
6314 DOM.setAttrib(e, 'id', v);
6316 ed = new tinymce.Editor(v, s);
6317 el.push(ed);
6318 ed.render(1);
6325 break;
6327 case "textareas":
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))
6335 return;
6337 if (!s.editor_selector || hasClass(v, s.editor_selector)) {
6338 // Can we use the name
6339 e = DOM.get(v.name);
6340 if (!v.id && !e)
6341 v.id = 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);
6348 el.push(ed);
6349 ed.render(1);
6352 break;
6355 // Call onInit when all editors are initialized
6356 if (s.oninit) {
6357 l = co = 0;
6359 each (el, function(ed) {
6360 co++;
6362 if (!ed.initialized) {
6363 // Wait for it
6364 ed.onInit.add(function() {
6365 l++;
6367 // All done
6368 if (l == co)
6369 execCallback(s, 'oninit');
6371 } else
6372 l++;
6374 // All done
6375 if (l == co)
6376 execCallback(s, 'oninit');
6382 get : function(id) {
6383 return this.editors[id];
6386 getInstanceById : function(id) {
6387 return this.get(id);
6390 add : function(e) {
6391 this.editors[e.id] = e;
6392 this._setActive(e);
6394 return e;
6397 remove : function(e) {
6398 var t = this;
6400 // Not in the collection
6401 if (!t.editors[e.id])
6402 return null;
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) {
6409 t._setActive(e);
6410 return false; // Break
6414 e.destroy();
6416 return e;
6419 execCommand : function(c, u, v) {
6420 var t = this, ed = t.get(v), w;
6422 // Manager commands
6423 switch (c) {
6424 case "mceFocus":
6425 ed.focus();
6426 return true;
6428 case "mceAddEditor":
6429 case "mceAddControl":
6430 if (!t.get(v))
6431 new tinymce.Editor(v, t.settings).render();
6433 return true;
6435 case "mceAddFrameControl":
6436 w = v.window;
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);
6446 ed.render();
6448 // Fix IE memory leaks
6449 if (tinymce.isIE) {
6450 function clr() {
6451 ed.destroy();
6452 w.detachEvent('onunload', clr);
6453 w = w.tinyMCE = w.tinymce = null; // IE leak
6456 w.attachEvent('onunload', clr);
6459 v.page_window = null;
6461 return true;
6463 case "mceRemoveEditor":
6464 case "mceRemoveControl":
6465 ed.remove();
6466 return true;
6468 case 'mceToggleEditor':
6469 if (!ed) {
6470 t.execCommand('mceAddControl', 0, v);
6471 return true;
6474 if (ed.isHidden())
6475 ed.show();
6476 else
6477 ed.hide();
6479 return true;
6482 // Run command on active editor
6483 if (t.activeEditor)
6484 return t.activeEditor.execCommand(c, u, v);
6486 return false;
6489 execInstanceCommand : function(id, c, u, v) {
6490 var ed = this.get(id);
6492 if (ed)
6493 return ed.execCommand(c, u, v);
6495 return false;
6498 triggerSave : function() {
6499 each(this.editors, function(e) {
6500 e.save();
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) {
6511 if (g === 'common')
6512 i18n[lc + '.' + k] = o;
6513 else
6514 i18n[lc + '.' + g + '.' + k] = o;
6518 } else {
6519 each(o, function(o, k) {
6520 i18n[p + '.' + k] = o;
6525 // Private methods
6527 _setActive : function(e) {
6528 this.selectedInstance = this.activeEditor = e;
6533 tinymce.EditorManager.preInit();
6534 })();
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 */
6541 (function() {
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) {
6549 var t = this;
6551 t.id = t.editorId = id;
6552 t.execCommands = {};
6553 t.queryStateCommands = {};
6554 t.queryValueCommands = {};
6555 t.plugins = {};
6557 // Add events to the editor
6558 each([
6559 'onPreInit',
6560 'onBeforeRenderUI',
6561 'onPostRender',
6562 'onInit',
6563 'onRemove',
6564 'onActivate',
6565 'onDeactivate',
6566 'onClick',
6567 'onEvent',
6568 'onMouseUp',
6569 'onMouseDown',
6570 'onDblClick',
6571 'onKeyDown',
6572 'onKeyUp',
6573 'onKeyPress',
6574 'onContextMenu',
6575 'onSubmit',
6576 'onReset',
6577 'onPaste',
6578 'onPreProcess',
6579 'onPostProcess',
6580 'onBeforeSetContent',
6581 'onBeforeGetContent',
6582 'onSetContent',
6583 'onGetContent',
6584 'onLoadContent',
6585 'onSaveContent',
6586 'onNodeChange',
6587 'onChange',
6588 'onBeforeExecCommand',
6589 'onExecCommand',
6590 'onUndo',
6591 'onRedo',
6592 'onVisualAid',
6593 'onSetProgressState'
6594 ], function(e) {
6595 t[e] = new Dispatcher(t);
6598 // Default editor config
6599 t.settings = s = extend({
6600 id : id,
6601 language : 'en',
6602 docs_language : 'en',
6603 theme : 'simple',
6604 skin : 'default',
6605 delta_width : 0,
6606 delta_height : 0,
6607 popup_css : '',
6608 plugins : '',
6609 document_base_url : tinymce.documentBaseURL,
6610 add_form_submit_trigger : 1,
6611 submit_patch : 1,
6612 add_unload_trigger : 1,
6613 convert_urls : 1,
6614 relative_urls : 1,
6615 remove_script_host : 1,
6616 table_inline_editing : 0,
6617 object_resizing : 1,
6618 cleanup : 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',
6626 visual : 1,
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',
6634 hidden_input : 1,
6635 padd_empty_editor : 1,
6636 render_ui : 1,
6637 init_theme : 1,
6638 force_p_newlines : 1,
6639 indentation : '30px'
6640 }, s);
6642 // Setup URIs
6643 t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {
6644 base_uri : tinyMCE.baseURI
6646 t.baseURI = EditorManager.baseURI;
6648 // Call setup
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() {
6658 t.render();
6660 return;
6663 // Force strict loading mode if render us called by user and not internally
6664 if (!nst) {
6665 s.strict_loading_mode = 1;
6666 tinyMCE.settings = s;
6669 // Element not found, then skip initialization
6670 if (!t.getElement())
6671 return;
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) {
6686 if (o.save)
6687 o.content = DOM.encode(o.content);
6691 if (s.add_form_submit_trigger) {
6692 t.onSubmit.addToTop(function() {
6693 if (t.initialized) {
6694 t.save();
6695 t.isNotDirty = 1;
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;
6713 if (!n)
6714 return;
6716 // Already patched
6717 if (n._mceOldSubmit)
6718 return;
6720 // Check page uses id="submit" or name="submit" for it's submit button
6721 if (!n.submit.nodeType && !n.submit.length) {
6722 t.formElement = n;
6723 n._mceOldSubmit = n.submit;
6724 n.submit = function() {
6725 // Save all instances
6726 EditorManager.triggerSave();
6727 t.isNotDirty = 1;
6729 return this._mceOldSubmit(this);
6733 n = null;
6737 // Load scripts
6738 function loadScripts() {
6739 if (s.language)
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')
6749 return;
6751 PluginManager.load(p, 'plugins/' + p + '/editor_plugin' + tinymce.suffix + '.js');
6755 // Init when que is loaded
6756 sl.loadQueue(function() {
6757 if (s.ask) {
6758 function ask() {
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) {
6764 if (s)
6765 t.init();
6767 }, 0);
6770 Event.add(t.id, 'focus', ask);
6771 return;
6774 if (!t.removed)
6775 t.init();
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);
6783 } else
6784 loadScripts();
6787 init : function() {
6788 var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re;
6790 EditorManager.add(t);
6792 // Create theme
6793 s.theme = s.theme.replace(/-/, '');
6794 o = ThemeManager.get(s.theme);
6795 t.theme = new o();
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;
6804 if (c) {
6805 po = new c(t, u);
6807 t.plugins[p] = po;
6809 if (po.init)
6810 po.init(t, u);
6814 // Setup popup CSS path(s)
6815 if (s.popup_css)
6816 s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);
6817 else
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);
6827 // Pass through
6828 t.undoManager.onAdd.add(function(um, l) {
6829 if (!l.initial)
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))
6851 t.nodeChanged();
6854 // Remove ghost selections on images and tables in Gecko
6855 if (isGecko) {
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);
6869 // Measure box
6870 if (s.render_ui) {
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);
6882 // Render UI
6883 o = t.theme.renderUI({
6884 targetNode : e,
6885 width : w,
6886 height : h,
6887 deltaWidth : s.delta_width,
6888 deltaHeight : s.delta_height
6891 t.editorContainer = o.editorContainer;
6895 // Resize editor
6896 DOM.setStyles(o.sizeContainer || o.editorContainer, {
6897 width : w,
6898 height : h
6901 h = (o.iframeHeight || h) + ((h + '').indexOf('%') == -1 ? (o.deltaHeight || 0) : '');
6902 if (h < 100)
6903 h = 100;
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();})()';
6934 // Create iframe
6935 n = DOM.add(o.iframeContainer, 'iframe', {
6936 id : t.id + "_ifr",
6937 src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7
6938 frameBorder : '0',
6939 style : {
6940 width : '100%',
6941 height : h
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';
6953 } else {
6954 if (!isIE || !tinymce.relaxedDomain)
6955 t.setupIframe();
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) {
6966 d.open();
6967 d.write(t.iframeHTML);
6968 d.close();
6971 // Design mode needs to be added here Ctrl+A will fail otherwise
6972 if (!isIE) {
6973 try {
6974 if (!s.readonly)
6975 d.designMode = 'On';
6976 } catch (ex) {
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
6983 if (isIE) {
6984 // It will not steal focus if we hide it while setting contentEditable
6985 b = t.getBody();
6986 DOM.hide(b);
6988 if (!s.readonly)
6989 b.contentEditable = true;
6991 DOM.show(b);
6994 // Setup objects
6995 t.dom = new tinymce.DOM.DOMUtils(t.getDoc(), {
6996 keep_values : true,
6997 url_converter : t.convertURL,
6998 url_converter_scope : t,
6999 hex_colors : s.force_hex_style_colors,
7000 class_filter : s.class_filter,
7001 update_styles : 1,
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,
7020 dom : t.dom
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);
7029 // Pass through
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;
7043 if (!s.readonly)
7044 t._addEvents();
7046 t.controlManager.onPostRender.dispatch(t, t.controlManager);
7047 t.onPostRender.dispatch(t);
7049 if (s.directionality)
7050 t.getBody().dir = s.directionality;
7052 if (s.nowrap)
7053 t.getBody().style.whiteSpace = "nowrap";
7055 if (s.auto_resize)
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) {
7061 var n;
7063 if (v.indexOf('~') === 0) {
7064 v = v.substring(1);
7065 n = 'span';
7066 } else
7067 n = 'div';
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) {
7076 if (o.set)
7077 handleCustom(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());
7091 if (h)
7092 o.content = h;
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) {
7104 if (o.initial)
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*$/, '');
7120 if (o.set)
7121 o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';
7125 if (s.verify_css_classes) {
7126 t.serializer.attribValueFilter = function(n, v) {
7127 var s, cl;
7129 if (n == 'class') {
7130 // Build regexp for classes
7131 if (!t.classesRE) {
7132 cl = t.dom.getClasses();
7134 if (cl.length > 0) {
7135 s = '';
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 : '';
7148 return v;
7152 if (s.convert_fonts_to_spans)
7153 t._convertFonts();
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) {
7164 if (o.set)
7165 t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);
7167 if (o.get)
7168 t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);
7171 t.onPostProcess.add(function(ed, o) {
7172 if (o.set)
7173 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
7175 if (o.get)
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) {
7182 if (o.save)
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)
7190 Event.cancel(e);
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());
7199 //}, 1);
7202 // Remove empty contents
7203 if (s.padd_empty_editor) {
7204 t.onPostProcess.add(function(ed, o) {
7205 o.content = o.content.replace(/^(<p>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');
7209 if (isGecko && !s.readonly) {
7210 try {
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';
7215 } catch (ex) {
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 () {
7223 if (t.removed)
7224 return;
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);
7234 t.focus(true);
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
7245 if (s.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();
7252 }, 100);
7254 }, 1);
7256 e = null;
7260 focus : function(sf) {
7261 var oed, t = this, ce = t.settings.content_editable;
7263 if (!sf) {
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()))
7268 t.getWin().focus();
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;
7285 if (!f)
7286 return;
7288 // Look through lookup
7289 if (t.callbackLookup && (s = t.callbackLookup[n])) {
7290 f = s.func;
7291 s = s.scope;
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;
7308 if (!s)
7309 return '';
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') {
7324 o = {};
7326 if (is(v, 'string')) {
7327 each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {
7328 v = v.split('=');
7330 if (v.length > 1)
7331 o[tr(v[0])] = tr(v[1]);
7332 else
7333 o[tr(v[0])] = tr(v);
7335 } else
7336 o = v;
7338 return o;
7341 return 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
7353 s.isCollapsed(),
7359 addButton : function(n, s) {
7360 var t = this;
7362 t.buttons = t.buttons || {};
7363 t.buttons[n] = s;
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) {
7379 var t = this, c;
7381 if (!t.settings.custom_shortcuts)
7382 return false;
7384 t.shortcuts = t.shortcuts || {};
7386 if (is(cmd_func, 'string')) {
7387 c = cmd_func;
7389 cmd_func = function() {
7390 t.execCommand(c, false, null);
7394 if (is(cmd_func, 'object')) {
7395 c = cmd_func;
7397 cmd_func = function() {
7398 t.execCommand(c[0], c[1], c[2]);
7402 each(explode(pa), function(pa) {
7403 var o = {
7404 func : cmd_func,
7405 scope : sc || this,
7406 desc : desc,
7407 alt : false,
7408 ctrl : false,
7409 shift : false
7412 each(explode(pa, '+'), function(v) {
7413 switch (v) {
7414 case 'alt':
7415 case 'ctrl':
7416 case 'shift':
7417 o[v] = true;
7418 break;
7420 default:
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;
7429 return true;
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))
7436 t.focus();
7438 o = {};
7439 t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);
7440 if (o.terminate)
7441 return false;
7443 // Command callback
7444 if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {
7445 t.onExecCommand.dispatch(t, cmd, ui, val, a);
7446 return true;
7449 // Registred commands
7450 if (o = t.execCommands[cmd]) {
7451 st = o.func.call(o.scope, ui, val);
7453 // Fall through on true
7454 if (st !== true) {
7455 t.onExecCommand.dispatch(t, cmd, ui, val, a);
7456 return st;
7460 // Plugin commands
7461 each(t.plugins, function(p) {
7462 if (p.execCommand && p.execCommand(cmd, ui, val)) {
7463 t.onExecCommand.dispatch(t, cmd, ui, val, a);
7464 s = 1;
7465 return false;
7469 if (s)
7470 return true;
7472 // Theme commands
7473 if (t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {
7474 t.onExecCommand.dispatch(t, cmd, ui, val, a);
7475 return true;
7478 // Editor commands
7479 if (t.editorCommands.execCommand(cmd, ui, val)) {
7480 t.onExecCommand.dispatch(t, cmd, ui, val, a);
7481 return true;
7484 // Browser commands
7485 t.getDoc().execCommand(cmd, ui, val);
7486 t.onExecCommand.dispatch(t, cmd, ui, val, a);
7489 queryCommandState : function(c) {
7490 var t = this, o, s;
7492 // Is hidden then return undefined
7493 if (t._isHidden())
7494 return;
7496 // Registred commands
7497 if (o = t.queryStateCommands[c]) {
7498 s = o.func.call(o.scope);
7500 // Fall though on true
7501 if (s !== true)
7502 return s;
7505 // Registred commands
7506 o = t.editorCommands.queryCommandState(c);
7507 if (o !== -1)
7508 return o;
7510 // Browser commands
7511 try {
7512 return this.getDoc().queryCommandState(c);
7513 } catch (ex) {
7514 // Fails sometimes see bug: 1896577
7518 queryCommandValue : function(c) {
7519 var t = this, o, s;
7521 // Is hidden then return undefined
7522 if (t._isHidden())
7523 return;
7525 // Registred commands
7526 if (o = t.queryValueCommands[c]) {
7527 s = o.func.call(o.scope);
7529 // Fall though on true
7530 if (s !== true)
7531 return s;
7534 // Registred commands
7535 o = t.editorCommands.queryCommandValue(c);
7536 if (is(o))
7537 return o;
7539 // Browser commands
7540 try {
7541 return this.getDoc().queryCommandValue(c);
7542 } catch (ex) {
7543 // Fails sometimes see bug: 1896577
7547 show : function() {
7548 var t = this;
7550 DOM.show(t.getContainer());
7551 DOM.hide(t.id);
7552 t.load();
7555 hide : function() {
7556 var t = this, d = t.getDoc();
7558 // Fixed bug where IE has a blinking cursor left from the editor
7559 if (isIE && d)
7560 d.execCommand('SelectAll');
7562 // We must save before we hide so Safari doesn't crash
7563 t.save();
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);
7575 return b;
7578 resizeToContent : function() {
7579 var t = this;
7581 DOM.setStyle(t.id + "_ifr", 'height', t.getBody().scrollHeight);
7584 load : function(o) {
7585 var t = this, e = t.getElement(), h;
7587 o = o || {};
7588 o.load = true;
7590 h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);
7591 o.element = e;
7593 if (!o.no_events)
7594 t.onLoadContent.dispatch(t, o);
7596 o.element = e = null;
7598 return h;
7601 save : function(o) {
7602 var t = this, e = t.getElement(), h, f;
7604 if (!t.initialized)
7605 return;
7607 o = o || {};
7608 o.save = true;
7610 // Add undo level will trigger onchange event
7611 if (!o.no_events) {
7612 t.undoManager.typing = 0;
7613 t.undoManager.add();
7616 o.element = e;
7617 h = o.content = t.getContent(o);
7619 if (!o.no_events)
7620 t.onSaveContent.dispatch(t, o);
7622 h = o.content;
7624 if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {
7625 e.innerHTML = h;
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) {
7631 e.value = h;
7632 return false;
7636 } else
7637 e.value = h;
7639 o.element = e = null;
7641 return h;
7644 setContent : function(h, o) {
7645 var t = this;
7647 o = o || {};
7648 o.format = o.format || 'html';
7649 o.set = true;
7650 o.content = h;
7652 if (!o.no_events)
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" />');
7659 o.format = 'raw';
7662 o.content = t.dom.setHTML(t.getBody(), tinymce.trim(o.content));
7664 if (o.format != 'raw' && t.settings.cleanup) {
7665 o.getInner = true;
7666 o.content = t.dom.setHTML(t.getBody(), t.serializer.serialize(t.getBody(), o));
7669 if (!o.no_events)
7670 t.onSetContent.dispatch(t, o);
7672 return o.content;
7675 getContent : function(o) {
7676 var t = this, h;
7678 o = o || {};
7679 o.format = o.format || 'html';
7680 o.get = true;
7682 if (!o.no_events)
7683 t.onBeforeGetContent.dispatch(t, o);
7685 if (o.format != 'raw' && t.settings.cleanup) {
7686 o.getInner = true;
7687 h = t.serializer.serialize(t.getBody(), o);
7688 } else
7689 h = t.getBody().innerHTML;
7691 h = h.replace(/^\s*|\s*$/g, '');
7692 o.content = h;
7694 if (!o.no_events)
7695 t.onGetContent.dispatch(t, o);
7697 return o.content;
7700 isDirty : function() {
7701 var t = this;
7703 return tinymce.trim(t.startContent) != tinymce.trim(t.getContent({format : 'raw', no_events : 1})) && !t.isNotDirty;
7706 getContainer : function() {
7707 var t = this;
7709 if (!t.container)
7710 t.container = DOM.get(t.editorContainer || t.id + '_parent');
7712 return t.container;
7715 getContentAreaContainer : function() {
7716 return this.contentAreaContainer;
7719 getElement : function() {
7720 return DOM.get(this.settings.content_element || this.id);
7723 getWin : function() {
7724 var t = this, e;
7726 if (!t.contentWindow) {
7727 e = DOM.get(t.id + "_ifr");
7729 if (e)
7730 t.contentWindow = e.contentWindow;
7733 return t.contentWindow;
7736 getDoc : function() {
7737 var t = this, w;
7739 if (!t.contentDocument) {
7740 w = t.getWin();
7742 if (w)
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)
7762 return u;
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);
7771 return u;
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) {
7783 var v;
7785 switch (e.nodeName) {
7786 case 'TABLE':
7787 v = t.dom.getAttrib(e, 'border');
7789 if (!v || v == '0') {
7790 if (t.hasVisual)
7791 t.dom.addClass(e, s.visual_table_class);
7792 else
7793 t.dom.removeClass(e, s.visual_table_class);
7796 return;
7798 case 'A':
7799 v = t.dom.getAttrib(e, 'name');
7801 if (v) {
7802 if (t.hasVisual)
7803 t.dom.addClass(e, 'mceItemAnchor');
7804 else
7805 t.dom.removeClass(e, 'mceItemAnchor');
7808 return;
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
7819 t.hide();
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);
7828 DOM.remove(e);
7831 destroy : function(s) {
7832 var t = this;
7834 // One time is enough
7835 if (t.destroyed)
7836 return;
7838 if (!s) {
7839 tinymce.removeUnload(t.destroy);
7840 tinyMCE.onBeforeUnload.remove(t._beforeUnload);
7842 // Manual destroy
7843 if (t.theme.destroy)
7844 t.theme.destroy();
7846 // Destroy controls, selection and dom
7847 t.controlManager.destroy();
7848 t.selection.destroy();
7849 t.dom.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;
7871 if (t.selection)
7872 t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;
7874 t.destroyed = 1;
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',
7884 click : 'onClick',
7885 keyup : 'onKeyUp',
7886 keydown : 'onKeyDown',
7887 keypress : 'onKeyPress',
7888 submit : 'onSubmit',
7889 reset : 'onReset',
7890 contextmenu : 'onContextMenu',
7891 dblclick : 'onDblClick',
7892 paste : 'onPaste' // Doesn't work in all browsers yet
7895 function eventHandler(e, o) {
7896 var ty = e.type;
7898 // Don't fire events when it's removed
7899 if (t.removed)
7900 return;
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);
7909 // Add DOM events
7910 each(lo, function(v, k) {
7911 switch (k) {
7912 case 'contextmenu':
7913 if (tinymce.isOpera) {
7914 // Fake contextmenu on Opera
7915 Event.add(t.getBody(), 'mousedown', function(e) {
7916 if (e.ctrlKey) {
7917 e.fakeType = 'contextmenu';
7918 eventHandler(e);
7921 } else
7922 Event.add(t.getBody(), k, eventHandler);
7923 break;
7925 case 'paste':
7926 Event.add(t.getBody(), k, function(e) {
7927 var tx, h, el, r;
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');
7935 // Get HTML data
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');
7941 h = el.innerHTML;
7942 DOM.remove(el);
7945 eventHandler(e, {text : tx, html : h});
7947 break;
7949 case 'submit':
7950 case 'reset':
7951 Event.add(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);
7952 break;
7954 default:
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) {
7960 t.focus(true);
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) {
7970 var v;
7972 if (v = e.getAttribute('mce_src'))
7973 e.src = t.documentBaseURI.toAbsolute(v);
7975 });*/
7977 Event.add(t.getDoc(), 'DOMNodeInserted', function(e) {
7978 var v;
7980 e = e.target;
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
7988 if (isGecko) {
7989 function setOpts() {
7990 var t = this, d = t.getDoc(), s = t.settings;
7992 if (isGecko && !s.readonly) {
7993 if (t._isHidden()) {
7994 try {
7995 if (!s.content_editable)
7996 d.designMode = 'On';
7997 } catch (ex) {
7998 // Fails if it's hidden
8002 try {
8003 // Try new Gecko method
8004 d.execCommand("styleWithCSS", 0, false);
8005 } catch (ex) {
8006 // Use old method
8007 if (!t._isHidden())
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) {
8027 var c = e.keyCode;
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)
8030 t.nodeChanged();
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) {
8045 var x, i, f, el, v;
8047 function find(d) {
8048 f = DOM.getParent(ed.id, 'form');
8049 el = f.elements;
8051 if (f) {
8052 each(el, function(e, i) {
8053 if (e.id == ed.id) {
8054 x = i;
8055 return false;
8059 if (d > 0) {
8060 for (i = x + 1; i < el.length; i++) {
8061 if (el[i].type != 'hidden')
8062 return el[i];
8064 } else {
8065 for (i = x - 1; i >= 0; i--) {
8066 if (el[i].type != 'hidden')
8067 return el[i];
8072 return null;
8075 if (e.keyCode === 9) {
8076 v = explode(ed.getParam('tab_focus'));
8078 if (v.length == 1) {
8079 v[1] = v[0];
8080 v[0] = ':prev';
8083 // Find element to focus
8084 if (e.shiftKey) {
8085 if (v[0] == ':prev')
8086 el = find(-1);
8087 else
8088 el = DOM.get(v[0]);
8089 } else {
8090 if (v[1] == ':next')
8091 el = find(1);
8092 else
8093 el = DOM.get(v[1]);
8096 if (el) {
8097 if (ed = EditorManager.get(el.id || el.name))
8098 ed.focus();
8099 else
8100 window.setTimeout(function() {window.focus();el.focus();}, 10);
8102 return Event.cancel(e);
8107 t.onKeyUp.add(tabCancel);
8109 if (isGecko) {
8110 t.onKeyPress.add(tabHandler);
8111 t.onKeyDown.add(tabCancel);
8112 } else
8113 t.onKeyDown.add(tabHandler);
8116 // Add shortcuts
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
8124 if (isGecko) {
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>']);
8138 function find(e) {
8139 var v = null;
8141 if (!e.altKey && !e.ctrlKey && !e.metaKey)
8142 return v;
8144 each(t.shortcuts, function(o) {
8145 if (o.ctrl != e.ctrlKey && (!tinymce.isMac || o.ctrl == e.metaKey))
8146 return;
8148 if (o.alt != e.altKey)
8149 return;
8151 if (o.shift != e.shiftKey)
8152 return;
8154 if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {
8155 v = o;
8156 return false;
8160 return v;
8163 t.onKeyUp.add(function(ed, e) {
8164 var o = find(e);
8166 if (o)
8167 return Event.cancel(e);
8170 t.onKeyPress.add(function(ed, e) {
8171 var o = find(e);
8173 if (o)
8174 return Event.cancel(e);
8177 t.onKeyDown.add(function(ed, e) {
8178 var o = find(e);
8180 if (o) {
8181 o.func.call(o.scope);
8182 return Event.cancel(e);
8187 if (tinymce.isIE) {
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;
8193 e = e.target;
8195 // Don't do this action for non image elements
8196 if (e.nodeName !== 'IMG')
8197 return;
8199 if (re)
8200 Event.remove(re.node, re.ev, re.cb);
8202 if (!t.dom.hasClass(e, 'mceItemNoResize')) {
8203 ev = 'resizeend';
8204 cb = Event.add(e, ev, function(e) {
8205 var v;
8207 e = e.target;
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', '');
8219 } else {
8220 ev = 'resizestart';
8221 cb = Event.add(e, 'resizestart', Event.cancel, Event);
8224 re = t.resizeInfo = {
8225 node : e,
8226 ev : ev,
8227 cb : cb
8231 t.onKeyDown.add(function(ed, e) {
8232 switch (e.keyCode) {
8233 case 8:
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) {
8245 Event.prevent(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
8257 if (tinymce.isIE) {
8258 Event.add(t.getWin(), 'blur', function(e) {
8259 var n;
8261 // Check added for fullscreen bug
8262 if (t.selection) {
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())
8267 addUndo();
8270 } else {
8271 Event.add(t.getDoc(), 'blur', function() {
8272 if (t.selection && !t.removed)
8273 addUndo();
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;
8294 return;
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)
8310 return;
8312 if (o.get) {
8313 each(t.dom.select('table,u,strike', o.node), function(n) {
8314 switch (n.nodeName) {
8315 case 'TABLE':
8316 if (v = dom.getAttrib(n, 'height')) {
8317 dom.setStyle(n, 'height', v);
8318 dom.setAttrib(n, 'height', '');
8320 break;
8322 case 'U':
8323 case 'STRIKE':
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');
8328 break;
8331 } else if (o.set) {
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, ''));
8336 } else {
8337 // Convert spans to elements
8338 if (n.style.textDecoration == 'underline')
8339 na = 'u';
8340 else if (n.style.textDecoration == 'line-through')
8341 na = 'strike';
8342 else
8343 na = '';
8345 if (na) {
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) {
8364 if (o.initial)
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;
8373 // No need
8374 if (!s.inline_styles)
8375 return;
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)
8382 sl = explode(sl);
8384 if (cl = s.font_size_classes)
8385 cl = explode(cl);
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)
8392 return;
8394 nl = t.dom.select('span', no);
8395 for (x = nl.length - 1; x >= 0; x--) {
8396 n = nl[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
8406 st = f.style;
8407 if (st.color || st.fontFamily) {
8408 st.color = st.fontFamily = '';
8409 dom.setAttrib(f, 'mce_style', ''); // Remove cached style data
8412 if (sl) {
8413 i = inArray(sl, dom.getStyle(n, 'fontSize'));
8415 if (i != -1) {
8416 dom.setAttrib(f, 'size', '' + (i + 1 || 1));
8417 //f.style.fontSize = '';
8419 } else if (cl) {
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));
8426 if (i == -1)
8427 i = inArray(fzn, v);
8429 if (i != -1) {
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);
8441 f = n = null;
8445 // Run on setup
8446 t.onSetContent.add(function(ed, o) {
8447 convertToFonts(ed.getBody());
8450 // Run on cleanup
8451 t.onPreProcess.add(function(ed, o) {
8452 var n, sp, nl, x;
8454 // Keep unit tests happy
8455 if (!s.inline_styles)
8456 return;
8458 if (o.get) {
8459 nl = t.dom.select('font', o.node);
8460 for (x = nl.length - 1; x >= 0; x--) {
8461 n = nl[x];
8463 sp = dom.create('span', {
8464 style : dom.getAttrib(n, 'style'),
8465 'class' : dom.getAttrib(n, 'class')
8468 dom.setStyles(sp, {
8469 fontFamily : dom.getAttrib(n, 'face'),
8470 color : dom.getAttrib(n, 'color'),
8471 backgroundColor : n.style.backgroundColor
8474 if (n.size) {
8475 if (sl)
8476 dom.setStyle(sp, 'fontSize', sl[parseInt(n.size) - 1]);
8477 else
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() {
8489 var s;
8491 if (!isGecko)
8492 return 0;
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) {
8501 var d = [], i;
8503 s = s.replace(/<(\/)?([^\s>]+)[^>]*?>/g, function(a, b, c) {
8504 var e;
8506 // Handle end element
8507 if (b === '/') {
8508 if (!d.length)
8509 return '';
8511 if (c !== d[d.length - 1].tag) {
8512 for (i=d.length - 1; i>=0; i--) {
8513 if (d[i].tag === c) {
8514 d[i].close = 1;
8515 break;
8519 return '';
8520 } else {
8521 d.pop();
8523 if (d.length && d[d.length - 1].close) {
8524 a = a + '</' + d[d.length - 1].tag + '>';
8525 d.pop();
8528 } else {
8529 // Ignore these
8530 if (/^(br|hr|input|meta|img|link|param)$/i.test(c))
8531 return a;
8533 // Ignore closed ones
8534 if (/\/>$/.test(a))
8535 return a;
8537 d.push({tag : c}); // Push start element
8540 return a;
8543 // End all open tags
8544 for (i=d.length - 1; i>=0; i--)
8545 s += '</' + d[i].tag + '>';
8547 return s;
8551 })();
8553 /* file:jscripts/tiny_mce/classes/EditorCommands.js */
8555 (function() {
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) {
8560 this.editor = ed;
8563 execCommand : function(cmd, ui, val) {
8564 var t = this, ed = t.editor, f;
8566 switch (cmd) {
8567 case 'Cut':
8568 case 'Copy':
8569 case 'Paste':
8570 try {
8571 ed.getDoc().execCommand(cmd, ui, val);
8572 } catch (ex) {
8573 if (isGecko) {
8574 ed.windowManager.confirm(ed.getLang('clipboard_msg'), function(s) {
8575 if (s)
8576 window.open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', 'mceExternal');
8578 } else
8579 ed.windowManager.alert(ed.getLang('clipboard_no_support'));
8582 return true;
8584 // Ignore these
8585 case 'mceResetDesignMode':
8586 case 'mceBeginUndoLevel':
8587 return true;
8589 // Ignore these
8590 case 'unlink':
8591 t.UnLink();
8592 return true;
8594 // Bundle these together
8595 case 'JustifyLeft':
8596 case 'JustifyCenter':
8597 case 'JustifyRight':
8598 case 'JustifyFull':
8599 t.mceJustify(cmd, cmd.substring(7).toLowerCase());
8600 return true;
8602 case 'mceEndUndoLevel':
8603 case 'mceAddUndoLevel':
8604 ed.undoManager.add();
8605 return true;
8607 default:
8608 f = this[cmd];
8610 if (f) {
8611 f.call(this, ui, val);
8612 return true;
8616 return false;
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);
8625 iv = parseInt(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);
8632 return;
8635 ed.getDoc().execCommand('Indent', false, null);
8637 if (isIE) {
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);
8652 iv = parseInt(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 : '');
8660 return;
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;
8681 ed.addVisual();
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'))
8694 v = {href : v};
8696 function set(e) {
8697 each(v, function(v, k) {
8698 ed.dom.setAttrib(e, k, v);
8702 if (!e) {
8703 ed.execCommand('CreateLink', false, 'javascript:mctmp(0);');
8704 each(ed.dom.select('a'), function(e) {
8705 if (e.href == 'javascript:mctmp(0);')
8706 set(e);
8708 } else {
8709 if (v.href)
8710 set(e);
8711 else
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);
8723 s.collapse(0);
8726 FontName : function(u, v) {
8727 var t = this, ed = t.editor, s = ed.selection, e;
8729 if (!v) {
8730 if (s.isCollapsed())
8731 s.select(s.getNode());
8733 t.RemoveFormat();
8734 } else
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);
8749 // Add style values
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) {
8754 if (!bm)
8755 bm = ed.selection.getBookmark();
8757 ed.dom.remove(e, 1);
8758 return;
8761 // Setup font size based on font size value
8762 if (v = e.size) {
8763 if (fzc && fzc.length > 0)
8764 ed.dom.setAttrib(e, 'class', fzc[parseInt(v) - 1]);
8765 else
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];
8777 if (f)
8778 return f.call(this, c);
8780 return false;
8783 queryCommandState : function(cmd) {
8784 var f;
8786 switch (cmd) {
8787 // Bundle these together
8788 case 'JustifyLeft':
8789 case 'JustifyCenter':
8790 case 'JustifyRight':
8791 case 'JustifyFull':
8792 return this.queryStateJustify(cmd, cmd.substring(7).toLowerCase());
8794 default:
8795 if (f = this['queryState' + cmd])
8796 return f.call(this, cmd);
8799 return -1;
8802 _queryState : function(c) {
8803 try {
8804 return this.editor.getDoc().queryCommandState(c);
8805 } catch (ex) {
8806 // Ignore exception
8810 _queryVal : function(c) {
8811 try {
8812 return this.editor.getDoc().queryCommandValue(c);
8813 } catch (ex) {
8814 // Ignore exception
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'))
8823 v = p.size;
8825 return v;
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'))
8835 v = p.face;
8837 if (!v)
8838 v = this._queryVal('FontName');
8840 return v;
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))
8847 rm = 1;
8849 bl = dom.getParent(n, ed.dom.isBlock);
8851 if (nn == 'IMG') {
8852 if (v == 'full')
8853 return;
8855 if (rm) {
8856 if (v == 'center')
8857 dom.setStyle(bl || n.parentNode, 'textAlign', '');
8859 dom.setStyle(n, 'float', '');
8860 this.mceRepaint();
8861 return;
8864 if (v == 'center') {
8865 // Do not change table elements
8866 if (bl && /^(TD|TH)$/.test(bl.nodeName))
8867 bl = 0;
8869 if (!bl || bl.childNodes.length > 1) {
8870 nb = dom.create('p');
8871 nb.appendChild(n.cloneNode(false));
8873 if (bl)
8874 dom.insertAfter(nb, bl);
8875 else
8876 dom.insertAfter(nb, n);
8878 dom.remove(n);
8879 n = nb.firstChild;
8880 bl = nb;
8883 dom.setStyle(bl, 'textAlign', v);
8884 dom.setStyle(n, 'float', '');
8885 } else {
8886 dom.setStyle(n, 'float', v);
8887 dom.setStyle(bl || n.parentNode, 'textAlign', '');
8890 this.mceRepaint();
8891 return;
8894 // Handle the alignment outselfs, less quirks in all browsers
8895 if (ed.settings.inline_styles && ed.settings.forced_root_block) {
8896 if (rm)
8897 v = '';
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);
8904 return;
8905 } else if (!rm)
8906 ed.getDoc().execCommand(c, false, null);
8908 if (ed.settings.inline_styles) {
8909 if (rm) {
8910 dom.getParent(ed.selection.getNode(), function(n) {
8911 if (n.style && n.style.textAlign)
8912 dom.setStyle(n, 'textAlign', '');
8915 return;
8918 each(dom.select('*'), function(n) {
8919 var v = n.align;
8921 if (v) {
8922 if (v == 'full')
8923 v = 'justify';
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();
8942 // Setup regexp
8943 re = ed.settings.merge_styles_invalid_parents;
8944 if (tinymce.is(re, 'string'))
8945 re = new RegExp(re, 'i');
8947 if (isIE) {
8948 r1 = r.duplicate();
8949 r1.collapse(true);
8950 sc = r1.parentElement();
8952 r2 = r.duplicate();
8953 r2.collapse(false);
8954 ec = r2.parentElement();
8956 if (sc != ec) {
8957 r1.move('character', 1);
8958 sc = r1.parentElement();
8961 if (sc == ec) {
8962 r1 = r.duplicate();
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;
8968 } else {
8969 function getParent(n) {
8970 return dom.getParent(n, function(n) {return n.nodeType == 1;});
8973 sc = r.startContainer;
8974 ec = r.endContainer;
8975 so = r.startOffset;
8976 eo = r.endOffset;
8978 if (!r.collapsed) {
8979 if (sc == ec) {
8980 if (so - eo < 2) {
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)
8990 return null;
8992 if (so == 0) {
8993 sp = getParent(sc);
8995 if (sp && sp.firstChild != sc)
8996 sp = null;
8999 if (so == sc.nodeValue.length) {
9000 e = sc.nextSibling;
9002 if (e && e.nodeType == 1)
9003 sp = sc.nextSibling;
9006 if (eo == 0) {
9007 e = ec.previousSibling;
9009 if (e && e.nodeType == 1)
9010 ep = e;
9013 if (eo == ec.nodeValue.length) {
9014 ep = getParent(ec);
9016 if (ep && ep.lastChild != ec)
9017 ep = null;
9020 // Same element
9021 if (sp == ep)
9022 return re && sp && re.test(sp.nodeName) ? null : sp;
9025 return null;
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 />');
9032 else
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
9040 if (isWebKit)
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'});
9042 else
9043 ed.getDoc().execCommand('RemoveFormat', false, null);
9045 t.mceSetStyleInfo(0, {command : 'removeformat'});
9046 ed.addVisual();
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) {
9055 case 'setattrib':
9056 return dom.setAttrib(n, v.name, v.value);
9058 case 'setstyle':
9059 return dom.setStyle(n, v.name, v.value);
9061 case 'removeformat':
9062 return dom.setAttrib(n, 'class', '');
9067 // Setup regexp
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)
9074 set(e, 1);
9075 else {
9076 // Generate wrappers and set styles on them
9077 d.execCommand('FontName', false, '__');
9078 each(isWebKit ? dom.select('span') : dom.select('font'), function(n) {
9079 var sp, e;
9081 if (dom.getAttrib(n, 'face') == '__' || n.style.fontFamily === '__') {
9082 sp = dom.create(nn, {mce_new : '1'});
9084 set(sp);
9086 each (n.childNodes, function(n) {
9087 sp.appendChild(n.cloneNode(true));
9090 dom.replace(sp, n);
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')) {
9101 // Find new wrapper
9102 p = dom.getParent(n, function(n) {
9103 return n.nodeType == 1 && dom.getAttrib(n, 'mce_new');
9106 if (p)
9107 dom.remove(n, 1);
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'))
9116 return;
9118 if (ed.settings.force_span_wrappers && p.nodeName != 'SPAN')
9119 return;
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)
9150 return 1;
9152 return n.parentNode.style.textAlign == v;
9155 n = dom.getParent(ed.selection.getStart(), function(n) {
9156 return n.nodeType == 1 && n.style.textAlign;
9159 if (v == 'full')
9160 v = 'justify';
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();
9171 function set(s) {
9172 if (!isGecko)
9173 return;
9175 try {
9176 // Try new Gecko method
9177 d.execCommand("styleWithCSS", 0, s);
9178 } catch (ex) {
9179 // Use old
9180 d.execCommand("useCSS", 0, !s);
9184 if (isGecko || isOpera) {
9185 set(true);
9186 d.execCommand('hilitecolor', false, val);
9187 set(false);
9188 } else
9189 d.execCommand('BackColor', false, val);
9192 Undo : function() {
9193 var ed = this.editor;
9195 if (ed.settings.custom_undo_redo) {
9196 ed.undoManager.undo();
9197 ed.nodeChanged();
9198 } else
9199 ed.getDoc().execCommand('Undo', false, null);
9202 Redo : function() {
9203 var ed = this.editor;
9205 if (ed.settings.custom_undo_redo) {
9206 ed.undoManager.redo();
9207 ed.nodeChanged();
9208 } else
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) {
9220 return isBlock(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
9225 if (bl) {
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);
9237 ed.nodeChanged();
9238 return;
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())
9264 return;
9266 b = s.getBookmark();
9267 ed.dom.remove(n, 1);
9268 s.moveToBookmark(b);
9269 ed.nodeChanged();
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) {
9277 s.select(n);
9278 ed.nodeChanged();
9279 return false;
9281 }, ed.getBody());
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) {
9303 try {
9304 s = e.selection;
9305 b = s.getBookmark(true);
9307 if (s.getSel())
9308 s.getSel().selectAllChildren(e.getBody());
9310 s.collapse(true);
9311 s.moveToBookmark(b);
9312 } catch (ex) {
9313 // Ignore
9318 queryStateUnderline : function() {
9319 var ed = this.editor, n = ed.selection.getNode();
9321 if (n && n.nodeName == 'A')
9322 return false;
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)
9332 return true;
9334 if ((n = ed.dom.getParent(ed.selection.getEnd(), ed.dom.isBlock)) && parseInt(n.style.paddingLeft) > 0)
9335 return true;
9336 } else
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;
9357 function getBQ(e) {
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
9371 if (getBQ(eb)) {
9372 bq2 = bq.cloneNode(false);
9374 while (n = eb.nextSibling)
9375 bq2.appendChild(n.parentNode.removeChild(n));
9378 // Add new bq after
9379 if (bq2)
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
9396 if (!bm) {
9397 // Move caret inside empty block element
9398 if (!isIE) {
9399 r = ed.getDoc().createRange();
9400 r.setStart(sb, 0);
9401 r.setEnd(sb, 0);
9402 s.setRng(r);
9403 } else {
9404 s.select(sb);
9405 s.collapse(0);
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) {
9409 r = s.getRng();
9410 r.move('character', -1);
9411 r.select();
9414 } else
9415 t.editor.selection.moveToBookmark(bm);
9417 return;
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
9425 return;
9428 if (!sb || !eb)
9429 return;
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) {
9439 bq = e;
9440 return;
9443 // No BQ found, create one
9444 if (!bq) {
9445 bq = dom.create('blockquote');
9446 e.parentNode.insertBefore(bq, e);
9449 // Add children from existing BQ
9450 if (e.nodeName == 'BLOCKQUOTE' && bq) {
9451 n = e.firstChild;
9453 while (n) {
9454 bq.appendChild(n.cloneNode(true));
9455 n = n.nextSibling;
9458 dom.remove(e);
9459 return;
9462 // Add non BQ element to BQ
9463 bq.appendChild(dom.remove(e));
9466 if (!bm) {
9467 // Move caret inside empty block element
9468 if (!isIE) {
9469 r = ed.getDoc().createRange();
9470 r.setStart(sb, 0);
9471 r.setEnd(sb, 0);
9472 s.setRng(r);
9473 } else {
9474 s.select(sb);
9475 s.collapse(1);
9477 } else
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')
9493 dom.remove(e, 1);
9496 t.editor.selection.moveToBookmark(b);
9497 return;
9500 each(t._getSelectedBlocks(findBQ(s.getStart()), findBQ(s.getEnd())), function(e) {
9501 var n;
9503 // Found existing BQ add to this one
9504 if (e.nodeName == 'BLOCKQUOTE' && !bq) {
9505 bq = e;
9506 return;
9509 // No BQ found, create one
9510 if (!bq) {
9511 bq = dom.create('blockquote');
9512 e.parentNode.insertBefore(bq, e);
9515 // Add children from existing BQ
9516 if (e.nodeName == 'BLOCKQUOTE' && bq) {
9517 n = e.firstChild;
9519 while (n) {
9520 bq.appendChild(n.cloneNode(true));
9521 n = n.nextSibling;
9524 dom.remove(e);
9526 return;
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);
9542 if (sb)
9543 bl.push(sb);
9545 if (sb && eb && sb != eb) {
9546 n = sb;
9548 while ((n = n.nextSibling) && n != eb) {
9549 if (dom.isBlock(n))
9550 bl.push(n);
9554 if (eb && sb != eb)
9555 bl.push(eb);
9557 return bl;
9560 })();
9563 /* file:jscripts/tiny_mce/classes/UndoManager.js */
9565 tinymce.create('tinymce.UndoManager', {
9566 index : 0,
9567 data : null,
9568 typing : 0,
9570 UndoManager : function(ed) {
9571 var t = this, Dispatcher = tinymce.util.Dispatcher;
9573 t.editor = ed;
9574 t.data = [];
9575 t.onAdd = new Dispatcher(this);
9576 t.onUndo = new Dispatcher(this);
9577 t.onRedo = new Dispatcher(this);
9580 add : function(l) {
9581 var t = this, i, ed = t.editor, b, s = ed.settings, la;
9583 l = l || {};
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)
9590 return null;
9592 // Time to compress
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];
9598 t.data.length--;
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)
9607 t.index++;
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)
9612 return null;
9614 // Add level
9615 t.data.length = t.index + 1;
9616 t.data[t.index++] = l;
9618 if (l.initial)
9619 t.index = 0;
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);
9626 ed.isNotDirty = 0;
9628 //console.dir(t.data);
9630 return l;
9633 undo : function() {
9634 var t = this, ed = t.editor, l = l, i;
9636 if (t.typing) {
9637 t.add();
9638 t.typing = 0;
9641 if (t.index > 0) {
9642 // If undo on last index then take snapshot
9643 if (t.index == t.data.length && t.index > 1) {
9644 i = t.index;
9645 t.typing = 0;
9647 if (!t.add())
9648 t.index = i;
9650 --t.index;
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);
9660 return l;
9663 redo : function() {
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);
9674 return l;
9677 clear : function() {
9678 var t = this;
9680 t.data = [];
9681 t.index = 0;
9682 t.typing = 0;
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 */
9697 (function() {
9698 // Shorten names
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;
9712 t.editor = ed;
9713 t.dom = ed.dom;
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|&#160;|&nbsp;)<\/' + 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( )([^>]+)>(&nbsp;|&#160;)<\\\/p>|<p>(&nbsp;|&#160;)<\\\/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) {
9727 if (isOpera)
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 &nbsp; 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 + '>');
9736 } else {
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
9763 if (isIE) {
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');
9771 s.select(n);
9772 s.collapse();
9773 return Event.cancel(e);
9778 return;
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>&nbsp;</p>
9787 if (p && p.nodeName == 'p' && (p.childNodes.length == 1 || p.lastChild == n)) {
9788 p.replaceChild(ed.getDoc().createTextNode('\u00a0'), n);
9791 });*/
9793 ed.onKeyPress.add(function(ed, e) {
9794 if (e.keyCode == 13 && !e.shiftKey) {
9795 if (!t.insertPara(e))
9796 Event.cancel(e);
9800 if (isGecko) {
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);
9822 return ne;
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);
9836 sel.select(n);
9837 sel.collapse();
9838 ed.nodeChanged();
9839 } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') {
9840 bl = ed.dom.getParent(n, 'P');
9842 if (bl) {
9843 ren(bl, s.element);
9844 ed.nodeChanged();
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()) {
9855 c++;
9857 // Index by node
9858 if (t == 0 && n == s)
9859 return c;
9861 // Node by index
9862 if (t == 1 && c == s)
9863 return n;
9866 return -1;
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)
9875 return true;
9877 // Wrap non blocks into blocks
9878 for (i = nl.length - 1; i >= 0; i--) {
9879 nx = nl[i];
9881 // Is text or non block element
9882 if (nx.nodeType == 3 || (!t.dom.isBlock(nx) && nx.nodeType != 8)) {
9883 if (!bl) {
9884 // Create new block but ignore whitespace
9885 if (nx.nodeType != 3 || /[^\s]/g.test(nx.nodeValue)) {
9886 // Store selection
9887 if (si == -2 && r) {
9888 if (!isIE) {
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;})) {
9891 so = r.startOffset;
9892 eo = r.endOffset;
9893 si = t.find(b, 0, r.startContainer);
9894 ei = t.find(b, 0, r.endContainer);
9896 } else {
9897 tr = d.body.createTextRange();
9898 tr.moveToElementText(b);
9899 tr.collapse(1);
9900 bp = tr.move('character', c) * -1;
9902 tr = r.duplicate();
9903 tr.collapse(1);
9904 sp = tr.move('character', c) * -1;
9906 tr = r.duplicate();
9907 tr.collapse(0);
9908 le = (tr.move('character', c) * -1) - sp;
9910 si = sp - bp;
9911 ei = le;
9915 bl = ed.dom.create(ed.settings.forced_root_block);
9916 bl.appendChild(nx.cloneNode(1));
9917 nx.parentNode.replaceChild(bl, nx);
9919 } else {
9920 if (bl.hasChildNodes())
9921 bl.insertBefore(nx, bl.firstChild);
9922 else
9923 bl.appendChild(nx);
9925 } else
9926 bl = null; // Time to create new block
9929 // Restore selection
9930 if (si != -2) {
9931 if (!isIE) {
9932 bl = b.getElementsByTagName(ed.settings.element)[0];
9933 r = d.createRange();
9935 // Select last location or generated block
9936 if (si != -1)
9937 r.setStart(t.find(b, 1, si), so);
9938 else
9939 r.setStart(bl, 0);
9941 // Select last location or generated block
9942 if (ei != -1)
9943 r.setEnd(t.find(b, 1, ei), eo);
9944 else
9945 r.setEnd(bl, 0);
9947 if (s) {
9948 s.removeAllRanges();
9949 s.addRange(r);
9951 } else {
9952 try {
9953 r = s.createRange();
9954 r.moveToElementText(b);
9955 r.collapse(1);
9956 r.moveStart('character', si);
9957 r.moveEnd('character', ei);
9958 r.select();
9959 } catch (ex) {
9960 // Ignore
9966 getParentBlock : function(n) {
9967 var d = this.dom;
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) {
9977 n = n.innerHTML;
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)
9987 // return true;
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);
9994 rb.collapse(true);
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);
10001 ra.collapse(true);
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);
10021 r.collapse(1);
10022 ed.selection.setRng(r);
10024 return false;
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;
10030 so = eo = 0;
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); }))
10050 return true;
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))) {
10054 bn = se.element;
10055 sb = null;
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))) {
10060 bn = se.element;
10061 eb = null;
10064 // Use P instead
10065 if (/(TD|TABLE|TH|CAPTION)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(sb.style.cssFloat))) {
10066 bn = se.element;
10067 sb = eb = null;
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
10082 n = sc = sn;
10083 do {
10084 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))
10085 break;
10087 sc = n;
10088 } while ((n = n.previousSibling ? n.previousSibling : n.parentNode));
10090 // Find end chop node
10091 n = ec = en;
10092 do {
10093 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))
10094 break;
10096 ec = n;
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);
10102 else
10103 rb.setStartBefore(sc);
10105 rb.setEnd(sn, so);
10106 bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari
10108 // Place secnd chop part within new block element
10109 try {
10110 ra.setEndAfter(ec);
10111 } catch(ex) {
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);
10122 } else {
10123 if (rb.startContainer.nodeName == bn && rb.startOffset == 0)
10124 r.setStartBefore(rb.startContainer);
10125 else
10126 r.setStart(rb.startContainer, rb.startOffset);
10129 if (!ec.nextSibling && ec.parentNode.nodeName == bn)
10130 r.setEndAfter(ec.parentNode);
10131 else
10132 r.setEnd(ra.endContainer, ra.endOffset);
10134 // Delete and replace it with new block elements
10135 r.deleteContents();
10137 if (isOpera)
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
10148 if (isEmpty(bef))
10149 bef.innerHTML = '<br />';
10151 if (isEmpty(aft))
10152 aft.innerHTML = isOpera ? '&nbsp;' : '<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) {
10156 r.insertNode(bef);
10157 r.insertNode(aft);
10158 } else {
10159 r.insertNode(aft);
10160 r.insertNode(bef);
10163 // Normalize
10164 aft.normalize();
10165 bef.normalize();
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);
10174 r.collapse(1);
10175 s.removeAllRanges();
10176 s.addRange(r);
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));
10188 return false;
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
10199 n = sc;
10200 while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ;
10202 if (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())
10207 n = tn;
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);
10213 se.setRng(r);
10215 // Remove the target container
10216 ed.dom.remove(sc);
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) {
10226 var pr;
10228 e = e.target;
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))
10239 return;
10241 // Only remove BR elements that got inserted in the middle of the text
10242 if (e.previousSibling || e.nextSibling)
10243 ed.dom.remove(e);
10247 // Listen for new nodes
10248 Event._add(b, 'DOMNodeInserted', handler);
10250 // Remove listener
10251 window.setTimeout(function() {
10252 Event._remove(b, 'DOMNodeInserted', handler);
10253 }, 1);
10256 })();
10258 /* file:jscripts/tiny_mce/classes/ControlManager.js */
10260 (function() {
10261 // Shorten names
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) {
10266 var t = this, i;
10268 s = s || {};
10269 t.editor = ed;
10270 t.controls = {};
10271 t.onAdd = new tinymce.util.Dispatcher(t);
10272 t.onPostRender = new tinymce.util.Dispatcher(t);
10273 t.prefix = s.prefix || ed.id + '_';
10274 t._cls = {};
10276 t.onPostRender.add(function() {
10277 each(t.controls, function(c) {
10278 c.postRender();
10283 get : function(id) {
10284 return this.controls[this.prefix + id] || this.controls[id];
10287 setActive : function(id, s) {
10288 var c = null;
10290 if (c = this.get(id))
10291 c.setActive(s);
10293 return c;
10296 setDisabled : function(id, s) {
10297 var c = null;
10299 if (c = this.get(id))
10300 c.setDisabled(s);
10302 return c;
10305 add : function(c) {
10306 var t = this;
10308 if (c) {
10309 t.controls[c.id] = c;
10310 t.onAdd.dispatch(c, t);
10313 return c;
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);
10323 if (c)
10324 return false;
10328 switch (n) {
10329 case "|":
10330 case "separator":
10331 return t.createSeparator();
10334 if (!c && ed.buttons && (c = ed.buttons[n]))
10335 return t.createButton(n, c);
10337 return t.add(c);
10340 createDropMenu : function(id, s, cc) {
10341 var t = this, ed = t.editor, c, bm, v, cls;
10343 s = extend({
10344 'class' : 'mceDropDown',
10345 constrain : ed.settings.constrain_menus
10346 }, s);
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);
10360 if (!s.onclick) {
10361 s.onclick = function(v) {
10362 ed.execCommand(s.cmd, s.ui || false, s.value);
10367 ed.onRemove.add(function() {
10368 c.destroy();
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() {
10378 if (bm)
10379 ed.selection.moveToBookmark(bm);
10383 return t.add(c);
10386 createListBox : function(id, s, cc) {
10387 var t = this, ed = t.editor, cmd, c, cls;
10389 if (t.get(id))
10390 return null;
10392 s.title = ed.translate(s.title);
10393 s.scope = s.scope || ed;
10395 if (!s.onselect) {
10396 s.onselect = function(v) {
10397 ed.execCommand(s.cmd, s.ui || false, v || s.value);
10401 s = extend({
10402 title : s.title,
10403 'class' : 'mce_' + id,
10404 scope : s.scope,
10405 control_manager : t
10406 }, s);
10408 id = t.prefix + id;
10410 if (ed.settings.use_native_selects)
10411 c = new tinymce.ui.NativeListBox(id, s);
10412 else {
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;
10435 if (c.hideMenu)
10436 ed.onMouseDown.add(c.hideMenu, c);
10438 return t.add(c);
10441 createButton : function(id, s, cc) {
10442 var t = this, ed = t.editor, o, c, cls;
10444 if (t.get(id))
10445 return null;
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);
10457 s = extend({
10458 title : s.title,
10459 'class' : 'mce_' + id,
10460 unavailable_prefix : ed.getLang('unavailable', ''),
10461 scope : s.scope,
10462 control_manager : t
10463 }, s);
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);
10471 } else {
10472 cls = t._cls.button || tinymce.ui.Button;
10473 c = new cls(id, s);
10476 return t.add(c);
10479 createMenuButton : function(id, s) {
10480 s = s || {};
10481 s.menu_button = 1;
10483 return this.createButton(id, s);
10486 createSplitButton : function(id, s, cc) {
10487 var t = this, ed = t.editor, cmd, c, cls;
10489 if (t.get(id))
10490 return null;
10492 s.title = ed.translate(s.title);
10493 s.scope = s.scope || ed;
10495 if (!s.onclick) {
10496 s.onclick = function(v) {
10497 ed.execCommand(s.cmd, s.ui || false, v || s.value);
10501 if (!s.onselect) {
10502 s.onselect = function(v) {
10503 ed.execCommand(s.cmd, s.ui || false, v || s.value);
10507 s = extend({
10508 title : s.title,
10509 'class' : 'mce_' + id,
10510 scope : s.scope,
10511 control_manager : t
10512 }, s);
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);
10519 return c;
10522 createColorSplitButton : function(id, s, cc) {
10523 var t = this, ed = t.editor, cmd, c, cls, bm;
10525 if (t.get(id))
10526 return null;
10528 s.title = ed.translate(s.title);
10529 s.scope = s.scope || ed;
10531 if (!s.onclick) {
10532 s.onclick = function(v) {
10533 ed.execCommand(s.cmd, s.ui || false, v || s.value);
10537 if (!s.onselect) {
10538 s.onselect = function(v) {
10539 ed.execCommand(s.cmd, s.ui || false, v || s.value);
10543 s = extend({
10544 title : s.title,
10545 'class' : 'mce_' + id,
10546 'menu_class' : ed.getParam('skin') + 'Skin',
10547 scope : s.scope,
10548 more_colors_title : ed.getLang('more_colors')
10549 }, s);
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() {
10558 c.destroy();
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() {
10568 if (bm) {
10569 ed.selection.moveToBookmark(bm);
10570 bm = 0;
10575 return t.add(c);
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);
10585 if (t.get(id))
10586 return null;
10588 return t.add(c);
10591 createSeparator : function(cc) {
10592 var cls = cc || this._cls.separator || tinymce.ui.Separator;
10594 return new cls();
10597 setControlType : function(n, c) {
10598 return this._cls[n.toLowerCase()] = c;
10601 destroy : function() {
10602 each(this.controls, function(c) {
10603 c.destroy();
10606 this.controls = null;
10610 })();
10612 /* file:jscripts/tiny_mce/classes/WindowManager.js */
10614 (function() {
10615 var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;
10617 tinymce.create('tinymce.WindowManager', {
10618 WindowManager : function(ed) {
10619 var t = this;
10621 t.editor = ed;
10622 t.onOpen = new Dispatcher(t);
10623 t.onClose = new Dispatcher(t);
10624 t.params = {};
10625 t.features = {};
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
10632 s = s || {};
10633 p = p || {};
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);
10642 p.inline = false;
10643 p.mce_width = s.width;
10644 p.mce_height = s.height;
10645 p.mce_auto_focus = s.auto_focus;
10647 if (mo) {
10648 if (isIE) {
10649 s.center = true;
10650 s.help = false;
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)) {
10663 if (isIE && mo)
10664 f += (f ? ';' : '') + k + ':' + v;
10665 else
10666 f += (f ? ',' : '') + k + '=' + v;
10670 t.features = s;
10671 t.params = p;
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);
10680 try {
10681 if (isIE && mo) {
10682 w = 1;
10683 window.showModalDialog(u, window, f);
10684 } else
10685 w = window.open(u, s.name, f);
10686 } catch (ex) {
10687 // Ignore
10690 if (!w)
10691 alert(t.editor.getLang('popup_blocked'));
10694 close : function(w) {
10695 w.close();
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) {
10706 w = w || window;
10708 cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t))));
10711 alert : function(tx, cb, s, w) {
10712 var t = this;
10714 w = w || window;
10715 w.alert(t._decode(t.editor.getLang(tx, tx)));
10717 if (cb)
10718 cb.call(s || t);
10721 // Internal functions
10723 _decode : function(s) {
10724 return tinymce.DOM.decode(s).replace(/\\n/g, '\n');
10728 }());