comparison media/js/tiny_mce/tiny_mce_src.js @ 45:a5b4c5ce0658

Breaking down and controlling all media files, including javascript libraries.
author Brian Neal <bgneal@gmail.com>
date Fri, 19 Jun 2009 03:16:03 +0000
parents
children 149c3567fec1
comparison
equal deleted inserted replaced
44:08cd19c1ee50 45:a5b4c5ce0658
1 var tinymce = {
2 majorVersion : '3',
3 minorVersion : '2.2.3',
4 releaseDate : '2009-03-26',
5
6 _init : function() {
7 var t = this, d = document, w = window, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
8
9 // Browser checks
10 t.isOpera = w.opera && opera.buildNumber;
11 t.isWebKit = /WebKit/.test(ua);
12 t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName);
13 t.isIE6 = t.isIE && /MSIE [56]/.test(ua);
14 t.isGecko = !t.isWebKit && /Gecko/.test(ua);
15 t.isMac = ua.indexOf('Mac') != -1;
16 t.isAir = /adobeair/i.test(ua);
17
18 // TinyMCE .NET webcontrol might be setting the values for TinyMCE
19 if (w.tinyMCEPreInit) {
20 t.suffix = tinyMCEPreInit.suffix;
21 t.baseURL = tinyMCEPreInit.base;
22 t.query = tinyMCEPreInit.query;
23 return;
24 }
25
26 // Get suffix and base
27 t.suffix = '';
28
29 // If base element found, add that infront of baseURL
30 nl = d.getElementsByTagName('base');
31 for (i=0; i<nl.length; i++) {
32 if (v = nl[i].href) {
33 // Host only value like http://site.com or http://site.com:8008
34 if (/^https?:\/\/[^\/]+$/.test(v))
35 v += '/';
36
37 base = v ? v.match(/.*\//)[0] : ''; // Get only directory
38 }
39 }
40
41 function getBase(n) {
42 if (n.src && /tiny_mce(|_dev|_src|_gzip|_jquery|_prototype).js/.test(n.src)) {
43 if (/_(src|dev)\.js/g.test(n.src))
44 t.suffix = '_src';
45
46 if ((p = n.src.indexOf('?')) != -1)
47 t.query = n.src.substring(p + 1);
48
49 t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));
50
51 // If path to script is relative and a base href was found add that one infront
52 if (base && t.baseURL.indexOf('://') == -1)
53 t.baseURL = base + t.baseURL;
54
55 return t.baseURL;
56 }
57
58 return null;
59 };
60
61 // Check document
62 nl = d.getElementsByTagName('script');
63 for (i=0; i<nl.length; i++) {
64 if (getBase(nl[i]))
65 return;
66 }
67
68 // Check head
69 n = d.getElementsByTagName('head')[0];
70 if (n) {
71 nl = n.getElementsByTagName('script');
72 for (i=0; i<nl.length; i++) {
73 if (getBase(nl[i]))
74 return;
75 }
76 }
77
78 return;
79 },
80
81 is : function(o, t) {
82 var n = typeof(o);
83
84 if (!t)
85 return n != 'undefined';
86
87 if (t == 'array' && (o.hasOwnProperty && o instanceof Array))
88 return true;
89
90 return n == t;
91 },
92
93
94 each : function(o, cb, s) {
95 var n, l;
96
97 if (!o)
98 return 0;
99
100 s = s || o;
101
102 if (typeof(o.length) != 'undefined') {
103 // Indexed arrays, needed for Safari
104 for (n=0, l = o.length; n<l; n++) {
105 if (cb.call(s, o[n], n, o) === false)
106 return 0;
107 }
108 } else {
109 // Hashtables
110 for (n in o) {
111 if (o.hasOwnProperty(n)) {
112 if (cb.call(s, o[n], n, o) === false)
113 return 0;
114 }
115 }
116 }
117
118 return 1;
119 },
120
121 map : function(a, f) {
122 var o = [];
123
124 tinymce.each(a, function(v) {
125 o.push(f(v));
126 });
127
128 return o;
129 },
130
131 grep : function(a, f) {
132 var o = [];
133
134 tinymce.each(a, function(v) {
135 if (!f || f(v))
136 o.push(v);
137 });
138
139 return o;
140 },
141
142 inArray : function(a, v) {
143 var i, l;
144
145 if (a) {
146 for (i = 0, l = a.length; i < l; i++) {
147 if (a[i] === v)
148 return i;
149 }
150 }
151
152 return -1;
153 },
154
155 extend : function(o, e) {
156 var i, a = arguments;
157
158 for (i=1; i<a.length; i++) {
159 e = a[i];
160
161 tinymce.each(e, function(v, n) {
162 if (typeof(v) !== 'undefined')
163 o[n] = v;
164 });
165 }
166
167 return o;
168 },
169
170 trim : function(s) {
171 return (s ? '' + s : '').replace(/^\s*|\s*$/g, '');
172 },
173
174
175 create : function(s, p) {
176 var t = this, sp, ns, cn, scn, c, de = 0;
177
178 // Parse : <prefix> <class>:<super class>
179 s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);
180 cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name
181
182 // Create namespace for new class
183 ns = t.createNS(s[3].replace(/\.\w+$/, ''));
184
185 // Class already exists
186 if (ns[cn])
187 return;
188
189 // Make pure static class
190 if (s[2] == 'static') {
191 ns[cn] = p;
192
193 if (this.onCreate)
194 this.onCreate(s[2], s[3], ns[cn]);
195
196 return;
197 }
198
199 // Create default constructor
200 if (!p[cn]) {
201 p[cn] = function() {};
202 de = 1;
203 }
204
205 // Add constructor and methods
206 ns[cn] = p[cn];
207 t.extend(ns[cn].prototype, p);
208
209 // Extend
210 if (s[5]) {
211 sp = t.resolve(s[5]).prototype;
212 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name
213
214 // Extend constructor
215 c = ns[cn];
216 if (de) {
217 // Add passthrough constructor
218 ns[cn] = function() {
219 return sp[scn].apply(this, arguments);
220 };
221 } else {
222 // Add inherit constructor
223 ns[cn] = function() {
224 this.parent = sp[scn];
225 return c.apply(this, arguments);
226 };
227 }
228 ns[cn].prototype[cn] = ns[cn];
229
230 // Add super methods
231 t.each(sp, function(f, n) {
232 ns[cn].prototype[n] = sp[n];
233 });
234
235 // Add overridden methods
236 t.each(p, function(f, n) {
237 // Extend methods if needed
238 if (sp[n]) {
239 ns[cn].prototype[n] = function() {
240 this.parent = sp[n];
241 return f.apply(this, arguments);
242 };
243 } else {
244 if (n != cn)
245 ns[cn].prototype[n] = f;
246 }
247 });
248 }
249
250 // Add static methods
251 t.each(p['static'], function(f, n) {
252 ns[cn][n] = f;
253 });
254
255 if (this.onCreate)
256 this.onCreate(s[2], s[3], ns[cn].prototype);
257 },
258
259 walk : function(o, f, n, s) {
260 s = s || this;
261
262 if (o) {
263 if (n)
264 o = o[n];
265
266 tinymce.each(o, function(o, i) {
267 if (f.call(s, o, i, n) === false)
268 return false;
269
270 tinymce.walk(o, f, n, s);
271 });
272 }
273 },
274
275 createNS : function(n, o) {
276 var i, v;
277
278 o = o || window;
279
280 n = n.split('.');
281 for (i=0; i<n.length; i++) {
282 v = n[i];
283
284 if (!o[v])
285 o[v] = {};
286
287 o = o[v];
288 }
289
290 return o;
291 },
292
293 resolve : function(n, o) {
294 var i, l;
295
296 o = o || window;
297
298 n = n.split('.');
299 for (i=0, l = n.length; i<l; i++) {
300 o = o[n[i]];
301
302 if (!o)
303 break;
304 }
305
306 return o;
307 },
308
309 addUnload : function(f, s) {
310 var t = this, w = window;
311
312 f = {func : f, scope : s || this};
313
314 if (!t.unloads) {
315 function unload() {
316 var li = t.unloads, o, n;
317
318 if (li) {
319 // Call unload handlers
320 for (n in li) {
321 o = li[n];
322
323 if (o && o.func)
324 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy
325 }
326
327 // Detach unload function
328 if (w.detachEvent) {
329 w.detachEvent('onbeforeunload', fakeUnload);
330 w.detachEvent('onunload', unload);
331 } else if (w.removeEventListener)
332 w.removeEventListener('unload', unload, false);
333
334 // Destroy references
335 t.unloads = o = li = w = unload = null;
336
337 // Run garbarge collector on IE
338 if (window.CollectGarbage)
339 window.CollectGarbage();
340 }
341 };
342
343 function fakeUnload() {
344 var d = document;
345
346 // Is there things still loading, then do some magic
347 if (d.readyState == 'interactive') {
348 function stop() {
349 // Prevent memory leak
350 d.detachEvent('onstop', stop);
351
352 // Call unload handler
353 unload();
354
355 d = null;
356 };
357
358 // Fire unload when the currently loading page is stopped
359 d.attachEvent('onstop', stop);
360
361 // Remove onstop listener after a while to prevent the unload function
362 // to execute if the user presses cancel in an onbeforeunload
363 // confirm dialog and then presses the browser stop button
364 window.setTimeout(function() {
365 d.detachEvent('onstop', stop);
366 }, 0);
367 }
368 };
369
370 // Attach unload handler
371 if (w.attachEvent) {
372 w.attachEvent('onunload', unload);
373 w.attachEvent('onbeforeunload', fakeUnload);
374 } else if (w.addEventListener)
375 w.addEventListener('unload', unload, false);
376
377 // Setup initial unload handler array
378 t.unloads = [f];
379 } else
380 t.unloads.push(f);
381
382 return f;
383 },
384
385 removeUnload : function(f) {
386 var u = this.unloads, r = null;
387
388 tinymce.each(u, function(o, i) {
389 if (o && o.func == f) {
390 u.splice(i, 1);
391 r = f;
392 return false;
393 }
394 });
395
396 return r;
397 },
398
399 explode : function(s, d) {
400 return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;
401 },
402
403 _addVer : function(u) {
404 var v;
405
406 if (!this.query)
407 return u;
408
409 v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;
410
411 if (u.indexOf('#') == -1)
412 return u + v;
413
414 return u.replace('#', v + '#');
415 }
416
417 };
418
419 // Required for GZip AJAX loading
420 window.tinymce = tinymce;
421
422 // Initialize the API
423 tinymce._init();
424 tinymce.create('tinymce.util.Dispatcher', {
425 scope : null,
426 listeners : null,
427
428 Dispatcher : function(s) {
429 this.scope = s || this;
430 this.listeners = [];
431 },
432
433 add : function(cb, s) {
434 this.listeners.push({cb : cb, scope : s || this.scope});
435
436 return cb;
437 },
438
439 addToTop : function(cb, s) {
440 this.listeners.unshift({cb : cb, scope : s || this.scope});
441
442 return cb;
443 },
444
445 remove : function(cb) {
446 var l = this.listeners, o = null;
447
448 tinymce.each(l, function(c, i) {
449 if (cb == c.cb) {
450 o = cb;
451 l.splice(i, 1);
452 return false;
453 }
454 });
455
456 return o;
457 },
458
459 dispatch : function() {
460 var s, a = arguments, i, li = this.listeners, c;
461
462 // Needs to be a real loop since the listener count might change while looping
463 // And this is also more efficient
464 for (i = 0; i<li.length; i++) {
465 c = li[i];
466 s = c.cb.apply(c.scope, a);
467
468 if (s === false)
469 break;
470 }
471
472 return s;
473 }
474
475 });
476 (function() {
477 var each = tinymce.each;
478
479 tinymce.create('tinymce.util.URI', {
480 URI : function(u, s) {
481 var t = this, o, a, b;
482
483 // Default settings
484 s = t.settings = s || {};
485
486 // Strange app protocol or local anchor
487 if (/^(mailto|tel|news|javascript|about):/i.test(u) || /^\s*#/.test(u)) {
488 t.source = u;
489 return;
490 }
491
492 // Absolute path with no host, fake host and protocol
493 if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)
494 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;
495
496 // Relative path
497 if (u.indexOf(':/') === -1 && u.indexOf('//') !== 0)
498 u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u);
499
500 // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
501 u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
502 u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);
503 each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {
504 var s = u[i];
505
506 // Zope 3 workaround, they use @@something
507 if (s)
508 s = s.replace(/\(mce_at\)/g, '@@');
509
510 t[v] = s;
511 });
512
513 if (b = s.base_uri) {
514 if (!t.protocol)
515 t.protocol = b.protocol;
516
517 if (!t.userInfo)
518 t.userInfo = b.userInfo;
519
520 if (!t.port && t.host == 'mce_host')
521 t.port = b.port;
522
523 if (!t.host || t.host == 'mce_host')
524 t.host = b.host;
525
526 t.source = '';
527 }
528
529 //t.path = t.path || '/';
530 },
531
532 setPath : function(p) {
533 var t = this;
534
535 p = /^(.*?)\/?(\w+)?$/.exec(p);
536
537 // Update path parts
538 t.path = p[0];
539 t.directory = p[1];
540 t.file = p[2];
541
542 // Rebuild source
543 t.source = '';
544 t.getURI();
545 },
546
547 toRelative : function(u) {
548 var t = this, o;
549
550 if (u === "./")
551 return u;
552
553 u = new tinymce.util.URI(u, {base_uri : t});
554
555 // Not on same domain/port or protocol
556 if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)
557 return u.getURI();
558
559 o = t.toRelPath(t.path, u.path);
560
561 // Add query
562 if (u.query)
563 o += '?' + u.query;
564
565 // Add anchor
566 if (u.anchor)
567 o += '#' + u.anchor;
568
569 return o;
570 },
571
572 toAbsolute : function(u, nh) {
573 var u = new tinymce.util.URI(u, {base_uri : this});
574
575 return u.getURI(this.host == u.host ? nh : 0);
576 },
577
578 toRelPath : function(base, path) {
579 var items, bp = 0, out = '', i, l;
580
581 // Split the paths
582 base = base.substring(0, base.lastIndexOf('/'));
583 base = base.split('/');
584 items = path.split('/');
585
586 if (base.length >= items.length) {
587 for (i = 0, l = base.length; i < l; i++) {
588 if (i >= items.length || base[i] != items[i]) {
589 bp = i + 1;
590 break;
591 }
592 }
593 }
594
595 if (base.length < items.length) {
596 for (i = 0, l = items.length; i < l; i++) {
597 if (i >= base.length || base[i] != items[i]) {
598 bp = i + 1;
599 break;
600 }
601 }
602 }
603
604 if (bp == 1)
605 return path;
606
607 for (i = 0, l = base.length - (bp - 1); i < l; i++)
608 out += "../";
609
610 for (i = bp - 1, l = items.length; i < l; i++) {
611 if (i != bp - 1)
612 out += "/" + items[i];
613 else
614 out += items[i];
615 }
616
617 return out;
618 },
619
620 toAbsPath : function(base, path) {
621 var i, nb = 0, o = [], tr;
622
623 // Split paths
624 tr = /\/$/.test(path) ? '/' : '';
625 base = base.split('/');
626 path = path.split('/');
627
628 // Remove empty chunks
629 each(base, function(k) {
630 if (k)
631 o.push(k);
632 });
633
634 base = o;
635
636 // Merge relURLParts chunks
637 for (i = path.length - 1, o = []; i >= 0; i--) {
638 // Ignore empty or .
639 if (path[i].length == 0 || path[i] == ".")
640 continue;
641
642 // Is parent
643 if (path[i] == '..') {
644 nb++;
645 continue;
646 }
647
648 // Move up
649 if (nb > 0) {
650 nb--;
651 continue;
652 }
653
654 o.push(path[i]);
655 }
656
657 i = base.length - nb;
658
659 // If /a/b/c or /
660 if (i <= 0)
661 return '/' + o.reverse().join('/') + tr;
662
663 return '/' + base.slice(0, i).join('/') + '/' + o.reverse().join('/') + tr;
664 },
665
666 getURI : function(nh) {
667 var s, t = this;
668
669 // Rebuild source
670 if (!t.source || nh) {
671 s = '';
672
673 if (!nh) {
674 if (t.protocol)
675 s += t.protocol + '://';
676
677 if (t.userInfo)
678 s += t.userInfo + '@';
679
680 if (t.host)
681 s += t.host;
682
683 if (t.port)
684 s += ':' + t.port;
685 }
686
687 if (t.path)
688 s += t.path;
689
690 if (t.query)
691 s += '?' + t.query;
692
693 if (t.anchor)
694 s += '#' + t.anchor;
695
696 t.source = s;
697 }
698
699 return t.source;
700 }
701
702 });
703 })();
704 (function() {
705 var each = tinymce.each;
706
707 tinymce.create('static tinymce.util.Cookie', {
708 getHash : function(n) {
709 var v = this.get(n), h;
710
711 if (v) {
712 each(v.split('&'), function(v) {
713 v = v.split('=');
714 h = h || {};
715 h[unescape(v[0])] = unescape(v[1]);
716 });
717 }
718
719 return h;
720 },
721
722 setHash : function(n, v, e, p, d, s) {
723 var o = '';
724
725 each(v, function(v, k) {
726 o += (!o ? '' : '&') + escape(k) + '=' + escape(v);
727 });
728
729 this.set(n, o, e, p, d, s);
730 },
731
732 get : function(n) {
733 var c = document.cookie, e, p = n + "=", b;
734
735 // Strict mode
736 if (!c)
737 return;
738
739 b = c.indexOf("; " + p);
740
741 if (b == -1) {
742 b = c.indexOf(p);
743
744 if (b != 0)
745 return null;
746 } else
747 b += 2;
748
749 e = c.indexOf(";", b);
750
751 if (e == -1)
752 e = c.length;
753
754 return unescape(c.substring(b + p.length, e));
755 },
756
757 set : function(n, v, e, p, d, s) {
758 document.cookie = n + "=" + escape(v) +
759 ((e) ? "; expires=" + e.toGMTString() : "") +
760 ((p) ? "; path=" + escape(p) : "") +
761 ((d) ? "; domain=" + d : "") +
762 ((s) ? "; secure" : "");
763 },
764
765 remove : function(n, p) {
766 var d = new Date();
767
768 d.setTime(d.getTime() - 1000);
769
770 this.set(n, '', d, p, d);
771 }
772
773 });
774 })();
775 tinymce.create('static tinymce.util.JSON', {
776 serialize : function(o) {
777 var i, v, s = tinymce.util.JSON.serialize, t;
778
779 if (o == null)
780 return 'null';
781
782 t = typeof o;
783
784 if (t == 'string') {
785 v = '\bb\tt\nn\ff\rr\""\'\'\\\\';
786
787 return '"' + o.replace(/([\u0080-\uFFFF\x00-\x1f\"])/g, function(a, b) {
788 i = v.indexOf(b);
789
790 if (i + 1)
791 return '\\' + v.charAt(i + 1);
792
793 a = b.charCodeAt().toString(16);
794
795 return '\\u' + '0000'.substring(a.length) + a;
796 }) + '"';
797 }
798
799 if (t == 'object') {
800 if (o.hasOwnProperty && o instanceof Array) {
801 for (i=0, v = '['; i<o.length; i++)
802 v += (i > 0 ? ',' : '') + s(o[i]);
803
804 return v + ']';
805 }
806
807 v = '{';
808
809 for (i in o)
810 v += typeof o[i] != 'function' ? (v.length > 1 ? ',"' : '"') + i + '":' + s(o[i]) : '';
811
812 return v + '}';
813 }
814
815 return '' + o;
816 },
817
818 parse : function(s) {
819 try {
820 return eval('(' + s + ')');
821 } catch (ex) {
822 // Ignore
823 }
824 }
825
826 });
827 tinymce.create('static tinymce.util.XHR', {
828 send : function(o) {
829 var x, t, w = window, c = 0;
830
831 // Default settings
832 o.scope = o.scope || this;
833 o.success_scope = o.success_scope || o.scope;
834 o.error_scope = o.error_scope || o.scope;
835 o.async = o.async === false ? false : true;
836 o.data = o.data || '';
837
838 function get(s) {
839 x = 0;
840
841 try {
842 x = new ActiveXObject(s);
843 } catch (ex) {
844 }
845
846 return x;
847 };
848
849 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');
850
851 if (x) {
852 if (x.overrideMimeType)
853 x.overrideMimeType(o.content_type);
854
855 x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);
856
857 if (o.content_type)
858 x.setRequestHeader('Content-Type', o.content_type);
859
860 x.send(o.data);
861
862 function ready() {
863 if (!o.async || x.readyState == 4 || c++ > 10000) {
864 if (o.success && c < 10000 && x.status == 200)
865 o.success.call(o.success_scope, '' + x.responseText, x, o);
866 else if (o.error)
867 o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);
868
869 x = null;
870 } else
871 w.setTimeout(ready, 10);
872 };
873
874 // Syncronous request
875 if (!o.async)
876 return ready();
877
878 // Wait for response, onReadyStateChange can not be used since it leaks memory in IE
879 t = w.setTimeout(ready, 10);
880 }
881
882 }
883 });
884 (function() {
885 var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;
886
887 tinymce.create('tinymce.util.JSONRequest', {
888 JSONRequest : function(s) {
889 this.settings = extend({
890 }, s);
891 this.count = 0;
892 },
893
894 send : function(o) {
895 var ecb = o.error, scb = o.success;
896
897 o = extend(this.settings, o);
898
899 o.success = function(c, x) {
900 c = JSON.parse(c);
901
902 if (typeof(c) == 'undefined') {
903 c = {
904 error : 'JSON Parse error.'
905 };
906 }
907
908 if (c.error)
909 ecb.call(o.error_scope || o.scope, c.error, x);
910 else
911 scb.call(o.success_scope || o.scope, c.result);
912 };
913
914 o.error = function(ty, x) {
915 ecb.call(o.error_scope || o.scope, ty, x);
916 };
917
918 o.data = JSON.serialize({
919 id : o.id || 'c' + (this.count++),
920 method : o.method,
921 params : o.params
922 });
923
924 // JSON content type for Ruby on rails. Bug: #1883287
925 o.content_type = 'application/json';
926
927 XHR.send(o);
928 },
929
930 'static' : {
931 sendRPC : function(o) {
932 return new tinymce.util.JSONRequest().send(o);
933 }
934 }
935
936 });
937 }());(function(tinymce) {
938 // Shorten names
939 var each = tinymce.each, is = tinymce.is;
940 var isWebKit = tinymce.isWebKit, isIE = tinymce.isIE;
941
942 tinymce.create('tinymce.dom.DOMUtils', {
943 doc : null,
944 root : null,
945 files : null,
946 pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,
947 props : {
948 "for" : "htmlFor",
949 "class" : "className",
950 className : "className",
951 checked : "checked",
952 disabled : "disabled",
953 maxlength : "maxLength",
954 readonly : "readOnly",
955 selected : "selected",
956 value : "value",
957 id : "id",
958 name : "name",
959 type : "type"
960 },
961
962 DOMUtils : function(d, s) {
963 var t = this;
964
965 t.doc = d;
966 t.win = window;
967 t.files = {};
968 t.cssFlicker = false;
969 t.counter = 0;
970 t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat";
971 t.stdMode = d.documentMode === 8;
972
973 this.settings = s = tinymce.extend({
974 keep_values : false,
975 hex_colors : 1,
976 process_html : 1
977 }, s);
978
979 // Fix IE6SP2 flicker and check it failed for pre SP2
980 if (tinymce.isIE6) {
981 try {
982 d.execCommand('BackgroundImageCache', false, true);
983 } catch (e) {
984 t.cssFlicker = true;
985 }
986 }
987
988 tinymce.addUnload(t.destroy, t);
989 },
990
991 getRoot : function() {
992 var t = this, s = t.settings;
993
994 return (s && t.get(s.root_element)) || t.doc.body;
995 },
996
997 getViewPort : function(w) {
998 var d, b;
999
1000 w = !w ? this.win : w;
1001 d = w.document;
1002 b = this.boxModel ? d.documentElement : d.body;
1003
1004 // Returns viewport size excluding scrollbars
1005 return {
1006 x : w.pageXOffset || b.scrollLeft,
1007 y : w.pageYOffset || b.scrollTop,
1008 w : w.innerWidth || b.clientWidth,
1009 h : w.innerHeight || b.clientHeight
1010 };
1011 },
1012
1013 getRect : function(e) {
1014 var p, t = this, sr;
1015
1016 e = t.get(e);
1017 p = t.getPos(e);
1018 sr = t.getSize(e);
1019
1020 return {
1021 x : p.x,
1022 y : p.y,
1023 w : sr.w,
1024 h : sr.h
1025 };
1026 },
1027
1028 getSize : function(e) {
1029 var t = this, w, h;
1030
1031 e = t.get(e);
1032 w = t.getStyle(e, 'width');
1033 h = t.getStyle(e, 'height');
1034
1035 // Non pixel value, then force offset/clientWidth
1036 if (w.indexOf('px') === -1)
1037 w = 0;
1038
1039 // Non pixel value, then force offset/clientWidth
1040 if (h.indexOf('px') === -1)
1041 h = 0;
1042
1043 return {
1044 w : parseInt(w) || e.offsetWidth || e.clientWidth,
1045 h : parseInt(h) || e.offsetHeight || e.clientHeight
1046 };
1047 },
1048
1049 is : function(n, patt) {
1050 return tinymce.dom.Sizzle.matches(patt, n.nodeType ? [n] : n).length > 0;
1051 },
1052
1053 getParent : function(n, f, r) {
1054 return this.getParents(n, f, r, false);
1055 },
1056
1057 getParents : function(n, f, r, c) {
1058 var t = this, na, se = t.settings, o = [];
1059
1060 n = t.get(n);
1061 c = c === undefined;
1062
1063 if (se.strict_root)
1064 r = r || t.getRoot();
1065
1066 // Wrap node name as func
1067 if (is(f, 'string')) {
1068 na = f;
1069
1070 if (f === '*') {
1071 f = function(n) {return n.nodeType == 1;};
1072 } else {
1073 f = function(n) {
1074 return t.is(n, na);
1075 };
1076 }
1077 }
1078
1079 while (n) {
1080 if (n == r)
1081 break;
1082
1083 if (!f || f(n)) {
1084 if (c)
1085 o.push(n);
1086 else
1087 return n;
1088 }
1089
1090 n = n.parentNode;
1091 }
1092
1093 return c ? o : null;
1094 },
1095
1096 get : function(e) {
1097 var n;
1098
1099 if (e && this.doc && typeof(e) == 'string') {
1100 n = e;
1101 e = this.doc.getElementById(e);
1102
1103 // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick
1104 if (e && e.id !== n)
1105 return this.doc.getElementsByName(n)[1];
1106 }
1107
1108 return e;
1109 },
1110
1111
1112 select : function(pa, s) {
1113 var t = this;
1114
1115 return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []);
1116 },
1117
1118
1119 add : function(p, n, a, h, c) {
1120 var t = this;
1121
1122 return this.run(p, function(p) {
1123 var e, k;
1124
1125 e = is(n, 'string') ? t.doc.createElement(n) : n;
1126 t.setAttribs(e, a);
1127
1128 if (h) {
1129 if (h.nodeType)
1130 e.appendChild(h);
1131 else
1132 t.setHTML(e, h);
1133 }
1134
1135 return !c ? p.appendChild(e) : e;
1136 });
1137 },
1138
1139 create : function(n, a, h) {
1140 return this.add(this.doc.createElement(n), n, a, h, 1);
1141 },
1142
1143 createHTML : function(n, a, h) {
1144 var o = '', t = this, k;
1145
1146 o += '<' + n;
1147
1148 for (k in a) {
1149 if (a.hasOwnProperty(k))
1150 o += ' ' + k + '="' + t.encode(a[k]) + '"';
1151 }
1152
1153 if (tinymce.is(h))
1154 return o + '>' + h + '</' + n + '>';
1155
1156 return o + ' />';
1157 },
1158
1159 remove : function(n, k) {
1160 var t = this;
1161
1162 return this.run(n, function(n) {
1163 var p, g, i;
1164
1165 p = n.parentNode;
1166
1167 if (!p)
1168 return null;
1169
1170 if (k) {
1171 for (i = n.childNodes.length - 1; i >= 0; i--)
1172 t.insertAfter(n.childNodes[i], n);
1173
1174 //each(n.childNodes, function(c) {
1175 // p.insertBefore(c.cloneNode(true), n);
1176 //});
1177 }
1178
1179 // Fix IE psuedo leak
1180 if (t.fixPsuedoLeaks) {
1181 p = n.cloneNode(true);
1182 k = 'IELeakGarbageBin';
1183 g = t.get(k) || t.add(t.doc.body, 'div', {id : k, style : 'display:none'});
1184 g.appendChild(n);
1185 g.innerHTML = '';
1186
1187 return p;
1188 }
1189
1190 return p.removeChild(n);
1191 });
1192 },
1193
1194
1195 setStyle : function(n, na, v) {
1196 var t = this;
1197
1198 return t.run(n, function(e) {
1199 var s, i;
1200
1201 s = e.style;
1202
1203 // Camelcase it, if needed
1204 na = na.replace(/-(\D)/g, function(a, b){
1205 return b.toUpperCase();
1206 });
1207
1208 // Default px suffix on these
1209 if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v)))
1210 v += 'px';
1211
1212 switch (na) {
1213 case 'opacity':
1214 // IE specific opacity
1215 if (isIE) {
1216 s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")";
1217
1218 if (!n.currentStyle || !n.currentStyle.hasLayout)
1219 s.display = 'inline-block';
1220 }
1221
1222 // Fix for older browsers
1223 s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || '';
1224 break;
1225
1226 case 'float':
1227 isIE ? s.styleFloat = v : s.cssFloat = v;
1228 break;
1229
1230 default:
1231 s[na] = v || '';
1232 }
1233
1234 // Force update of the style data
1235 if (t.settings.update_styles)
1236 t.setAttrib(e, 'mce_style');
1237 });
1238 },
1239
1240 getStyle : function(n, na, c) {
1241 n = this.get(n);
1242
1243 if (!n)
1244 return false;
1245
1246 // Gecko
1247 if (this.doc.defaultView && c) {
1248 // Remove camelcase
1249 na = na.replace(/[A-Z]/g, function(a){
1250 return '-' + a;
1251 });
1252
1253 try {
1254 return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);
1255 } catch (ex) {
1256 // Old safari might fail
1257 return null;
1258 }
1259 }
1260
1261 // Camelcase it, if needed
1262 na = na.replace(/-(\D)/g, function(a, b){
1263 return b.toUpperCase();
1264 });
1265
1266 if (na == 'float')
1267 na = isIE ? 'styleFloat' : 'cssFloat';
1268
1269 // IE & Opera
1270 if (n.currentStyle && c)
1271 return n.currentStyle[na];
1272
1273 return n.style[na];
1274 },
1275
1276 setStyles : function(e, o) {
1277 var t = this, s = t.settings, ol;
1278
1279 ol = s.update_styles;
1280 s.update_styles = 0;
1281
1282 each(o, function(v, n) {
1283 t.setStyle(e, n, v);
1284 });
1285
1286 // Update style info
1287 s.update_styles = ol;
1288 if (s.update_styles)
1289 t.setAttrib(e, s.cssText);
1290 },
1291
1292 setAttrib : function(e, n, v) {
1293 var t = this;
1294
1295 // Whats the point
1296 if (!e || !n)
1297 return;
1298
1299 // Strict XML mode
1300 if (t.settings.strict)
1301 n = n.toLowerCase();
1302
1303 return this.run(e, function(e) {
1304 var s = t.settings;
1305
1306 switch (n) {
1307 case "style":
1308 if (!is(v, 'string')) {
1309 each(v, function(v, n) {
1310 t.setStyle(e, n, v);
1311 });
1312
1313 return;
1314 }
1315
1316 // No mce_style for elements with these since they might get resized by the user
1317 if (s.keep_values) {
1318 if (v && !t._isRes(v))
1319 e.setAttribute('mce_style', v, 2);
1320 else
1321 e.removeAttribute('mce_style', 2);
1322 }
1323
1324 e.style.cssText = v;
1325 break;
1326
1327 case "class":
1328 e.className = v || ''; // Fix IE null bug
1329 break;
1330
1331 case "src":
1332 case "href":
1333 if (s.keep_values) {
1334 if (s.url_converter)
1335 v = s.url_converter.call(s.url_converter_scope || t, v, n, e);
1336
1337 t.setAttrib(e, 'mce_' + n, v, 2);
1338 }
1339
1340 break;
1341
1342 case "shape":
1343 e.setAttribute('mce_style', v);
1344 break;
1345 }
1346
1347 if (is(v) && v !== null && v.length !== 0)
1348 e.setAttribute(n, '' + v, 2);
1349 else
1350 e.removeAttribute(n, 2);
1351 });
1352 },
1353
1354 setAttribs : function(e, o) {
1355 var t = this;
1356
1357 return this.run(e, function(e) {
1358 each(o, function(v, n) {
1359 t.setAttrib(e, n, v);
1360 });
1361 });
1362 },
1363
1364
1365 getAttrib : function(e, n, dv) {
1366 var v, t = this;
1367
1368 e = t.get(e);
1369
1370 if (!e || e.nodeType !== 1)
1371 return false;
1372
1373 if (!is(dv))
1374 dv = '';
1375
1376 // Try the mce variant for these
1377 if (/^(src|href|style|coords|shape)$/.test(n)) {
1378 v = e.getAttribute("mce_" + n);
1379
1380 if (v)
1381 return v;
1382 }
1383
1384 if (isIE && t.props[n]) {
1385 v = e[t.props[n]];
1386 v = v && v.nodeValue ? v.nodeValue : v;
1387 }
1388
1389 if (!v)
1390 v = e.getAttribute(n, 2);
1391
1392 if (n === 'style') {
1393 v = v || e.style.cssText;
1394
1395 if (v) {
1396 v = t.serializeStyle(t.parseStyle(v));
1397
1398 if (t.settings.keep_values && !t._isRes(v))
1399 e.setAttribute('mce_style', v);
1400 }
1401 }
1402
1403 // Remove Apple and WebKit stuff
1404 if (isWebKit && n === "class" && v)
1405 v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, '');
1406
1407 // Handle IE issues
1408 if (isIE) {
1409 switch (n) {
1410 case 'rowspan':
1411 case 'colspan':
1412 // IE returns 1 as default value
1413 if (v === 1)
1414 v = '';
1415
1416 break;
1417
1418 case 'size':
1419 // IE returns +0 as default value for size
1420 if (v === '+0' || v === 20 || v === 0)
1421 v = '';
1422
1423 break;
1424
1425 case 'width':
1426 case 'height':
1427 case 'vspace':
1428 case 'checked':
1429 case 'disabled':
1430 case 'readonly':
1431 if (v === 0)
1432 v = '';
1433
1434 break;
1435
1436 case 'hspace':
1437 // IE returns -1 as default value
1438 if (v === -1)
1439 v = '';
1440
1441 break;
1442
1443 case 'maxlength':
1444 case 'tabindex':
1445 // IE returns default value
1446 if (v === 32768 || v === 2147483647 || v === '32768')
1447 v = '';
1448
1449 break;
1450
1451 case 'multiple':
1452 case 'compact':
1453 case 'noshade':
1454 case 'nowrap':
1455 if (v === 65535)
1456 return n;
1457
1458 return dv;
1459
1460 case 'shape':
1461 v = v.toLowerCase();
1462 break;
1463
1464 default:
1465 // IE has odd anonymous function for event attributes
1466 if (n.indexOf('on') === 0 && v)
1467 v = ('' + v).replace(/^function\s+anonymous\(\)\s+\{\s+(.*)\s+\}$/, '$1');
1468 }
1469 }
1470
1471 return (v !== undefined && v !== null && v !== '') ? '' + v : dv;
1472 },
1473
1474 getPos : function(n) {
1475 var t = this, x = 0, y = 0, e, d = t.doc, r;
1476
1477 n = t.get(n);
1478
1479 // Use getBoundingClientRect on IE, Opera has it but it's not perfect
1480 if (n && isIE && !t.stdMode) {
1481 n = n.getBoundingClientRect();
1482 e = t.boxModel ? d.documentElement : d.body;
1483 x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border
1484 x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x;
1485 n.top += t.win.self != t.win.top ? 2 : 0; // IE adds some strange extra cord if used in a frameset
1486
1487 return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x};
1488 }
1489
1490 r = n;
1491 while (r) {
1492 x += r.offsetLeft || 0;
1493 y += r.offsetTop || 0;
1494 r = r.offsetParent;
1495 }
1496
1497 r = n;
1498 while (r) {
1499 // Opera 9.25 bug fix, fixed in 9.50
1500 if (!/^table-row|inline.*/i.test(t.getStyle(r, "display", 1))) {
1501 x -= r.scrollLeft || 0;
1502 y -= r.scrollTop || 0;
1503 }
1504
1505 r = r.parentNode;
1506
1507 // No node type or document type
1508 if (!r.nodeType || r.nodeType == 9 || r.nodeName.toLowerCase() == 'body')
1509 break;
1510 }
1511
1512 return {x : x, y : y};
1513 },
1514
1515 parseStyle : function(st) {
1516 var t = this, s = t.settings, o = {};
1517
1518 if (!st)
1519 return o;
1520
1521 function compress(p, s, ot) {
1522 var t, r, b, l;
1523
1524 // Get values and check it it needs compressing
1525 t = o[p + '-top' + s];
1526 if (!t)
1527 return;
1528
1529 r = o[p + '-right' + s];
1530 if (t != r)
1531 return;
1532
1533 b = o[p + '-bottom' + s];
1534 if (r != b)
1535 return;
1536
1537 l = o[p + '-left' + s];
1538 if (b != l)
1539 return;
1540
1541 // Compress
1542 o[ot] = l;
1543 delete o[p + '-top' + s];
1544 delete o[p + '-right' + s];
1545 delete o[p + '-bottom' + s];
1546 delete o[p + '-left' + s];
1547 };
1548
1549 function compress2(ta, a, b, c) {
1550 var t;
1551
1552 t = o[a];
1553 if (!t)
1554 return;
1555
1556 t = o[b];
1557 if (!t)
1558 return;
1559
1560 t = o[c];
1561 if (!t)
1562 return;
1563
1564 // Compress
1565 o[ta] = o[a] + ' ' + o[b] + ' ' + o[c];
1566 delete o[a];
1567 delete o[b];
1568 delete o[c];
1569 };
1570
1571 st = st.replace(/&(#?[a-z0-9]+);/g, '&$1_MCE_SEMI_'); // Protect entities
1572
1573 each(st.split(';'), function(v) {
1574 var sv, ur = [];
1575
1576 if (v) {
1577 v = v.replace(/_MCE_SEMI_/g, ';'); // Restore entities
1578 v = v.replace(/url\([^\)]+\)/g, function(v) {ur.push(v);return 'url(' + ur.length + ')';});
1579 v = v.split(':');
1580 sv = tinymce.trim(v[1]);
1581 sv = sv.replace(/url\(([^\)]+)\)/g, function(a, b) {return ur[parseInt(b) - 1];});
1582
1583 sv = sv.replace(/rgb\([^\)]+\)/g, function(v) {
1584 return t.toHex(v);
1585 });
1586
1587 if (s.url_converter) {
1588 sv = sv.replace(/url\([\'\"]?([^\)\'\"]+)[\'\"]?\)/g, function(x, c) {
1589 return 'url(' + s.url_converter.call(s.url_converter_scope || t, t.decode(c), 'style', null) + ')';
1590 });
1591 }
1592
1593 o[tinymce.trim(v[0]).toLowerCase()] = sv;
1594 }
1595 });
1596
1597 compress("border", "", "border");
1598 compress("border", "-width", "border-width");
1599 compress("border", "-color", "border-color");
1600 compress("border", "-style", "border-style");
1601 compress("padding", "", "padding");
1602 compress("margin", "", "margin");
1603 compress2('border', 'border-width', 'border-style', 'border-color');
1604
1605 if (isIE) {
1606 // Remove pointless border
1607 if (o.border == 'medium none')
1608 o.border = '';
1609 }
1610
1611 return o;
1612 },
1613
1614 serializeStyle : function(o) {
1615 var s = '';
1616
1617 each(o, function(v, k) {
1618 if (k && v) {
1619 if (tinymce.isGecko && k.indexOf('-moz-') === 0)
1620 return;
1621
1622 switch (k) {
1623 case 'color':
1624 case 'background-color':
1625 v = v.toLowerCase();
1626 break;
1627 }
1628
1629 s += (s ? ' ' : '') + k + ': ' + v + ';';
1630 }
1631 });
1632
1633 return s;
1634 },
1635
1636 loadCSS : function(u) {
1637 var t = this, d = t.doc;
1638
1639 if (!u)
1640 u = '';
1641
1642 each(u.split(','), function(u) {
1643 if (t.files[u])
1644 return;
1645
1646 t.files[u] = true;
1647 t.add(t.select('head')[0], 'link', {rel : 'stylesheet', href : tinymce._addVer(u)});
1648 });
1649 },
1650
1651
1652 addClass : function(e, c) {
1653 return this.run(e, function(e) {
1654 var o;
1655
1656 if (!c)
1657 return 0;
1658
1659 if (this.hasClass(e, c))
1660 return e.className;
1661
1662 o = this.removeClass(e, c);
1663
1664 return e.className = (o != '' ? (o + ' ') : '') + c;
1665 });
1666 },
1667
1668 removeClass : function(e, c) {
1669 var t = this, re;
1670
1671 return t.run(e, function(e) {
1672 var v;
1673
1674 if (t.hasClass(e, c)) {
1675 if (!re)
1676 re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g");
1677
1678 v = e.className.replace(re, ' ');
1679
1680 return e.className = tinymce.trim(v != ' ' ? v : '');
1681 }
1682
1683 return e.className;
1684 });
1685 },
1686
1687 hasClass : function(n, c) {
1688 n = this.get(n);
1689
1690 if (!n || !c)
1691 return false;
1692
1693 return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1;
1694 },
1695
1696 show : function(e) {
1697 return this.setStyle(e, 'display', 'block');
1698 },
1699
1700 hide : function(e) {
1701 return this.setStyle(e, 'display', 'none');
1702 },
1703
1704 isHidden : function(e) {
1705 e = this.get(e);
1706
1707 return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none';
1708 },
1709
1710
1711 uniqueId : function(p) {
1712 return (!p ? 'mce_' : p) + (this.counter++);
1713 },
1714
1715 setHTML : function(e, h) {
1716 var t = this;
1717
1718 return this.run(e, function(e) {
1719 var x, i, nl, n, p, x;
1720
1721 h = t.processHTML(h);
1722
1723 if (isIE) {
1724 function set() {
1725 try {
1726 // IE will remove comments from the beginning
1727 // unless you padd the contents with something
1728 e.innerHTML = '<br />' + h;
1729 e.removeChild(e.firstChild);
1730 } catch (ex) {
1731 // 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
1732 // This seems to fix this problem
1733
1734 // Remove all child nodes
1735 while (e.firstChild)
1736 e.firstChild.removeNode();
1737
1738 // Create new div with HTML contents and a BR infront to keep comments
1739 x = t.create('div');
1740 x.innerHTML = '<br />' + h;
1741
1742 // Add all children from div to target
1743 each (x.childNodes, function(n, i) {
1744 // Skip br element
1745 if (i)
1746 e.appendChild(n);
1747 });
1748 }
1749 };
1750
1751 // IE has a serious bug when it comes to paragraphs it can produce an invalid
1752 // DOM tree if contents like this <p><ul><li>Item 1</li></ul></p> is inserted
1753 // It seems to be that IE doesn't like a root block element placed inside another root block element
1754 if (t.settings.fix_ie_paragraphs)
1755 h = h.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi, '<p$1 mce_keep="true">&nbsp;</p>');
1756
1757 set();
1758
1759 if (t.settings.fix_ie_paragraphs) {
1760 // Check for odd paragraphs this is a sign of a broken DOM
1761 nl = e.getElementsByTagName("p");
1762 for (i = nl.length - 1, x = 0; i >= 0; i--) {
1763 n = nl[i];
1764
1765 if (!n.hasChildNodes()) {
1766 if (!n.mce_keep) {
1767 x = 1; // Is broken
1768 break;
1769 }
1770
1771 n.removeAttribute('mce_keep');
1772 }
1773 }
1774 }
1775
1776 // Time to fix the madness IE left us
1777 if (x) {
1778 // So if we replace the p elements with divs and mark them and then replace them back to paragraphs
1779 // after we use innerHTML we can fix the DOM tree
1780 h = h.replace(/<p ([^>]+)>|<p>/g, '<div $1 mce_tmp="1">');
1781 h = h.replace(/<\/p>/g, '</div>');
1782
1783 // Set the new HTML with DIVs
1784 set();
1785
1786 // Replace all DIV elements with he mce_tmp attibute back to paragraphs
1787 // This is needed since IE has a annoying bug see above for details
1788 // This is a slow process but it has to be done. :(
1789 if (t.settings.fix_ie_paragraphs) {
1790 nl = e.getElementsByTagName("DIV");
1791 for (i = nl.length - 1; i >= 0; i--) {
1792 n = nl[i];
1793
1794 // Is it a temp div
1795 if (n.mce_tmp) {
1796 // Create new paragraph
1797 p = t.doc.createElement('p');
1798
1799 // Copy all attributes
1800 n.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi, function(a, b) {
1801 var v;
1802
1803 if (b !== 'mce_tmp') {
1804 v = n.getAttribute(b);
1805
1806 if (!v && b === 'class')
1807 v = n.className;
1808
1809 p.setAttribute(b, v);
1810 }
1811 });
1812
1813 // Append all children to new paragraph
1814 for (x = 0; x<n.childNodes.length; x++)
1815 p.appendChild(n.childNodes[x].cloneNode(true));
1816
1817 // Replace div with new paragraph
1818 n.swapNode(p);
1819 }
1820 }
1821 }
1822 }
1823 } else
1824 e.innerHTML = h;
1825
1826 return h;
1827 });
1828 },
1829
1830 processHTML : function(h) {
1831 var t = this, s = t.settings;
1832
1833 if (!s.process_html)
1834 return h;
1835
1836 // Convert strong and em to b and i in FF since it can't handle them
1837 if (tinymce.isGecko) {
1838 h = h.replace(/<(\/?)strong>|<strong( [^>]+)>/gi, '<$1b$2>');
1839 h = h.replace(/<(\/?)em>|<em( [^>]+)>/gi, '<$1i$2>');
1840 } else if (isIE) {
1841 h = h.replace(/&apos;/g, '&#39;'); // IE can't handle apos
1842 h = h.replace(/\s+(disabled|checked|readonly|selected)\s*=\s*[\"\']?(false|0)[\"\']?/gi, ''); // IE doesn't handle default values correct
1843 }
1844
1845 // Fix some issues
1846 h = h.replace(/<a( )([^>]+)\/>|<a\/>/gi, '<a$1$2></a>'); // Force open
1847
1848 // Store away src and href in mce_src and mce_href since browsers mess them up
1849 if (s.keep_values) {
1850 // Wrap scripts and styles in comments for serialization purposes
1851 if (/<script|style/.test(h)) {
1852 function trim(s) {
1853 // Remove prefix and suffix code for element
1854 s = s.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n');
1855 s = s.replace(/^[\r\n]*|[\r\n]*$/g, '');
1856 s = s.replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, '');
1857 s = s.replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, '');
1858
1859 return s;
1860 };
1861
1862 // Preserve script elements
1863 h = h.replace(/<script([^>]+|)>([\s\S]*?)<\/script>/g, function(v, a, b) {
1864 // Remove prefix and suffix code for script element
1865 b = trim(b);
1866
1867 // Force type attribute
1868 if (!a)
1869 a = ' type="text/javascript"';
1870
1871 // Wrap contents in a comment
1872 if (b)
1873 b = '<!--\n' + b + '\n// -->';
1874
1875 // Output fake element
1876 return '<mce:script' + a + '>' + b + '</mce:script>';
1877 });
1878
1879 // Preserve style elements
1880 h = h.replace(/<style([^>]+|)>([\s\S]*?)<\/style>/g, function(v, a, b) {
1881 b = trim(b);
1882 return '<mce:style' + a + '><!--\n' + b + '\n--></mce:style><style' + a + ' mce_bogus="1">' + b + '</style>';
1883 });
1884 }
1885
1886 h = h.replace(/<!\[CDATA\[([\s\S]+)\]\]>/g, '<!--[CDATA[$1]]-->');
1887
1888 // Process all tags with src, href or style
1889 h = h.replace(/<([\w:]+) [^>]*(src|href|style|shape|coords)[^>]*>/gi, function(a, n) {
1890 function handle(m, b, c) {
1891 var u = c;
1892
1893 // Tag already got a mce_ version
1894 if (a.indexOf('mce_' + b) != -1)
1895 return m;
1896
1897 if (b == 'style') {
1898 // Why did I need this one?
1899 //if (isIE)
1900 // u = t.serializeStyle(t.parseStyle(u));
1901
1902 // No mce_style for elements with these since they might get resized by the user
1903 if (t._isRes(c))
1904 return m;
1905
1906 if (s.hex_colors) {
1907 u = u.replace(/rgb\([^\)]+\)/g, function(v) {
1908 return t.toHex(v);
1909 });
1910 }
1911
1912 if (s.url_converter) {
1913 u = u.replace(/url\([\'\"]?([^\)\'\"]+)\)/g, function(x, c) {
1914 return 'url(' + t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), b, n)) + ')';
1915 });
1916 }
1917 } else if (b != 'coords' && b != 'shape') {
1918 if (s.url_converter)
1919 u = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), b, n));
1920 }
1921
1922 return ' ' + b + '="' + c + '" mce_' + b + '="' + u + '"';
1923 };
1924
1925 a = a.replace(/ (src|href|style|coords|shape)=[\"]([^\"]+)[\"]/gi, handle); // W3C
1926 a = a.replace(/ (src|href|style|coords|shape)=[\']([^\']+)[\']/gi, handle); // W3C
1927
1928 return a.replace(/ (src|href|style|coords|shape)=([^\s\"\'>]+)/gi, handle); // IE
1929 });
1930 }
1931
1932 return h;
1933 },
1934
1935 getOuterHTML : function(e) {
1936 var d;
1937
1938 e = this.get(e);
1939
1940 if (!e)
1941 return null;
1942
1943 if (e.outerHTML !== undefined)
1944 return e.outerHTML;
1945
1946 d = (e.ownerDocument || this.doc).createElement("body");
1947 d.appendChild(e.cloneNode(true));
1948
1949 return d.innerHTML;
1950 },
1951
1952 setOuterHTML : function(e, h, d) {
1953 var t = this;
1954
1955 return this.run(e, function(e) {
1956 var n, tp;
1957
1958 e = t.get(e);
1959 d = d || e.ownerDocument || t.doc;
1960
1961 if (isIE && e.nodeType == 1)
1962 e.outerHTML = h;
1963 else {
1964 tp = d.createElement("body");
1965 tp.innerHTML = h;
1966
1967 n = tp.lastChild;
1968 while (n) {
1969 t.insertAfter(n.cloneNode(true), e);
1970 n = n.previousSibling;
1971 }
1972
1973 t.remove(e);
1974 }
1975 });
1976 },
1977
1978 decode : function(s) {
1979 var e, n, v;
1980
1981 // Look for entities to decode
1982 if (/&[^;]+;/.test(s)) {
1983 // Decode the entities using a div element not super efficient but less code
1984 e = this.doc.createElement("div");
1985 e.innerHTML = s;
1986 n = e.firstChild;
1987 v = '';
1988
1989 if (n) {
1990 do {
1991 v += n.nodeValue;
1992 } while (n.nextSibling);
1993 }
1994
1995 return v || s;
1996 }
1997
1998 return s;
1999 },
2000
2001 encode : function(s) {
2002 return s ? ('' + s).replace(/[<>&\"]/g, function (c, b) {
2003 switch (c) {
2004 case '&':
2005 return '&amp;';
2006
2007 case '"':
2008 return '&quot;';
2009
2010 case '<':
2011 return '&lt;';
2012
2013 case '>':
2014 return '&gt;';
2015 }
2016
2017 return c;
2018 }) : s;
2019 },
2020
2021
2022 insertAfter : function(n, r) {
2023 var t = this;
2024
2025 r = t.get(r);
2026
2027 return this.run(n, function(n) {
2028 var p, ns;
2029
2030 p = r.parentNode;
2031 ns = r.nextSibling;
2032
2033 if (ns)
2034 p.insertBefore(n, ns);
2035 else
2036 p.appendChild(n);
2037
2038 return n;
2039 });
2040 },
2041
2042
2043 isBlock : function(n) {
2044 if (n.nodeType && n.nodeType !== 1)
2045 return false;
2046
2047 n = n.nodeName || n;
2048
2049 return /^(H[1-6]|HR|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TR|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP)$/.test(n);
2050 },
2051
2052
2053 replace : function(n, o, k) {
2054 var t = this;
2055
2056 if (is(o, 'array'))
2057 n = n.cloneNode(true);
2058
2059 return t.run(o, function(o) {
2060 if (k) {
2061 each(o.childNodes, function(c) {
2062 n.appendChild(c.cloneNode(true));
2063 });
2064 }
2065
2066 // Fix IE psuedo leak for elements since replacing elements if fairly common
2067 // Will break parentNode for some unknown reason
2068 if (t.fixPsuedoLeaks && o.nodeType === 1) {
2069 o.parentNode.insertBefore(n, o);
2070 t.remove(o);
2071 return n;
2072 }
2073
2074 return o.parentNode.replaceChild(n, o);
2075 });
2076 },
2077
2078
2079 findCommonAncestor : function(a, b) {
2080 var ps = a, pe;
2081
2082 while (ps) {
2083 pe = b;
2084
2085 while (pe && ps != pe)
2086 pe = pe.parentNode;
2087
2088 if (ps == pe)
2089 break;
2090
2091 ps = ps.parentNode;
2092 }
2093
2094 if (!ps && a.ownerDocument)
2095 return a.ownerDocument.documentElement;
2096
2097 return ps;
2098 },
2099
2100 toHex : function(s) {
2101 var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);
2102
2103 function hex(s) {
2104 s = parseInt(s).toString(16);
2105
2106 return s.length > 1 ? s : '0' + s; // 0 -> 00
2107 };
2108
2109 if (c) {
2110 s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);
2111
2112 return s;
2113 }
2114
2115 return s;
2116 },
2117
2118 getClasses : function() {
2119 var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;
2120
2121 if (t.classes)
2122 return t.classes;
2123
2124 function addClasses(s) {
2125 // IE style imports
2126 each(s.imports, function(r) {
2127 addClasses(r);
2128 });
2129
2130 each(s.cssRules || s.rules, function(r) {
2131 // Real type or fake it on IE
2132 switch (r.type || 1) {
2133 // Rule
2134 case 1:
2135 if (r.selectorText) {
2136 each(r.selectorText.split(','), function(v) {
2137 v = v.replace(/^\s*|\s*$|^\s\./g, "");
2138
2139 // Is internal or it doesn't contain a class
2140 if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v))
2141 return;
2142
2143 // Remove everything but class name
2144 ov = v;
2145 v = v.replace(/.*\.([a-z0-9_\-]+).*/i, '$1');
2146
2147 // Filter classes
2148 if (f && !(v = f(v, ov)))
2149 return;
2150
2151 if (!lo[v]) {
2152 cl.push({'class' : v});
2153 lo[v] = 1;
2154 }
2155 });
2156 }
2157 break;
2158
2159 // Import
2160 case 3:
2161 addClasses(r.styleSheet);
2162 break;
2163 }
2164 });
2165 };
2166
2167 try {
2168 each(t.doc.styleSheets, addClasses);
2169 } catch (ex) {
2170 // Ignore
2171 }
2172
2173 if (cl.length > 0)
2174 t.classes = cl;
2175
2176 return cl;
2177 },
2178
2179 run : function(e, f, s) {
2180 var t = this, o;
2181
2182 if (t.doc && typeof(e) === 'string')
2183 e = t.get(e);
2184
2185 if (!e)
2186 return false;
2187
2188 s = s || this;
2189 if (!e.nodeType && (e.length || e.length === 0)) {
2190 o = [];
2191
2192 each(e, function(e, i) {
2193 if (e) {
2194 if (typeof(e) == 'string')
2195 e = t.doc.getElementById(e);
2196
2197 o.push(f.call(s, e, i));
2198 }
2199 });
2200
2201 return o;
2202 }
2203
2204 return f.call(s, e);
2205 },
2206
2207 getAttribs : function(n) {
2208 var o;
2209
2210 n = this.get(n);
2211
2212 if (!n)
2213 return [];
2214
2215 if (isIE) {
2216 o = [];
2217
2218 // Object will throw exception in IE
2219 if (n.nodeName == 'OBJECT')
2220 return n.attributes;
2221
2222 // It's crazy that this is faster in IE but it's because it returns all attributes all the time
2223 n.cloneNode(false).outerHTML.replace(/([a-z0-9\:\-_]+)=/gi, function(a, b) {
2224 o.push({specified : 1, nodeName : b});
2225 });
2226
2227 return o;
2228 }
2229
2230 return n.attributes;
2231 },
2232
2233 destroy : function(s) {
2234 var t = this;
2235
2236 t.win = t.doc = t.root = null;
2237
2238 // Manual destroy then remove unload handler
2239 if (!s)
2240 tinymce.removeUnload(t.destroy);
2241 },
2242
2243 createRng : function() {
2244 var d = this.doc;
2245
2246 return d.createRange ? d.createRange() : new tinymce.dom.Range(this);
2247 },
2248
2249 split : function(pe, e, re) {
2250 var t = this, r = t.createRng(), bef, aft, pa;
2251
2252 // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sence
2253 // but we don't want that in our code since it serves no purpose
2254 // For example if this is chopped:
2255 // <p>text 1<span><b>CHOP</b></span>text 2</p>
2256 // would produce:
2257 // <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>
2258 // this function will then trim of empty edges and produce:
2259 // <p>text 1</p><b>CHOP</b><p>text 2</p>
2260 function trimEdge(n, na) {
2261 n = n[na];
2262
2263 if (n && n[na] && n[na].nodeType == 1 && isEmpty(n[na]))
2264 t.remove(n[na]);
2265 };
2266
2267 function isEmpty(n) {
2268 n = t.getOuterHTML(n);
2269 n = n.replace(/<(img|hr|table)/gi, '-'); // Keep these convert them to - chars
2270 n = n.replace(/<[^>]+>/g, ''); // Remove all tags
2271
2272 return n.replace(/[ \t\r\n]+|&nbsp;|&#160;/g, '') == '';
2273 };
2274
2275 if (pe && e) {
2276 // Get before chunk
2277 r.setStartBefore(pe);
2278 r.setEndBefore(e);
2279 bef = r.extractContents();
2280
2281 // Get after chunk
2282 r = t.createRng();
2283 r.setStartAfter(e);
2284 r.setEndAfter(pe);
2285 aft = r.extractContents();
2286
2287 // Insert chunks and remove parent
2288 pa = pe.parentNode;
2289
2290 // Remove right side edge of the before contents
2291 trimEdge(bef, 'lastChild');
2292
2293 if (!isEmpty(bef))
2294 pa.insertBefore(bef, pe);
2295
2296 if (re)
2297 pa.replaceChild(re, e);
2298 else
2299 pa.insertBefore(e, pe);
2300
2301 // Remove left site edge of the after contents
2302 trimEdge(aft, 'firstChild');
2303
2304 if (!isEmpty(aft))
2305 pa.insertBefore(aft, pe);
2306
2307 t.remove(pe);
2308
2309 return re || e;
2310 }
2311 },
2312
2313 _isRes : function(c) {
2314 // Is live resizble element
2315 return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c);
2316 }
2317
2318 /*
2319 walk : function(n, f, s) {
2320 var d = this.doc, w;
2321
2322 if (d.createTreeWalker) {
2323 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
2324
2325 while ((n = w.nextNode()) != null)
2326 f.call(s || this, n);
2327 } else
2328 tinymce.walk(n, f, 'childNodes', s);
2329 }
2330 */
2331
2332 /*
2333 toRGB : function(s) {
2334 var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);
2335
2336 if (c) {
2337 // #FFF -> #FFFFFF
2338 if (!is(c[3]))
2339 c[3] = c[2] = c[1];
2340
2341 return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";
2342 }
2343
2344 return s;
2345 }
2346 */
2347
2348 });
2349
2350 // Setup page DOM
2351 tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});
2352 })(tinymce);
2353 (function(ns) {
2354 // Traverse constants
2355 var EXTRACT = 0, CLONE = 1, DELETE = 2, extend = tinymce.extend;
2356
2357 function indexOf(child, parent) {
2358 var i, node;
2359
2360 if (child.parentNode != parent)
2361 return -1;
2362
2363 for (node = parent.firstChild, i = 0; node != child; node = node.nextSibling)
2364 i++;
2365
2366 return i;
2367 };
2368
2369 function nodeIndex(n) {
2370 var i = 0;
2371
2372 while (n.previousSibling) {
2373 i++;
2374 n = n.previousSibling;
2375 }
2376
2377 return i;
2378 };
2379
2380 function getSelectedNode(container, offset) {
2381 var child;
2382
2383 if (container.nodeType == 3 /* TEXT_NODE */)
2384 return container;
2385
2386 if (offset < 0)
2387 return container;
2388
2389 child = container.firstChild;
2390 while (child != null && offset > 0) {
2391 --offset;
2392 child = child.nextSibling;
2393 }
2394
2395 if (child != null)
2396 return child;
2397
2398 return container;
2399 };
2400
2401 // Range constructor
2402 function Range(dom) {
2403 var d = dom.doc;
2404
2405 extend(this, {
2406 dom : dom,
2407
2408 // Inital states
2409 startContainer : d,
2410 startOffset : 0,
2411 endContainer : d,
2412 endOffset : 0,
2413 collapsed : true,
2414 commonAncestorContainer : d,
2415
2416 // Range constants
2417 START_TO_START : 0,
2418 START_TO_END : 1,
2419 END_TO_END : 2,
2420 END_TO_START : 3
2421 });
2422 };
2423
2424 // Add range methods
2425 extend(Range.prototype, {
2426 setStart : function(n, o) {
2427 this._setEndPoint(true, n, o);
2428 },
2429
2430 setEnd : function(n, o) {
2431 this._setEndPoint(false, n, o);
2432 },
2433
2434 setStartBefore : function(n) {
2435 this.setStart(n.parentNode, nodeIndex(n));
2436 },
2437
2438 setStartAfter : function(n) {
2439 this.setStart(n.parentNode, nodeIndex(n) + 1);
2440 },
2441
2442 setEndBefore : function(n) {
2443 this.setEnd(n.parentNode, nodeIndex(n));
2444 },
2445
2446 setEndAfter : function(n) {
2447 this.setEnd(n.parentNode, nodeIndex(n) + 1);
2448 },
2449
2450 collapse : function(ts) {
2451 var t = this;
2452
2453 if (ts) {
2454 t.endContainer = t.startContainer;
2455 t.endOffset = t.startOffset;
2456 } else {
2457 t.startContainer = t.endContainer;
2458 t.startOffset = t.endOffset;
2459 }
2460
2461 t.collapsed = true;
2462 },
2463
2464 selectNode : function(n) {
2465 this.setStartBefore(n);
2466 this.setEndAfter(n);
2467 },
2468
2469 selectNodeContents : function(n) {
2470 this.setStart(n, 0);
2471 this.setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);
2472 },
2473
2474 compareBoundaryPoints : function(h, r) {
2475 var t = this, sc = t.startContainer, so = t.startOffset, ec = t.endContainer, eo = t.endOffset;
2476
2477 // Check START_TO_START
2478 if (h === 0)
2479 return t._compareBoundaryPoints(sc, so, sc, so);
2480
2481 // Check START_TO_END
2482 if (h === 1)
2483 return t._compareBoundaryPoints(sc, so, ec, eo);
2484
2485 // Check END_TO_END
2486 if (h === 2)
2487 return t._compareBoundaryPoints(ec, eo, ec, eo);
2488
2489 // Check END_TO_START
2490 if (h === 3)
2491 return t._compareBoundaryPoints(ec, eo, sc, so);
2492 },
2493
2494 deleteContents : function() {
2495 this._traverse(DELETE);
2496 },
2497
2498 extractContents : function() {
2499 return this._traverse(EXTRACT);
2500 },
2501
2502 cloneContents : function() {
2503 return this._traverse(CLONE);
2504 },
2505
2506 insertNode : function(n) {
2507 var t = this, nn, o;
2508
2509 // Node is TEXT_NODE or CDATA
2510 if (n.nodeType === 3 || n.nodeType === 4) {
2511 nn = t.startContainer.splitText(t.startOffset);
2512 t.startContainer.parentNode.insertBefore(n, nn);
2513 } else {
2514 // Insert element node
2515 if (t.startContainer.childNodes.length > 0)
2516 o = t.startContainer.childNodes[t.startOffset];
2517
2518 t.startContainer.insertBefore(n, o);
2519 }
2520 },
2521
2522 surroundContents : function(n) {
2523 var t = this, f = t.extractContents();
2524
2525 t.insertNode(n);
2526 n.appendChild(f);
2527 t.selectNode(n);
2528 },
2529
2530 cloneRange : function() {
2531 var t = this;
2532
2533 return extend(new Range(t.dom), {
2534 startContainer : t.startContainer,
2535 startOffset : t.startOffset,
2536 endContainer : t.endContainer,
2537 endOffset : t.endOffset,
2538 collapsed : t.collapsed,
2539 commonAncestorContainer : t.commonAncestorContainer
2540 });
2541 },
2542
2543 /*
2544 toString : function() {
2545 // Not implemented
2546 },
2547
2548 detach : function() {
2549 // Not implemented
2550 },
2551 */
2552 // Internal methods
2553
2554 _isCollapsed : function() {
2555 return (this.startContainer == this.endContainer && this.startOffset == this.endOffset);
2556 },
2557
2558 _compareBoundaryPoints : function (containerA, offsetA, containerB, offsetB) {
2559 var c, offsetC, n, cmnRoot, childA, childB;
2560
2561 // In the first case the boundary-points have the same container. A is before B
2562 // if its offset is less than the offset of B, A is equal to B if its offset is
2563 // equal to the offset of B, and A is after B if its offset is greater than the
2564 // offset of B.
2565 if (containerA == containerB) {
2566 if (offsetA == offsetB) {
2567 return 0; // equal
2568 } else if (offsetA < offsetB) {
2569 return -1; // before
2570 } else {
2571 return 1; // after
2572 }
2573 }
2574
2575 // In the second case a child node C of the container of A is an ancestor
2576 // container of B. In this case, A is before B if the offset of A is less than or
2577 // equal to the index of the child node C and A is after B otherwise.
2578 c = containerB;
2579 while (c && c.parentNode != containerA) {
2580 c = c.parentNode;
2581 }
2582 if (c) {
2583 offsetC = 0;
2584 n = containerA.firstChild;
2585
2586 while (n != c && offsetC < offsetA) {
2587 offsetC++;
2588 n = n.nextSibling;
2589 }
2590
2591 if (offsetA <= offsetC) {
2592 return -1; // before
2593 } else {
2594 return 1; // after
2595 }
2596 }
2597
2598 // In the third case a child node C of the container of B is an ancestor container
2599 // of A. In this case, A is before B if the index of the child node C is less than
2600 // the offset of B and A is after B otherwise.
2601 c = containerA;
2602 while (c && c.parentNode != containerB) {
2603 c = c.parentNode;
2604 }
2605
2606 if (c) {
2607 offsetC = 0;
2608 n = containerB.firstChild;
2609
2610 while (n != c && offsetC < offsetB) {
2611 offsetC++;
2612 n = n.nextSibling;
2613 }
2614
2615 if (offsetC < offsetB) {
2616 return -1; // before
2617 } else {
2618 return 1; // after
2619 }
2620 }
2621
2622 // In the fourth case, none of three other cases hold: the containers of A and B
2623 // are siblings or descendants of sibling nodes. In this case, A is before B if
2624 // the container of A is before the container of B in a pre-order traversal of the
2625 // Ranges' context tree and A is after B otherwise.
2626 cmnRoot = this.dom.findCommonAncestor(containerA, containerB);
2627 childA = containerA;
2628
2629 while (childA && childA.parentNode != cmnRoot) {
2630 childA = childA.parentNode;
2631 }
2632
2633 if (!childA) {
2634 childA = cmnRoot;
2635 }
2636
2637 childB = containerB;
2638 while (childB && childB.parentNode != cmnRoot) {
2639 childB = childB.parentNode;
2640 }
2641
2642 if (!childB) {
2643 childB = cmnRoot;
2644 }
2645
2646 if (childA == childB) {
2647 return 0; // equal
2648 }
2649
2650 n = cmnRoot.firstChild;
2651 while (n) {
2652 if (n == childA) {
2653 return -1; // before
2654 }
2655
2656 if (n == childB) {
2657 return 1; // after
2658 }
2659
2660 n = n.nextSibling;
2661 }
2662 },
2663
2664 _setEndPoint : function(st, n, o) {
2665 var t = this, ec, sc;
2666
2667 if (st) {
2668 t.startContainer = n;
2669 t.startOffset = o;
2670 } else {
2671 t.endContainer = n;
2672 t.endOffset = o;
2673 }
2674
2675 // If one boundary-point of a Range is set to have a root container
2676 // other than the current one for the Range, the Range is collapsed to
2677 // the new position. This enforces the restriction that both boundary-
2678 // points of a Range must have the same root container.
2679 ec = t.endContainer;
2680 while (ec.parentNode)
2681 ec = ec.parentNode;
2682
2683 sc = t.startContainer;
2684 while (sc.parentNode)
2685 sc = sc.parentNode;
2686
2687 if (sc != ec) {
2688 t.collapse(st);
2689 } else {
2690 // The start position of a Range is guaranteed to never be after the
2691 // end position. To enforce this restriction, if the start is set to
2692 // be at a position after the end, the Range is collapsed to that
2693 // position.
2694 if (t._compareBoundaryPoints(t.startContainer, t.startOffset, t.endContainer, t.endOffset) > 0)
2695 t.collapse(st);
2696 }
2697
2698 t.collapsed = t._isCollapsed();
2699 t.commonAncestorContainer = t.dom.findCommonAncestor(t.startContainer, t.endContainer);
2700 },
2701
2702 // This code is heavily "inspired" by the Apache Xerces implementation. I hope they don't mind. :)
2703
2704 _traverse : function(how) {
2705 var t = this, c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;
2706
2707 if (t.startContainer == t.endContainer)
2708 return t._traverseSameContainer(how);
2709
2710 for (c = t.endContainer, p = c.parentNode; p != null; c = p, p = p.parentNode) {
2711 if (p == t.startContainer)
2712 return t._traverseCommonStartContainer(c, how);
2713
2714 ++endContainerDepth;
2715 }
2716
2717 for (c = t.startContainer, p = c.parentNode; p != null; c = p, p = p.parentNode) {
2718 if (p == t.endContainer)
2719 return t._traverseCommonEndContainer(c, how);
2720
2721 ++startContainerDepth;
2722 }
2723
2724 depthDiff = startContainerDepth - endContainerDepth;
2725
2726 startNode = t.startContainer;
2727 while (depthDiff > 0) {
2728 startNode = startNode.parentNode;
2729 depthDiff--;
2730 }
2731
2732 endNode = t.endContainer;
2733 while (depthDiff < 0) {
2734 endNode = endNode.parentNode;
2735 depthDiff++;
2736 }
2737
2738 // ascend the ancestor hierarchy until we have a common parent.
2739 for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {
2740 startNode = sp;
2741 endNode = ep;
2742 }
2743
2744 return t._traverseCommonAncestors(startNode, endNode, how);
2745 },
2746
2747 _traverseSameContainer : function(how) {
2748 var t = this, frag, s, sub, n, cnt, sibling, xferNode;
2749
2750 if (how != DELETE)
2751 frag = t.dom.doc.createDocumentFragment();
2752
2753 // If selection is empty, just return the fragment
2754 if (t.startOffset == t.endOffset)
2755 return frag;
2756
2757 // Text node needs special case handling
2758 if (t.startContainer.nodeType == 3 /* TEXT_NODE */) {
2759 // get the substring
2760 s = t.startContainer.nodeValue;
2761 sub = s.substring(t.startOffset, t.endOffset);
2762
2763 // set the original text node to its new value
2764 if (how != CLONE) {
2765 t.startContainer.deleteData(t.startOffset, t.endOffset - t.startOffset);
2766
2767 // Nothing is partially selected, so collapse to start point
2768 t.collapse(true);
2769 }
2770
2771 if (how == DELETE)
2772 return null;
2773
2774 frag.appendChild(t.dom.doc.createTextNode(sub));
2775 return frag;
2776 }
2777
2778 // Copy nodes between the start/end offsets.
2779 n = getSelectedNode(t.startContainer, t.startOffset);
2780 cnt = t.endOffset - t.startOffset;
2781
2782 while (cnt > 0) {
2783 sibling = n.nextSibling;
2784 xferNode = t._traverseFullySelected(n, how);
2785
2786 if (frag)
2787 frag.appendChild( xferNode );
2788
2789 --cnt;
2790 n = sibling;
2791 }
2792
2793 // Nothing is partially selected, so collapse to start point
2794 if (how != CLONE)
2795 t.collapse(true);
2796
2797 return frag;
2798 },
2799
2800 _traverseCommonStartContainer : function(endAncestor, how) {
2801 var t = this, frag, n, endIdx, cnt, sibling, xferNode;
2802
2803 if (how != DELETE)
2804 frag = t.dom.doc.createDocumentFragment();
2805
2806 n = t._traverseRightBoundary(endAncestor, how);
2807
2808 if (frag)
2809 frag.appendChild(n);
2810
2811 endIdx = indexOf(endAncestor, t.startContainer);
2812 cnt = endIdx - t.startOffset;
2813
2814 if (cnt <= 0) {
2815 // Collapse to just before the endAncestor, which
2816 // is partially selected.
2817 if (how != CLONE) {
2818 t.setEndBefore(endAncestor);
2819 t.collapse(false);
2820 }
2821
2822 return frag;
2823 }
2824
2825 n = endAncestor.previousSibling;
2826 while (cnt > 0) {
2827 sibling = n.previousSibling;
2828 xferNode = t._traverseFullySelected(n, how);
2829
2830 if (frag)
2831 frag.insertBefore(xferNode, frag.firstChild);
2832
2833 --cnt;
2834 n = sibling;
2835 }
2836
2837 // Collapse to just before the endAncestor, which
2838 // is partially selected.
2839 if (how != CLONE) {
2840 t.setEndBefore(endAncestor);
2841 t.collapse(false);
2842 }
2843
2844 return frag;
2845 },
2846
2847 _traverseCommonEndContainer : function(startAncestor, how) {
2848 var t = this, frag, startIdx, n, cnt, sibling, xferNode;
2849
2850 if (how != DELETE)
2851 frag = t.dom.doc.createDocumentFragment();
2852
2853 n = t._traverseLeftBoundary(startAncestor, how);
2854 if (frag)
2855 frag.appendChild(n);
2856
2857 startIdx = indexOf(startAncestor, t.endContainer);
2858 ++startIdx; // Because we already traversed it....
2859
2860 cnt = t.endOffset - startIdx;
2861 n = startAncestor.nextSibling;
2862 while (cnt > 0) {
2863 sibling = n.nextSibling;
2864 xferNode = t._traverseFullySelected(n, how);
2865
2866 if (frag)
2867 frag.appendChild(xferNode);
2868
2869 --cnt;
2870 n = sibling;
2871 }
2872
2873 if (how != CLONE) {
2874 t.setStartAfter(startAncestor);
2875 t.collapse(true);
2876 }
2877
2878 return frag;
2879 },
2880
2881 _traverseCommonAncestors : function(startAncestor, endAncestor, how) {
2882 var t = this, n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;
2883
2884 if (how != DELETE)
2885 frag = t.dom.doc.createDocumentFragment();
2886
2887 n = t._traverseLeftBoundary(startAncestor, how);
2888 if (frag)
2889 frag.appendChild(n);
2890
2891 commonParent = startAncestor.parentNode;
2892 startOffset = indexOf(startAncestor, commonParent);
2893 endOffset = indexOf(endAncestor, commonParent);
2894 ++startOffset;
2895
2896 cnt = endOffset - startOffset;
2897 sibling = startAncestor.nextSibling;
2898
2899 while (cnt > 0) {
2900 nextSibling = sibling.nextSibling;
2901 n = t._traverseFullySelected(sibling, how);
2902
2903 if (frag)
2904 frag.appendChild(n);
2905
2906 sibling = nextSibling;
2907 --cnt;
2908 }
2909
2910 n = t._traverseRightBoundary(endAncestor, how);
2911
2912 if (frag)
2913 frag.appendChild(n);
2914
2915 if (how != CLONE) {
2916 t.setStartAfter(startAncestor);
2917 t.collapse(true);
2918 }
2919
2920 return frag;
2921 },
2922
2923 _traverseRightBoundary : function(root, how) {
2924 var t = this, next = getSelectedNode(t.endContainer, t.endOffset - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent;
2925 var isFullySelected = next != t.endContainer;
2926
2927 if (next == root)
2928 return t._traverseNode(next, isFullySelected, false, how);
2929
2930 parent = next.parentNode;
2931 clonedParent = t._traverseNode(parent, false, false, how);
2932
2933 while (parent != null) {
2934 while (next != null) {
2935 prevSibling = next.previousSibling;
2936 clonedChild = t._traverseNode(next, isFullySelected, false, how);
2937
2938 if (how != DELETE)
2939 clonedParent.insertBefore(clonedChild, clonedParent.firstChild);
2940
2941 isFullySelected = true;
2942 next = prevSibling;
2943 }
2944
2945 if (parent == root)
2946 return clonedParent;
2947
2948 next = parent.previousSibling;
2949 parent = parent.parentNode;
2950
2951 clonedGrandParent = t._traverseNode(parent, false, false, how);
2952
2953 if (how != DELETE)
2954 clonedGrandParent.appendChild(clonedParent);
2955
2956 clonedParent = clonedGrandParent;
2957 }
2958
2959 // should never occur
2960 return null;
2961 },
2962
2963 _traverseLeftBoundary : function(root, how) {
2964 var t = this, next = getSelectedNode(t.startContainer, t.startOffset);
2965 var isFullySelected = next != t.startContainer, parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;
2966
2967 if (next == root)
2968 return t._traverseNode(next, isFullySelected, true, how);
2969
2970 parent = next.parentNode;
2971 clonedParent = t._traverseNode(parent, false, true, how);
2972
2973 while (parent != null) {
2974 while (next != null) {
2975 nextSibling = next.nextSibling;
2976 clonedChild = t._traverseNode(next, isFullySelected, true, how);
2977
2978 if (how != DELETE)
2979 clonedParent.appendChild(clonedChild);
2980
2981 isFullySelected = true;
2982 next = nextSibling;
2983 }
2984
2985 if (parent == root)
2986 return clonedParent;
2987
2988 next = parent.nextSibling;
2989 parent = parent.parentNode;
2990
2991 clonedGrandParent = t._traverseNode(parent, false, true, how);
2992
2993 if (how != DELETE)
2994 clonedGrandParent.appendChild(clonedParent);
2995
2996 clonedParent = clonedGrandParent;
2997 }
2998
2999 // should never occur
3000 return null;
3001 },
3002
3003 _traverseNode : function(n, isFullySelected, isLeft, how) {
3004 var t = this, txtValue, newNodeValue, oldNodeValue, offset, newNode;
3005
3006 if (isFullySelected)
3007 return t._traverseFullySelected(n, how);
3008
3009 if (n.nodeType == 3 /* TEXT_NODE */) {
3010 txtValue = n.nodeValue;
3011
3012 if (isLeft) {
3013 offset = t.startOffset;
3014 newNodeValue = txtValue.substring(offset);
3015 oldNodeValue = txtValue.substring(0, offset);
3016 } else {
3017 offset = t.endOffset;
3018 newNodeValue = txtValue.substring(0, offset);
3019 oldNodeValue = txtValue.substring(offset);
3020 }
3021
3022 if (how != CLONE)
3023 n.nodeValue = oldNodeValue;
3024
3025 if (how == DELETE)
3026 return null;
3027
3028 newNode = n.cloneNode(false);
3029 newNode.nodeValue = newNodeValue;
3030
3031 return newNode;
3032 }
3033
3034 if (how == DELETE)
3035 return null;
3036
3037 return n.cloneNode(false);
3038 },
3039
3040 _traverseFullySelected : function(n, how) {
3041 var t = this;
3042
3043 if (how != DELETE)
3044 return how == CLONE ? n.cloneNode(true) : n;
3045
3046 n.parentNode.removeChild(n);
3047 return null;
3048 }
3049 });
3050
3051 ns.Range = Range;
3052 })(tinymce.dom);
3053 (function() {
3054 function Selection(selection) {
3055 var t = this;
3056
3057 function getRange() {
3058 var dom = selection.dom, ieRange = selection.getRng(), domRange = dom.createRng(), startPos = {}, endPos = {};
3059
3060 // Handle control selection
3061 if (ieRange.item) {
3062 domRange.setStartBefore(ieRange.item(0));
3063 domRange.setEndAfter(ieRange.item(0));
3064
3065 return domRange;
3066 }
3067
3068 function findEndPoint(ie_rng, start, pos) {
3069 var rng, rng2, startElement;
3070
3071 rng = ie_rng.duplicate();
3072 rng.collapse(start);
3073 element = rng.parentElement();
3074
3075 // If element is block then we need to move one character
3076 // since the selection has a extra invisible character
3077 if (element.currentStyle.display == 'block') {
3078 rng = ie_rng.duplicate();
3079 rng2 = ie_rng.duplicate();
3080
3081 // Move one character at beginning/end of selection
3082 if (start)
3083 rng.moveStart('character', 1);
3084 else
3085 rng.moveEnd('character', -1);
3086
3087 // The range shouldn't have been changed so lets restore it
3088 if (rng.text != rng2.text)
3089 rng = rng2;
3090
3091 rng.collapse(start);
3092 element = rng.parentElement();
3093 }
3094
3095 pos.parent = element;
3096 pos.range = rng;
3097 };
3098
3099 function findIndexAndOffset(pos) {
3100 var rng = pos.range, i, nl, marker, sibling, idx = 0;
3101
3102 // Set parent and offset
3103 pos.offset = 0;
3104 pos.parent = rng.parentElement();
3105
3106 // Insert marker
3107 rng.pasteHTML('<span id="_mce"></span>');
3108 marker = dom.get('_mce');
3109
3110 // Find the makers node index excluding text node fragmentation
3111 nl = pos.parent.childNodes;
3112 for (i = 0; i < nl.length; i++) {
3113 if (nl[i] == marker) {
3114 pos.index = idx;
3115 break;
3116 }
3117
3118 if (i > 0 && (nl[i].nodeType != 3 || nl[i - 1].nodeType != 3))
3119 idx++;
3120 }
3121
3122 // Figure out the character offset excluding text node fragmentation
3123 sibling = marker.previousSibling;
3124 if (sibling) {
3125 if (sibling.nodeType === 3) {
3126 do {
3127 pos.offset += sibling.nodeValue.length;
3128 } while ((sibling = sibling.previousSibling) && sibling.nodeType == 3);
3129 } else
3130 pos.index++;
3131 }
3132
3133 // Remove the marker
3134 dom.remove(marker);
3135
3136 return pos;
3137 };
3138
3139 // Find end points
3140 findEndPoint(ieRange, true, startPos);
3141 findEndPoint(ieRange, false, endPos);
3142
3143 // Find start and end positions
3144 findIndexAndOffset(startPos);
3145 findIndexAndOffset(endPos);
3146
3147 // Normalize the elements to avoid fragmented dom
3148 startPos.parent.normalize();
3149 endPos.parent.normalize();
3150
3151 // Set start and end points of the domRange
3152 domRange.setStart(startPos.parent.childNodes[startPos.index], startPos.offset);
3153 domRange.setEnd(endPos.parent.childNodes[endPos.index], endPos.offset);
3154
3155 // Restore selection to new range
3156 t.addRange(domRange);
3157
3158 return domRange;
3159 };
3160
3161 this.addRange = function(rng) {
3162 var ieRng, startPos, endPos, body = selection.dom.doc.body;
3163
3164 // Element selection, then make a control range
3165 if (rng.startContainer.nodeType == 1) {
3166 ieRng = body.createControlRange();
3167 ieRng.addElement(rng.startContainer.childNodes[rng.startOffset]);
3168 return;
3169 }
3170
3171 function findPos(start) {
3172 var container, offset, rng2, pos;
3173
3174 // Get container and offset
3175 container = start ? rng.startContainer : rng.endContainer;
3176 offset = start ? rng.startOffset : rng.endOffset;
3177
3178 // Insert marker character
3179 container.nodeValue = container.nodeValue.substring(0, offset) + '\uFEFF' + container.nodeValue.substring(offset);
3180
3181 // Create range for whole parent element
3182 rng2 = body.createTextRange();
3183 rng2.moveToElementText(container.parentNode);
3184 pos = rng2.text.indexOf('\uFEFF');
3185 container.nodeValue = container.nodeValue.replace(/\uFEFF/, '');
3186
3187 if (start)
3188 startPos = pos;
3189 else
3190 endPos = pos;
3191 };
3192
3193 function setPos(start) {
3194 var rng2, container = start ? rng.startContainer : rng.endContainer;
3195
3196 rng2 = body.createTextRange();
3197 rng2.moveToElementText(container.parentNode);
3198 rng2.collapse(true);
3199 rng2.move('character', start ? startPos : endPos);
3200
3201 if (start)
3202 ieRng.setEndPoint('StartToStart', rng2);
3203 else
3204 ieRng.setEndPoint('EndToStart', rng2);
3205 };
3206
3207 // Create IE specific range
3208 ieRng = body.createTextRange();
3209
3210 // Find start/end pos
3211 findPos(true);
3212 findPos(false);
3213
3214 // Set start/end pos
3215 setPos(true);
3216 setPos(false);
3217
3218 ieRng.select();
3219 };
3220
3221 this.getRangeAt = function() {
3222 // todo: Implement range caching here later
3223 return getRange();
3224 };
3225 };
3226
3227 // Expose the selection object
3228 tinymce.dom.TridentSelection = Selection;
3229 })();
3230
3231 /*
3232 * Sizzle CSS Selector Engine - v1.0
3233 * Copyright 2009, The Dojo Foundation
3234 * Released under the MIT, BSD, and GPL Licenses.
3235 * More information: http://sizzlejs.com/
3236 */
3237 (function(){
3238
3239 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
3240 done = 0,
3241 toString = Object.prototype.toString,
3242 arraySplice = Array.prototype.splice,
3243 arrayPush = Array.prototype.push,
3244 arraySort = Array.prototype.sort;
3245
3246 var Sizzle = function(selector, context, results, seed) {
3247 results = results || [];
3248 var origContext = context = context || document;
3249
3250 if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
3251 return [];
3252 }
3253
3254 if ( !selector || typeof selector !== "string" ) {
3255 return results;
3256 }
3257
3258 var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context);
3259
3260 // Reset the position of the chunker regexp (start from head)
3261 chunker.lastIndex = 0;
3262
3263 while ( (m = chunker.exec(selector)) !== null ) {
3264 parts.push( m[1] );
3265
3266 if ( m[2] ) {
3267 extra = RegExp.rightContext;
3268 break;
3269 }
3270 }
3271
3272 if ( parts.length > 1 && origPOS.exec( selector ) ) {
3273 if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
3274 set = posProcess( parts[0] + parts[1], context );
3275 } else {
3276 set = Expr.relative[ parts[0] ] ?
3277 [ context ] :
3278 Sizzle( parts.shift(), context );
3279
3280 while ( parts.length ) {
3281 selector = parts.shift();
3282
3283 if ( Expr.relative[ selector ] )
3284 selector += parts.shift();
3285
3286 set = posProcess( selector, set );
3287 }
3288 }
3289 } else {
3290 // Take a shortcut and set the context if the root selector is an ID
3291 // (but not if it'll be faster if the inner selector is an ID)
3292 if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
3293 Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
3294 var ret = Sizzle.find( parts.shift(), context, contextXML );
3295 context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
3296 }
3297
3298 if ( context ) {
3299 var ret = seed ?
3300 { expr: parts.pop(), set: makeArray(seed) } :
3301 Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
3302 set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
3303
3304 if ( parts.length > 0 ) {
3305 checkSet = makeArray(set);
3306 } else {
3307 prune = false;
3308 }
3309
3310 while ( parts.length ) {
3311 var cur = parts.pop(), pop = cur;
3312
3313 if ( !Expr.relative[ cur ] ) {
3314 cur = "";
3315 } else {
3316 pop = parts.pop();
3317 }
3318
3319 if ( pop == null ) {
3320 pop = context;
3321 }
3322
3323 Expr.relative[ cur ]( checkSet, pop, contextXML );
3324 }
3325 } else {
3326 checkSet = parts = [];
3327 }
3328 }
3329
3330 if ( !checkSet ) {
3331 checkSet = set;
3332 }
3333
3334 if ( !checkSet ) {
3335 throw "Syntax error, unrecognized expression: " + (cur || selector);
3336 }
3337
3338 if ( toString.call(checkSet) === "[object Array]" ) {
3339 if ( !prune ) {
3340 arrayPush.apply( results, checkSet );
3341 } else if ( context && context.nodeType === 1 ) {
3342 for ( var i = 0; checkSet[i] != null; i++ ) {
3343 if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
3344 arrayPush.call( results, set[i] );
3345 }
3346 }
3347 } else {
3348 for ( var i = 0; checkSet[i] != null; i++ ) {
3349 if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
3350 arrayPush.call( results, set[i] );
3351 }
3352 }
3353 }
3354 } else {
3355 makeArray( checkSet, results );
3356 }
3357
3358 if ( extra ) {
3359 Sizzle( extra, origContext, results, seed );
3360 Sizzle.uniqueSort( results );
3361 }
3362
3363 return results;
3364 };
3365
3366 Sizzle.uniqueSort = function(results){
3367 if ( sortOrder ) {
3368 hasDuplicate = false;
3369 arraySort.call(results, sortOrder);
3370
3371 if ( hasDuplicate ) {
3372 for ( var i = 1; i < results.length; i++ ) {
3373 if ( results[i] === results[i-1] ) {
3374 arraySplice.call(results, i--, 1);
3375 }
3376 }
3377 }
3378 }
3379 };
3380
3381 Sizzle.matches = function(expr, set){
3382 return Sizzle(expr, null, null, set);
3383 };
3384
3385 Sizzle.find = function(expr, context, isXML){
3386 var set, match;
3387
3388 if ( !expr ) {
3389 return [];
3390 }
3391
3392 for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
3393 var type = Expr.order[i], match;
3394
3395 if ( (match = Expr.match[ type ].exec( expr )) ) {
3396 var left = RegExp.leftContext;
3397
3398 if ( left.substr( left.length - 1 ) !== "\\" ) {
3399 match[1] = (match[1] || "").replace(/\\/g, "");
3400 set = Expr.find[ type ]( match, context, isXML );
3401 if ( set != null ) {
3402 expr = expr.replace( Expr.match[ type ], "" );
3403 break;
3404 }
3405 }
3406 }
3407 }
3408
3409 if ( !set ) {
3410 set = context.getElementsByTagName("*");
3411 }
3412
3413 return {set: set, expr: expr};
3414 };
3415
3416 Sizzle.filter = function(expr, set, inplace, not){
3417 var old = expr, result = [], curLoop = set, match, anyFound,
3418 isXMLFilter = set && set[0] && isXML(set[0]);
3419
3420 while ( expr && set.length ) {
3421 for ( var type in Expr.filter ) {
3422 if ( (match = Expr.match[ type ].exec( expr )) != null ) {
3423 var filter = Expr.filter[ type ], found, item;
3424 anyFound = false;
3425
3426 if ( curLoop == result ) {
3427 result = [];
3428 }
3429
3430 if ( Expr.preFilter[ type ] ) {
3431 match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
3432
3433 if ( !match ) {
3434 anyFound = found = true;
3435 } else if ( match === true ) {
3436 continue;
3437 }
3438 }
3439
3440 if ( match ) {
3441 for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
3442 if ( item ) {
3443 found = filter( item, match, i, curLoop );
3444 var pass = not ^ !!found;
3445
3446 if ( inplace && found != null ) {
3447 if ( pass ) {
3448 anyFound = true;
3449 } else {
3450 curLoop[i] = false;
3451 }
3452 } else if ( pass ) {
3453 result.push( item );
3454 anyFound = true;
3455 }
3456 }
3457 }
3458 }
3459
3460 if ( found !== undefined ) {
3461 if ( !inplace ) {
3462 curLoop = result;
3463 }
3464
3465 expr = expr.replace( Expr.match[ type ], "" );
3466
3467 if ( !anyFound ) {
3468 return [];
3469 }
3470
3471 break;
3472 }
3473 }
3474 }
3475
3476 // Improper expression
3477 if ( expr == old ) {
3478 if ( anyFound == null ) {
3479 throw "Syntax error, unrecognized expression: " + expr;
3480 } else {
3481 break;
3482 }
3483 }
3484
3485 old = expr;
3486 }
3487
3488 return curLoop;
3489 };
3490
3491 var Expr = Sizzle.selectors = {
3492 order: [ "ID", "NAME", "TAG" ],
3493 match: {
3494 ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
3495 CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
3496 NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
3497 ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
3498 TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
3499 CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
3500 POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
3501 PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
3502 },
3503 attrMap: {
3504 "class": "className",
3505 "for": "htmlFor"
3506 },
3507 attrHandle: {
3508 href: function(elem){
3509 return elem.getAttribute("href");
3510 }
3511 },
3512 relative: {
3513 "+": function(checkSet, part, isXML){
3514 var isPartStr = typeof part === "string",
3515 isTag = isPartStr && !/\W/.test(part),
3516 isPartStrNotTag = isPartStr && !isTag;
3517
3518 if ( isTag && !isXML ) {
3519 part = part.toUpperCase();
3520 }
3521
3522 for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
3523 if ( (elem = checkSet[i]) ) {
3524 while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
3525
3526 checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
3527 elem || false :
3528 elem === part;
3529 }
3530 }
3531
3532 if ( isPartStrNotTag ) {
3533 Sizzle.filter( part, checkSet, true );
3534 }
3535 },
3536 ">": function(checkSet, part, isXML){
3537 var isPartStr = typeof part === "string";
3538
3539 if ( isPartStr && !/\W/.test(part) ) {
3540 part = isXML ? part : part.toUpperCase();
3541
3542 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
3543 var elem = checkSet[i];
3544 if ( elem ) {
3545 var parent = elem.parentNode;
3546 checkSet[i] = parent.nodeName === part ? parent : false;
3547 }
3548 }
3549 } else {
3550 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
3551 var elem = checkSet[i];
3552 if ( elem ) {
3553 checkSet[i] = isPartStr ?
3554 elem.parentNode :
3555 elem.parentNode === part;
3556 }
3557 }
3558
3559 if ( isPartStr ) {
3560 Sizzle.filter( part, checkSet, true );
3561 }
3562 }
3563 },
3564 "": function(checkSet, part, isXML){
3565 var doneName = done++, checkFn = dirCheck;
3566
3567 if ( !part.match(/\W/) ) {
3568 var nodeCheck = part = isXML ? part : part.toUpperCase();
3569 checkFn = dirNodeCheck;
3570 }
3571
3572 checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
3573 },
3574 "~": function(checkSet, part, isXML){
3575 var doneName = done++, checkFn = dirCheck;
3576
3577 if ( typeof part === "string" && !part.match(/\W/) ) {
3578 var nodeCheck = part = isXML ? part : part.toUpperCase();
3579 checkFn = dirNodeCheck;
3580 }
3581
3582 checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
3583 }
3584 },
3585 find: {
3586 ID: function(match, context, isXML){
3587 if ( typeof context.getElementById !== "undefined" && !isXML ) {
3588 var m = context.getElementById(match[1]);
3589 return m ? [m] : [];
3590 }
3591 },
3592 NAME: function(match, context, isXML){
3593 if ( typeof context.getElementsByName !== "undefined" ) {
3594 var ret = [], results = context.getElementsByName(match[1]);
3595
3596 for ( var i = 0, l = results.length; i < l; i++ ) {
3597 if ( results[i].getAttribute("name") === match[1] ) {
3598 ret.push( results[i] );
3599 }
3600 }
3601
3602 return ret.length === 0 ? null : ret;
3603 }
3604 },
3605 TAG: function(match, context){
3606 return context.getElementsByTagName(match[1]);
3607 }
3608 },
3609 preFilter: {
3610 CLASS: function(match, curLoop, inplace, result, not, isXML){
3611 match = " " + match[1].replace(/\\/g, "") + " ";
3612
3613 if ( isXML ) {
3614 return match;
3615 }
3616
3617 for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
3618 if ( elem ) {
3619 if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
3620 if ( !inplace )
3621 result.push( elem );
3622 } else if ( inplace ) {
3623 curLoop[i] = false;
3624 }
3625 }
3626 }
3627
3628 return false;
3629 },
3630 ID: function(match){
3631 return match[1].replace(/\\/g, "");
3632 },
3633 TAG: function(match, curLoop){
3634 for ( var i = 0; curLoop[i] === false; i++ ){}
3635 return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
3636 },
3637 CHILD: function(match){
3638 if ( match[1] == "nth" ) {
3639 // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
3640 var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
3641 match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
3642 !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
3643
3644 // calculate the numbers (first)n+(last) including if they are negative
3645 match[2] = (test[1] + (test[2] || 1)) - 0;
3646 match[3] = test[3] - 0;
3647 }
3648
3649 // TODO: Move to normal caching system
3650 match[0] = done++;
3651
3652 return match;
3653 },
3654 ATTR: function(match, curLoop, inplace, result, not, isXML){
3655 var name = match[1].replace(/\\/g, "");
3656
3657 if ( !isXML && Expr.attrMap[name] ) {
3658 match[1] = Expr.attrMap[name];
3659 }
3660
3661 if ( match[2] === "~=" ) {
3662 match[4] = " " + match[4] + " ";
3663 }
3664
3665 return match;
3666 },
3667 PSEUDO: function(match, curLoop, inplace, result, not){
3668 if ( match[1] === "not" ) {
3669 // If we're dealing with a complex expression, or a simple one
3670 if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {
3671 match[3] = Sizzle(match[3], null, null, curLoop);
3672 } else {
3673 var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
3674 if ( !inplace ) {
3675 result.push.apply( result, ret );
3676 }
3677 return false;
3678 }
3679 } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
3680 return true;
3681 }
3682
3683 return match;
3684 },
3685 POS: function(match){
3686 match.unshift( true );
3687 return match;
3688 }
3689 },
3690 filters: {
3691 enabled: function(elem){
3692 return elem.disabled === false && elem.type !== "hidden";
3693 },
3694 disabled: function(elem){
3695 return elem.disabled === true;
3696 },
3697 checked: function(elem){
3698 return elem.checked === true;
3699 },
3700 selected: function(elem){
3701 // Accessing this property makes selected-by-default
3702 // options in Safari work properly
3703 elem.parentNode.selectedIndex;
3704 return elem.selected === true;
3705 },
3706 parent: function(elem){
3707 return !!elem.firstChild;
3708 },
3709 empty: function(elem){
3710 return !elem.firstChild;
3711 },
3712 has: function(elem, i, match){
3713 return !!Sizzle( match[3], elem ).length;
3714 },
3715 header: function(elem){
3716 return /h\d/i.test( elem.nodeName );
3717 },
3718 text: function(elem){
3719 return "text" === elem.type;
3720 },
3721 radio: function(elem){
3722 return "radio" === elem.type;
3723 },
3724 checkbox: function(elem){
3725 return "checkbox" === elem.type;
3726 },
3727 file: function(elem){
3728 return "file" === elem.type;
3729 },
3730 password: function(elem){
3731 return "password" === elem.type;
3732 },
3733 submit: function(elem){
3734 return "submit" === elem.type;
3735 },
3736 image: function(elem){
3737 return "image" === elem.type;
3738 },
3739 reset: function(elem){
3740 return "reset" === elem.type;
3741 },
3742 button: function(elem){
3743 return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
3744 },
3745 input: function(elem){
3746 return /input|select|textarea|button/i.test(elem.nodeName);
3747 }
3748 },
3749 setFilters: {
3750 first: function(elem, i){
3751 return i === 0;
3752 },
3753 last: function(elem, i, match, array){
3754 return i === array.length - 1;
3755 },
3756 even: function(elem, i){
3757 return i % 2 === 0;
3758 },
3759 odd: function(elem, i){
3760 return i % 2 === 1;
3761 },
3762 lt: function(elem, i, match){
3763 return i < match[3] - 0;
3764 },
3765 gt: function(elem, i, match){
3766 return i > match[3] - 0;
3767 },
3768 nth: function(elem, i, match){
3769 return match[3] - 0 == i;
3770 },
3771 eq: function(elem, i, match){
3772 return match[3] - 0 == i;
3773 }
3774 },
3775 filter: {
3776 PSEUDO: function(elem, match, i, array){
3777 var name = match[1], filter = Expr.filters[ name ];
3778
3779 if ( filter ) {
3780 return filter( elem, i, match, array );
3781 } else if ( name === "contains" ) {
3782 return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
3783 } else if ( name === "not" ) {
3784 var not = match[3];
3785
3786 for ( var i = 0, l = not.length; i < l; i++ ) {
3787 if ( not[i] === elem ) {
3788 return false;
3789 }
3790 }
3791
3792 return true;
3793 }
3794 },
3795 CHILD: function(elem, match){
3796 var type = match[1], node = elem;
3797 switch (type) {
3798 case 'only':
3799 case 'first':
3800 while (node = node.previousSibling) {
3801 if ( node.nodeType === 1 ) return false;
3802 }
3803 if ( type == 'first') return true;
3804 node = elem;
3805 case 'last':
3806 while (node = node.nextSibling) {
3807 if ( node.nodeType === 1 ) return false;
3808 }
3809 return true;
3810 case 'nth':
3811 var first = match[2], last = match[3];
3812
3813 if ( first == 1 && last == 0 ) {
3814 return true;
3815 }
3816
3817 var doneName = match[0],
3818 parent = elem.parentNode;
3819
3820 if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
3821 var count = 0;
3822 for ( node = parent.firstChild; node; node = node.nextSibling ) {
3823 if ( node.nodeType === 1 ) {
3824 node.nodeIndex = ++count;
3825 }
3826 }
3827 parent.sizcache = doneName;
3828 }
3829
3830 var diff = elem.nodeIndex - last;
3831 if ( first == 0 ) {
3832 return diff == 0;
3833 } else {
3834 return ( diff % first == 0 && diff / first >= 0 );
3835 }
3836 }
3837 },
3838 ID: function(elem, match){
3839 return elem.nodeType === 1 && elem.getAttribute("id") === match;
3840 },
3841 TAG: function(elem, match){
3842 return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
3843 },
3844 CLASS: function(elem, match){
3845 return (" " + (elem.className || elem.getAttribute("class")) + " ")
3846 .indexOf( match ) > -1;
3847 },
3848 ATTR: function(elem, match){
3849 var name = match[1],
3850 result = Expr.attrHandle[ name ] ?
3851 Expr.attrHandle[ name ]( elem ) :
3852 elem[ name ] != null ?
3853 elem[ name ] :
3854 elem.getAttribute( name ),
3855 value = result + "",
3856 type = match[2],
3857 check = match[4];
3858
3859 return result == null ?
3860 type === "!=" :
3861 type === "=" ?
3862 value === check :
3863 type === "*=" ?
3864 value.indexOf(check) >= 0 :
3865 type === "~=" ?
3866 (" " + value + " ").indexOf(check) >= 0 :
3867 !check ?
3868 value && result !== false :
3869 type === "!=" ?
3870 value != check :
3871 type === "^=" ?
3872 value.indexOf(check) === 0 :
3873 type === "$=" ?
3874 value.substr(value.length - check.length) === check :
3875 type === "|=" ?
3876 value === check || value.substr(0, check.length + 1) === check + "-" :
3877 false;
3878 },
3879 POS: function(elem, match, i, array){
3880 var name = match[2], filter = Expr.setFilters[ name ];
3881
3882 if ( filter ) {
3883 return filter( elem, i, match, array );
3884 }
3885 }
3886 }
3887 };
3888
3889 var origPOS = Expr.match.POS;
3890
3891 for ( var type in Expr.match ) {
3892 Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
3893 }
3894
3895 var makeArray = function(array, results) {
3896 array = Array.prototype.slice.call( array );
3897
3898 if ( results ) {
3899 arrayPush.apply( results, array );
3900 return results;
3901 }
3902
3903 return array;
3904 };
3905
3906 // Perform a simple check to determine if the browser is capable of
3907 // converting a NodeList to an array using builtin methods.
3908 try {
3909 Array.prototype.slice.call( document.documentElement.childNodes );
3910
3911 // Provide a fallback method if it does not work
3912 } catch(e){
3913 makeArray = function(array, results) {
3914 var ret = results || [];
3915
3916 if ( toString.call(array) === "[object Array]" ) {
3917 Array.prototype.push.apply( ret, array );
3918 } else {
3919 if ( typeof array.length === "number" ) {
3920 for ( var i = 0, l = array.length; i < l; i++ ) {
3921 ret.push( array[i] );
3922 }
3923 } else {
3924 for ( var i = 0; array[i]; i++ ) {
3925 ret.push( array[i] );
3926 }
3927 }
3928 }
3929
3930 return ret;
3931 };
3932 }
3933
3934 var sortOrder;
3935
3936 if ( document.documentElement.compareDocumentPosition ) {
3937 sortOrder = function( a, b ) {
3938 var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
3939 if ( ret === 0 ) {
3940 hasDuplicate = true;
3941 }
3942 return ret;
3943 };
3944 } else if ( "sourceIndex" in document.documentElement ) {
3945 sortOrder = function( a, b ) {
3946 var ret = a.sourceIndex - b.sourceIndex;
3947 if ( ret === 0 ) {
3948 hasDuplicate = true;
3949 }
3950 return ret;
3951 };
3952 } else if ( document.createRange ) {
3953 sortOrder = function( a, b ) {
3954 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
3955 aRange.selectNode(a);
3956 aRange.collapse(true);
3957 bRange.selectNode(b);
3958 bRange.collapse(true);
3959 var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
3960 if ( ret === 0 ) {
3961 hasDuplicate = true;
3962 }
3963 return ret;
3964 };
3965 }
3966
3967 // Check to see if the browser returns elements by name when
3968 // querying by getElementById (and provide a workaround)
3969 (function(){
3970 // We're going to inject a fake input element with a specified name
3971 var form = document.createElement("form"),
3972 id = "script" + (new Date).getTime();
3973 form.innerHTML = "<input name='" + id + "'/>";
3974
3975 // Inject it into the root element, check its status, and remove it quickly
3976 var root = document.documentElement;
3977 root.insertBefore( form, root.firstChild );
3978
3979 // The workaround has to do additional checks after a getElementById
3980 // Which slows things down for other browsers (hence the branching)
3981 if ( !!document.getElementById( id ) ) {
3982 Expr.find.ID = function(match, context, isXML){
3983 if ( typeof context.getElementById !== "undefined" && !isXML ) {
3984 var m = context.getElementById(match[1]);
3985 return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
3986 }
3987 };
3988
3989 Expr.filter.ID = function(elem, match){
3990 var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
3991 return elem.nodeType === 1 && node && node.nodeValue === match;
3992 };
3993 }
3994
3995 root.removeChild( form );
3996 })();
3997
3998 (function(){
3999 // Check to see if the browser returns only elements
4000 // when doing getElementsByTagName("*")
4001
4002 // Create a fake element
4003 var div = document.createElement("div");
4004 div.appendChild( document.createComment("") );
4005
4006 // Make sure no comments are found
4007 if ( div.getElementsByTagName("*").length > 0 ) {
4008 Expr.find.TAG = function(match, context){
4009 var results = context.getElementsByTagName(match[1]);
4010
4011 // Filter out possible comments
4012 if ( match[1] === "*" ) {
4013 var tmp = [];
4014
4015 for ( var i = 0; results[i]; i++ ) {
4016 if ( results[i].nodeType === 1 ) {
4017 tmp.push( results[i] );
4018 }
4019 }
4020
4021 results = tmp;
4022 }
4023
4024 return results;
4025 };
4026 }
4027
4028 // Check to see if an attribute returns normalized href attributes
4029 div.innerHTML = "<a href='#'></a>";
4030 if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
4031 div.firstChild.getAttribute("href") !== "#" ) {
4032 Expr.attrHandle.href = function(elem){
4033 return elem.getAttribute("href", 2);
4034 };
4035 }
4036 })();
4037
4038 if ( document.querySelectorAll ) (function(){
4039 var oldSizzle = Sizzle, div = document.createElement("div");
4040 div.innerHTML = "<p class='TEST'></p>";
4041
4042 // Safari can't handle uppercase or unicode characters when
4043 // in quirks mode.
4044 if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
4045 return;
4046 }
4047
4048 Sizzle = function(query, context, extra, seed){
4049 context = context || document;
4050
4051 // Only use querySelectorAll on non-XML documents
4052 // (ID selectors don't work in non-HTML documents)
4053 if ( !seed && context.nodeType === 9 && !isXML(context) ) {
4054 try {
4055 return makeArray( context.querySelectorAll(query), extra );
4056 } catch(e){}
4057 }
4058
4059 return oldSizzle(query, context, extra, seed);
4060 };
4061
4062 for ( var prop in oldSizzle ) {
4063 Sizzle[ prop ] = oldSizzle[ prop ];
4064 }
4065 })();
4066
4067 if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
4068 var div = document.createElement("div");
4069 div.innerHTML = "<div class='test e'></div><div class='test'></div>";
4070
4071 // Opera can't find a second classname (in 9.6)
4072 if ( div.getElementsByClassName("e").length === 0 )
4073 return;
4074
4075 // Safari caches class attributes, doesn't catch changes (in 3.2)
4076 div.lastChild.className = "e";
4077
4078 if ( div.getElementsByClassName("e").length === 1 )
4079 return;
4080
4081 Expr.order.splice(1, 0, "CLASS");
4082 Expr.find.CLASS = function(match, context, isXML) {
4083 if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
4084 return context.getElementsByClassName(match[1]);
4085 }
4086 };
4087 })();
4088
4089 function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
4090 var sibDir = dir == "previousSibling" && !isXML;
4091 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4092 var elem = checkSet[i];
4093 if ( elem ) {
4094 if ( sibDir && elem.nodeType === 1 ){
4095 elem.sizcache = doneName;
4096 elem.sizset = i;
4097 }
4098 elem = elem[dir];
4099 var match = false;
4100
4101 while ( elem ) {
4102 if ( elem.sizcache === doneName ) {
4103 match = checkSet[elem.sizset];
4104 break;
4105 }
4106
4107 if ( elem.nodeType === 1 && !isXML ){
4108 elem.sizcache = doneName;
4109 elem.sizset = i;
4110 }
4111
4112 if ( elem.nodeName === cur ) {
4113 match = elem;
4114 break;
4115 }
4116
4117 elem = elem[dir];
4118 }
4119
4120 checkSet[i] = match;
4121 }
4122 }
4123 }
4124
4125 function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
4126 var sibDir = dir == "previousSibling" && !isXML;
4127 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4128 var elem = checkSet[i];
4129 if ( elem ) {
4130 if ( sibDir && elem.nodeType === 1 ) {
4131 elem.sizcache = doneName;
4132 elem.sizset = i;
4133 }
4134 elem = elem[dir];
4135 var match = false;
4136
4137 while ( elem ) {
4138 if ( elem.sizcache === doneName ) {
4139 match = checkSet[elem.sizset];
4140 break;
4141 }
4142
4143 if ( elem.nodeType === 1 ) {
4144 if ( !isXML ) {
4145 elem.sizcache = doneName;
4146 elem.sizset = i;
4147 }
4148 if ( typeof cur !== "string" ) {
4149 if ( elem === cur ) {
4150 match = true;
4151 break;
4152 }
4153
4154 } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
4155 match = elem;
4156 break;
4157 }
4158 }
4159
4160 elem = elem[dir];
4161 }
4162
4163 checkSet[i] = match;
4164 }
4165 }
4166 }
4167
4168 var contains = document.compareDocumentPosition ? function(a, b){
4169 return a.compareDocumentPosition(b) & 16;
4170 } : function(a, b){
4171 return a !== b && (a.contains ? a.contains(b) : true);
4172 };
4173
4174 var isXML = function(elem){
4175 return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
4176 !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
4177 };
4178
4179 var posProcess = function(selector, context){
4180 var tmpSet = [], later = "", match,
4181 root = context.nodeType ? [context] : context;
4182
4183 // Position selectors must be done after the filter
4184 // And so must :not(positional) so we move all PSEUDOs to the end
4185 while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
4186 later += match[0];
4187 selector = selector.replace( Expr.match.PSEUDO, "" );
4188 }
4189
4190 selector = Expr.relative[selector] ? selector + "*" : selector;
4191
4192 for ( var i = 0, l = root.length; i < l; i++ ) {
4193 Sizzle( selector, root[i], tmpSet );
4194 }
4195
4196 return Sizzle.filter( later, tmpSet );
4197 };
4198
4199 // EXPOSE
4200
4201 window.tinymce.dom.Sizzle = Sizzle;
4202
4203 })();
4204 (function(tinymce) {
4205 // Shorten names
4206 var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event;
4207
4208 tinymce.create('static tinymce.dom.Event', {
4209 inits : [],
4210 events : [],
4211
4212
4213 add : function(o, n, f, s) {
4214 var cb, t = this, el = t.events, r;
4215
4216 // Handle array
4217 if (o && o.hasOwnProperty && o instanceof Array) {
4218 r = [];
4219
4220 each(o, function(o) {
4221 o = DOM.get(o);
4222 r.push(t.add(o, n, f, s));
4223 });
4224
4225 return r;
4226 }
4227
4228 o = DOM.get(o);
4229
4230 if (!o)
4231 return;
4232
4233 // Setup event callback
4234 cb = function(e) {
4235 e = e || window.event;
4236
4237 // Patch in target in IE it's W3C valid
4238 if (e && !e.target && isIE)
4239 e.target = e.srcElement;
4240
4241 if (!s)
4242 return f(e);
4243
4244 return f.call(s, e);
4245 };
4246
4247 if (n == 'unload') {
4248 tinymce.unloads.unshift({func : cb});
4249 return cb;
4250 }
4251
4252 if (n == 'init') {
4253 if (t.domLoaded)
4254 cb();
4255 else
4256 t.inits.push(cb);
4257
4258 return cb;
4259 }
4260
4261 // Store away listener reference
4262 el.push({
4263 obj : o,
4264 name : n,
4265 func : f,
4266 cfunc : cb,
4267 scope : s
4268 });
4269
4270 t._add(o, n, cb);
4271
4272 return f;
4273 },
4274
4275 remove : function(o, n, f) {
4276 var t = this, a = t.events, s = false, r;
4277
4278 // Handle array
4279 if (o && o.hasOwnProperty && o instanceof Array) {
4280 r = [];
4281
4282 each(o, function(o) {
4283 o = DOM.get(o);
4284 r.push(t.remove(o, n, f));
4285 });
4286
4287 return r;
4288 }
4289
4290 o = DOM.get(o);
4291
4292 each(a, function(e, i) {
4293 if (e.obj == o && e.name == n && (!f || (e.func == f || e.cfunc == f))) {
4294 a.splice(i, 1);
4295 t._remove(o, n, e.cfunc);
4296 s = true;
4297 return false;
4298 }
4299 });
4300
4301 return s;
4302 },
4303
4304 clear : function(o) {
4305 var t = this, a = t.events, i, e;
4306
4307 if (o) {
4308 o = DOM.get(o);
4309
4310 for (i = a.length - 1; i >= 0; i--) {
4311 e = a[i];
4312
4313 if (e.obj === o) {
4314 t._remove(e.obj, e.name, e.cfunc);
4315 e.obj = e.cfunc = null;
4316 a.splice(i, 1);
4317 }
4318 }
4319 }
4320 },
4321
4322
4323 cancel : function(e) {
4324 if (!e)
4325 return false;
4326
4327 this.stop(e);
4328 return this.prevent(e);
4329 },
4330
4331 stop : function(e) {
4332 if (e.stopPropagation)
4333 e.stopPropagation();
4334 else
4335 e.cancelBubble = true;
4336
4337 return false;
4338 },
4339
4340 prevent : function(e) {
4341 if (e.preventDefault)
4342 e.preventDefault();
4343 else
4344 e.returnValue = false;
4345
4346 return false;
4347 },
4348
4349 _unload : function() {
4350 var t = Event;
4351
4352 each(t.events, function(e, i) {
4353 t._remove(e.obj, e.name, e.cfunc);
4354 e.obj = e.cfunc = null;
4355 });
4356
4357 t.events = [];
4358 t = null;
4359 },
4360
4361 _add : function(o, n, f) {
4362 if (o.attachEvent)
4363 o.attachEvent('on' + n, f);
4364 else if (o.addEventListener)
4365 o.addEventListener(n, f, false);
4366 else
4367 o['on' + n] = f;
4368 },
4369
4370 _remove : function(o, n, f) {
4371 if (o) {
4372 try {
4373 if (o.detachEvent)
4374 o.detachEvent('on' + n, f);
4375 else if (o.removeEventListener)
4376 o.removeEventListener(n, f, false);
4377 else
4378 o['on' + n] = null;
4379 } catch (ex) {
4380 // Might fail with permission denined on IE so we just ignore that
4381 }
4382 }
4383 },
4384
4385 _pageInit : function() {
4386 var e = Event;
4387
4388 // Safari on Mac fires this twice
4389 if (e.domLoaded)
4390 return;
4391
4392 e._remove(window, 'DOMContentLoaded', e._pageInit);
4393 e.domLoaded = true;
4394
4395 each(e.inits, function(c) {
4396 c();
4397 });
4398
4399 e.inits = [];
4400 },
4401
4402 _wait : function() {
4403 var t;
4404
4405 // No need since the document is already loaded
4406 if (window.tinyMCE_GZ && tinyMCE_GZ.loaded) {
4407 Event.domLoaded = 1;
4408 return;
4409 }
4410
4411 if (isIE && document.location.protocol != 'https:') {
4412 // Fake DOMContentLoaded on IE
4413 document.write('<script id=__ie_onload defer src=\'javascript:""\';><\/script>');
4414 DOM.get("__ie_onload").onreadystatechange = function() {
4415 if (this.readyState == "complete") {
4416 Event._pageInit();
4417 DOM.get("__ie_onload").onreadystatechange = null; // Prevent leak
4418 }
4419 };
4420 } else {
4421 Event._add(window, 'DOMContentLoaded', Event._pageInit, Event);
4422
4423 if (isIE || isWebKit) {
4424 t = setInterval(function() {
4425 if (/loaded|complete/.test(document.readyState)) {
4426 clearInterval(t);
4427 Event._pageInit();
4428 }
4429 }, 10);
4430 }
4431 }
4432 }
4433
4434 });
4435
4436 // Shorten name
4437 Event = tinymce.dom.Event;
4438
4439 // Dispatch DOM content loaded event for IE and Safari
4440 Event._wait();
4441 tinymce.addUnload(Event._unload);
4442 })(tinymce);
4443 (function(tinymce) {
4444 var each = tinymce.each;
4445
4446 tinymce.create('tinymce.dom.Element', {
4447 Element : function(id, s) {
4448 var t = this, dom, el;
4449
4450 s = s || {};
4451 t.id = id;
4452 t.dom = dom = s.dom || tinymce.DOM;
4453 t.settings = s;
4454
4455 // Only IE leaks DOM references, this is a lot faster
4456 if (!tinymce.isIE)
4457 el = t.dom.get(t.id);
4458
4459 each([
4460 'getPos',
4461 'getRect',
4462 'getParent',
4463 'add',
4464 'setStyle',
4465 'getStyle',
4466 'setStyles',
4467 'setAttrib',
4468 'setAttribs',
4469 'getAttrib',
4470 'addClass',
4471 'removeClass',
4472 'hasClass',
4473 'getOuterHTML',
4474 'setOuterHTML',
4475 'remove',
4476 'show',
4477 'hide',
4478 'isHidden',
4479 'setHTML',
4480 'get'
4481 ], function(k) {
4482 t[k] = function() {
4483 var a = [id], i;
4484
4485 for (i = 0; i < arguments.length; i++)
4486 a.push(arguments[i]);
4487
4488 a = dom[k].apply(dom, a);
4489 t.update(k);
4490
4491 return a;
4492 };
4493 });
4494 },
4495
4496 on : function(n, f, s) {
4497 return tinymce.dom.Event.add(this.id, n, f, s);
4498 },
4499
4500 getXY : function() {
4501 return {
4502 x : parseInt(this.getStyle('left')),
4503 y : parseInt(this.getStyle('top'))
4504 };
4505 },
4506
4507 getSize : function() {
4508 var n = this.dom.get(this.id);
4509
4510 return {
4511 w : parseInt(this.getStyle('width') || n.clientWidth),
4512 h : parseInt(this.getStyle('height') || n.clientHeight)
4513 };
4514 },
4515
4516 moveTo : function(x, y) {
4517 this.setStyles({left : x, top : y});
4518 },
4519
4520 moveBy : function(x, y) {
4521 var p = this.getXY();
4522
4523 this.moveTo(p.x + x, p.y + y);
4524 },
4525
4526 resizeTo : function(w, h) {
4527 this.setStyles({width : w, height : h});
4528 },
4529
4530 resizeBy : function(w, h) {
4531 var s = this.getSize();
4532
4533 this.resizeTo(s.w + w, s.h + h);
4534 },
4535
4536 update : function(k) {
4537 var t = this, b, dom = t.dom;
4538
4539 if (tinymce.isIE6 && t.settings.blocker) {
4540 k = k || '';
4541
4542 // Ignore getters
4543 if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0)
4544 return;
4545
4546 // Remove blocker on remove
4547 if (k == 'remove') {
4548 dom.remove(t.blocker);
4549 return;
4550 }
4551
4552 if (!t.blocker) {
4553 t.blocker = dom.uniqueId();
4554 b = dom.add(t.settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'});
4555 dom.setStyle(b, 'opacity', 0);
4556 } else
4557 b = dom.get(t.blocker);
4558
4559 dom.setStyle(b, 'left', t.getStyle('left', 1));
4560 dom.setStyle(b, 'top', t.getStyle('top', 1));
4561 dom.setStyle(b, 'width', t.getStyle('width', 1));
4562 dom.setStyle(b, 'height', t.getStyle('height', 1));
4563 dom.setStyle(b, 'display', t.getStyle('display', 1));
4564 dom.setStyle(b, 'zIndex', parseInt(t.getStyle('zIndex', 1) || 0) - 1);
4565 }
4566 }
4567
4568 });
4569 })(tinymce);
4570 (function(tinymce) {
4571 function trimNl(s) {
4572 return s.replace(/[\n\r]+/g, '');
4573 };
4574
4575 // Shorten names
4576 var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each;
4577
4578 tinymce.create('tinymce.dom.Selection', {
4579 Selection : function(dom, win, serializer) {
4580 var t = this;
4581
4582 t.dom = dom;
4583 t.win = win;
4584 t.serializer = serializer;
4585
4586 // Add events
4587 each([
4588 'onBeforeSetContent',
4589 'onBeforeGetContent',
4590 'onSetContent',
4591 'onGetContent'
4592 ], function(e) {
4593 t[e] = new tinymce.util.Dispatcher(t);
4594 });
4595
4596 // No W3C Range support
4597 if (!t.win.getSelection)
4598 t.tridentSel = new tinymce.dom.TridentSelection(t);
4599
4600 // Prevent leaks
4601 tinymce.addUnload(t.destroy, t);
4602 },
4603
4604 getContent : function(s) {
4605 var t = this, r = t.getRng(), e = t.dom.create("body"), se = t.getSel(), wb, wa, n;
4606
4607 s = s || {};
4608 wb = wa = '';
4609 s.get = true;
4610 s.format = s.format || 'html';
4611 t.onBeforeGetContent.dispatch(t, s);
4612
4613 if (s.format == 'text')
4614 return t.isCollapsed() ? '' : (r.text || (se.toString ? se.toString() : ''));
4615
4616 if (r.cloneContents) {
4617 n = r.cloneContents();
4618
4619 if (n)
4620 e.appendChild(n);
4621 } else if (is(r.item) || is(r.htmlText))
4622 e.innerHTML = r.item ? r.item(0).outerHTML : r.htmlText;
4623 else
4624 e.innerHTML = r.toString();
4625
4626 // Keep whitespace before and after
4627 if (/^\s/.test(e.innerHTML))
4628 wb = ' ';
4629
4630 if (/\s+$/.test(e.innerHTML))
4631 wa = ' ';
4632
4633 s.getInner = true;
4634
4635 s.content = t.isCollapsed() ? '' : wb + t.serializer.serialize(e, s) + wa;
4636 t.onGetContent.dispatch(t, s);
4637
4638 return s.content;
4639 },
4640
4641 setContent : function(h, s) {
4642 var t = this, r = t.getRng(), c, d = t.win.document;
4643
4644 s = s || {format : 'html'};
4645 s.set = true;
4646 h = s.content = t.dom.processHTML(h);
4647
4648 // Dispatch before set content event
4649 t.onBeforeSetContent.dispatch(t, s);
4650 h = s.content;
4651
4652 if (r.insertNode) {
4653 // Make caret marker since insertNode places the caret in the beginning of text after insert
4654 h += '<span id="__caret">_</span>';
4655
4656 // Delete and insert new node
4657 r.deleteContents();
4658 r.insertNode(t.getRng().createContextualFragment(h));
4659
4660 // Move to caret marker
4661 c = t.dom.get('__caret');
4662
4663 // Make sure we wrap it compleatly, Opera fails with a simple select call
4664 r = d.createRange();
4665 r.setStartBefore(c);
4666 r.setEndAfter(c);
4667 t.setRng(r);
4668
4669 // Delete the marker, and hopefully the caret gets placed in the right location
4670 // Removed this since it seems to remove &nbsp; in FF and simply deleting it
4671 // doesn't seem to affect the caret position in any browser
4672 //d.execCommand('Delete', false, null);
4673
4674 // Remove the caret position
4675 t.dom.remove('__caret');
4676 } else {
4677 if (r.item) {
4678 // Delete content and get caret text selection
4679 d.execCommand('Delete', false, null);
4680 r = t.getRng();
4681 }
4682
4683 r.pasteHTML(h);
4684 }
4685
4686 // Dispatch set content event
4687 t.onSetContent.dispatch(t, s);
4688 },
4689
4690 getStart : function() {
4691 var t = this, r = t.getRng(), e;
4692
4693 if (isIE) {
4694 if (r.item)
4695 return r.item(0);
4696
4697 r = r.duplicate();
4698 r.collapse(1);
4699 e = r.parentElement();
4700
4701 if (e && e.nodeName == 'BODY')
4702 return e.firstChild;
4703
4704 return e;
4705 } else {
4706 e = r.startContainer;
4707
4708 if (e.nodeName == 'BODY')
4709 return e.firstChild;
4710
4711 return t.dom.getParent(e, '*');
4712 }
4713 },
4714
4715 getEnd : function() {
4716 var t = this, r = t.getRng(), e;
4717
4718 if (isIE) {
4719 if (r.item)
4720 return r.item(0);
4721
4722 r = r.duplicate();
4723 r.collapse(0);
4724 e = r.parentElement();
4725
4726 if (e && e.nodeName == 'BODY')
4727 return e.lastChild;
4728
4729 return e;
4730 } else {
4731 e = r.endContainer;
4732
4733 if (e.nodeName == 'BODY')
4734 return e.lastChild;
4735
4736 return t.dom.getParent(e, '*');
4737 }
4738 },
4739
4740 getBookmark : function(si) {
4741 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;
4742 sx = vp.x;
4743 sy = vp.y;
4744
4745 // Simple bookmark fast but not as persistent
4746 if (si == 'simple')
4747 return {rng : r, scrollX : sx, scrollY : sy};
4748
4749 // Handle IE
4750 if (isIE) {
4751 // Control selection
4752 if (r.item) {
4753 e = r.item(0);
4754
4755 each(t.dom.select(e.nodeName), function(n, i) {
4756 if (e == n) {
4757 sp = i;
4758 return false;
4759 }
4760 });
4761
4762 return {
4763 tag : e.nodeName,
4764 index : sp,
4765 scrollX : sx,
4766 scrollY : sy
4767 };
4768 }
4769
4770 // Text selection
4771 tr = t.dom.doc.body.createTextRange();
4772 tr.moveToElementText(ro);
4773 tr.collapse(true);
4774 bp = Math.abs(tr.move('character', c));
4775
4776 tr = r.duplicate();
4777 tr.collapse(true);
4778 sp = Math.abs(tr.move('character', c));
4779
4780 tr = r.duplicate();
4781 tr.collapse(false);
4782 le = Math.abs(tr.move('character', c)) - sp;
4783
4784 return {
4785 start : sp - bp,
4786 length : le,
4787 scrollX : sx,
4788 scrollY : sy
4789 };
4790 }
4791
4792 // Handle W3C
4793 e = t.getNode();
4794 s = t.getSel();
4795
4796 if (!s)
4797 return null;
4798
4799 // Image selection
4800 if (e && e.nodeName == 'IMG') {
4801 return {
4802 scrollX : sx,
4803 scrollY : sy
4804 };
4805 }
4806
4807 // Text selection
4808
4809 function getPos(r, sn, en) {
4810 var w = t.dom.doc.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {};
4811
4812 while ((n = w.nextNode()) != null) {
4813 if (n == sn)
4814 d.start = p;
4815
4816 if (n == en) {
4817 d.end = p;
4818 return d;
4819 }
4820
4821 p += trimNl(n.nodeValue || '').length;
4822 }
4823
4824 return null;
4825 };
4826
4827 // Caret or selection
4828 if (s.anchorNode == s.focusNode && s.anchorOffset == s.focusOffset) {
4829 e = getPos(ro, s.anchorNode, s.focusNode);
4830
4831 if (!e)
4832 return {scrollX : sx, scrollY : sy};
4833
4834 // Count whitespace before
4835 trimNl(s.anchorNode.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;});
4836
4837 return {
4838 start : Math.max(e.start + s.anchorOffset - wb, 0),
4839 end : Math.max(e.end + s.focusOffset - wb, 0),
4840 scrollX : sx,
4841 scrollY : sy,
4842 beg : s.anchorOffset - wb == 0
4843 };
4844 } else {
4845 e = getPos(ro, r.startContainer, r.endContainer);
4846
4847 // Count whitespace before start and end container
4848 //(r.startContainer.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;});
4849 //(r.endContainer.nodeValue || '').replace(/^\s+/, function(a) {wa = a.length;});
4850
4851 if (!e)
4852 return {scrollX : sx, scrollY : sy};
4853
4854 return {
4855 start : Math.max(e.start + r.startOffset - wb, 0),
4856 end : Math.max(e.end + r.endOffset - wa, 0),
4857 scrollX : sx,
4858 scrollY : sy,
4859 beg : r.startOffset - wb == 0
4860 };
4861 }
4862 },
4863
4864 moveToBookmark : function(b) {
4865 var t = this, r = t.getRng(), s = t.getSel(), ro = t.dom.getRoot(), sd, nvl, nv;
4866
4867 function getPos(r, sp, ep) {
4868 var w = t.dom.doc.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {}, o, v, wa, wb;
4869
4870 while ((n = w.nextNode()) != null) {
4871 wa = wb = 0;
4872
4873 nv = n.nodeValue || '';
4874 //nv.replace(/^\s+[^\s]/, function(a) {wb = a.length - 1;});
4875 //nv.replace(/[^\s]\s+$/, function(a) {wa = a.length - 1;});
4876
4877 nvl = trimNl(nv).length;
4878 p += nvl;
4879
4880 if (p >= sp && !d.startNode) {
4881 o = sp - (p - nvl);
4882
4883 // Fix for odd quirk in FF
4884 if (b.beg && o >= nvl)
4885 continue;
4886
4887 d.startNode = n;
4888 d.startOffset = o + wb;
4889 }
4890
4891 if (p >= ep) {
4892 d.endNode = n;
4893 d.endOffset = ep - (p - nvl) + wb;
4894 return d;
4895 }
4896 }
4897
4898 return null;
4899 };
4900
4901 if (!b)
4902 return false;
4903
4904 t.win.scrollTo(b.scrollX, b.scrollY);
4905
4906 // Handle explorer
4907 if (isIE) {
4908 // Handle simple
4909 if (r = b.rng) {
4910 try {
4911 r.select();
4912 } catch (ex) {
4913 // Ignore
4914 }
4915
4916 return true;
4917 }
4918
4919 t.win.focus();
4920
4921 // Handle control bookmark
4922 if (b.tag) {
4923 r = ro.createControlRange();
4924
4925 each(t.dom.select(b.tag), function(n, i) {
4926 if (i == b.index)
4927 r.addElement(n);
4928 });
4929 } else {
4930 // Try/catch needed since this operation breaks when TinyMCE is placed in hidden divs/tabs
4931 try {
4932 // Incorrect bookmark
4933 if (b.start < 0)
4934 return true;
4935
4936 r = s.createRange();
4937 r.moveToElementText(ro);
4938 r.collapse(true);
4939 r.moveStart('character', b.start);
4940 r.moveEnd('character', b.length);
4941 } catch (ex2) {
4942 return true;
4943 }
4944 }
4945
4946 try {
4947 r.select();
4948 } catch (ex) {
4949 // Needed for some odd IE bug #1843306
4950 }
4951
4952 return true;
4953 }
4954
4955 // Handle W3C
4956 if (!s)
4957 return false;
4958
4959 // Handle simple
4960 if (b.rng) {
4961 s.removeAllRanges();
4962 s.addRange(b.rng);
4963 } else {
4964 if (is(b.start) && is(b.end)) {
4965 try {
4966 sd = getPos(ro, b.start, b.end);
4967
4968 if (sd) {
4969 r = t.dom.doc.createRange();
4970 r.setStart(sd.startNode, sd.startOffset);
4971 r.setEnd(sd.endNode, sd.endOffset);
4972 s.removeAllRanges();
4973 s.addRange(r);
4974 }
4975
4976 if (!tinymce.isOpera)
4977 t.win.focus();
4978 } catch (ex) {
4979 // Ignore
4980 }
4981 }
4982 }
4983 },
4984
4985 select : function(n, c) {
4986 var t = this, r = t.getRng(), s = t.getSel(), b, fn, ln, d = t.win.document;
4987
4988 function find(n, start) {
4989 var walker, o;
4990
4991 if (n) {
4992 walker = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
4993
4994 // Find first/last non empty text node
4995 while (n = walker.nextNode()) {
4996 o = n;
4997
4998 if (tinymce.trim(n.nodeValue).length != 0) {
4999 if (start)
5000 return n;
5001 else
5002 o = n;
5003 }
5004 }
5005 }
5006
5007 return o;
5008 };
5009
5010 if (isIE) {
5011 try {
5012 b = d.body;
5013
5014 if (/^(IMG|TABLE)$/.test(n.nodeName)) {
5015 r = b.createControlRange();
5016 r.addElement(n);
5017 } else {
5018 r = b.createTextRange();
5019 r.moveToElementText(n);
5020 }
5021
5022 r.select();
5023 } catch (ex) {
5024 // Throws illigal agrument in IE some times
5025 }
5026 } else {
5027 if (c) {
5028 fn = find(n, 1) || t.dom.select('br:first', n)[0];
5029 ln = find(n, 0) || t.dom.select('br:last', n)[0];
5030
5031 if (fn && ln) {
5032 r = d.createRange();
5033
5034 if (fn.nodeName == 'BR')
5035 r.setStartBefore(fn);
5036 else
5037 r.setStart(fn, 0);
5038
5039 if (ln.nodeName == 'BR')
5040 r.setEndBefore(ln);
5041 else
5042 r.setEnd(ln, ln.nodeValue.length);
5043 } else
5044 r.selectNode(n);
5045 } else
5046 r.selectNode(n);
5047
5048 t.setRng(r);
5049 }
5050
5051 return n;
5052 },
5053
5054 isCollapsed : function() {
5055 var t = this, r = t.getRng(), s = t.getSel();
5056
5057 if (!r || r.item)
5058 return false;
5059
5060 return !s || r.boundingWidth == 0 || r.collapsed;
5061 },
5062
5063 collapse : function(b) {
5064 var t = this, r = t.getRng(), n;
5065
5066 // Control range on IE
5067 if (r.item) {
5068 n = r.item(0);
5069 r = this.win.document.body.createTextRange();
5070 r.moveToElementText(n);
5071 }
5072
5073 r.collapse(!!b);
5074 t.setRng(r);
5075 },
5076
5077 getSel : function() {
5078 var t = this, w = this.win;
5079
5080 return w.getSelection ? w.getSelection() : w.document.selection;
5081 },
5082
5083 getRng : function(w3c) {
5084 var t = this, s, r;
5085
5086 // Found tridentSel object then we need to use that one
5087 if (w3c && t.tridentSel)
5088 return t.tridentSel.getRangeAt(0);
5089
5090 try {
5091 if (s = t.getSel())
5092 r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : t.win.document.createRange());
5093 } catch (ex) {
5094 // IE throws unspecified error here if TinyMCE is placed in a frame/iframe
5095 }
5096
5097 // No range found then create an empty one
5098 // This can occur when the editor is placed in a hidden container element on Gecko
5099 // Or on IE when there was an exception
5100 if (!r)
5101 r = isIE ? t.win.document.body.createTextRange() : t.win.document.createRange();
5102
5103 return r;
5104 },
5105
5106 setRng : function(r) {
5107 var s, t = this;
5108
5109 if (!t.tridentSel) {
5110 s = t.getSel();
5111
5112 if (s) {
5113 s.removeAllRanges();
5114 s.addRange(r);
5115 }
5116 } else {
5117 // Is W3C Range
5118 if (r.cloneRange) {
5119 t.tridentSel.addRange(r);
5120 return;
5121 }
5122
5123 // Is IE specific range
5124 try {
5125 r.select();
5126 } catch (ex) {
5127 // Needed for some odd IE bug #1843306
5128 }
5129 }
5130 },
5131
5132 setNode : function(n) {
5133 var t = this;
5134
5135 t.setContent(t.dom.getOuterHTML(n));
5136
5137 return n;
5138 },
5139
5140 getNode : function() {
5141 var t = this, r = t.getRng(), s = t.getSel(), e;
5142
5143 if (!isIE) {
5144 // Range maybe lost after the editor is made visible again
5145 if (!r)
5146 return t.dom.getRoot();
5147
5148 e = r.commonAncestorContainer;
5149
5150 // Handle selection a image or other control like element such as anchors
5151 if (!r.collapsed) {
5152 // If the anchor node is a element instead of a text node then return this element
5153 if (tinymce.isWebKit && s.anchorNode && s.anchorNode.nodeType == 1)
5154 return s.anchorNode.childNodes[s.anchorOffset];
5155
5156 if (r.startContainer == r.endContainer) {
5157 if (r.startOffset - r.endOffset < 2) {
5158 if (r.startContainer.hasChildNodes())
5159 e = r.startContainer.childNodes[r.startOffset];
5160 }
5161 }
5162 }
5163
5164 return t.dom.getParent(e, '*');
5165 }
5166
5167 return r.item ? r.item(0) : r.parentElement();
5168 },
5169
5170 getSelectedBlocks : function(st, en) {
5171 var t = this, dom = t.dom, sb, eb, n, bl = [];
5172
5173 sb = dom.getParent(st || t.getStart(), dom.isBlock);
5174 eb = dom.getParent(en || t.getEnd(), dom.isBlock);
5175
5176 if (sb)
5177 bl.push(sb);
5178
5179 if (sb && eb && sb != eb) {
5180 n = sb;
5181
5182 while ((n = n.nextSibling) && n != eb) {
5183 if (dom.isBlock(n))
5184 bl.push(n);
5185 }
5186 }
5187
5188 if (eb && sb != eb)
5189 bl.push(eb);
5190
5191 return bl;
5192 },
5193
5194 destroy : function(s) {
5195 var t = this;
5196
5197 t.win = null;
5198
5199 // Manual destroy then remove unload handler
5200 if (!s)
5201 tinymce.removeUnload(t.destroy);
5202 }
5203
5204 });
5205 })(tinymce);
5206 (function(tinymce) {
5207 tinymce.create('tinymce.dom.XMLWriter', {
5208 node : null,
5209
5210 XMLWriter : function(s) {
5211 // Get XML document
5212 function getXML() {
5213 var i = document.implementation;
5214
5215 if (!i || !i.createDocument) {
5216 // Try IE objects
5217 try {return new ActiveXObject('MSXML2.DOMDocument');} catch (ex) {}
5218 try {return new ActiveXObject('Microsoft.XmlDom');} catch (ex) {}
5219 } else
5220 return i.createDocument('', '', null);
5221 };
5222
5223 this.doc = getXML();
5224
5225 // Since Opera and WebKit doesn't escape > into &gt; we need to do it our self to normalize the output for all browsers
5226 this.valid = tinymce.isOpera || tinymce.isWebKit;
5227
5228 this.reset();
5229 },
5230
5231 reset : function() {
5232 var t = this, d = t.doc;
5233
5234 if (d.firstChild)
5235 d.removeChild(d.firstChild);
5236
5237 t.node = d.appendChild(d.createElement("html"));
5238 },
5239
5240 writeStartElement : function(n) {
5241 var t = this;
5242
5243 t.node = t.node.appendChild(t.doc.createElement(n));
5244 },
5245
5246 writeAttribute : function(n, v) {
5247 if (this.valid)
5248 v = v.replace(/>/g, '%MCGT%');
5249
5250 this.node.setAttribute(n, v);
5251 },
5252
5253 writeEndElement : function() {
5254 this.node = this.node.parentNode;
5255 },
5256
5257 writeFullEndElement : function() {
5258 var t = this, n = t.node;
5259
5260 n.appendChild(t.doc.createTextNode(""));
5261 t.node = n.parentNode;
5262 },
5263
5264 writeText : function(v) {
5265 if (this.valid)
5266 v = v.replace(/>/g, '%MCGT%');
5267
5268 this.node.appendChild(this.doc.createTextNode(v));
5269 },
5270
5271 writeCDATA : function(v) {
5272 this.node.appendChild(this.doc.createCDATA(v));
5273 },
5274
5275 writeComment : function(v) {
5276 // Fix for bug #2035694
5277 if (tinymce.isIE)
5278 v = v.replace(/^\-|\-$/g, ' ');
5279
5280 this.node.appendChild(this.doc.createComment(v.replace(/\-\-/g, ' ')));
5281 },
5282
5283 getContent : function() {
5284 var h;
5285
5286 h = this.doc.xml || new XMLSerializer().serializeToString(this.doc);
5287 h = h.replace(/<\?[^?]+\?>|<html>|<\/html>|<html\/>|<!DOCTYPE[^>]+>/g, '');
5288 h = h.replace(/ ?\/>/g, ' />');
5289
5290 if (this.valid)
5291 h = h.replace(/\%MCGT%/g, '&gt;');
5292
5293 return h;
5294 }
5295
5296 });
5297 })(tinymce);
5298 (function(tinymce) {
5299 tinymce.create('tinymce.dom.StringWriter', {
5300 str : null,
5301 tags : null,
5302 count : 0,
5303 settings : null,
5304 indent : null,
5305
5306 StringWriter : function(s) {
5307 this.settings = tinymce.extend({
5308 indent_char : ' ',
5309 indentation : 1
5310 }, s);
5311
5312 this.reset();
5313 },
5314
5315 reset : function() {
5316 this.indent = '';
5317 this.str = "";
5318 this.tags = [];
5319 this.count = 0;
5320 },
5321
5322 writeStartElement : function(n) {
5323 this._writeAttributesEnd();
5324 this.writeRaw('<' + n);
5325 this.tags.push(n);
5326 this.inAttr = true;
5327 this.count++;
5328 this.elementCount = this.count;
5329 },
5330
5331 writeAttribute : function(n, v) {
5332 var t = this;
5333
5334 t.writeRaw(" " + t.encode(n) + '="' + t.encode(v) + '"');
5335 },
5336
5337 writeEndElement : function() {
5338 var n;
5339
5340 if (this.tags.length > 0) {
5341 n = this.tags.pop();
5342
5343 if (this._writeAttributesEnd(1))
5344 this.writeRaw('</' + n + '>');
5345
5346 if (this.settings.indentation > 0)
5347 this.writeRaw('\n');
5348 }
5349 },
5350
5351 writeFullEndElement : function() {
5352 if (this.tags.length > 0) {
5353 this._writeAttributesEnd();
5354 this.writeRaw('</' + this.tags.pop() + '>');
5355
5356 if (this.settings.indentation > 0)
5357 this.writeRaw('\n');
5358 }
5359 },
5360
5361 writeText : function(v) {
5362 this._writeAttributesEnd();
5363 this.writeRaw(this.encode(v));
5364 this.count++;
5365 },
5366
5367 writeCDATA : function(v) {
5368 this._writeAttributesEnd();
5369 this.writeRaw('<![CDATA[' + v + ']]>');
5370 this.count++;
5371 },
5372
5373 writeComment : function(v) {
5374 this._writeAttributesEnd();
5375 this.writeRaw('<!-- ' + v + '-->');
5376 this.count++;
5377 },
5378
5379 writeRaw : function(v) {
5380 this.str += v;
5381 },
5382
5383 encode : function(s) {
5384 return s.replace(/[<>&"]/g, function(v) {
5385 switch (v) {
5386 case '<':
5387 return '&lt;';
5388
5389 case '>':
5390 return '&gt;';
5391
5392 case '&':
5393 return '&amp;';
5394
5395 case '"':
5396 return '&quot;';
5397 }
5398
5399 return v;
5400 });
5401 },
5402
5403 getContent : function() {
5404 return this.str;
5405 },
5406
5407 _writeAttributesEnd : function(s) {
5408 if (!this.inAttr)
5409 return;
5410
5411 this.inAttr = false;
5412
5413 if (s && this.elementCount == this.count) {
5414 this.writeRaw(' />');
5415 return false;
5416 }
5417
5418 this.writeRaw('>');
5419
5420 return true;
5421 }
5422
5423 });
5424 })(tinymce);
5425 (function(tinymce) {
5426 // Shorten names
5427 var extend = tinymce.extend, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, isIE = tinymce.isIE, isGecko = tinymce.isGecko;
5428
5429 function wildcardToRE(s) {
5430 return s.replace(/([?+*])/g, '.$1');
5431 };
5432
5433 tinymce.create('tinymce.dom.Serializer', {
5434 Serializer : function(s) {
5435 var t = this;
5436
5437 t.key = 0;
5438 t.onPreProcess = new Dispatcher(t);
5439 t.onPostProcess = new Dispatcher(t);
5440
5441 try {
5442 t.writer = new tinymce.dom.XMLWriter();
5443 } catch (ex) {
5444 // IE might throw exception if ActiveX is disabled so we then switch to the slightly slower StringWriter
5445 t.writer = new tinymce.dom.StringWriter();
5446 }
5447
5448 // Default settings
5449 t.settings = s = extend({
5450 dom : tinymce.DOM,
5451 valid_nodes : 0,
5452 node_filter : 0,
5453 attr_filter : 0,
5454 invalid_attrs : /^(mce_|_moz_)/,
5455 closed : /(br|hr|input|meta|img|link|param)/,
5456 entity_encoding : 'named',
5457 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',
5458 bool_attrs : /(checked|disabled|readonly|selected|nowrap)/,
5459 valid_elements : '*[*]',
5460 extended_valid_elements : 0,
5461 valid_child_elements : 0,
5462 invalid_elements : 0,
5463 fix_table_elements : 1,
5464 fix_list_elements : true,
5465 fix_content_duplication : true,
5466 convert_fonts_to_spans : false,
5467 font_size_classes : 0,
5468 font_size_style_values : 0,
5469 apply_source_formatting : 0,
5470 indent_mode : 'simple',
5471 indent_char : '\t',
5472 indent_levels : 1,
5473 remove_linebreaks : 1,
5474 remove_redundant_brs : 1,
5475 element_format : 'xhtml'
5476 }, s);
5477
5478 t.dom = s.dom;
5479
5480 if (s.remove_redundant_brs) {
5481 t.onPostProcess.add(function(se, o) {
5482 // Remove BR elements at end of list elements since they get rendered in IE
5483 o.content = o.content.replace(/<br \/>(\s*<\/li>)/g, '$1');
5484 });
5485 }
5486
5487 // Remove XHTML element endings i.e. produce crap :) XHTML is better
5488 if (s.element_format == 'html') {
5489 t.onPostProcess.add(function(se, o) {
5490 o.content = o.content.replace(/<([^>]+) \/>/g, '<$1>');
5491 });
5492 }
5493
5494 if (s.fix_list_elements) {
5495 t.onPreProcess.add(function(se, o) {
5496 var nl, x, a = ['ol', 'ul'], i, n, p, r = /^(OL|UL)$/, np;
5497
5498 function prevNode(e, n) {
5499 var a = n.split(','), i;
5500
5501 while ((e = e.previousSibling) != null) {
5502 for (i=0; i<a.length; i++) {
5503 if (e.nodeName == a[i])
5504 return e;
5505 }
5506 }
5507
5508 return null;
5509 };
5510
5511 for (x=0; x<a.length; x++) {
5512 nl = t.dom.select(a[x], o.node);
5513
5514 for (i=0; i<nl.length; i++) {
5515 n = nl[i];
5516 p = n.parentNode;
5517
5518 if (r.test(p.nodeName)) {
5519 np = prevNode(n, 'LI');
5520
5521 if (!np) {
5522 np = t.dom.create('li');
5523 np.innerHTML = '&nbsp;';
5524 np.appendChild(n);
5525 p.insertBefore(np, p.firstChild);
5526 } else
5527 np.appendChild(n);
5528 }
5529 }
5530 }
5531 });
5532 }
5533
5534 if (s.fix_table_elements) {
5535 t.onPreProcess.add(function(se, o) {
5536 each(t.dom.select('p table', o.node), function(n) {
5537 t.dom.split(t.dom.getParent(n, 'p'), n);
5538 });
5539 });
5540 }
5541 },
5542
5543 setEntities : function(s) {
5544 var t = this, a, i, l = {}, re = '', v;
5545
5546 // No need to setup more than once
5547 if (t.entityLookup)
5548 return;
5549
5550 // Build regex and lookup array
5551 a = s.split(',');
5552 for (i = 0; i < a.length; i += 2) {
5553 v = a[i];
5554
5555 // Don't add default &amp; &quot; etc.
5556 if (v == 34 || v == 38 || v == 60 || v == 62)
5557 continue;
5558
5559 l[String.fromCharCode(a[i])] = a[i + 1];
5560
5561 v = parseInt(a[i]).toString(16);
5562 re += '\\u' + '0000'.substring(v.length) + v;
5563 }
5564
5565 if (!re) {
5566 t.settings.entity_encoding = 'raw';
5567 return;
5568 }
5569
5570 t.entitiesRE = new RegExp('[' + re + ']', 'g');
5571 t.entityLookup = l;
5572 },
5573
5574 setValidChildRules : function(s) {
5575 this.childRules = null;
5576 this.addValidChildRules(s);
5577 },
5578
5579 addValidChildRules : function(s) {
5580 var t = this, inst, intr, bloc;
5581
5582 if (!s)
5583 return;
5584
5585 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';
5586 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';
5587 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';
5588
5589 each(s.split(','), function(s) {
5590 var p = s.split(/\[|\]/), re;
5591
5592 s = '';
5593 each(p[1].split('|'), function(v) {
5594 if (s)
5595 s += '|';
5596
5597 switch (v) {
5598 case '%itrans':
5599 v = intr;
5600 break;
5601
5602 case '%itrans_na':
5603 v = intr.substring(2);
5604 break;
5605
5606 case '%istrict':
5607 v = inst;
5608 break;
5609
5610 case '%istrict_na':
5611 v = inst.substring(2);
5612 break;
5613
5614 case '%btrans':
5615 v = bloc;
5616 break;
5617
5618 case '%bstrict':
5619 v = bloc;
5620 break;
5621 }
5622
5623 s += v;
5624 });
5625 re = new RegExp('^(' + s.toLowerCase() + ')$', 'i');
5626
5627 each(p[0].split('/'), function(s) {
5628 t.childRules = t.childRules || {};
5629 t.childRules[s] = re;
5630 });
5631 });
5632
5633 // Build regex
5634 s = '';
5635 each(t.childRules, function(v, k) {
5636 if (s)
5637 s += '|';
5638
5639 s += k;
5640 });
5641
5642 t.parentElementsRE = new RegExp('^(' + s.toLowerCase() + ')$', 'i');
5643
5644 /*console.debug(t.parentElementsRE.toString());
5645 each(t.childRules, function(v) {
5646 console.debug(v.toString());
5647 });*/
5648 },
5649
5650 setRules : function(s) {
5651 var t = this;
5652
5653 t._setup();
5654 t.rules = {};
5655 t.wildRules = [];
5656 t.validElements = {};
5657
5658 return t.addRules(s);
5659 },
5660
5661 addRules : function(s) {
5662 var t = this, dr;
5663
5664 if (!s)
5665 return;
5666
5667 t._setup();
5668
5669 each(s.split(','), function(s) {
5670 var p = s.split(/\[|\]/), tn = p[0].split('/'), ra, at, wat, va = [];
5671
5672 // Extend with default rules
5673 if (dr)
5674 at = tinymce.extend([], dr.attribs);
5675
5676 // Parse attributes
5677 if (p.length > 1) {
5678 each(p[1].split('|'), function(s) {
5679 var ar = {}, i;
5680
5681 at = at || [];
5682
5683 // Parse attribute rule
5684 s = s.replace(/::/g, '~');
5685 s = /^([!\-])?([\w*.?~_\-]+|)([=:<])?(.+)?$/.exec(s);
5686 s[2] = s[2].replace(/~/g, ':');
5687
5688 // Add required attributes
5689 if (s[1] == '!') {
5690 ra = ra || [];
5691 ra.push(s[2]);
5692 }
5693
5694 // Remove inherited attributes
5695 if (s[1] == '-') {
5696 for (i = 0; i <at.length; i++) {
5697 if (at[i].name == s[2]) {
5698 at.splice(i, 1);
5699 return;
5700 }
5701 }
5702 }
5703
5704 switch (s[3]) {
5705 // Add default attrib values
5706 case '=':
5707 ar.defaultVal = s[4] || '';
5708 break;
5709
5710 // Add forced attrib values
5711 case ':':
5712 ar.forcedVal = s[4];
5713 break;
5714
5715 // Add validation values
5716 case '<':
5717 ar.validVals = s[4].split('?');
5718 break;
5719 }
5720
5721 if (/[*.?]/.test(s[2])) {
5722 wat = wat || [];
5723 ar.nameRE = new RegExp('^' + wildcardToRE(s[2]) + '$');
5724 wat.push(ar);
5725 } else {
5726 ar.name = s[2];
5727 at.push(ar);
5728 }
5729
5730 va.push(s[2]);
5731 });
5732 }
5733
5734 // Handle element names
5735 each(tn, function(s, i) {
5736 var pr = s.charAt(0), x = 1, ru = {};
5737
5738 // Extend with default rule data
5739 if (dr) {
5740 if (dr.noEmpty)
5741 ru.noEmpty = dr.noEmpty;
5742
5743 if (dr.fullEnd)
5744 ru.fullEnd = dr.fullEnd;
5745
5746 if (dr.padd)
5747 ru.padd = dr.padd;
5748 }
5749
5750 // Handle prefixes
5751 switch (pr) {
5752 case '-':
5753 ru.noEmpty = true;
5754 break;
5755
5756 case '+':
5757 ru.fullEnd = true;
5758 break;
5759
5760 case '#':
5761 ru.padd = true;
5762 break;
5763
5764 default:
5765 x = 0;
5766 }
5767
5768 tn[i] = s = s.substring(x);
5769 t.validElements[s] = 1;
5770
5771 // Add element name or element regex
5772 if (/[*.?]/.test(tn[0])) {
5773 ru.nameRE = new RegExp('^' + wildcardToRE(tn[0]) + '$');
5774 t.wildRules = t.wildRules || {};
5775 t.wildRules.push(ru);
5776 } else {
5777 ru.name = tn[0];
5778
5779 // Store away default rule
5780 if (tn[0] == '@')
5781 dr = ru;
5782
5783 t.rules[s] = ru;
5784 }
5785
5786 ru.attribs = at;
5787
5788 if (ra)
5789 ru.requiredAttribs = ra;
5790
5791 if (wat) {
5792 // Build valid attributes regexp
5793 s = '';
5794 each(va, function(v) {
5795 if (s)
5796 s += '|';
5797
5798 s += '(' + wildcardToRE(v) + ')';
5799 });
5800 ru.validAttribsRE = new RegExp('^' + s.toLowerCase() + '$');
5801 ru.wildAttribs = wat;
5802 }
5803 });
5804 });
5805
5806 // Build valid elements regexp
5807 s = '';
5808 each(t.validElements, function(v, k) {
5809 if (s)
5810 s += '|';
5811
5812 if (k != '@')
5813 s += k;
5814 });
5815 t.validElementsRE = new RegExp('^(' + wildcardToRE(s.toLowerCase()) + ')$');
5816
5817 //console.debug(t.validElementsRE.toString());
5818 //console.dir(t.rules);
5819 //console.dir(t.wildRules);
5820 },
5821
5822 findRule : function(n) {
5823 var t = this, rl = t.rules, i, r;
5824
5825 t._setup();
5826
5827 // Exact match
5828 r = rl[n];
5829 if (r)
5830 return r;
5831
5832 // Try wildcards
5833 rl = t.wildRules;
5834 for (i = 0; i < rl.length; i++) {
5835 if (rl[i].nameRE.test(n))
5836 return rl[i];
5837 }
5838
5839 return null;
5840 },
5841
5842 findAttribRule : function(ru, n) {
5843 var i, wa = ru.wildAttribs;
5844
5845 for (i = 0; i < wa.length; i++) {
5846 if (wa[i].nameRE.test(n))
5847 return wa[i];
5848 }
5849
5850 return null;
5851 },
5852
5853 serialize : function(n, o) {
5854 var h, t = this;
5855
5856 t._setup();
5857 o = o || {};
5858 o.format = o.format || 'html';
5859 t.processObj = o;
5860 n = n.cloneNode(true);
5861 t.key = '' + (parseInt(t.key) + 1);
5862
5863 // Pre process
5864 if (!o.no_events) {
5865 o.node = n;
5866 t.onPreProcess.dispatch(t, o);
5867 }
5868
5869 // Serialize HTML DOM into a string
5870 t.writer.reset();
5871 t._serializeNode(n, o.getInner);
5872
5873 // Post process
5874 o.content = t.writer.getContent();
5875
5876 if (!o.no_events)
5877 t.onPostProcess.dispatch(t, o);
5878
5879 t._postProcess(o);
5880 o.node = null;
5881
5882 return tinymce.trim(o.content);
5883 },
5884
5885 // Internal functions
5886
5887 _postProcess : function(o) {
5888 var t = this, s = t.settings, h = o.content, sc = [], p;
5889
5890 if (o.format == 'html') {
5891 // Protect some elements
5892 p = t._protect({
5893 content : h,
5894 patterns : [
5895 {pattern : /(<script[^>]*>)(.*?)(<\/script>)/g},
5896 {pattern : /(<style[^>]*>)(.*?)(<\/style>)/g},
5897 {pattern : /(<pre[^>]*>)(.*?)(<\/pre>)/g, encode : 1},
5898 {pattern : /(<!--\[CDATA\[)(.*?)(\]\]-->)/g}
5899 ]
5900 });
5901
5902 h = p.content;
5903
5904 // Entity encode
5905 if (s.entity_encoding !== 'raw')
5906 h = t._encode(h);
5907
5908 // Use BR instead of &nbsp; padded P elements inside editor and use <p>&nbsp;</p> outside editor
5909 /* if (o.set)
5910 h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p><br /></p>');
5911 else
5912 h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p>$1</p>');*/
5913
5914 // Since Gecko and Safari keeps whitespace in the DOM we need to
5915 // remove it inorder to match other browsers. But I think Gecko and Safari is right.
5916 // This process is only done when getting contents out from the editor.
5917 if (!o.set) {
5918 // We need to replace paragraph whitespace with an nbsp before indentation to keep the \u00a0 char
5919 h = h.replace(/<p>\s+<\/p>|<p([^>]+)>\s+<\/p>/g, s.entity_encoding == 'numeric' ? '<p$1>&#160;</p>' : '<p$1>&nbsp;</p>');
5920
5921 if (s.remove_linebreaks) {
5922 h = h.replace(/\r?\n|\r/g, ' ');
5923 h = h.replace(/(<[^>]+>)\s+/g, '$1 ');
5924 h = h.replace(/\s+(<\/[^>]+>)/g, ' $1');
5925 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
5926 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
5927 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
5928 }
5929
5930 // Simple indentation
5931 if (s.apply_source_formatting && s.indent_mode == 'simple') {
5932 // Add line breaks before and after block elements
5933 h = h.replace(/<(\/?)(ul|hr|table|meta|link|tbody|tr|object|body|head|html|map)(|[^>]+)>\s*/g, '\n<$1$2$3>\n');
5934 h = h.replace(/\s*<(p|h[1-6]|blockquote|div|title|style|pre|script|td|li|area)(|[^>]+)>/g, '\n<$1$2>');
5935 h = h.replace(/<\/(p|h[1-6]|blockquote|div|title|style|pre|script|td|li)>\s*/g, '</$1>\n');
5936 h = h.replace(/\n\n/g, '\n');
5937 }
5938 }
5939
5940 h = t._unprotect(h, p);
5941
5942 // Restore CDATA sections
5943 h = h.replace(/<!--\[CDATA\[([\s\S]+)\]\]-->/g, '<![CDATA[$1]]>');
5944
5945 // Restore the \u00a0 character if raw mode is enabled
5946 if (s.entity_encoding == 'raw')
5947 h = h.replace(/<p>&nbsp;<\/p>|<p([^>]+)>&nbsp;<\/p>/g, '<p$1>\u00a0</p>');
5948 }
5949
5950 o.content = h;
5951 },
5952
5953 _serializeNode : function(n, inn) {
5954 var t = this, s = t.settings, w = t.writer, hc, el, cn, i, l, a, at, no, v, nn, ru, ar, iv;
5955
5956 if (!s.node_filter || s.node_filter(n)) {
5957 switch (n.nodeType) {
5958 case 1: // Element
5959 if (n.hasAttribute ? n.hasAttribute('mce_bogus') : n.getAttribute('mce_bogus'))
5960 return;
5961
5962 iv = false;
5963 hc = n.hasChildNodes();
5964 nn = n.getAttribute('mce_name') || n.nodeName.toLowerCase();
5965
5966 // Add correct prefix on IE
5967 if (isIE) {
5968 if (n.scopeName !== 'HTML' && n.scopeName !== 'html')
5969 nn = n.scopeName + ':' + nn;
5970 }
5971
5972 // Remove mce prefix on IE needed for the abbr element
5973 if (nn.indexOf('mce:') === 0)
5974 nn = nn.substring(4);
5975
5976 // Check if valid
5977 if (!t.validElementsRE.test(nn) || (t.invalidElementsRE && t.invalidElementsRE.test(nn)) || inn) {
5978 iv = true;
5979 break;
5980 }
5981
5982 if (isIE) {
5983 // Fix IE content duplication (DOM can have multiple copies of the same node)
5984 if (s.fix_content_duplication) {
5985 if (n.mce_serialized == t.key)
5986 return;
5987
5988 n.mce_serialized = t.key;
5989 }
5990
5991 // IE sometimes adds a / infront of the node name
5992 if (nn.charAt(0) == '/')
5993 nn = nn.substring(1);
5994 } else if (isGecko) {
5995 // Ignore br elements
5996 if (n.nodeName === 'BR' && n.getAttribute('type') == '_moz')
5997 return;
5998 }
5999
6000 // Check if valid child
6001 if (t.childRules) {
6002 if (t.parentElementsRE.test(t.elementName)) {
6003 if (!t.childRules[t.elementName].test(nn)) {
6004 iv = true;
6005 break;
6006 }
6007 }
6008
6009 t.elementName = nn;
6010 }
6011
6012 ru = t.findRule(nn);
6013 nn = ru.name || nn;
6014
6015 // Skip empty nodes or empty node name in IE
6016 if ((!hc && ru.noEmpty) || (isIE && !nn)) {
6017 iv = true;
6018 break;
6019 }
6020
6021 // Check required
6022 if (ru.requiredAttribs) {
6023 a = ru.requiredAttribs;
6024
6025 for (i = a.length - 1; i >= 0; i--) {
6026 if (this.dom.getAttrib(n, a[i]) !== '')
6027 break;
6028 }
6029
6030 // None of the required was there
6031 if (i == -1) {
6032 iv = true;
6033 break;
6034 }
6035 }
6036
6037 w.writeStartElement(nn);
6038
6039 // Add ordered attributes
6040 if (ru.attribs) {
6041 for (i=0, at = ru.attribs, l = at.length; i<l; i++) {
6042 a = at[i];
6043 v = t._getAttrib(n, a);
6044
6045 if (v !== null)
6046 w.writeAttribute(a.name, v);
6047 }
6048 }
6049
6050 // Add wild attributes
6051 if (ru.validAttribsRE) {
6052 at = t.dom.getAttribs(n);
6053 for (i=at.length-1; i>-1; i--) {
6054 no = at[i];
6055
6056 if (no.specified) {
6057 a = no.nodeName.toLowerCase();
6058
6059 if (s.invalid_attrs.test(a) || !ru.validAttribsRE.test(a))
6060 continue;
6061
6062 ar = t.findAttribRule(ru, a);
6063 v = t._getAttrib(n, ar, a);
6064
6065 if (v !== null)
6066 w.writeAttribute(a, v);
6067 }
6068 }
6069 }
6070
6071 // Padd empty nodes with a &nbsp;
6072 if (ru.padd) {
6073 // If it has only one bogus child, padd it anyway workaround for <td><br /></td> bug
6074 if (hc && (cn = n.firstChild) && cn.nodeType === 1 && n.childNodes.length === 1) {
6075 if (cn.hasAttribute ? cn.hasAttribute('mce_bogus') : cn.getAttribute('mce_bogus'))
6076 w.writeText('\u00a0');
6077 } else if (!hc)
6078 w.writeText('\u00a0'); // No children then padd it
6079 }
6080
6081 break;
6082
6083 case 3: // Text
6084 // Check if valid child
6085 if (t.childRules && t.parentElementsRE.test(t.elementName)) {
6086 if (!t.childRules[t.elementName].test(n.nodeName))
6087 return;
6088 }
6089
6090 return w.writeText(n.nodeValue);
6091
6092 case 4: // CDATA
6093 return w.writeCDATA(n.nodeValue);
6094
6095 case 8: // Comment
6096 return w.writeComment(n.nodeValue);
6097 }
6098 } else if (n.nodeType == 1)
6099 hc = n.hasChildNodes();
6100
6101 if (hc) {
6102 cn = n.firstChild;
6103
6104 while (cn) {
6105 t._serializeNode(cn);
6106 t.elementName = nn;
6107 cn = cn.nextSibling;
6108 }
6109 }
6110
6111 // Write element end
6112 if (!iv) {
6113 if (hc || !s.closed.test(nn))
6114 w.writeFullEndElement();
6115 else
6116 w.writeEndElement();
6117 }
6118 },
6119
6120 _protect : function(o) {
6121 var t = this;
6122
6123 o.items = o.items || [];
6124
6125 function enc(s) {
6126 return s.replace(/[\r\n\\]/g, function(c) {
6127 if (c === '\n')
6128 return '\\n';
6129 else if (c === '\\')
6130 return '\\\\';
6131
6132 return '\\r';
6133 });
6134 };
6135
6136 function dec(s) {
6137 return s.replace(/\\[\\rn]/g, function(c) {
6138 if (c === '\\n')
6139 return '\n';
6140 else if (c === '\\\\')
6141 return '\\';
6142
6143 return '\r';
6144 });
6145 };
6146
6147 each(o.patterns, function(p) {
6148 o.content = dec(enc(o.content).replace(p.pattern, function(x, a, b, c) {
6149 b = dec(b);
6150
6151 if (p.encode)
6152 b = t._encode(b);
6153
6154 o.items.push(b);
6155 return a + '<!--mce:' + (o.items.length - 1) + '-->' + c;
6156 }));
6157 });
6158
6159 return o;
6160 },
6161
6162 _unprotect : function(h, o) {
6163 h = h.replace(/\<!--mce:([0-9]+)--\>/g, function(a, b) {
6164 return o.items[parseInt(b)];
6165 });
6166
6167 o.items = [];
6168
6169 return h;
6170 },
6171
6172 _encode : function(h) {
6173 var t = this, s = t.settings, l;
6174
6175 // Entity encode
6176 if (s.entity_encoding !== 'raw') {
6177 if (s.entity_encoding.indexOf('named') != -1) {
6178 t.setEntities(s.entities);
6179 l = t.entityLookup;
6180
6181 h = h.replace(t.entitiesRE, function(a) {
6182 var v;
6183
6184 if (v = l[a])
6185 a = '&' + v + ';';
6186
6187 return a;
6188 });
6189 }
6190
6191 if (s.entity_encoding.indexOf('numeric') != -1) {
6192 h = h.replace(/[\u007E-\uFFFF]/g, function(a) {
6193 return '&#' + a.charCodeAt(0) + ';';
6194 });
6195 }
6196 }
6197
6198 return h;
6199 },
6200
6201 _setup : function() {
6202 var t = this, s = this.settings;
6203
6204 if (t.done)
6205 return;
6206
6207 t.done = 1;
6208
6209 t.setRules(s.valid_elements);
6210 t.addRules(s.extended_valid_elements);
6211 t.addValidChildRules(s.valid_child_elements);
6212
6213 if (s.invalid_elements)
6214 t.invalidElementsRE = new RegExp('^(' + wildcardToRE(s.invalid_elements.replace(/,/g, '|').toLowerCase()) + ')$');
6215
6216 if (s.attrib_value_filter)
6217 t.attribValueFilter = s.attribValueFilter;
6218 },
6219
6220 _getAttrib : function(n, a, na) {
6221 var i, v;
6222
6223 na = na || a.name;
6224
6225 if (a.forcedVal && (v = a.forcedVal)) {
6226 if (v === '{$uid}')
6227 return this.dom.uniqueId();
6228
6229 return v;
6230 }
6231
6232 v = this.dom.getAttrib(n, na);
6233
6234 // Bool attr
6235 if (this.settings.bool_attrs.test(na) && v) {
6236 v = ('' + v).toLowerCase();
6237
6238 if (v === 'false' || v === '0')
6239 return null;
6240
6241 v = na;
6242 }
6243
6244 switch (na) {
6245 case 'rowspan':
6246 case 'colspan':
6247 // Whats the point? Remove usless attribute value
6248 if (v == '1')
6249 v = '';
6250
6251 break;
6252 }
6253
6254 if (this.attribValueFilter)
6255 v = this.attribValueFilter(na, v, n);
6256
6257 if (a.validVals) {
6258 for (i = a.validVals.length - 1; i >= 0; i--) {
6259 if (v == a.validVals[i])
6260 break;
6261 }
6262
6263 if (i == -1)
6264 return null;
6265 }
6266
6267 if (v === '' && typeof(a.defaultVal) != 'undefined') {
6268 v = a.defaultVal;
6269
6270 if (v === '{$uid}')
6271 return this.dom.uniqueId();
6272
6273 return v;
6274 } else {
6275 // Remove internal mceItemXX classes when content is extracted from editor
6276 if (na == 'class' && this.processObj.get)
6277 v = v.replace(/\s?mceItem\w+\s?/g, '');
6278 }
6279
6280 if (v === '')
6281 return null;
6282
6283
6284 return v;
6285 }
6286
6287 });
6288 })(tinymce);
6289 (function(tinymce) {
6290 var each = tinymce.each, Event = tinymce.dom.Event;
6291
6292 tinymce.create('tinymce.dom.ScriptLoader', {
6293 ScriptLoader : function(s) {
6294 this.settings = s || {};
6295 this.queue = [];
6296 this.lookup = {};
6297 },
6298
6299 isDone : function(u) {
6300 return this.lookup[u] ? this.lookup[u].state == 2 : 0;
6301 },
6302
6303 markDone : function(u) {
6304 this.lookup[u] = {state : 2, url : u};
6305 },
6306
6307 add : function(u, cb, s, pr) {
6308 var t = this, lo = t.lookup, o;
6309
6310 if (o = lo[u]) {
6311 // Is loaded fire callback
6312 if (cb && o.state == 2)
6313 cb.call(s || this);
6314
6315 return o;
6316 }
6317
6318 o = {state : 0, url : u, func : cb, scope : s || this};
6319
6320 if (pr)
6321 t.queue.unshift(o);
6322 else
6323 t.queue.push(o);
6324
6325 lo[u] = o;
6326
6327 return o;
6328 },
6329
6330 load : function(u, cb, s) {
6331 var t = this, o;
6332
6333 if (o = t.lookup[u]) {
6334 // Is loaded fire callback
6335 if (cb && o.state == 2)
6336 cb.call(s || t);
6337
6338 return o;
6339 }
6340
6341 function loadScript(u) {
6342 if (Event.domLoaded || t.settings.strict_mode) {
6343 tinymce.util.XHR.send({
6344 url : tinymce._addVer(u),
6345 error : t.settings.error,
6346 async : false,
6347 success : function(co) {
6348 t.eval(co);
6349 }
6350 });
6351 } else
6352 document.write('<script type="text/javascript" src="' + tinymce._addVer(u) + '"></script>');
6353 };
6354
6355 if (!tinymce.is(u, 'string')) {
6356 each(u, function(u) {
6357 loadScript(u);
6358 });
6359
6360 if (cb)
6361 cb.call(s || t);
6362 } else {
6363 loadScript(u);
6364
6365 if (cb)
6366 cb.call(s || t);
6367 }
6368 },
6369
6370 loadQueue : function(cb, s) {
6371 var t = this;
6372
6373 if (!t.queueLoading) {
6374 t.queueLoading = 1;
6375 t.queueCallbacks = [];
6376
6377 t.loadScripts(t.queue, function() {
6378 t.queueLoading = 0;
6379
6380 if (cb)
6381 cb.call(s || t);
6382
6383 each(t.queueCallbacks, function(o) {
6384 o.func.call(o.scope);
6385 });
6386 });
6387 } else if (cb)
6388 t.queueCallbacks.push({func : cb, scope : s || t});
6389 },
6390
6391 eval : function(co) {
6392 var w = window;
6393
6394 // Evaluate script
6395 if (!w.execScript) {
6396 try {
6397 eval.call(w, co);
6398 } catch (ex) {
6399 eval(co, w); // Firefox 3.0a8
6400 }
6401 } else
6402 w.execScript(co); // IE
6403 },
6404
6405 loadScripts : function(sc, cb, s) {
6406 var t = this, lo = t.lookup;
6407
6408 function done(o) {
6409 o.state = 2; // Has been loaded
6410
6411 // Run callback
6412 if (o.func)
6413 o.func.call(o.scope || t);
6414 };
6415
6416 function allDone() {
6417 var l;
6418
6419 // Check if all files are loaded
6420 l = sc.length;
6421 each(sc, function(o) {
6422 o = lo[o.url];
6423
6424 if (o.state === 2) {// It has finished loading
6425 done(o);
6426 l--;
6427 } else
6428 load(o);
6429 });
6430
6431 // They are all loaded
6432 if (l === 0 && cb) {
6433 cb.call(s || t);
6434 cb = 0;
6435 }
6436 };
6437
6438 function load(o) {
6439 if (o.state > 0)
6440 return;
6441
6442 o.state = 1; // Is loading
6443
6444 tinymce.dom.ScriptLoader.loadScript(o.url, function() {
6445 done(o);
6446 allDone();
6447 });
6448
6449 /*
6450 tinymce.util.XHR.send({
6451 url : o.url,
6452 error : t.settings.error,
6453 success : function(co) {
6454 t.eval(co);
6455 done(o);
6456 allDone();
6457 }
6458 });
6459 */
6460 };
6461
6462 each(sc, function(o) {
6463 var u = o.url;
6464
6465 // Add to queue if needed
6466 if (!lo[u]) {
6467 lo[u] = o;
6468 t.queue.push(o);
6469 } else
6470 o = lo[u];
6471
6472 // Is already loading or has been loaded
6473 if (o.state > 0)
6474 return;
6475
6476 if (!Event.domLoaded && !t.settings.strict_mode) {
6477 var ix, ol = '';
6478
6479 // Add onload events
6480 if (cb || o.func) {
6481 o.state = 1; // Is loading
6482
6483 ix = tinymce.dom.ScriptLoader._addOnLoad(function() {
6484 done(o);
6485 allDone();
6486 });
6487
6488 if (tinymce.isIE)
6489 ol = ' onreadystatechange="';
6490 else
6491 ol = ' onload="';
6492
6493 ol += 'tinymce.dom.ScriptLoader._onLoad(this,\'' + u + '\',' + ix + ');"';
6494 }
6495
6496 document.write('<script type="text/javascript" src="' + tinymce._addVer(u) + '"' + ol + '></script>');
6497
6498 if (!o.func)
6499 done(o);
6500 } else
6501 load(o);
6502 });
6503
6504 allDone();
6505 },
6506
6507 // Static methods
6508 'static' : {
6509 _addOnLoad : function(f) {
6510 var t = this;
6511
6512 t._funcs = t._funcs || [];
6513 t._funcs.push(f);
6514
6515 return t._funcs.length - 1;
6516 },
6517
6518 _onLoad : function(e, u, ix) {
6519 if (!tinymce.isIE || e.readyState == 'complete')
6520 this._funcs[ix].call(this);
6521 },
6522
6523 loadScript : function(u, cb) {
6524 var id = tinymce.DOM.uniqueId(), e;
6525
6526 function done() {
6527 Event.clear(id);
6528 tinymce.DOM.remove(id);
6529
6530 if (cb) {
6531 cb.call(document, u);
6532 cb = 0;
6533 }
6534 };
6535
6536 if (tinymce.isIE) {
6537 /* Event.add(e, 'readystatechange', function(e) {
6538 if (e.target && e.target.readyState == 'complete')
6539 done();
6540 });*/
6541
6542 tinymce.util.XHR.send({
6543 url : tinymce._addVer(u),
6544 async : false,
6545 success : function(co) {
6546 window.execScript(co);
6547 done();
6548 }
6549 });
6550 } else {
6551 e = tinymce.DOM.create('script', {id : id, type : 'text/javascript', src : tinymce._addVer(u)});
6552 Event.add(e, 'load', done);
6553
6554 // Check for head or body
6555 (document.getElementsByTagName('head')[0] || document.body).appendChild(e);
6556 }
6557 }
6558 }
6559
6560 });
6561
6562 // Global script loader
6563 tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();
6564 })(tinymce);
6565 (function(tinymce) {
6566 // Shorten class names
6567 var DOM = tinymce.DOM, is = tinymce.is;
6568
6569 tinymce.create('tinymce.ui.Control', {
6570 Control : function(id, s) {
6571 this.id = id;
6572 this.settings = s = s || {};
6573 this.rendered = false;
6574 this.onRender = new tinymce.util.Dispatcher(this);
6575 this.classPrefix = '';
6576 this.scope = s.scope || this;
6577 this.disabled = 0;
6578 this.active = 0;
6579 },
6580
6581 setDisabled : function(s) {
6582 var e;
6583
6584 if (s != this.disabled) {
6585 e = DOM.get(this.id);
6586
6587 // Add accessibility title for unavailable actions
6588 if (e && this.settings.unavailable_prefix) {
6589 if (s) {
6590 this.prevTitle = e.title;
6591 e.title = this.settings.unavailable_prefix + ": " + e.title;
6592 } else
6593 e.title = this.prevTitle;
6594 }
6595
6596 this.setState('Disabled', s);
6597 this.setState('Enabled', !s);
6598 this.disabled = s;
6599 }
6600 },
6601
6602 isDisabled : function() {
6603 return this.disabled;
6604 },
6605
6606 setActive : function(s) {
6607 if (s != this.active) {
6608 this.setState('Active', s);
6609 this.active = s;
6610 }
6611 },
6612
6613 isActive : function() {
6614 return this.active;
6615 },
6616
6617 setState : function(c, s) {
6618 var n = DOM.get(this.id);
6619
6620 c = this.classPrefix + c;
6621
6622 if (s)
6623 DOM.addClass(n, c);
6624 else
6625 DOM.removeClass(n, c);
6626 },
6627
6628 isRendered : function() {
6629 return this.rendered;
6630 },
6631
6632 renderHTML : function() {
6633 },
6634
6635 renderTo : function(n) {
6636 DOM.setHTML(n, this.renderHTML());
6637 },
6638
6639 postRender : function() {
6640 var t = this, b;
6641
6642 // Set pending states
6643 if (is(t.disabled)) {
6644 b = t.disabled;
6645 t.disabled = -1;
6646 t.setDisabled(b);
6647 }
6648
6649 if (is(t.active)) {
6650 b = t.active;
6651 t.active = -1;
6652 t.setActive(b);
6653 }
6654 },
6655
6656 remove : function() {
6657 DOM.remove(this.id);
6658 this.destroy();
6659 },
6660
6661 destroy : function() {
6662 tinymce.dom.Event.clear(this.id);
6663 }
6664
6665 });
6666 })(tinymce);tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {
6667 Container : function(id, s) {
6668 this.parent(id, s);
6669 this.controls = [];
6670 this.lookup = {};
6671 },
6672
6673 add : function(c) {
6674 this.lookup[c.id] = c;
6675 this.controls.push(c);
6676
6677 return c;
6678 },
6679
6680 get : function(n) {
6681 return this.lookup[n];
6682 }
6683
6684 });
6685
6686 tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
6687 Separator : function(id, s) {
6688 this.parent(id, s);
6689 this.classPrefix = 'mceSeparator';
6690 },
6691
6692 renderHTML : function() {
6693 return tinymce.DOM.createHTML('span', {'class' : this.classPrefix});
6694 }
6695
6696 });
6697 (function(tinymce) {
6698 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;
6699
6700 tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', {
6701 MenuItem : function(id, s) {
6702 this.parent(id, s);
6703 this.classPrefix = 'mceMenuItem';
6704 },
6705
6706 setSelected : function(s) {
6707 this.setState('Selected', s);
6708 this.selected = s;
6709 },
6710
6711 isSelected : function() {
6712 return this.selected;
6713 },
6714
6715 postRender : function() {
6716 var t = this;
6717
6718 t.parent();
6719
6720 // Set pending state
6721 if (is(t.selected))
6722 t.setSelected(t.selected);
6723 }
6724
6725 });
6726 })(tinymce);
6727 (function(tinymce) {
6728 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;
6729
6730 tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', {
6731 Menu : function(id, s) {
6732 var t = this;
6733
6734 t.parent(id, s);
6735 t.items = {};
6736 t.collapsed = false;
6737 t.menuCount = 0;
6738 t.onAddItem = new tinymce.util.Dispatcher(this);
6739 },
6740
6741 expand : function(d) {
6742 var t = this;
6743
6744 if (d) {
6745 walk(t, function(o) {
6746 if (o.expand)
6747 o.expand();
6748 }, 'items', t);
6749 }
6750
6751 t.collapsed = false;
6752 },
6753
6754 collapse : function(d) {
6755 var t = this;
6756
6757 if (d) {
6758 walk(t, function(o) {
6759 if (o.collapse)
6760 o.collapse();
6761 }, 'items', t);
6762 }
6763
6764 t.collapsed = true;
6765 },
6766
6767 isCollapsed : function() {
6768 return this.collapsed;
6769 },
6770
6771 add : function(o) {
6772 if (!o.settings)
6773 o = new tinymce.ui.MenuItem(o.id || DOM.uniqueId(), o);
6774
6775 this.onAddItem.dispatch(this, o);
6776
6777 return this.items[o.id] = o;
6778 },
6779
6780 addSeparator : function() {
6781 return this.add({separator : true});
6782 },
6783
6784 addMenu : function(o) {
6785 if (!o.collapse)
6786 o = this.createMenu(o);
6787
6788 this.menuCount++;
6789
6790 return this.add(o);
6791 },
6792
6793 hasMenus : function() {
6794 return this.menuCount !== 0;
6795 },
6796
6797 remove : function(o) {
6798 delete this.items[o.id];
6799 },
6800
6801 removeAll : function() {
6802 var t = this;
6803
6804 walk(t, function(o) {
6805 if (o.removeAll)
6806 o.removeAll();
6807 else
6808 o.remove();
6809
6810 o.destroy();
6811 }, 'items', t);
6812
6813 t.items = {};
6814 },
6815
6816 createMenu : function(o) {
6817 var m = new tinymce.ui.Menu(o.id || DOM.uniqueId(), o);
6818
6819 m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem);
6820
6821 return m;
6822 }
6823
6824 });
6825 })(tinymce);(function(tinymce) {
6826 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element;
6827
6828 tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', {
6829 DropMenu : function(id, s) {
6830 s = s || {};
6831 s.container = s.container || DOM.doc.body;
6832 s.offset_x = s.offset_x || 0;
6833 s.offset_y = s.offset_y || 0;
6834 s.vp_offset_x = s.vp_offset_x || 0;
6835 s.vp_offset_y = s.vp_offset_y || 0;
6836
6837 if (is(s.icons) && !s.icons)
6838 s['class'] += ' mceNoIcons';
6839
6840 this.parent(id, s);
6841 this.onShowMenu = new tinymce.util.Dispatcher(this);
6842 this.onHideMenu = new tinymce.util.Dispatcher(this);
6843 this.classPrefix = 'mceMenu';
6844 },
6845
6846 createMenu : function(s) {
6847 var t = this, cs = t.settings, m;
6848
6849 s.container = s.container || cs.container;
6850 s.parent = t;
6851 s.constrain = s.constrain || cs.constrain;
6852 s['class'] = s['class'] || cs['class'];
6853 s.vp_offset_x = s.vp_offset_x || cs.vp_offset_x;
6854 s.vp_offset_y = s.vp_offset_y || cs.vp_offset_y;
6855 m = new tinymce.ui.DropMenu(s.id || DOM.uniqueId(), s);
6856
6857 m.onAddItem.add(t.onAddItem.dispatch, t.onAddItem);
6858
6859 return m;
6860 },
6861
6862 update : function() {
6863 var t = this, s = t.settings, tb = DOM.get('menu_' + t.id + '_tbl'), co = DOM.get('menu_' + t.id + '_co'), tw, th;
6864
6865 tw = s.max_width ? Math.min(tb.clientWidth, s.max_width) : tb.clientWidth;
6866 th = s.max_height ? Math.min(tb.clientHeight, s.max_height) : tb.clientHeight;
6867
6868 if (!DOM.boxModel)
6869 t.element.setStyles({width : tw + 2, height : th + 2});
6870 else
6871 t.element.setStyles({width : tw, height : th});
6872
6873 if (s.max_width)
6874 DOM.setStyle(co, 'width', tw);
6875
6876 if (s.max_height) {
6877 DOM.setStyle(co, 'height', th);
6878
6879 if (tb.clientHeight < s.max_height)
6880 DOM.setStyle(co, 'overflow', 'hidden');
6881 }
6882 },
6883
6884 showMenu : function(x, y, px) {
6885 var t = this, s = t.settings, co, vp = DOM.getViewPort(), w, h, mx, my, ot = 2, dm, tb, cp = t.classPrefix;
6886
6887 t.collapse(1);
6888
6889 if (t.isMenuVisible)
6890 return;
6891
6892 if (!t.rendered) {
6893 co = DOM.add(t.settings.container, t.renderNode());
6894
6895 each(t.items, function(o) {
6896 o.postRender();
6897 });
6898
6899 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});
6900 } else
6901 co = DOM.get('menu_' + t.id);
6902
6903 // Move layer out of sight unless it's Opera since it scrolls to top of page due to an bug
6904 if (!tinymce.isOpera)
6905 DOM.setStyles(co, {left : -0xFFFF , top : -0xFFFF});
6906
6907 DOM.show(co);
6908 t.update();
6909
6910 x += s.offset_x || 0;
6911 y += s.offset_y || 0;
6912 vp.w -= 4;
6913 vp.h -= 4;
6914
6915 // Move inside viewport if not submenu
6916 if (s.constrain) {
6917 w = co.clientWidth - ot;
6918 h = co.clientHeight - ot;
6919 mx = vp.x + vp.w;
6920 my = vp.y + vp.h;
6921
6922 if ((x + s.vp_offset_x + w) > mx)
6923 x = px ? px - w : Math.max(0, (mx - s.vp_offset_x) - w);
6924
6925 if ((y + s.vp_offset_y + h) > my)
6926 y = Math.max(0, (my - s.vp_offset_y) - h);
6927 }
6928
6929 DOM.setStyles(co, {left : x , top : y});
6930 t.element.update();
6931
6932 t.isMenuVisible = 1;
6933 t.mouseClickFunc = Event.add(co, 'click', function(e) {
6934 var m;
6935
6936 e = e.target;
6937
6938 if (e && (e = DOM.getParent(e, 'TR')) && !DOM.hasClass(e, cp + 'ItemSub')) {
6939 m = t.items[e.id];
6940
6941 if (m.isDisabled())
6942 return;
6943
6944 dm = t;
6945
6946 while (dm) {
6947 if (dm.hideMenu)
6948 dm.hideMenu();
6949
6950 dm = dm.settings.parent;
6951 }
6952
6953 if (m.settings.onclick)
6954 m.settings.onclick(e);
6955
6956 return Event.cancel(e); // Cancel to fix onbeforeunload problem
6957 }
6958 });
6959
6960 if (t.hasMenus()) {
6961 t.mouseOverFunc = Event.add(co, 'mouseover', function(e) {
6962 var m, r, mi;
6963
6964 e = e.target;
6965 if (e && (e = DOM.getParent(e, 'TR'))) {
6966 m = t.items[e.id];
6967
6968 if (t.lastMenu)
6969 t.lastMenu.collapse(1);
6970
6971 if (m.isDisabled())
6972 return;
6973
6974 if (e && DOM.hasClass(e, cp + 'ItemSub')) {
6975 //p = DOM.getPos(s.container);
6976 r = DOM.getRect(e);
6977 m.showMenu((r.x + r.w - ot), r.y - ot, r.x);
6978 t.lastMenu = m;
6979 DOM.addClass(DOM.get(m.id).firstChild, cp + 'ItemActive');
6980 }
6981 }
6982 });
6983 }
6984
6985 t.onShowMenu.dispatch(t);
6986
6987 if (s.keyboard_focus) {
6988 Event.add(co, 'keydown', t._keyHandler, t);
6989 DOM.select('a', 'menu_' + t.id)[0].focus(); // Select first link
6990 t._focusIdx = 0;
6991 }
6992 },
6993
6994 hideMenu : function(c) {
6995 var t = this, co = DOM.get('menu_' + t.id), e;
6996
6997 if (!t.isMenuVisible)
6998 return;
6999
7000 Event.remove(co, 'mouseover', t.mouseOverFunc);
7001 Event.remove(co, 'click', t.mouseClickFunc);
7002 Event.remove(co, 'keydown', t._keyHandler);
7003 DOM.hide(co);
7004 t.isMenuVisible = 0;
7005
7006 if (!c)
7007 t.collapse(1);
7008
7009 if (t.element)
7010 t.element.hide();
7011
7012 if (e = DOM.get(t.id))
7013 DOM.removeClass(e.firstChild, t.classPrefix + 'ItemActive');
7014
7015 t.onHideMenu.dispatch(t);
7016 },
7017
7018 add : function(o) {
7019 var t = this, co;
7020
7021 o = t.parent(o);
7022
7023 if (t.isRendered && (co = DOM.get('menu_' + t.id)))
7024 t._add(DOM.select('tbody', co)[0], o);
7025
7026 return o;
7027 },
7028
7029 collapse : function(d) {
7030 this.parent(d);
7031 this.hideMenu(1);
7032 },
7033
7034 remove : function(o) {
7035 DOM.remove(o.id);
7036 this.destroy();
7037
7038 return this.parent(o);
7039 },
7040
7041 destroy : function() {
7042 var t = this, co = DOM.get('menu_' + t.id);
7043
7044 Event.remove(co, 'mouseover', t.mouseOverFunc);
7045 Event.remove(co, 'click', t.mouseClickFunc);
7046
7047 if (t.element)
7048 t.element.remove();
7049
7050 DOM.remove(co);
7051 },
7052
7053 renderNode : function() {
7054 var t = this, s = t.settings, n, tb, co, w;
7055
7056 w = DOM.create('div', {id : 'menu_' + t.id, 'class' : s['class'], 'style' : 'position:absolute;left:0;top:0;z-index:200000'});
7057 co = DOM.add(w, 'div', {id : 'menu_' + t.id + '_co', 'class' : t.classPrefix + (s['class'] ? ' ' + s['class'] : '')});
7058 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});
7059
7060 if (s.menu_line)
7061 DOM.add(co, 'span', {'class' : t.classPrefix + 'Line'});
7062
7063 // n = DOM.add(co, 'div', {id : 'menu_' + t.id + '_co', 'class' : 'mceMenuContainer'});
7064 n = DOM.add(co, 'table', {id : 'menu_' + t.id + '_tbl', border : 0, cellPadding : 0, cellSpacing : 0});
7065 tb = DOM.add(n, 'tbody');
7066
7067 each(t.items, function(o) {
7068 t._add(tb, o);
7069 });
7070
7071 t.rendered = true;
7072
7073 return w;
7074 },
7075
7076 // Internal functions
7077
7078 _keyHandler : function(e) {
7079 var t = this, kc = e.keyCode;
7080
7081 function focus(d) {
7082 var i = t._focusIdx + d, e = DOM.select('a', 'menu_' + t.id)[i];
7083
7084 if (e) {
7085 t._focusIdx = i;
7086 e.focus();
7087 }
7088 };
7089
7090 switch (kc) {
7091 case 38:
7092 focus(-1); // Select first link
7093 return;
7094
7095 case 40:
7096 focus(1);
7097 return;
7098
7099 case 13:
7100 return;
7101
7102 case 27:
7103 return this.hideMenu();
7104 }
7105 },
7106
7107 _add : function(tb, o) {
7108 var n, s = o.settings, a, ro, it, cp = this.classPrefix, ic;
7109
7110 if (s.separator) {
7111 ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'ItemSeparator'});
7112 DOM.add(ro, 'td', {'class' : cp + 'ItemSeparator'});
7113
7114 if (n = ro.previousSibling)
7115 DOM.addClass(n, 'mceLast');
7116
7117 return;
7118 }
7119
7120 n = ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'Item ' + cp + 'ItemEnabled'});
7121 n = it = DOM.add(n, 'td');
7122 n = a = DOM.add(n, 'a', {href : 'javascript:;', onclick : "return false;", onmousedown : 'return false;'});
7123
7124 DOM.addClass(it, s['class']);
7125 // n = DOM.add(n, 'span', {'class' : 'item'});
7126
7127 ic = DOM.add(n, 'span', {'class' : 'mceIcon' + (s.icon ? ' mce_' + s.icon : '')});
7128
7129 if (s.icon_src)
7130 DOM.add(ic, 'img', {src : s.icon_src});
7131
7132 n = DOM.add(n, s.element || 'span', {'class' : 'mceText', title : o.settings.title}, o.settings.title);
7133
7134 if (o.settings.style)
7135 DOM.setAttrib(n, 'style', o.settings.style);
7136
7137 if (tb.childNodes.length == 1)
7138 DOM.addClass(ro, 'mceFirst');
7139
7140 if ((n = ro.previousSibling) && DOM.hasClass(n, cp + 'ItemSeparator'))
7141 DOM.addClass(ro, 'mceFirst');
7142
7143 if (o.collapse)
7144 DOM.addClass(ro, cp + 'ItemSub');
7145
7146 if (n = ro.previousSibling)
7147 DOM.removeClass(n, 'mceLast');
7148
7149 DOM.addClass(ro, 'mceLast');
7150 }
7151
7152 });
7153 })(tinymce);(function(tinymce) {
7154 var DOM = tinymce.DOM;
7155
7156 tinymce.create('tinymce.ui.Button:tinymce.ui.Control', {
7157 Button : function(id, s) {
7158 this.parent(id, s);
7159 this.classPrefix = 'mceButton';
7160 },
7161
7162 renderHTML : function() {
7163 var cp = this.classPrefix, s = this.settings, h, l;
7164
7165 l = DOM.encode(s.label || '');
7166 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) + '">';
7167
7168 if (s.image)
7169 h += '<img class="mceIcon" src="' + s.image + '" />' + l + '</a>';
7170 else
7171 h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '') + '</a>';
7172
7173 return h;
7174 },
7175
7176 postRender : function() {
7177 var t = this, s = t.settings;
7178
7179 tinymce.dom.Event.add(t.id, 'click', function(e) {
7180 if (!t.isDisabled())
7181 return s.onclick.call(s.scope, e);
7182 });
7183 }
7184
7185 });
7186 })(tinymce);
7187 (function(tinymce) {
7188 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;
7189
7190 tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {
7191 ListBox : function(id, s) {
7192 var t = this;
7193
7194 t.parent(id, s);
7195 t.items = [];
7196 t.onChange = new Dispatcher(t);
7197 t.onPostRender = new Dispatcher(t);
7198 t.onAdd = new Dispatcher(t);
7199 t.onRenderMenu = new tinymce.util.Dispatcher(this);
7200 t.classPrefix = 'mceListBox';
7201 },
7202
7203 select : function(va) {
7204 var t = this, fv, f;
7205
7206 if (va == undefined)
7207 return t.selectByIndex(-1);
7208
7209 // Is string or number make function selector
7210 if (va && va.call)
7211 f = va;
7212 else {
7213 f = function(v) {
7214 return v == va;
7215 };
7216 }
7217
7218 // Do we need to do something?
7219 if (va != t.selectedValue) {
7220 // Find item
7221 each(t.items, function(o, i) {
7222 if (f(o.value)) {
7223 fv = 1;
7224 t.selectByIndex(i);
7225 return false;
7226 }
7227 });
7228
7229 if (!fv)
7230 t.selectByIndex(-1);
7231 }
7232 },
7233
7234 selectByIndex : function(idx) {
7235 var t = this, e, o;
7236
7237 if (idx != t.selectedIndex) {
7238 e = DOM.get(t.id + '_text');
7239 o = t.items[idx];
7240
7241 if (o) {
7242 t.selectedValue = o.value;
7243 t.selectedIndex = idx;
7244 DOM.setHTML(e, DOM.encode(o.title));
7245 DOM.removeClass(e, 'mceTitle');
7246 } else {
7247 DOM.setHTML(e, DOM.encode(t.settings.title));
7248 DOM.addClass(e, 'mceTitle');
7249 t.selectedValue = t.selectedIndex = null;
7250 }
7251
7252 e = 0;
7253 }
7254 },
7255
7256 add : function(n, v, o) {
7257 var t = this;
7258
7259 o = o || {};
7260 o = tinymce.extend(o, {
7261 title : n,
7262 value : v
7263 });
7264
7265 t.items.push(o);
7266 t.onAdd.dispatch(t, o);
7267 },
7268
7269 getLength : function() {
7270 return this.items.length;
7271 },
7272
7273 renderHTML : function() {
7274 var h = '', t = this, s = t.settings, cp = t.classPrefix;
7275
7276 h = '<table id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>';
7277 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>';
7278 h += '<td>' + DOM.createHTML('a', {id : t.id + '_open', tabindex : -1, href : 'javascript:;', 'class' : 'mceOpen', onclick : "return false;", onmousedown : 'return false;'}, '<span></span>') + '</td>';
7279 h += '</tr></tbody></table>';
7280
7281 return h;
7282 },
7283
7284 showMenu : function() {
7285 var t = this, p1, p2, e = DOM.get(this.id), m;
7286
7287 if (t.isDisabled() || t.items.length == 0)
7288 return;
7289
7290 if (t.menu && t.menu.isMenuVisible)
7291 return t.hideMenu();
7292
7293 if (!t.isMenuRendered) {
7294 t.renderMenu();
7295 t.isMenuRendered = true;
7296 }
7297
7298 p1 = DOM.getPos(this.settings.menu_container);
7299 p2 = DOM.getPos(e);
7300
7301 m = t.menu;
7302 m.settings.offset_x = p2.x;
7303 m.settings.offset_y = p2.y;
7304 m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus
7305
7306 // Select in menu
7307 if (t.oldID)
7308 m.items[t.oldID].setSelected(0);
7309
7310 each(t.items, function(o) {
7311 if (o.value === t.selectedValue) {
7312 m.items[o.id].setSelected(1);
7313 t.oldID = o.id;
7314 }
7315 });
7316
7317 m.showMenu(0, e.clientHeight);
7318
7319 Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
7320 DOM.addClass(t.id, t.classPrefix + 'Selected');
7321
7322 //DOM.get(t.id + '_text').focus();
7323 },
7324
7325 hideMenu : function(e) {
7326 var t = this;
7327
7328 // Prevent double toogles by canceling the mouse click event to the button
7329 if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open'))
7330 return;
7331
7332 if (!e || !DOM.getParent(e.target, '.mceMenu')) {
7333 DOM.removeClass(t.id, t.classPrefix + 'Selected');
7334 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
7335
7336 if (t.menu)
7337 t.menu.hideMenu();
7338 }
7339 },
7340
7341 renderMenu : function() {
7342 var t = this, m;
7343
7344 m = t.settings.control_manager.createDropMenu(t.id + '_menu', {
7345 menu_line : 1,
7346 'class' : t.classPrefix + 'Menu mceNoIcons',
7347 max_width : 150,
7348 max_height : 150
7349 });
7350
7351 m.onHideMenu.add(t.hideMenu, t);
7352
7353 m.add({
7354 title : t.settings.title,
7355 'class' : 'mceMenuItemTitle',
7356 onclick : function() {
7357 if (t.settings.onselect('') !== false)
7358 t.select(''); // Must be runned after
7359 }
7360 });
7361
7362 each(t.items, function(o) {
7363 o.id = DOM.uniqueId();
7364 o.onclick = function() {
7365 if (t.settings.onselect(o.value) !== false)
7366 t.select(o.value); // Must be runned after
7367 };
7368
7369 m.add(o);
7370 });
7371
7372 t.onRenderMenu.dispatch(t, m);
7373 t.menu = m;
7374 },
7375
7376 postRender : function() {
7377 var t = this, cp = t.classPrefix;
7378
7379 Event.add(t.id, 'click', t.showMenu, t);
7380 Event.add(t.id + '_text', 'focus', function(e) {
7381 if (!t._focused) {
7382 t.keyDownHandler = Event.add(t.id + '_text', 'keydown', function(e) {
7383 var idx = -1, v, kc = e.keyCode;
7384
7385 // Find current index
7386 each(t.items, function(v, i) {
7387 if (t.selectedValue == v.value)
7388 idx = i;
7389 });
7390
7391 // Move up/down
7392 if (kc == 38)
7393 v = t.items[idx - 1];
7394 else if (kc == 40)
7395 v = t.items[idx + 1];
7396 else if (kc == 13) {
7397 // Fake select on enter
7398 v = t.selectedValue;
7399 t.selectedValue = null; // Needs to be null to fake change
7400 t.settings.onselect(v);
7401 return Event.cancel(e);
7402 }
7403
7404 if (v) {
7405 t.hideMenu();
7406 t.select(v.value);
7407 }
7408 });
7409 }
7410
7411 t._focused = 1;
7412 });
7413 Event.add(t.id + '_text', 'blur', function() {Event.remove(t.id + '_text', 'keydown', t.keyDownHandler); t._focused = 0;});
7414
7415 // Old IE doesn't have hover on all elements
7416 if (tinymce.isIE6 || !DOM.boxModel) {
7417 Event.add(t.id, 'mouseover', function() {
7418 if (!DOM.hasClass(t.id, cp + 'Disabled'))
7419 DOM.addClass(t.id, cp + 'Hover');
7420 });
7421
7422 Event.add(t.id, 'mouseout', function() {
7423 if (!DOM.hasClass(t.id, cp + 'Disabled'))
7424 DOM.removeClass(t.id, cp + 'Hover');
7425 });
7426 }
7427
7428 t.onPostRender.dispatch(t, DOM.get(t.id));
7429 },
7430
7431 destroy : function() {
7432 this.parent();
7433
7434 Event.clear(this.id + '_text');
7435 }
7436
7437 });
7438 })(tinymce);(function(tinymce) {
7439 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;
7440
7441 tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {
7442 NativeListBox : function(id, s) {
7443 this.parent(id, s);
7444 this.classPrefix = 'mceNativeListBox';
7445 },
7446
7447 setDisabled : function(s) {
7448 DOM.get(this.id).disabled = s;
7449 },
7450
7451 isDisabled : function() {
7452 return DOM.get(this.id).disabled;
7453 },
7454
7455 select : function(va) {
7456 var t = this, fv, f;
7457
7458 if (va == undefined)
7459 return t.selectByIndex(-1);
7460
7461 // Is string or number make function selector
7462 if (va && va.call)
7463 f = va;
7464 else {
7465 f = function(v) {
7466 return v == va;
7467 };
7468 }
7469
7470 // Do we need to do something?
7471 if (va != t.selectedValue) {
7472 // Find item
7473 each(t.items, function(o, i) {
7474 if (f(o.value)) {
7475 fv = 1;
7476 t.selectByIndex(i);
7477 return false;
7478 }
7479 });
7480
7481 if (!fv)
7482 t.selectByIndex(-1);
7483 }
7484 },
7485
7486 selectByIndex : function(idx) {
7487 DOM.get(this.id).selectedIndex = idx + 1;
7488 this.selectedValue = this.items[idx] ? this.items[idx].value : null;
7489 },
7490
7491 add : function(n, v, a) {
7492 var o, t = this;
7493
7494 a = a || {};
7495 a.value = v;
7496
7497 if (t.isRendered())
7498 DOM.add(DOM.get(this.id), 'option', a, n);
7499
7500 o = {
7501 title : n,
7502 value : v,
7503 attribs : a
7504 };
7505
7506 t.items.push(o);
7507 t.onAdd.dispatch(t, o);
7508 },
7509
7510 getLength : function() {
7511 return DOM.get(this.id).options.length - 1;
7512 },
7513
7514 renderHTML : function() {
7515 var h, t = this;
7516
7517 h = DOM.createHTML('option', {value : ''}, '-- ' + t.settings.title + ' --');
7518
7519 each(t.items, function(it) {
7520 h += DOM.createHTML('option', {value : it.value}, it.title);
7521 });
7522
7523 h = DOM.createHTML('select', {id : t.id, 'class' : 'mceNativeListBox'}, h);
7524
7525 return h;
7526 },
7527
7528 postRender : function() {
7529 var t = this, ch;
7530
7531 t.rendered = true;
7532
7533 function onChange(e) {
7534 var v = t.items[e.target.selectedIndex - 1];
7535
7536 if (v && (v = v.value)) {
7537 t.onChange.dispatch(t, v);
7538
7539 if (t.settings.onselect)
7540 t.settings.onselect(v);
7541 }
7542 };
7543
7544 Event.add(t.id, 'change', onChange);
7545
7546 // Accessibility keyhandler
7547 Event.add(t.id, 'keydown', function(e) {
7548 var bf;
7549
7550 Event.remove(t.id, 'change', ch);
7551
7552 bf = Event.add(t.id, 'blur', function() {
7553 Event.add(t.id, 'change', onChange);
7554 Event.remove(t.id, 'blur', bf);
7555 });
7556
7557 if (e.keyCode == 13 || e.keyCode == 32) {
7558 onChange(e);
7559 return Event.cancel(e);
7560 }
7561 });
7562
7563 t.onPostRender.dispatch(t, DOM.get(t.id));
7564 }
7565
7566 });
7567 })(tinymce);(function(tinymce) {
7568 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;
7569
7570 tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', {
7571 MenuButton : function(id, s) {
7572 this.parent(id, s);
7573 this.onRenderMenu = new tinymce.util.Dispatcher(this);
7574 s.menu_container = s.menu_container || DOM.doc.body;
7575 },
7576
7577 showMenu : function() {
7578 var t = this, p1, p2, e = DOM.get(t.id), m;
7579
7580 if (t.isDisabled())
7581 return;
7582
7583 if (!t.isMenuRendered) {
7584 t.renderMenu();
7585 t.isMenuRendered = true;
7586 }
7587
7588 if (t.isMenuVisible)
7589 return t.hideMenu();
7590
7591 p1 = DOM.getPos(t.settings.menu_container);
7592 p2 = DOM.getPos(e);
7593
7594 m = t.menu;
7595 m.settings.offset_x = p2.x;
7596 m.settings.offset_y = p2.y;
7597 m.settings.vp_offset_x = p2.x;
7598 m.settings.vp_offset_y = p2.y;
7599 m.settings.keyboard_focus = t._focused;
7600 m.showMenu(0, e.clientHeight);
7601
7602 Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
7603 t.setState('Selected', 1);
7604
7605 t.isMenuVisible = 1;
7606 },
7607
7608 renderMenu : function() {
7609 var t = this, m;
7610
7611 m = t.settings.control_manager.createDropMenu(t.id + '_menu', {
7612 menu_line : 1,
7613 'class' : this.classPrefix + 'Menu',
7614 icons : t.settings.icons
7615 });
7616
7617 m.onHideMenu.add(t.hideMenu, t);
7618
7619 t.onRenderMenu.dispatch(t, m);
7620 t.menu = m;
7621 },
7622
7623 hideMenu : function(e) {
7624 var t = this;
7625
7626 // Prevent double toogles by canceling the mouse click event to the button
7627 if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id || e.id === t.id + '_open';}))
7628 return;
7629
7630 if (!e || !DOM.getParent(e.target, '.mceMenu')) {
7631 t.setState('Selected', 0);
7632 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
7633 if (t.menu)
7634 t.menu.hideMenu();
7635 }
7636
7637 t.isMenuVisible = 0;
7638 },
7639
7640 postRender : function() {
7641 var t = this, s = t.settings;
7642
7643 Event.add(t.id, 'click', function() {
7644 if (!t.isDisabled()) {
7645 if (s.onclick)
7646 s.onclick(t.value);
7647
7648 t.showMenu();
7649 }
7650 });
7651 }
7652
7653 });
7654 })(tinymce);
7655 (function(tinymce) {
7656 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;
7657
7658 tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', {
7659 SplitButton : function(id, s) {
7660 this.parent(id, s);
7661 this.classPrefix = 'mceSplitButton';
7662 },
7663
7664 renderHTML : function() {
7665 var h, t = this, s = t.settings, h1;
7666
7667 h = '<tbody><tr>';
7668
7669 if (s.image)
7670 h1 = DOM.createHTML('img ', {src : s.image, 'class' : 'mceAction ' + s['class']});
7671 else
7672 h1 = DOM.createHTML('span', {'class' : 'mceAction ' + s['class']}, '');
7673
7674 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>';
7675
7676 h1 = DOM.createHTML('span', {'class' : 'mceOpen ' + s['class']});
7677 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>';
7678
7679 h += '</tr></tbody>';
7680
7681 return DOM.createHTML('table', {id : t.id, 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', onmousedown : 'return false;', title : s.title}, h);
7682 },
7683
7684 postRender : function() {
7685 var t = this, s = t.settings;
7686
7687 if (s.onclick) {
7688 Event.add(t.id + '_action', 'click', function() {
7689 if (!t.isDisabled())
7690 s.onclick(t.value);
7691 });
7692 }
7693
7694 Event.add(t.id + '_open', 'click', t.showMenu, t);
7695 Event.add(t.id + '_open', 'focus', function() {t._focused = 1;});
7696 Event.add(t.id + '_open', 'blur', function() {t._focused = 0;});
7697
7698 // Old IE doesn't have hover on all elements
7699 if (tinymce.isIE6 || !DOM.boxModel) {
7700 Event.add(t.id, 'mouseover', function() {
7701 if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))
7702 DOM.addClass(t.id, 'mceSplitButtonHover');
7703 });
7704
7705 Event.add(t.id, 'mouseout', function() {
7706 if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))
7707 DOM.removeClass(t.id, 'mceSplitButtonHover');
7708 });
7709 }
7710 },
7711
7712 destroy : function() {
7713 this.parent();
7714
7715 Event.clear(this.id + '_action');
7716 Event.clear(this.id + '_open');
7717 }
7718
7719 });
7720 })(tinymce);
7721 (function(tinymce) {
7722 var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each;
7723
7724 tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', {
7725 ColorSplitButton : function(id, s) {
7726 var t = this;
7727
7728 t.parent(id, s);
7729
7730 t.settings = s = tinymce.extend({
7731 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',
7732 grid_width : 8,
7733 default_color : '#888888'
7734 }, t.settings);
7735
7736 t.onShowMenu = new tinymce.util.Dispatcher(t);
7737 t.onHideMenu = new tinymce.util.Dispatcher(t);
7738
7739 t.value = s.default_color;
7740 },
7741
7742 showMenu : function() {
7743 var t = this, r, p, e, p2;
7744
7745 if (t.isDisabled())
7746 return;
7747
7748 if (!t.isMenuRendered) {
7749 t.renderMenu();
7750 t.isMenuRendered = true;
7751 }
7752
7753 if (t.isMenuVisible)
7754 return t.hideMenu();
7755
7756 e = DOM.get(t.id);
7757 DOM.show(t.id + '_menu');
7758 DOM.addClass(e, 'mceSplitButtonSelected');
7759 p2 = DOM.getPos(e);
7760 DOM.setStyles(t.id + '_menu', {
7761 left : p2.x,
7762 top : p2.y + e.clientHeight,
7763 zIndex : 200000
7764 });
7765 e = 0;
7766
7767 Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
7768
7769 if (t._focused) {
7770 t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) {
7771 if (e.keyCode == 27)
7772 t.hideMenu();
7773 });
7774
7775 DOM.select('a', t.id + '_menu')[0].focus(); // Select first link
7776 }
7777
7778 t.onShowMenu.dispatch(t);
7779
7780 t.isMenuVisible = 1;
7781 },
7782
7783 hideMenu : function(e) {
7784 var t = this;
7785
7786 // Prevent double toogles by canceling the mouse click event to the button
7787 if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';}))
7788 return;
7789
7790 if (!e || !DOM.getParent(e.target, '.mceSplitButtonMenu')) {
7791 DOM.removeClass(t.id, 'mceSplitButtonSelected');
7792 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
7793 Event.remove(t.id + '_menu', 'keydown', t._keyHandler);
7794 DOM.hide(t.id + '_menu');
7795 }
7796
7797 t.onHideMenu.dispatch(t);
7798
7799 t.isMenuVisible = 0;
7800 },
7801
7802 renderMenu : function() {
7803 var t = this, m, i = 0, s = t.settings, n, tb, tr, w;
7804
7805 w = DOM.add(s.menu_container, 'div', {id : t.id + '_menu', 'class' : s['menu_class'] + ' ' + s['class'], style : 'position:absolute;left:0;top:-1000px;'});
7806 m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'});
7807 DOM.add(m, 'span', {'class' : 'mceMenuLine'});
7808
7809 n = DOM.add(m, 'table', {'class' : 'mceColorSplitMenu'});
7810 tb = DOM.add(n, 'tbody');
7811
7812 // Generate color grid
7813 i = 0;
7814 each(is(s.colors, 'array') ? s.colors : s.colors.split(','), function(c) {
7815 c = c.replace(/^#/, '');
7816
7817 if (!i--) {
7818 tr = DOM.add(tb, 'tr');
7819 i = s.grid_width - 1;
7820 }
7821
7822 n = DOM.add(tr, 'td');
7823
7824 n = DOM.add(n, 'a', {
7825 href : 'javascript:;',
7826 style : {
7827 backgroundColor : '#' + c
7828 },
7829 mce_color : '#' + c
7830 });
7831 });
7832
7833 if (s.more_colors_func) {
7834 n = DOM.add(tb, 'tr');
7835 n = DOM.add(n, 'td', {colspan : s.grid_width, 'class' : 'mceMoreColors'});
7836 n = DOM.add(n, 'a', {id : t.id + '_more', href : 'javascript:;', onclick : 'return false;', 'class' : 'mceMoreColors'}, s.more_colors_title);
7837
7838 Event.add(n, 'click', function(e) {
7839 s.more_colors_func.call(s.more_colors_scope || this);
7840 return Event.cancel(e); // Cancel to fix onbeforeunload problem
7841 });
7842 }
7843
7844 DOM.addClass(m, 'mceColorSplitMenu');
7845
7846 Event.add(t.id + '_menu', 'click', function(e) {
7847 var c;
7848
7849 e = e.target;
7850
7851 if (e.nodeName == 'A' && (c = e.getAttribute('mce_color')))
7852 t.setColor(c);
7853
7854 return Event.cancel(e); // Prevent IE auto save warning
7855 });
7856
7857 return w;
7858 },
7859
7860 setColor : function(c) {
7861 var t = this;
7862
7863 DOM.setStyle(t.id + '_preview', 'backgroundColor', c);
7864
7865 t.value = c;
7866 t.hideMenu();
7867 t.settings.onselect(c);
7868 },
7869
7870 postRender : function() {
7871 var t = this, id = t.id;
7872
7873 t.parent();
7874 DOM.add(id + '_action', 'div', {id : id + '_preview', 'class' : 'mceColorPreview'});
7875 DOM.setStyle(t.id + '_preview', 'backgroundColor', t.value);
7876 },
7877
7878 destroy : function() {
7879 this.parent();
7880
7881 Event.clear(this.id + '_menu');
7882 Event.clear(this.id + '_more');
7883 DOM.remove(this.id + '_menu');
7884 }
7885
7886 });
7887 })(tinymce);
7888 tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
7889 renderHTML : function() {
7890 var t = this, h = '', c, co, dom = tinymce.DOM, s = t.settings, i, pr, nx, cl;
7891
7892 cl = t.controls;
7893 for (i=0; i<cl.length; i++) {
7894 // Get current control, prev control, next control and if the control is a list box or not
7895 co = cl[i];
7896 pr = cl[i - 1];
7897 nx = cl[i + 1];
7898
7899 // Add toolbar start
7900 if (i === 0) {
7901 c = 'mceToolbarStart';
7902
7903 if (co.Button)
7904 c += ' mceToolbarStartButton';
7905 else if (co.SplitButton)
7906 c += ' mceToolbarStartSplitButton';
7907 else if (co.ListBox)
7908 c += ' mceToolbarStartListBox';
7909
7910 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));
7911 }
7912
7913 // Add toolbar end before list box and after the previous button
7914 // This is to fix the o2k7 editor skins
7915 if (pr && co.ListBox) {
7916 if (pr.Button || pr.SplitButton)
7917 h += dom.createHTML('td', {'class' : 'mceToolbarEnd'}, dom.createHTML('span', null, '<!-- IE -->'));
7918 }
7919
7920 // Render control HTML
7921
7922 // IE 8 quick fix, needed to propertly generate a hit area for anchors
7923 if (dom.stdMode)
7924 h += '<td style="position: relative">' + co.renderHTML() + '</td>';
7925 else
7926 h += '<td>' + co.renderHTML() + '</td>';
7927
7928 // Add toolbar start after list box and before the next button
7929 // This is to fix the o2k7 editor skins
7930 if (nx && co.ListBox) {
7931 if (nx.Button || nx.SplitButton)
7932 h += dom.createHTML('td', {'class' : 'mceToolbarStart'}, dom.createHTML('span', null, '<!-- IE -->'));
7933 }
7934 }
7935
7936 c = 'mceToolbarEnd';
7937
7938 if (co.Button)
7939 c += ' mceToolbarEndButton';
7940 else if (co.SplitButton)
7941 c += ' mceToolbarEndSplitButton';
7942 else if (co.ListBox)
7943 c += ' mceToolbarEndListBox';
7944
7945 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));
7946
7947 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>');
7948 }
7949
7950 });
7951 (function(tinymce) {
7952 var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;
7953
7954 tinymce.create('tinymce.AddOnManager', {
7955 items : [],
7956 urls : {},
7957 lookup : {},
7958 onAdd : new Dispatcher(this),
7959
7960 get : function(n) {
7961 return this.lookup[n];
7962 },
7963
7964 requireLangPack : function(n) {
7965 var u, s = tinymce.EditorManager.settings;
7966
7967 if (s && s.language) {
7968 u = this.urls[n] + '/langs/' + s.language + '.js';
7969
7970 if (!tinymce.dom.Event.domLoaded && !s.strict_mode)
7971 tinymce.ScriptLoader.load(u);
7972 else
7973 tinymce.ScriptLoader.add(u);
7974 }
7975 },
7976
7977 add : function(id, o) {
7978 this.items.push(o);
7979 this.lookup[id] = o;
7980 this.onAdd.dispatch(this, id, o);
7981
7982 return o;
7983 },
7984
7985 load : function(n, u, cb, s) {
7986 var t = this;
7987
7988 if (t.urls[n])
7989 return;
7990
7991 if (u.indexOf('/') != 0 && u.indexOf('://') == -1)
7992 u = tinymce.baseURL + '/' + u;
7993
7994 t.urls[n] = u.substring(0, u.lastIndexOf('/'));
7995 tinymce.ScriptLoader.add(u, cb, s);
7996 }
7997
7998 });
7999
8000 // Create plugin and theme managers
8001 tinymce.PluginManager = new tinymce.AddOnManager();
8002 tinymce.ThemeManager = new tinymce.AddOnManager();
8003 }(tinymce));(function(tinymce) {
8004 // Shorten names
8005 var each = tinymce.each, extend = tinymce.extend, DOM = tinymce.DOM, Event = tinymce.dom.Event, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, explode = tinymce.explode;
8006
8007 tinymce.create('static tinymce.EditorManager', {
8008 editors : {},
8009 i18n : {},
8010 activeEditor : null,
8011
8012 preInit : function() {
8013 var t = this, lo = window.location;
8014
8015 // Setup some URLs where the editor API is located and where the document is
8016 tinymce.documentBaseURL = lo.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
8017 if (!/[\/\\]$/.test(tinymce.documentBaseURL))
8018 tinymce.documentBaseURL += '/';
8019
8020 tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL);
8021 tinymce.EditorManager.baseURI = new tinymce.util.URI(tinymce.baseURL);
8022
8023 // User specified a document.domain value
8024 if (document.domain && lo.hostname != document.domain)
8025 tinymce.relaxedDomain = document.domain;
8026
8027 // Add before unload listener
8028 // This was required since IE was leaking memory if you added and removed beforeunload listeners
8029 // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event
8030 t.onBeforeUnload = new tinymce.util.Dispatcher(t);
8031
8032 // Must be on window or IE will leak if the editor is placed in frame or iframe
8033 Event.add(window, 'beforeunload', function(e) {
8034 t.onBeforeUnload.dispatch(t, e);
8035 });
8036 },
8037
8038 init : function(s) {
8039 var t = this, pl, sl = tinymce.ScriptLoader, c, e, el = [], ed;
8040
8041 function execCallback(se, n, s) {
8042 var f = se[n];
8043
8044 if (!f)
8045 return;
8046
8047 if (tinymce.is(f, 'string')) {
8048 s = f.replace(/\.\w+$/, '');
8049 s = s ? tinymce.resolve(s) : 0;
8050 f = tinymce.resolve(f);
8051 }
8052
8053 return f.apply(s || this, Array.prototype.slice.call(arguments, 2));
8054 };
8055
8056 s = extend({
8057 theme : "simple",
8058 language : "en",
8059 strict_loading_mode : document.contentType == 'application/xhtml+xml'
8060 }, s);
8061
8062 t.settings = s;
8063
8064 // If page not loaded and strict mode isn't enabled then load them
8065 if (!Event.domLoaded && !s.strict_loading_mode) {
8066 // Load language
8067 if (s.language)
8068 sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');
8069
8070 // Load theme
8071 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
8072 ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');
8073
8074 // Load plugins
8075 if (s.plugins) {
8076 pl = explode(s.plugins);
8077
8078 // Load compat2x first
8079 if (tinymce.inArray(pl, 'compat2x') != -1)
8080 PluginManager.load('compat2x', 'plugins/compat2x/editor_plugin' + tinymce.suffix + '.js');
8081
8082 // Load rest if plugins
8083 each(pl, function(v) {
8084 if (v && v.charAt(0) != '-' && !PluginManager.urls[v]) {
8085 // Skip safari plugin for other browsers
8086 if (!tinymce.isWebKit && v == 'safari')
8087 return;
8088
8089 PluginManager.load(v, 'plugins/' + v + '/editor_plugin' + tinymce.suffix + '.js');
8090 }
8091 });
8092 }
8093
8094 sl.loadQueue();
8095 }
8096
8097 // Legacy call
8098 Event.add(document, 'init', function() {
8099 var l, co;
8100
8101 execCallback(s, 'onpageload');
8102
8103 // Verify that it's a valid browser
8104 if (s.browsers) {
8105 l = false;
8106
8107 each(explode(s.browsers), function(v) {
8108 switch (v) {
8109 case 'ie':
8110 case 'msie':
8111 if (tinymce.isIE)
8112 l = true;
8113 break;
8114
8115 case 'gecko':
8116 if (tinymce.isGecko)
8117 l = true;
8118 break;
8119
8120 case 'safari':
8121 case 'webkit':
8122 if (tinymce.isWebKit)
8123 l = true;
8124 break;
8125
8126 case 'opera':
8127 if (tinymce.isOpera)
8128 l = true;
8129
8130 break;
8131 }
8132 });
8133
8134 // Not a valid one
8135 if (!l)
8136 return;
8137 }
8138
8139 switch (s.mode) {
8140 case "exact":
8141 l = s.elements || '';
8142
8143 if(l.length > 0) {
8144 each(explode(l), function(v) {
8145 if (DOM.get(v)) {
8146 ed = new tinymce.Editor(v, s);
8147 el.push(ed);
8148 ed.render(1);
8149 } else {
8150 c = 0;
8151
8152 each(document.forms, function(f) {
8153 each(f.elements, function(e) {
8154 if (e.name === v) {
8155 v = 'mce_editor_' + c;
8156 DOM.setAttrib(e, 'id', v);
8157
8158 ed = new tinymce.Editor(v, s);
8159 el.push(ed);
8160 ed.render(1);
8161 }
8162 });
8163 });
8164 }
8165 });
8166 }
8167 break;
8168
8169 case "textareas":
8170 case "specific_textareas":
8171 function hasClass(n, c) {
8172 return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);
8173 };
8174
8175 each(DOM.select('textarea'), function(v) {
8176 if (s.editor_deselector && hasClass(v, s.editor_deselector))
8177 return;
8178
8179 if (!s.editor_selector || hasClass(v, s.editor_selector)) {
8180 // Can we use the name
8181 e = DOM.get(v.name);
8182 if (!v.id && !e)
8183 v.id = v.name;
8184
8185 // Generate unique name if missing or already exists
8186 if (!v.id || t.get(v.id))
8187 v.id = DOM.uniqueId();
8188
8189 ed = new tinymce.Editor(v.id, s);
8190 el.push(ed);
8191 ed.render(1);
8192 }
8193 });
8194 break;
8195 }
8196
8197 // Call onInit when all editors are initialized
8198 if (s.oninit) {
8199 l = co = 0;
8200
8201 each (el, function(ed) {
8202 co++;
8203
8204 if (!ed.initialized) {
8205 // Wait for it
8206 ed.onInit.add(function() {
8207 l++;
8208
8209 // All done
8210 if (l == co)
8211 execCallback(s, 'oninit');
8212 });
8213 } else
8214 l++;
8215
8216 // All done
8217 if (l == co)
8218 execCallback(s, 'oninit');
8219 });
8220 }
8221 });
8222 },
8223
8224 get : function(id) {
8225 return this.editors[id];
8226 },
8227
8228 getInstanceById : function(id) {
8229 return this.get(id);
8230 },
8231
8232 add : function(e) {
8233 this.editors[e.id] = e;
8234 this._setActive(e);
8235
8236 return e;
8237 },
8238
8239 remove : function(e) {
8240 var t = this;
8241
8242 // Not in the collection
8243 if (!t.editors[e.id])
8244 return null;
8245
8246 delete t.editors[e.id];
8247
8248 // Select another editor since the active one was removed
8249 if (t.activeEditor == e) {
8250 each(t.editors, function(e) {
8251 t._setActive(e);
8252 return false; // Break
8253 });
8254 }
8255
8256 e.destroy();
8257
8258 return e;
8259 },
8260
8261 execCommand : function(c, u, v) {
8262 var t = this, ed = t.get(v), w;
8263
8264 // Manager commands
8265 switch (c) {
8266 case "mceFocus":
8267 ed.focus();
8268 return true;
8269
8270 case "mceAddEditor":
8271 case "mceAddControl":
8272 if (!t.get(v))
8273 new tinymce.Editor(v, t.settings).render();
8274
8275 return true;
8276
8277 case "mceAddFrameControl":
8278 w = v.window;
8279
8280 // Add tinyMCE global instance and tinymce namespace to specified window
8281 w.tinyMCE = tinyMCE;
8282 w.tinymce = tinymce;
8283
8284 tinymce.DOM.doc = w.document;
8285 tinymce.DOM.win = w;
8286
8287 ed = new tinymce.Editor(v.element_id, v);
8288 ed.render();
8289
8290 // Fix IE memory leaks
8291 if (tinymce.isIE) {
8292 function clr() {
8293 ed.destroy();
8294 w.detachEvent('onunload', clr);
8295 w = w.tinyMCE = w.tinymce = null; // IE leak
8296 };
8297
8298 w.attachEvent('onunload', clr);
8299 }
8300
8301 v.page_window = null;
8302
8303 return true;
8304
8305 case "mceRemoveEditor":
8306 case "mceRemoveControl":
8307 if (ed)
8308 ed.remove();
8309
8310 return true;
8311
8312 case 'mceToggleEditor':
8313 if (!ed) {
8314 t.execCommand('mceAddControl', 0, v);
8315 return true;
8316 }
8317
8318 if (ed.isHidden())
8319 ed.show();
8320 else
8321 ed.hide();
8322
8323 return true;
8324 }
8325
8326 // Run command on active editor
8327 if (t.activeEditor)
8328 return t.activeEditor.execCommand(c, u, v);
8329
8330 return false;
8331 },
8332
8333 execInstanceCommand : function(id, c, u, v) {
8334 var ed = this.get(id);
8335
8336 if (ed)
8337 return ed.execCommand(c, u, v);
8338
8339 return false;
8340 },
8341
8342 triggerSave : function() {
8343 each(this.editors, function(e) {
8344 e.save();
8345 });
8346 },
8347
8348 addI18n : function(p, o) {
8349 var lo, i18n = this.i18n;
8350
8351 if (!tinymce.is(p, 'string')) {
8352 each(p, function(o, lc) {
8353 each(o, function(o, g) {
8354 each(o, function(o, k) {
8355 if (g === 'common')
8356 i18n[lc + '.' + k] = o;
8357 else
8358 i18n[lc + '.' + g + '.' + k] = o;
8359 });
8360 });
8361 });
8362 } else {
8363 each(o, function(o, k) {
8364 i18n[p + '.' + k] = o;
8365 });
8366 }
8367 },
8368
8369 // Private methods
8370
8371 _setActive : function(e) {
8372 this.selectedInstance = this.activeEditor = e;
8373 }
8374
8375 });
8376
8377 tinymce.EditorManager.preInit();
8378 })(tinymce);
8379
8380 // Short for editor manager window.tinyMCE is needed when TinyMCE gets loaded though a XHR call
8381 var tinyMCE = window.tinyMCE = tinymce.EditorManager;
8382 (function(tinymce) {
8383 var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend, Dispatcher = tinymce.util.Dispatcher;
8384 var each = tinymce.each, isGecko = tinymce.isGecko, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit;
8385 var is = tinymce.is, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, EditorManager = tinymce.EditorManager;
8386 var inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;
8387
8388 tinymce.create('tinymce.Editor', {
8389 Editor : function(id, s) {
8390 var t = this;
8391
8392 t.id = t.editorId = id;
8393 t.execCommands = {};
8394 t.queryStateCommands = {};
8395 t.queryValueCommands = {};
8396 t.plugins = {};
8397
8398 // Add events to the editor
8399 each([
8400 'onPreInit',
8401 'onBeforeRenderUI',
8402 'onPostRender',
8403 'onInit',
8404 'onRemove',
8405 'onActivate',
8406 'onDeactivate',
8407 'onClick',
8408 'onEvent',
8409 'onMouseUp',
8410 'onMouseDown',
8411 'onDblClick',
8412 'onKeyDown',
8413 'onKeyUp',
8414 'onKeyPress',
8415 'onContextMenu',
8416 'onSubmit',
8417 'onReset',
8418 'onPaste',
8419 'onPreProcess',
8420 'onPostProcess',
8421 'onBeforeSetContent',
8422 'onBeforeGetContent',
8423 'onSetContent',
8424 'onGetContent',
8425 'onLoadContent',
8426 'onSaveContent',
8427 'onNodeChange',
8428 'onChange',
8429 'onBeforeExecCommand',
8430 'onExecCommand',
8431 'onUndo',
8432 'onRedo',
8433 'onVisualAid',
8434 'onSetProgressState'
8435 ], function(e) {
8436 t[e] = new Dispatcher(t);
8437 });
8438
8439 // Default editor config
8440 t.settings = s = extend({
8441 id : id,
8442 language : 'en',
8443 docs_language : 'en',
8444 theme : 'simple',
8445 skin : 'default',
8446 delta_width : 0,
8447 delta_height : 0,
8448 popup_css : '',
8449 plugins : '',
8450 document_base_url : tinymce.documentBaseURL,
8451 add_form_submit_trigger : 1,
8452 submit_patch : 1,
8453 add_unload_trigger : 1,
8454 convert_urls : 1,
8455 relative_urls : 1,
8456 remove_script_host : 1,
8457 table_inline_editing : 0,
8458 object_resizing : 1,
8459 cleanup : 1,
8460 accessibility_focus : 1,
8461 custom_shortcuts : 1,
8462 custom_undo_redo_keyboard_shortcuts : 1,
8463 custom_undo_redo_restore_selection : 1,
8464 custom_undo_redo : 1,
8465 doctype : '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">',
8466 visual_table_class : 'mceItemTable',
8467 visual : 1,
8468 inline_styles : true,
8469 convert_fonts_to_spans : true,
8470 font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',
8471 apply_source_formatting : 1,
8472 directionality : 'ltr',
8473 forced_root_block : 'p',
8474 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',
8475 hidden_input : 1,
8476 padd_empty_editor : 1,
8477 render_ui : 1,
8478 init_theme : 1,
8479 force_p_newlines : 1,
8480 indentation : '30px',
8481 keep_styles : 1,
8482 fix_table_elements : 1,
8483 removeformat_selector : 'span,b,strong,em,i,font,u,strike'
8484 }, s);
8485
8486 // Setup URIs
8487 t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {
8488 base_uri : tinyMCE.baseURI
8489 });
8490 t.baseURI = EditorManager.baseURI;
8491
8492 // Call setup
8493 t.execCallback('setup', t);
8494 },
8495
8496 render : function(nst) {
8497 var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader;
8498
8499 // Page is not loaded yet, wait for it
8500 if (!Event.domLoaded) {
8501 Event.add(document, 'init', function() {
8502 t.render();
8503 });
8504 return;
8505 }
8506
8507 // Force strict loading mode if render us called by user and not internally
8508 if (!nst) {
8509 s.strict_loading_mode = 1;
8510 tinyMCE.settings = s;
8511 }
8512
8513 // Element not found, then skip initialization
8514 if (!t.getElement())
8515 return;
8516
8517 if (s.strict_loading_mode) {
8518 sl.settings.strict_mode = s.strict_loading_mode;
8519 tinymce.DOM.settings.strict = 1;
8520 }
8521
8522 // Add hidden input for non input elements inside form elements
8523 if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))
8524 DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);
8525
8526 if (tinymce.WindowManager)
8527 t.windowManager = new tinymce.WindowManager(t);
8528
8529 if (s.encoding == 'xml') {
8530 t.onGetContent.add(function(ed, o) {
8531 if (o.save)
8532 o.content = DOM.encode(o.content);
8533 });
8534 }
8535
8536 if (s.add_form_submit_trigger) {
8537 t.onSubmit.addToTop(function() {
8538 if (t.initialized) {
8539 t.save();
8540 t.isNotDirty = 1;
8541 }
8542 });
8543 }
8544
8545 if (s.add_unload_trigger) {
8546 t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {
8547 if (t.initialized && !t.destroyed && !t.isHidden())
8548 t.save({format : 'raw', no_events : true});
8549 });
8550 }
8551
8552 tinymce.addUnload(t.destroy, t);
8553
8554 if (s.submit_patch) {
8555 t.onBeforeRenderUI.add(function() {
8556 var n = t.getElement().form;
8557
8558 if (!n)
8559 return;
8560
8561 // Already patched
8562 if (n._mceOldSubmit)
8563 return;
8564
8565 // Check page uses id="submit" or name="submit" for it's submit button
8566 if (!n.submit.nodeType && !n.submit.length) {
8567 t.formElement = n;
8568 n._mceOldSubmit = n.submit;
8569 n.submit = function() {
8570 // Save all instances
8571 EditorManager.triggerSave();
8572 t.isNotDirty = 1;
8573
8574 return this._mceOldSubmit(this);
8575 };
8576 }
8577
8578 n = null;
8579 });
8580 }
8581
8582 // Load scripts
8583 function loadScripts() {
8584 if (s.language)
8585 sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');
8586
8587 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
8588 ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');
8589
8590 each(explode(s.plugins), function(p) {
8591 if (p && p.charAt(0) != '-' && !PluginManager.urls[p]) {
8592 // Skip safari plugin for other browsers
8593 if (!isWebKit && p == 'safari')
8594 return;
8595
8596 PluginManager.load(p, 'plugins/' + p + '/editor_plugin' + tinymce.suffix + '.js');
8597 }
8598 });
8599
8600 // Init when que is loaded
8601 sl.loadQueue(function() {
8602 if (!t.removed)
8603 t.init();
8604 });
8605 };
8606
8607 // Load compat2x first
8608 if (s.plugins.indexOf('compat2x') != -1) {
8609 PluginManager.load('compat2x', 'plugins/compat2x/editor_plugin' + tinymce.suffix + '.js');
8610 sl.loadQueue(loadScripts);
8611 } else
8612 loadScripts();
8613 },
8614
8615 init : function() {
8616 var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re;
8617
8618 EditorManager.add(t);
8619
8620 // Create theme
8621 if (s.theme) {
8622 s.theme = s.theme.replace(/-/, '');
8623 o = ThemeManager.get(s.theme);
8624 t.theme = new o();
8625
8626 if (t.theme.init && s.init_theme)
8627 t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));
8628 }
8629
8630 // Create all plugins
8631 each(explode(s.plugins.replace(/\-/g, '')), function(p) {
8632 var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;
8633
8634 if (c) {
8635 po = new c(t, u);
8636
8637 t.plugins[p] = po;
8638
8639 if (po.init)
8640 po.init(t, u);
8641 }
8642 });
8643
8644 // Setup popup CSS path(s)
8645 if (s.popup_css !== false) {
8646 if (s.popup_css)
8647 s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);
8648 else
8649 s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");
8650 }
8651
8652 if (s.popup_css_add)
8653 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);
8654
8655 // Setup control factory
8656 t.controlManager = new tinymce.ControlManager(t);
8657 t.undoManager = new tinymce.UndoManager(t);
8658
8659 // Pass through
8660 t.undoManager.onAdd.add(function(um, l) {
8661 if (!l.initial)
8662 return t.onChange.dispatch(t, l, um);
8663 });
8664
8665 t.undoManager.onUndo.add(function(um, l) {
8666 return t.onUndo.dispatch(t, l, um);
8667 });
8668
8669 t.undoManager.onRedo.add(function(um, l) {
8670 return t.onRedo.dispatch(t, l, um);
8671 });
8672
8673 if (s.custom_undo_redo) {
8674 t.onExecCommand.add(function(ed, cmd, ui, val, a) {
8675 if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))
8676 t.undoManager.add();
8677 });
8678 }
8679
8680 t.onExecCommand.add(function(ed, c) {
8681 // Don't refresh the select lists until caret move
8682 if (!/^(FontName|FontSize)$/.test(c))
8683 t.nodeChanged();
8684 });
8685
8686 // Remove ghost selections on images and tables in Gecko
8687 if (isGecko) {
8688 function repaint(a, o) {
8689 if (!o || !o.initial)
8690 t.execCommand('mceRepaint');
8691 };
8692
8693 t.onUndo.add(repaint);
8694 t.onRedo.add(repaint);
8695 t.onSetContent.add(repaint);
8696 }
8697
8698 // Enables users to override the control factory
8699 t.onBeforeRenderUI.dispatch(t, t.controlManager);
8700
8701 // Measure box
8702 if (s.render_ui) {
8703 w = s.width || e.style.width || e.offsetWidth;
8704 h = s.height || e.style.height || e.offsetHeight;
8705 t.orgDisplay = e.style.display;
8706 re = /^[0-9\.]+(|px)$/i;
8707
8708 if (re.test('' + w))
8709 w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);
8710
8711 if (re.test('' + h))
8712 h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);
8713
8714 // Render UI
8715 o = t.theme.renderUI({
8716 targetNode : e,
8717 width : w,
8718 height : h,
8719 deltaWidth : s.delta_width,
8720 deltaHeight : s.delta_height
8721 });
8722
8723 t.editorContainer = o.editorContainer;
8724 }
8725
8726
8727 // Resize editor
8728 DOM.setStyles(o.sizeContainer || o.editorContainer, {
8729 width : w,
8730 height : h
8731 });
8732
8733 h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');
8734 if (h < 100)
8735 h = 100;
8736
8737 t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml"><base href="' + t.documentBaseURI.getURI() + '" />';
8738 t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
8739
8740 if (tinymce.relaxedDomain)
8741 t.iframeHTML += '<script type="text/javascript">document.domain = "' + tinymce.relaxedDomain + '";</script>';
8742
8743 bi = s.body_id || 'tinymce';
8744 if (bi.indexOf('=') != -1) {
8745 bi = t.getParam('body_id', '', 'hash');
8746 bi = bi[t.id] || bi;
8747 }
8748
8749 bc = s.body_class || '';
8750 if (bc.indexOf('=') != -1) {
8751 bc = t.getParam('body_class', '', 'hash');
8752 bc = bc[t.id] || '';
8753 }
8754
8755 t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"></body></html>';
8756
8757 // Domain relaxing enabled, then set document domain
8758 if (tinymce.relaxedDomain) {
8759 // We need to write the contents here in IE since multiple writes messes up refresh button and back button
8760 if (isIE || (tinymce.isOpera && parseFloat(opera.version()) >= 9.5))
8761 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();})()';
8762 else if (tinymce.isOpera)
8763 u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";document.close();ed.setupIframe();})()';
8764 }
8765
8766 // Create iframe
8767 n = DOM.add(o.iframeContainer, 'iframe', {
8768 id : t.id + "_ifr",
8769 src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7
8770 frameBorder : '0',
8771 style : {
8772 width : '100%',
8773 height : h
8774 }
8775 });
8776
8777 t.contentAreaContainer = o.iframeContainer;
8778 DOM.get(o.editorContainer).style.display = t.orgDisplay;
8779 DOM.get(t.id).style.display = 'none';
8780
8781 if (!isIE || !tinymce.relaxedDomain)
8782 t.setupIframe();
8783
8784 e = n = o = null; // Cleanup
8785 },
8786
8787 setupIframe : function() {
8788 var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;
8789
8790 // Setup iframe body
8791 if (!isIE || !tinymce.relaxedDomain) {
8792 d.open();
8793 d.write(t.iframeHTML);
8794 d.close();
8795 }
8796
8797 // Design mode needs to be added here Ctrl+A will fail otherwise
8798 if (!isIE) {
8799 try {
8800 if (!s.readonly)
8801 d.designMode = 'On';
8802 } catch (ex) {
8803 // Will fail on Gecko if the editor is placed in an hidden container element
8804 // The design mode will be set ones the editor is focused
8805 }
8806 }
8807
8808 // IE needs to use contentEditable or it will display non secure items for HTTPS
8809 if (isIE) {
8810 // It will not steal focus if we hide it while setting contentEditable
8811 b = t.getBody();
8812 DOM.hide(b);
8813
8814 if (!s.readonly)
8815 b.contentEditable = true;
8816
8817 DOM.show(b);
8818 }
8819
8820 // Setup objects
8821 t.dom = new tinymce.DOM.DOMUtils(t.getDoc(), {
8822 keep_values : true,
8823 url_converter : t.convertURL,
8824 url_converter_scope : t,
8825 hex_colors : s.force_hex_style_colors,
8826 class_filter : s.class_filter,
8827 update_styles : 1,
8828 fix_ie_paragraphs : 1
8829 });
8830
8831 t.serializer = new tinymce.dom.Serializer({
8832 entity_encoding : s.entity_encoding,
8833 entities : s.entities,
8834 valid_elements : s.verify_html === false ? '*[*]' : s.valid_elements,
8835 extended_valid_elements : s.extended_valid_elements,
8836 valid_child_elements : s.valid_child_elements,
8837 invalid_elements : s.invalid_elements,
8838 fix_table_elements : s.fix_table_elements,
8839 fix_list_elements : s.fix_list_elements,
8840 fix_content_duplication : s.fix_content_duplication,
8841 convert_fonts_to_spans : s.convert_fonts_to_spans,
8842 font_size_classes : s.font_size_classes,
8843 font_size_style_values : s.font_size_style_values,
8844 apply_source_formatting : s.apply_source_formatting,
8845 remove_linebreaks : s.remove_linebreaks,
8846 element_format : s.element_format,
8847 dom : t.dom
8848 });
8849
8850 t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);
8851 t.forceBlocks = new tinymce.ForceBlocks(t, {
8852 forced_root_block : s.forced_root_block
8853 });
8854 t.editorCommands = new tinymce.EditorCommands(t);
8855
8856 // Pass through
8857 t.serializer.onPreProcess.add(function(se, o) {
8858 return t.onPreProcess.dispatch(t, o, se);
8859 });
8860
8861 t.serializer.onPostProcess.add(function(se, o) {
8862 return t.onPostProcess.dispatch(t, o, se);
8863 });
8864
8865 t.onPreInit.dispatch(t);
8866
8867 if (!s.gecko_spellcheck)
8868 t.getBody().spellcheck = 0;
8869
8870 if (!s.readonly)
8871 t._addEvents();
8872
8873 t.controlManager.onPostRender.dispatch(t, t.controlManager);
8874 t.onPostRender.dispatch(t);
8875
8876 if (s.directionality)
8877 t.getBody().dir = s.directionality;
8878
8879 if (s.nowrap)
8880 t.getBody().style.whiteSpace = "nowrap";
8881
8882 if (s.auto_resize)
8883 t.onNodeChange.add(t.resizeToContent, t);
8884
8885 if (s.custom_elements) {
8886 function handleCustom(ed, o) {
8887 each(explode(s.custom_elements), function(v) {
8888 var n;
8889
8890 if (v.indexOf('~') === 0) {
8891 v = v.substring(1);
8892 n = 'span';
8893 } else
8894 n = 'div';
8895
8896 o.content = o.content.replace(new RegExp('<(' + v + ')([^>]*)>', 'g'), '<' + n + ' mce_name="$1"$2>');
8897 o.content = o.content.replace(new RegExp('</(' + v + ')>', 'g'), '</' + n + '>');
8898 });
8899 };
8900
8901 t.onBeforeSetContent.add(handleCustom);
8902 t.onPostProcess.add(function(ed, o) {
8903 if (o.set)
8904 handleCustom(ed, o)
8905 });
8906 }
8907
8908 if (s.handle_node_change_callback) {
8909 t.onNodeChange.add(function(ed, cm, n) {
8910 t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());
8911 });
8912 }
8913
8914 if (s.save_callback) {
8915 t.onSaveContent.add(function(ed, o) {
8916 var h = t.execCallback('save_callback', t.id, o.content, t.getBody());
8917
8918 if (h)
8919 o.content = h;
8920 });
8921 }
8922
8923 if (s.onchange_callback) {
8924 t.onChange.add(function(ed, l) {
8925 t.execCallback('onchange_callback', t, l);
8926 });
8927 }
8928
8929 if (s.convert_newlines_to_brs) {
8930 t.onBeforeSetContent.add(function(ed, o) {
8931 if (o.initial)
8932 o.content = o.content.replace(/\r?\n/g, '<br />');
8933 });
8934 }
8935
8936 if (s.fix_nesting && isIE) {
8937 t.onBeforeSetContent.add(function(ed, o) {
8938 o.content = t._fixNesting(o.content);
8939 });
8940 }
8941
8942 if (s.preformatted) {
8943 t.onPostProcess.add(function(ed, o) {
8944 o.content = o.content.replace(/^\s*<pre.*?>/, '');
8945 o.content = o.content.replace(/<\/pre>\s*$/, '');
8946
8947 if (o.set)
8948 o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';
8949 });
8950 }
8951
8952 if (s.verify_css_classes) {
8953 t.serializer.attribValueFilter = function(n, v) {
8954 var s, cl;
8955
8956 if (n == 'class') {
8957 // Build regexp for classes
8958 if (!t.classesRE) {
8959 cl = t.dom.getClasses();
8960
8961 if (cl.length > 0) {
8962 s = '';
8963
8964 each (cl, function(o) {
8965 s += (s ? '|' : '') + o['class'];
8966 });
8967
8968 t.classesRE = new RegExp('(' + s + ')', 'gi');
8969 }
8970 }
8971
8972 return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';
8973 }
8974
8975 return v;
8976 };
8977 }
8978
8979 if (s.convert_fonts_to_spans)
8980 t._convertFonts();
8981
8982 if (s.inline_styles)
8983 t._convertInlineElements();
8984
8985 if (s.cleanup_callback) {
8986 t.onBeforeSetContent.add(function(ed, o) {
8987 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
8988 });
8989
8990 t.onPreProcess.add(function(ed, o) {
8991 if (o.set)
8992 t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);
8993
8994 if (o.get)
8995 t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);
8996 });
8997
8998 t.onPostProcess.add(function(ed, o) {
8999 if (o.set)
9000 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
9001
9002 if (o.get)
9003 o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);
9004 });
9005 }
9006
9007 if (s.save_callback) {
9008 t.onGetContent.add(function(ed, o) {
9009 if (o.save)
9010 o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());
9011 });
9012 }
9013
9014 if (s.handle_event_callback) {
9015 t.onEvent.add(function(ed, e, o) {
9016 if (t.execCallback('handle_event_callback', e, ed, o) === false)
9017 Event.cancel(e);
9018 });
9019 }
9020
9021 // Add visual aids when new contents is added
9022 t.onSetContent.add(function() {
9023 t.addVisual(t.getBody());
9024 });
9025
9026 // Remove empty contents
9027 if (s.padd_empty_editor) {
9028 t.onPostProcess.add(function(ed, o) {
9029 o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');
9030 });
9031 }
9032
9033 // Fix gecko link bug, when a link is placed at the end of block elements there is
9034 // no way to move the caret behind the link. This fix adds a bogus br element after the link
9035 if (isGecko) {
9036 function fixLinks(ed, o) {
9037 each(ed.dom.select('a'), function(n) {
9038 var pn = n.parentNode;
9039
9040 if (ed.dom.isBlock(pn) && pn.lastChild === n)
9041 ed.dom.add(pn, 'br', {'mce_bogus' : 1});
9042 });
9043 };
9044
9045 t.onExecCommand.add(function(ed, cmd) {
9046 if (cmd === 'CreateLink')
9047 fixLinks(ed);
9048 });
9049
9050 t.onSetContent.add(t.selection.onSetContent.add(fixLinks));
9051 }
9052
9053 if (isGecko && !s.readonly) {
9054 try {
9055 // Design mode must be set here once again to fix a bug where
9056 // Ctrl+A/Delete/Backspace didn't work if the editor was added using mceAddControl then removed then added again
9057 d.designMode = 'Off';
9058 d.designMode = 'On';
9059 } catch (ex) {
9060 // Will fail on Gecko if the editor is placed in an hidden container element
9061 // The design mode will be set ones the editor is focused
9062 }
9063 }
9064
9065 // A small timeout was needed since firefox will remove. Bug: #1838304
9066 setTimeout(function () {
9067 if (t.removed)
9068 return;
9069
9070 t.load({initial : true, format : (s.cleanup_on_startup ? 'html' : 'raw')});
9071 t.startContent = t.getContent({format : 'raw'});
9072 t.undoManager.add({initial : true});
9073 t.initialized = true;
9074
9075 t.onInit.dispatch(t);
9076 t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());
9077 t.execCallback('init_instance_callback', t);
9078 t.focus(true);
9079 t.nodeChanged({initial : 1});
9080
9081 // Load specified content CSS last
9082 if (s.content_css) {
9083 tinymce.each(explode(s.content_css), function(u) {
9084 t.dom.loadCSS(t.documentBaseURI.toAbsolute(u));
9085 });
9086 }
9087
9088 // Handle auto focus
9089 if (s.auto_focus) {
9090 setTimeout(function () {
9091 var ed = EditorManager.get(s.auto_focus);
9092
9093 ed.selection.select(ed.getBody(), 1);
9094 ed.selection.collapse(1);
9095 ed.getWin().focus();
9096 }, 100);
9097 }
9098 }, 1);
9099
9100 e = null;
9101 },
9102
9103
9104 focus : function(sf) {
9105 var oed, t = this, ce = t.settings.content_editable;
9106
9107 if (!sf) {
9108 // Is not content editable or the selection is outside the area in IE
9109 // the IE statement is needed to avoid bluring if element selections inside layers since
9110 // the layer is like it's own document in IE
9111 if (!ce && (!isIE || t.selection.getNode().ownerDocument != t.getDoc()))
9112 t.getWin().focus();
9113
9114 }
9115
9116 if (EditorManager.activeEditor != t) {
9117 if ((oed = EditorManager.activeEditor) != null)
9118 oed.onDeactivate.dispatch(oed, t);
9119
9120 t.onActivate.dispatch(t, oed);
9121 }
9122
9123 EditorManager._setActive(t);
9124 },
9125
9126 execCallback : function(n) {
9127 var t = this, f = t.settings[n], s;
9128
9129 if (!f)
9130 return;
9131
9132 // Look through lookup
9133 if (t.callbackLookup && (s = t.callbackLookup[n])) {
9134 f = s.func;
9135 s = s.scope;
9136 }
9137
9138 if (is(f, 'string')) {
9139 s = f.replace(/\.\w+$/, '');
9140 s = s ? tinymce.resolve(s) : 0;
9141 f = tinymce.resolve(f);
9142 t.callbackLookup = t.callbackLookup || {};
9143 t.callbackLookup[n] = {func : f, scope : s};
9144 }
9145
9146 return f.apply(s || t, Array.prototype.slice.call(arguments, 1));
9147 },
9148
9149 translate : function(s) {
9150 var c = this.settings.language || 'en', i18n = EditorManager.i18n;
9151
9152 if (!s)
9153 return '';
9154
9155 return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {
9156 return i18n[c + '.' + b] || '{#' + b + '}';
9157 });
9158 },
9159
9160 getLang : function(n, dv) {
9161 return EditorManager.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}');
9162 },
9163
9164 getParam : function(n, dv, ty) {
9165 var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;
9166
9167 if (ty === 'hash') {
9168 o = {};
9169
9170 if (is(v, 'string')) {
9171 each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {
9172 v = v.split('=');
9173
9174 if (v.length > 1)
9175 o[tr(v[0])] = tr(v[1]);
9176 else
9177 o[tr(v[0])] = tr(v);
9178 });
9179 } else
9180 o = v;
9181
9182 return o;
9183 }
9184
9185 return v;
9186 },
9187
9188 nodeChanged : function(o) {
9189 var t = this, s = t.selection, n = s.getNode() || t.getBody();
9190
9191 // Fix for bug #1896577 it seems that this can not be fired while the editor is loading
9192 if (t.initialized) {
9193 t.onNodeChange.dispatch(
9194 t,
9195 o ? o.controlManager || t.controlManager : t.controlManager,
9196 isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n, // Fix for IE initial state
9197 s.isCollapsed(),
9198 o
9199 );
9200 }
9201 },
9202
9203 addButton : function(n, s) {
9204 var t = this;
9205
9206 t.buttons = t.buttons || {};
9207 t.buttons[n] = s;
9208 },
9209
9210 addCommand : function(n, f, s) {
9211 this.execCommands[n] = {func : f, scope : s || this};
9212 },
9213
9214 addQueryStateHandler : function(n, f, s) {
9215 this.queryStateCommands[n] = {func : f, scope : s || this};
9216 },
9217
9218 addQueryValueHandler : function(n, f, s) {
9219 this.queryValueCommands[n] = {func : f, scope : s || this};
9220 },
9221
9222 addShortcut : function(pa, desc, cmd_func, sc) {
9223 var t = this, c;
9224
9225 if (!t.settings.custom_shortcuts)
9226 return false;
9227
9228 t.shortcuts = t.shortcuts || {};
9229
9230 if (is(cmd_func, 'string')) {
9231 c = cmd_func;
9232
9233 cmd_func = function() {
9234 t.execCommand(c, false, null);
9235 };
9236 }
9237
9238 if (is(cmd_func, 'object')) {
9239 c = cmd_func;
9240
9241 cmd_func = function() {
9242 t.execCommand(c[0], c[1], c[2]);
9243 };
9244 }
9245
9246 each(explode(pa), function(pa) {
9247 var o = {
9248 func : cmd_func,
9249 scope : sc || this,
9250 desc : desc,
9251 alt : false,
9252 ctrl : false,
9253 shift : false
9254 };
9255
9256 each(explode(pa, '+'), function(v) {
9257 switch (v) {
9258 case 'alt':
9259 case 'ctrl':
9260 case 'shift':
9261 o[v] = true;
9262 break;
9263
9264 default:
9265 o.charCode = v.charCodeAt(0);
9266 o.keyCode = v.toUpperCase().charCodeAt(0);
9267 }
9268 });
9269
9270 t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;
9271 });
9272
9273 return true;
9274 },
9275
9276 execCommand : function(cmd, ui, val, a) {
9277 var t = this, s = 0, o, st;
9278
9279 if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))
9280 t.focus();
9281
9282 o = {};
9283 t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);
9284 if (o.terminate)
9285 return false;
9286
9287 // Command callback
9288 if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {
9289 t.onExecCommand.dispatch(t, cmd, ui, val, a);
9290 return true;
9291 }
9292
9293 // Registred commands
9294 if (o = t.execCommands[cmd]) {
9295 st = o.func.call(o.scope, ui, val);
9296
9297 // Fall through on true
9298 if (st !== true) {
9299 t.onExecCommand.dispatch(t, cmd, ui, val, a);
9300 return st;
9301 }
9302 }
9303
9304 // Plugin commands
9305 each(t.plugins, function(p) {
9306 if (p.execCommand && p.execCommand(cmd, ui, val)) {
9307 t.onExecCommand.dispatch(t, cmd, ui, val, a);
9308 s = 1;
9309 return false;
9310 }
9311 });
9312
9313 if (s)
9314 return true;
9315
9316 // Theme commands
9317 if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {
9318 t.onExecCommand.dispatch(t, cmd, ui, val, a);
9319 return true;
9320 }
9321
9322 // Execute global commands
9323 if (tinymce.GlobalCommands.execCommand(t, cmd, ui, val)) {
9324 t.onExecCommand.dispatch(t, cmd, ui, val, a);
9325 return true;
9326 }
9327
9328 // Editor commands
9329 if (t.editorCommands.execCommand(cmd, ui, val)) {
9330 t.onExecCommand.dispatch(t, cmd, ui, val, a);
9331 return true;
9332 }
9333
9334 // Browser commands
9335 t.getDoc().execCommand(cmd, ui, val);
9336 t.onExecCommand.dispatch(t, cmd, ui, val, a);
9337 },
9338
9339 queryCommandState : function(c) {
9340 var t = this, o, s;
9341
9342 // Is hidden then return undefined
9343 if (t._isHidden())
9344 return;
9345
9346 // Registred commands
9347 if (o = t.queryStateCommands[c]) {
9348 s = o.func.call(o.scope);
9349
9350 // Fall though on true
9351 if (s !== true)
9352 return s;
9353 }
9354
9355 // Registred commands
9356 o = t.editorCommands.queryCommandState(c);
9357 if (o !== -1)
9358 return o;
9359
9360 // Browser commands
9361 try {
9362 return this.getDoc().queryCommandState(c);
9363 } catch (ex) {
9364 // Fails sometimes see bug: 1896577
9365 }
9366 },
9367
9368 queryCommandValue : function(c) {
9369 var t = this, o, s;
9370
9371 // Is hidden then return undefined
9372 if (t._isHidden())
9373 return;
9374
9375 // Registred commands
9376 if (o = t.queryValueCommands[c]) {
9377 s = o.func.call(o.scope);
9378
9379 // Fall though on true
9380 if (s !== true)
9381 return s;
9382 }
9383
9384 // Registred commands
9385 o = t.editorCommands.queryCommandValue(c);
9386 if (is(o))
9387 return o;
9388
9389 // Browser commands
9390 try {
9391 return this.getDoc().queryCommandValue(c);
9392 } catch (ex) {
9393 // Fails sometimes see bug: 1896577
9394 }
9395 },
9396
9397 show : function() {
9398 var t = this;
9399
9400 DOM.show(t.getContainer());
9401 DOM.hide(t.id);
9402 t.load();
9403 },
9404
9405 hide : function() {
9406 var t = this, d = t.getDoc();
9407
9408 // Fixed bug where IE has a blinking cursor left from the editor
9409 if (isIE && d)
9410 d.execCommand('SelectAll');
9411
9412 // We must save before we hide so Safari doesn't crash
9413 t.save();
9414 DOM.hide(t.getContainer());
9415 DOM.setStyle(t.id, 'display', t.orgDisplay);
9416 },
9417
9418 isHidden : function() {
9419 return !DOM.isHidden(this.id);
9420 },
9421
9422 setProgressState : function(b, ti, o) {
9423 this.onSetProgressState.dispatch(this, b, ti, o);
9424
9425 return b;
9426 },
9427
9428 resizeToContent : function() {
9429 var t = this;
9430
9431 DOM.setStyle(t.id + "_ifr", 'height', t.getBody().scrollHeight);
9432 },
9433
9434 load : function(o) {
9435 var t = this, e = t.getElement(), h;
9436
9437 if (e) {
9438 o = o || {};
9439 o.load = true;
9440
9441 // Double encode existing entities in the value
9442 h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);
9443 o.element = e;
9444
9445 if (!o.no_events)
9446 t.onLoadContent.dispatch(t, o);
9447
9448 o.element = e = null;
9449
9450 return h;
9451 }
9452 },
9453
9454 save : function(o) {
9455 var t = this, e = t.getElement(), h, f;
9456
9457 if (!e || !t.initialized)
9458 return;
9459
9460 o = o || {};
9461 o.save = true;
9462
9463 // Add undo level will trigger onchange event
9464 if (!o.no_events) {
9465 t.undoManager.typing = 0;
9466 t.undoManager.add();
9467 }
9468
9469 o.element = e;
9470 h = o.content = t.getContent(o);
9471
9472 if (!o.no_events)
9473 t.onSaveContent.dispatch(t, o);
9474
9475 h = o.content;
9476
9477 if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {
9478 e.innerHTML = h;
9479
9480 // Update hidden form element
9481 if (f = DOM.getParent(t.id, 'form')) {
9482 each(f.elements, function(e) {
9483 if (e.name == t.id) {
9484 e.value = h;
9485 return false;
9486 }
9487 });
9488 }
9489 } else
9490 e.value = h;
9491
9492 o.element = e = null;
9493
9494 return h;
9495 },
9496
9497 setContent : function(h, o) {
9498 var t = this;
9499
9500 o = o || {};
9501 o.format = o.format || 'html';
9502 o.set = true;
9503 o.content = h;
9504
9505 if (!o.no_events)
9506 t.onBeforeSetContent.dispatch(t, o);
9507
9508 // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
9509 // It will also be impossible to place the caret in the editor unless there is a BR element present
9510 if (!tinymce.isIE && (h.length === 0 || /^\s+$/.test(h))) {
9511 o.content = t.dom.setHTML(t.getBody(), '<br mce_bogus="1" />');
9512 o.format = 'raw';
9513 }
9514
9515 o.content = t.dom.setHTML(t.getBody(), tinymce.trim(o.content));
9516
9517 if (o.format != 'raw' && t.settings.cleanup) {
9518 o.getInner = true;
9519 o.content = t.dom.setHTML(t.getBody(), t.serializer.serialize(t.getBody(), o));
9520 }
9521
9522 if (!o.no_events)
9523 t.onSetContent.dispatch(t, o);
9524
9525 return o.content;
9526 },
9527
9528 getContent : function(o) {
9529 var t = this, h;
9530
9531 o = o || {};
9532 o.format = o.format || 'html';
9533 o.get = true;
9534
9535 if (!o.no_events)
9536 t.onBeforeGetContent.dispatch(t, o);
9537
9538 if (o.format != 'raw' && t.settings.cleanup) {
9539 o.getInner = true;
9540 h = t.serializer.serialize(t.getBody(), o);
9541 } else
9542 h = t.getBody().innerHTML;
9543
9544 h = h.replace(/^\s*|\s*$/g, '');
9545 o.content = h;
9546
9547 if (!o.no_events)
9548 t.onGetContent.dispatch(t, o);
9549
9550 return o.content;
9551 },
9552
9553 isDirty : function() {
9554 var t = this;
9555
9556 return tinymce.trim(t.startContent) != tinymce.trim(t.getContent({format : 'raw', no_events : 1})) && !t.isNotDirty;
9557 },
9558
9559 getContainer : function() {
9560 var t = this;
9561
9562 if (!t.container)
9563 t.container = DOM.get(t.editorContainer || t.id + '_parent');
9564
9565 return t.container;
9566 },
9567
9568 getContentAreaContainer : function() {
9569 return this.contentAreaContainer;
9570 },
9571
9572 getElement : function() {
9573 return DOM.get(this.settings.content_element || this.id);
9574 },
9575
9576 getWin : function() {
9577 var t = this, e;
9578
9579 if (!t.contentWindow) {
9580 e = DOM.get(t.id + "_ifr");
9581
9582 if (e)
9583 t.contentWindow = e.contentWindow;
9584 }
9585
9586 return t.contentWindow;
9587 },
9588
9589 getDoc : function() {
9590 var t = this, w;
9591
9592 if (!t.contentDocument) {
9593 w = t.getWin();
9594
9595 if (w)
9596 t.contentDocument = w.document;
9597 }
9598
9599 return t.contentDocument;
9600 },
9601
9602 getBody : function() {
9603 return this.bodyElement || this.getDoc().body;
9604 },
9605
9606 convertURL : function(u, n, e) {
9607 var t = this, s = t.settings;
9608
9609 // Use callback instead
9610 if (s.urlconverter_callback)
9611 return t.execCallback('urlconverter_callback', u, e, true, n);
9612
9613 // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs
9614 if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)
9615 return u;
9616
9617 // Convert to relative
9618 if (s.relative_urls)
9619 return t.documentBaseURI.toRelative(u);
9620
9621 // Convert to absolute
9622 u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);
9623
9624 return u;
9625 },
9626
9627 addVisual : function(e) {
9628 var t = this, s = t.settings;
9629
9630 e = e || t.getBody();
9631
9632 if (!is(t.hasVisual))
9633 t.hasVisual = s.visual;
9634
9635 each(t.dom.select('table,a', e), function(e) {
9636 var v;
9637
9638 switch (e.nodeName) {
9639 case 'TABLE':
9640 v = t.dom.getAttrib(e, 'border');
9641
9642 if (!v || v == '0') {
9643 if (t.hasVisual)
9644 t.dom.addClass(e, s.visual_table_class);
9645 else
9646 t.dom.removeClass(e, s.visual_table_class);
9647 }
9648
9649 return;
9650
9651 case 'A':
9652 v = t.dom.getAttrib(e, 'name');
9653
9654 if (v) {
9655 if (t.hasVisual)
9656 t.dom.addClass(e, 'mceItemAnchor');
9657 else
9658 t.dom.removeClass(e, 'mceItemAnchor');
9659 }
9660
9661 return;
9662 }
9663 });
9664
9665 t.onVisualAid.dispatch(t, e, t.hasVisual);
9666 },
9667
9668 remove : function() {
9669 var t = this, e = t.getContainer();
9670
9671 t.removed = 1; // Cancels post remove event execution
9672 t.hide();
9673
9674 t.execCallback('remove_instance_callback', t);
9675 t.onRemove.dispatch(t);
9676
9677 // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command
9678 t.onExecCommand.listeners = [];
9679
9680 EditorManager.remove(t);
9681 DOM.remove(e);
9682 },
9683
9684 destroy : function(s) {
9685 var t = this;
9686
9687 // One time is enough
9688 if (t.destroyed)
9689 return;
9690
9691 if (!s) {
9692 tinymce.removeUnload(t.destroy);
9693 tinyMCE.onBeforeUnload.remove(t._beforeUnload);
9694
9695 // Manual destroy
9696 if (t.theme && t.theme.destroy)
9697 t.theme.destroy();
9698
9699 // Destroy controls, selection and dom
9700 t.controlManager.destroy();
9701 t.selection.destroy();
9702 t.dom.destroy();
9703
9704 // Remove all events
9705
9706 // Don't clear the window or document if content editable
9707 // is enabled since other instances might still be present
9708 if (!t.settings.content_editable) {
9709 Event.clear(t.getWin());
9710 Event.clear(t.getDoc());
9711 }
9712
9713 Event.clear(t.getBody());
9714 Event.clear(t.formElement);
9715 }
9716
9717 if (t.formElement) {
9718 t.formElement.submit = t.formElement._mceOldSubmit;
9719 t.formElement._mceOldSubmit = null;
9720 }
9721
9722 t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;
9723
9724 if (t.selection)
9725 t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;
9726
9727 t.destroyed = 1;
9728 },
9729
9730 // Internal functions
9731
9732 _addEvents : function() {
9733 // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset
9734 var t = this, i, s = t.settings, lo = {
9735 mouseup : 'onMouseUp',
9736 mousedown : 'onMouseDown',
9737 click : 'onClick',
9738 keyup : 'onKeyUp',
9739 keydown : 'onKeyDown',
9740 keypress : 'onKeyPress',
9741 submit : 'onSubmit',
9742 reset : 'onReset',
9743 contextmenu : 'onContextMenu',
9744 dblclick : 'onDblClick',
9745 paste : 'onPaste' // Doesn't work in all browsers yet
9746 };
9747
9748 function eventHandler(e, o) {
9749 var ty = e.type;
9750
9751 // Don't fire events when it's removed
9752 if (t.removed)
9753 return;
9754
9755 // Generic event handler
9756 if (t.onEvent.dispatch(t, e, o) !== false) {
9757 // Specific event handler
9758 t[lo[e.fakeType || e.type]].dispatch(t, e, o);
9759 }
9760 };
9761
9762 // Add DOM events
9763 each(lo, function(v, k) {
9764 switch (k) {
9765 case 'contextmenu':
9766 if (tinymce.isOpera) {
9767 // Fake contextmenu on Opera
9768 Event.add(t.getBody(), 'mousedown', function(e) {
9769 if (e.ctrlKey) {
9770 e.fakeType = 'contextmenu';
9771 eventHandler(e);
9772 }
9773 });
9774 } else
9775 Event.add(t.getBody(), k, eventHandler);
9776 break;
9777
9778 case 'paste':
9779 Event.add(t.getBody(), k, function(e) {
9780 var tx, h, el, r;
9781
9782 // Get plain text data
9783 if (e.clipboardData)
9784 tx = e.clipboardData.getData('text/plain');
9785 else if (tinymce.isIE)
9786 tx = t.getWin().clipboardData.getData('Text');
9787
9788 // Get HTML data
9789 /*if (tinymce.isIE) {
9790 el = DOM.add(DOM.doc.body, 'div', {style : 'visibility:hidden;overflow:hidden;position:absolute;width:1px;height:1px'});
9791 r = DOM.doc.body.createTextRange();
9792 r.moveToElementText(el);
9793 r.execCommand('Paste');
9794 h = el.innerHTML;
9795 DOM.remove(el);
9796 }*/
9797
9798 eventHandler(e, {text : tx, html : h});
9799 });
9800 break;
9801
9802 case 'submit':
9803 case 'reset':
9804 Event.add(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);
9805 break;
9806
9807 default:
9808 Event.add(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler);
9809 }
9810 });
9811
9812 Event.add(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) {
9813 t.focus(true);
9814 });
9815
9816
9817 // Fixes bug where a specified document_base_uri could result in broken images
9818 // This will also fix drag drop of images in Gecko
9819 if (tinymce.isGecko) {
9820 // Convert all images to absolute URLs
9821 /* t.onSetContent.add(function(ed, o) {
9822 each(ed.dom.select('img'), function(e) {
9823 var v;
9824
9825 if (v = e.getAttribute('mce_src'))
9826 e.src = t.documentBaseURI.toAbsolute(v);
9827 })
9828 });*/
9829
9830 Event.add(t.getDoc(), 'DOMNodeInserted', function(e) {
9831 var v;
9832
9833 e = e.target;
9834
9835 if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('mce_src')))
9836 e.src = t.documentBaseURI.toAbsolute(v);
9837 });
9838 }
9839
9840 // Set various midas options in Gecko
9841 if (isGecko) {
9842 function setOpts() {
9843 var t = this, d = t.getDoc(), s = t.settings;
9844
9845 if (isGecko && !s.readonly) {
9846 if (t._isHidden()) {
9847 try {
9848 if (!s.content_editable)
9849 d.designMode = 'On';
9850 } catch (ex) {
9851 // Fails if it's hidden
9852 }
9853 }
9854
9855 try {
9856 // Try new Gecko method
9857 d.execCommand("styleWithCSS", 0, false);
9858 } catch (ex) {
9859 // Use old method
9860 if (!t._isHidden())
9861 try {d.execCommand("useCSS", 0, true);} catch (ex) {}
9862 }
9863
9864 if (!s.table_inline_editing)
9865 try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {}
9866
9867 if (!s.object_resizing)
9868 try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {}
9869 }
9870 };
9871
9872 t.onBeforeExecCommand.add(setOpts);
9873 t.onMouseDown.add(setOpts);
9874 }
9875
9876 // Add node change handlers
9877 t.onMouseUp.add(t.nodeChanged);
9878 t.onClick.add(t.nodeChanged);
9879 t.onKeyUp.add(function(ed, e) {
9880 var c = e.keyCode;
9881
9882 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)
9883 t.nodeChanged();
9884 });
9885
9886 // Add reset handler
9887 t.onReset.add(function() {
9888 t.setContent(t.startContent, {format : 'raw'});
9889 });
9890
9891 // Add shortcuts
9892 if (s.custom_shortcuts) {
9893 if (s.custom_undo_redo_keyboard_shortcuts) {
9894 t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo');
9895 t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo');
9896 }
9897
9898 // Add default shortcuts for gecko
9899 if (isGecko) {
9900 t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');
9901 t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');
9902 t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');
9903 }
9904
9905 // BlockFormat shortcuts keys
9906 for (i=1; i<=6; i++)
9907 t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, '<h' + i + '>']);
9908
9909 t.addShortcut('ctrl+7', '', ['FormatBlock', false, '<p>']);
9910 t.addShortcut('ctrl+8', '', ['FormatBlock', false, '<div>']);
9911 t.addShortcut('ctrl+9', '', ['FormatBlock', false, '<address>']);
9912
9913 function find(e) {
9914 var v = null;
9915
9916 if (!e.altKey && !e.ctrlKey && !e.metaKey)
9917 return v;
9918
9919 each(t.shortcuts, function(o) {
9920 if (tinymce.isMac && o.ctrl != e.metaKey)
9921 return;
9922 else if (!tinymce.isMac && o.ctrl != e.ctrlKey)
9923 return;
9924
9925 if (o.alt != e.altKey)
9926 return;
9927
9928 if (o.shift != e.shiftKey)
9929 return;
9930
9931 if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {
9932 v = o;
9933 return false;
9934 }
9935 });
9936
9937 return v;
9938 };
9939
9940 t.onKeyUp.add(function(ed, e) {
9941 var o = find(e);
9942
9943 if (o)
9944 return Event.cancel(e);
9945 });
9946
9947 t.onKeyPress.add(function(ed, e) {
9948 var o = find(e);
9949
9950 if (o)
9951 return Event.cancel(e);
9952 });
9953
9954 t.onKeyDown.add(function(ed, e) {
9955 var o = find(e);
9956
9957 if (o) {
9958 o.func.call(o.scope);
9959 return Event.cancel(e);
9960 }
9961 });
9962 }
9963
9964 if (tinymce.isIE) {
9965 // Fix so resize will only update the width and height attributes not the styles of an image
9966 // It will also block mceItemNoResize items
9967 Event.add(t.getDoc(), 'controlselect', function(e) {
9968 var re = t.resizeInfo, cb;
9969
9970 e = e.target;
9971
9972 // Don't do this action for non image elements
9973 if (e.nodeName !== 'IMG')
9974 return;
9975
9976 if (re)
9977 Event.remove(re.node, re.ev, re.cb);
9978
9979 if (!t.dom.hasClass(e, 'mceItemNoResize')) {
9980 ev = 'resizeend';
9981 cb = Event.add(e, ev, function(e) {
9982 var v;
9983
9984 e = e.target;
9985
9986 if (v = t.dom.getStyle(e, 'width')) {
9987 t.dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, ''));
9988 t.dom.setStyle(e, 'width', '');
9989 }
9990
9991 if (v = t.dom.getStyle(e, 'height')) {
9992 t.dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, ''));
9993 t.dom.setStyle(e, 'height', '');
9994 }
9995 });
9996 } else {
9997 ev = 'resizestart';
9998 cb = Event.add(e, 'resizestart', Event.cancel, Event);
9999 }
10000
10001 re = t.resizeInfo = {
10002 node : e,
10003 ev : ev,
10004 cb : cb
10005 };
10006 });
10007
10008 t.onKeyDown.add(function(ed, e) {
10009 switch (e.keyCode) {
10010 case 8:
10011 // Fix IE control + backspace browser bug
10012 if (t.selection.getRng().item) {
10013 t.selection.getRng().item(0).removeNode();
10014 return Event.cancel(e);
10015 }
10016 }
10017 });
10018 }
10019
10020 if (tinymce.isOpera) {
10021 t.onClick.add(function(ed, e) {
10022 Event.prevent(e);
10023 });
10024 }
10025
10026 // Add custom undo/redo handlers
10027 if (s.custom_undo_redo) {
10028 function addUndo() {
10029 t.undoManager.typing = 0;
10030 t.undoManager.add();
10031 };
10032
10033 // Add undo level on editor blur
10034 if (tinymce.isIE) {
10035 Event.add(t.getWin(), 'blur', function(e) {
10036 var n;
10037
10038 // Check added for fullscreen bug
10039 if (t.selection) {
10040 n = t.selection.getNode();
10041
10042 // Add undo level is selection was lost to another document
10043 if (!t.removed && n.ownerDocument && n.ownerDocument != t.getDoc())
10044 addUndo();
10045 }
10046 });
10047 } else {
10048 Event.add(t.getDoc(), 'blur', function() {
10049 if (t.selection && !t.removed)
10050 addUndo();
10051 });
10052 }
10053
10054 t.onMouseDown.add(addUndo);
10055
10056 t.onKeyUp.add(function(ed, e) {
10057 if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45 || e.ctrlKey) {
10058 t.undoManager.typing = 0;
10059 t.undoManager.add();
10060 }
10061 });
10062
10063 t.onKeyDown.add(function(ed, e) {
10064 // Is caracter positon keys
10065 if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) {
10066 if (t.undoManager.typing) {
10067 t.undoManager.add();
10068 t.undoManager.typing = 0;
10069 }
10070
10071 return;
10072 }
10073
10074 if (!t.undoManager.typing) {
10075 t.undoManager.add();
10076 t.undoManager.typing = 1;
10077 }
10078 });
10079 }
10080 },
10081
10082 _convertInlineElements : function() {
10083 var t = this, s = t.settings, dom = t.dom, v, e, na, st, sp;
10084
10085 function convert(ed, o) {
10086 if (!s.inline_styles)
10087 return;
10088
10089 if (o.get) {
10090 each(t.dom.select('table,u,strike', o.node), function(n) {
10091 switch (n.nodeName) {
10092 case 'TABLE':
10093 if (v = dom.getAttrib(n, 'height')) {
10094 dom.setStyle(n, 'height', v);
10095 dom.setAttrib(n, 'height', '');
10096 }
10097 break;
10098
10099 case 'U':
10100 case 'STRIKE':
10101 //sp = dom.create('span', {style : dom.getAttrib(n, 'style')});
10102 n.style.textDecoration = n.nodeName == 'U' ? 'underline' : 'line-through';
10103 dom.setAttrib(n, 'mce_style', '');
10104 dom.setAttrib(n, 'mce_name', 'span');
10105 break;
10106 }
10107 });
10108 } else if (o.set) {
10109 each(t.dom.select('table,span', o.node).reverse(), function(n) {
10110 if (n.nodeName == 'TABLE') {
10111 if (v = dom.getStyle(n, 'height'))
10112 dom.setAttrib(n, 'height', v.replace(/[^0-9%]+/g, ''));
10113 } else {
10114 // Convert spans to elements
10115 if (n.style.textDecoration == 'underline')
10116 na = 'u';
10117 else if (n.style.textDecoration == 'line-through')
10118 na = 'strike';
10119 else
10120 na = '';
10121
10122 if (na) {
10123 n.style.textDecoration = '';
10124 dom.setAttrib(n, 'mce_style', '');
10125
10126 e = dom.create(na, {
10127 style : dom.getAttrib(n, 'style')
10128 });
10129
10130 dom.replace(e, n, 1);
10131 }
10132 }
10133 });
10134 }
10135 };
10136
10137 t.onPreProcess.add(convert);
10138
10139 if (!s.cleanup_on_startup) {
10140 t.onSetContent.add(function(ed, o) {
10141 if (o.initial)
10142 convert(t, {node : t.getBody(), set : 1});
10143 });
10144 }
10145 },
10146
10147 _convertFonts : function() {
10148 var t = this, s = t.settings, dom = t.dom, fz, fzn, sl, cl;
10149
10150 // No need
10151 if (!s.inline_styles)
10152 return;
10153
10154 // Font pt values and font size names
10155 fz = [8, 10, 12, 14, 18, 24, 36];
10156 fzn = ['xx-small', 'x-small','small','medium','large','x-large', 'xx-large'];
10157
10158 if (sl = s.font_size_style_values)
10159 sl = explode(sl);
10160
10161 if (cl = s.font_size_classes)
10162 cl = explode(cl);
10163
10164 function process(no) {
10165 var n, sp, nl, x;
10166
10167 // Keep unit tests happy
10168 if (!s.inline_styles)
10169 return;
10170
10171 nl = t.dom.select('font', no);
10172 for (x = nl.length - 1; x >= 0; x--) {
10173 n = nl[x];
10174
10175 sp = dom.create('span', {
10176 style : dom.getAttrib(n, 'style'),
10177 'class' : dom.getAttrib(n, 'class')
10178 });
10179
10180 dom.setStyles(sp, {
10181 fontFamily : dom.getAttrib(n, 'face'),
10182 color : dom.getAttrib(n, 'color'),
10183 backgroundColor : n.style.backgroundColor
10184 });
10185
10186 if (n.size) {
10187 if (sl)
10188 dom.setStyle(sp, 'fontSize', sl[parseInt(n.size) - 1]);
10189 else
10190 dom.setAttrib(sp, 'class', cl[parseInt(n.size) - 1]);
10191 }
10192
10193 dom.setAttrib(sp, 'mce_style', '');
10194 dom.replace(sp, n, 1);
10195 }
10196 };
10197
10198 // Run on cleanup
10199 t.onPreProcess.add(function(ed, o) {
10200 if (o.get)
10201 process(o.node);
10202 });
10203
10204 t.onSetContent.add(function(ed, o) {
10205 if (o.initial)
10206 process(o.node);
10207 });
10208 },
10209
10210 _isHidden : function() {
10211 var s;
10212
10213 if (!isGecko)
10214 return 0;
10215
10216 // Weird, wheres that cursor selection?
10217 s = this.selection.getSel();
10218 return (!s || !s.rangeCount || s.rangeCount == 0);
10219 },
10220
10221 // Fix for bug #1867292
10222 _fixNesting : function(s) {
10223 var d = [], i;
10224
10225 s = s.replace(/<(\/)?([^\s>]+)[^>]*?>/g, function(a, b, c) {
10226 var e;
10227
10228 // Handle end element
10229 if (b === '/') {
10230 if (!d.length)
10231 return '';
10232
10233 if (c !== d[d.length - 1].tag) {
10234 for (i=d.length - 1; i>=0; i--) {
10235 if (d[i].tag === c) {
10236 d[i].close = 1;
10237 break;
10238 }
10239 }
10240
10241 return '';
10242 } else {
10243 d.pop();
10244
10245 if (d.length && d[d.length - 1].close) {
10246 a = a + '</' + d[d.length - 1].tag + '>';
10247 d.pop();
10248 }
10249 }
10250 } else {
10251 // Ignore these
10252 if (/^(br|hr|input|meta|img|link|param)$/i.test(c))
10253 return a;
10254
10255 // Ignore closed ones
10256 if (/\/>$/.test(a))
10257 return a;
10258
10259 d.push({tag : c}); // Push start element
10260 }
10261
10262 return a;
10263 });
10264
10265 // End all open tags
10266 for (i=d.length - 1; i>=0; i--)
10267 s += '</' + d[i].tag + '>';
10268
10269 return s;
10270 }
10271
10272 });
10273 })(tinymce);
10274 (function(tinymce) {
10275 var each = tinymce.each, isIE = tinymce.isIE, isGecko = tinymce.isGecko, isOpera = tinymce.isOpera, isWebKit = tinymce.isWebKit;
10276
10277 tinymce.create('tinymce.EditorCommands', {
10278 EditorCommands : function(ed) {
10279 this.editor = ed;
10280 },
10281
10282 execCommand : function(cmd, ui, val) {
10283 var t = this, ed = t.editor, f;
10284
10285 switch (cmd) {
10286 // Ignore these
10287 case 'mceResetDesignMode':
10288 case 'mceBeginUndoLevel':
10289 return true;
10290
10291 // Ignore these
10292 case 'unlink':
10293 t.UnLink();
10294 return true;
10295
10296 // Bundle these together
10297 case 'JustifyLeft':
10298 case 'JustifyCenter':
10299 case 'JustifyRight':
10300 case 'JustifyFull':
10301 t.mceJustify(cmd, cmd.substring(7).toLowerCase());
10302 return true;
10303
10304 default:
10305 f = this[cmd];
10306
10307 if (f) {
10308 f.call(this, ui, val);
10309 return true;
10310 }
10311 }
10312
10313 return false;
10314 },
10315
10316 Indent : function() {
10317 var ed = this.editor, d = ed.dom, s = ed.selection, e, iv, iu;
10318
10319 // Setup indent level
10320 iv = ed.settings.indentation;
10321 iu = /[a-z%]+$/i.exec(iv);
10322 iv = parseInt(iv);
10323
10324 if (ed.settings.inline_styles && (!this.queryStateInsertUnorderedList() && !this.queryStateInsertOrderedList())) {
10325 each(s.getSelectedBlocks(), function(e) {
10326 d.setStyle(e, 'paddingLeft', (parseInt(e.style.paddingLeft || 0) + iv) + iu);
10327 });
10328
10329 return;
10330 }
10331
10332 ed.getDoc().execCommand('Indent', false, null);
10333
10334 if (isIE) {
10335 d.getParent(s.getNode(), function(n) {
10336 if (n.nodeName == 'BLOCKQUOTE') {
10337 n.dir = n.style.cssText = '';
10338 }
10339 });
10340 }
10341 },
10342
10343 Outdent : function() {
10344 var ed = this.editor, d = ed.dom, s = ed.selection, e, v, iv, iu;
10345
10346 // Setup indent level
10347 iv = ed.settings.indentation;
10348 iu = /[a-z%]+$/i.exec(iv);
10349 iv = parseInt(iv);
10350
10351 if (ed.settings.inline_styles && (!this.queryStateInsertUnorderedList() && !this.queryStateInsertOrderedList())) {
10352 each(s.getSelectedBlocks(), function(e) {
10353 v = Math.max(0, parseInt(e.style.paddingLeft || 0) - iv);
10354 d.setStyle(e, 'paddingLeft', v ? v + iu : '');
10355 });
10356
10357 return;
10358 }
10359
10360 ed.getDoc().execCommand('Outdent', false, null);
10361 },
10362
10363 /*
10364 mceSetAttribute : function(u, v) {
10365 var ed = this.editor, d = ed.dom, e;
10366
10367 if (e = d.getParent(ed.selection.getNode(), d.isBlock))
10368 d.setAttrib(e, v.name, v.value);
10369 },
10370 */
10371 mceSetContent : function(u, v) {
10372 this.editor.setContent(v);
10373 },
10374
10375 mceToggleVisualAid : function() {
10376 var ed = this.editor;
10377
10378 ed.hasVisual = !ed.hasVisual;
10379 ed.addVisual();
10380 },
10381
10382 mceReplaceContent : function(u, v) {
10383 var s = this.editor.selection;
10384
10385 s.setContent(v.replace(/\{\$selection\}/g, s.getContent({format : 'text'})));
10386 },
10387
10388 mceInsertLink : function(u, v) {
10389 var ed = this.editor, s = ed.selection, e = ed.dom.getParent(s.getNode(), 'A');
10390
10391 if (tinymce.is(v, 'string'))
10392 v = {href : v};
10393
10394 function set(e) {
10395 each(v, function(v, k) {
10396 ed.dom.setAttrib(e, k, v);
10397 });
10398 };
10399
10400 if (!e) {
10401 ed.execCommand('CreateLink', false, 'javascript:mctmp(0);');
10402 each(ed.dom.select('a[href=javascript:mctmp(0);]'), function(e) {
10403 set(e);
10404 });
10405 } else {
10406 if (v.href)
10407 set(e);
10408 else
10409 ed.dom.remove(e, 1);
10410 }
10411 },
10412
10413 UnLink : function() {
10414 var ed = this.editor, s = ed.selection;
10415
10416 if (s.isCollapsed())
10417 s.select(s.getNode());
10418
10419 ed.getDoc().execCommand('unlink', false, null);
10420 s.collapse(0);
10421 },
10422
10423 FontName : function(u, v) {
10424 var t = this, ed = t.editor, s = ed.selection, e;
10425
10426 if (!v) {
10427 if (s.isCollapsed())
10428 s.select(s.getNode());
10429 } else {
10430 if (ed.settings.convert_fonts_to_spans)
10431 t._applyInlineStyle('span', {style : {fontFamily : v}});
10432 else
10433 ed.getDoc().execCommand('FontName', false, v);
10434 }
10435 },
10436
10437 FontSize : function(u, v) {
10438 var ed = this.editor, s = ed.settings, fc, fs;
10439
10440 // Use style options instead
10441 if (s.convert_fonts_to_spans && v >= 1 && v <= 7) {
10442 fs = tinymce.explode(s.font_size_style_values);
10443 fc = tinymce.explode(s.font_size_classes);
10444
10445 if (fc)
10446 v = fc[v - 1] || v;
10447 else
10448 v = fs[v - 1] || v;
10449 }
10450
10451 if (v >= 1 && v <= 7)
10452 ed.getDoc().execCommand('FontSize', false, v);
10453 else
10454 this._applyInlineStyle('span', {style : {fontSize : v}});
10455 },
10456
10457 queryCommandValue : function(c) {
10458 var f = this['queryValue' + c];
10459
10460 if (f)
10461 return f.call(this, c);
10462
10463 return false;
10464 },
10465
10466 queryCommandState : function(cmd) {
10467 var f;
10468
10469 switch (cmd) {
10470 // Bundle these together
10471 case 'JustifyLeft':
10472 case 'JustifyCenter':
10473 case 'JustifyRight':
10474 case 'JustifyFull':
10475 return this.queryStateJustify(cmd, cmd.substring(7).toLowerCase());
10476
10477 default:
10478 if (f = this['queryState' + cmd])
10479 return f.call(this, cmd);
10480 }
10481
10482 return -1;
10483 },
10484
10485 _queryState : function(c) {
10486 try {
10487 return this.editor.getDoc().queryCommandState(c);
10488 } catch (ex) {
10489 // Ignore exception
10490 }
10491 },
10492
10493 _queryVal : function(c) {
10494 try {
10495 return this.editor.getDoc().queryCommandValue(c);
10496 } catch (ex) {
10497 // Ignore exception
10498 }
10499 },
10500
10501 queryValueFontSize : function() {
10502 var ed = this.editor, v = 0, p;
10503
10504 if (p = ed.dom.getParent(ed.selection.getNode(), 'SPAN'))
10505 v = p.style.fontSize;
10506
10507 if (!v && (isOpera || isWebKit)) {
10508 if (p = ed.dom.getParent(ed.selection.getNode(), 'FONT'))
10509 v = p.size;
10510
10511 return v;
10512 }
10513
10514 return v || this._queryVal('FontSize');
10515 },
10516
10517 queryValueFontName : function() {
10518 var ed = this.editor, v = 0, p;
10519
10520 if (p = ed.dom.getParent(ed.selection.getNode(), 'FONT'))
10521 v = p.face;
10522
10523 if (p = ed.dom.getParent(ed.selection.getNode(), 'SPAN'))
10524 v = p.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();
10525
10526 if (!v)
10527 v = this._queryVal('FontName');
10528
10529 return v;
10530 },
10531
10532 mceJustify : function(c, v) {
10533 var ed = this.editor, se = ed.selection, n = se.getNode(), nn = n.nodeName, bl, nb, dom = ed.dom, rm;
10534
10535 if (ed.settings.inline_styles && this.queryStateJustify(c, v))
10536 rm = 1;
10537
10538 bl = dom.getParent(n, ed.dom.isBlock);
10539
10540 if (nn == 'IMG') {
10541 if (v == 'full')
10542 return;
10543
10544 if (rm) {
10545 if (v == 'center')
10546 dom.setStyle(bl || n.parentNode, 'textAlign', '');
10547
10548 dom.setStyle(n, 'float', '');
10549 this.mceRepaint();
10550 return;
10551 }
10552
10553 if (v == 'center') {
10554 // Do not change table elements
10555 if (bl && /^(TD|TH)$/.test(bl.nodeName))
10556 bl = 0;
10557
10558 if (!bl || bl.childNodes.length > 1) {
10559 nb = dom.create('p');
10560 nb.appendChild(n.cloneNode(false));
10561
10562 if (bl)
10563 dom.insertAfter(nb, bl);
10564 else
10565 dom.insertAfter(nb, n);
10566
10567 dom.remove(n);
10568 n = nb.firstChild;
10569 bl = nb;
10570 }
10571
10572 dom.setStyle(bl, 'textAlign', v);
10573 dom.setStyle(n, 'float', '');
10574 } else {
10575 dom.setStyle(n, 'float', v);
10576 dom.setStyle(bl || n.parentNode, 'textAlign', '');
10577 }
10578
10579 this.mceRepaint();
10580 return;
10581 }
10582
10583 // Handle the alignment outselfs, less quirks in all browsers
10584 if (ed.settings.inline_styles && ed.settings.forced_root_block) {
10585 if (rm)
10586 v = '';
10587
10588 each(se.getSelectedBlocks(dom.getParent(se.getStart(), dom.isBlock), dom.getParent(se.getEnd(), dom.isBlock)), function(e) {
10589 dom.setAttrib(e, 'align', '');
10590 dom.setStyle(e, 'textAlign', v == 'full' ? 'justify' : v);
10591 });
10592
10593 return;
10594 } else if (!rm)
10595 ed.getDoc().execCommand(c, false, null);
10596
10597 if (ed.settings.inline_styles) {
10598 if (rm) {
10599 dom.getParent(ed.selection.getNode(), function(n) {
10600 if (n.style && n.style.textAlign)
10601 dom.setStyle(n, 'textAlign', '');
10602 });
10603
10604 return;
10605 }
10606
10607 each(dom.select('*'), function(n) {
10608 var v = n.align;
10609
10610 if (v) {
10611 if (v == 'full')
10612 v = 'justify';
10613
10614 dom.setStyle(n, 'textAlign', v);
10615 dom.setAttrib(n, 'align', '');
10616 }
10617 });
10618 }
10619 },
10620
10621 mceSetCSSClass : function(u, v) {
10622 this.mceSetStyleInfo(0, {command : 'setattrib', name : 'class', value : v});
10623 },
10624
10625 getSelectedElement : function() {
10626 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;
10627
10628 if (se.isCollapsed() || r.item)
10629 return se.getNode();
10630
10631 // Setup regexp
10632 re = ed.settings.merge_styles_invalid_parents;
10633 if (tinymce.is(re, 'string'))
10634 re = new RegExp(re, 'i');
10635
10636 if (isIE) {
10637 r1 = r.duplicate();
10638 r1.collapse(true);
10639 sc = r1.parentElement();
10640
10641 r2 = r.duplicate();
10642 r2.collapse(false);
10643 ec = r2.parentElement();
10644
10645 if (sc != ec) {
10646 r1.move('character', 1);
10647 sc = r1.parentElement();
10648 }
10649
10650 if (sc == ec) {
10651 r1 = r.duplicate();
10652 r1.moveToElementText(sc);
10653
10654 if (r1.compareEndPoints('StartToStart', r) == 0 && r1.compareEndPoints('EndToEnd', r) == 0)
10655 return re && re.test(sc.nodeName) ? null : sc;
10656 }
10657 } else {
10658 function getParent(n) {
10659 return dom.getParent(n, '*');
10660 };
10661
10662 sc = r.startContainer;
10663 ec = r.endContainer;
10664 so = r.startOffset;
10665 eo = r.endOffset;
10666
10667 if (!r.collapsed) {
10668 if (sc == ec) {
10669 if (so - eo < 2) {
10670 if (sc.hasChildNodes()) {
10671 sp = sc.childNodes[so];
10672 return re && re.test(sp.nodeName) ? null : sp;
10673 }
10674 }
10675 }
10676 }
10677
10678 if (sc.nodeType != 3 || ec.nodeType != 3)
10679 return null;
10680
10681 if (so == 0) {
10682 sp = getParent(sc);
10683
10684 if (sp && sp.firstChild != sc)
10685 sp = null;
10686 }
10687
10688 if (so == sc.nodeValue.length) {
10689 e = sc.nextSibling;
10690
10691 if (e && e.nodeType == 1)
10692 sp = sc.nextSibling;
10693 }
10694
10695 if (eo == 0) {
10696 e = ec.previousSibling;
10697
10698 if (e && e.nodeType == 1)
10699 ep = e;
10700 }
10701
10702 if (eo == ec.nodeValue.length) {
10703 ep = getParent(ec);
10704
10705 if (ep && ep.lastChild != ec)
10706 ep = null;
10707 }
10708
10709 // Same element
10710 if (sp == ep)
10711 return re && sp && re.test(sp.nodeName) ? null : sp;
10712 }
10713
10714 return null;
10715 },
10716
10717 mceSetStyleInfo : function(u, v) {
10718 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;
10719
10720 function set(n, e) {
10721 if (n.nodeType == 1) {
10722 switch (v.command) {
10723 case 'setattrib':
10724 return dom.setAttrib(n, v.name, v.value);
10725
10726 case 'setstyle':
10727 return dom.setStyle(n, v.name, v.value);
10728
10729 case 'removeformat':
10730 return dom.setAttrib(n, 'class', '');
10731 }
10732 }
10733 };
10734
10735 // Setup regexp
10736 re = ed.settings.merge_styles_invalid_parents;
10737 if (tinymce.is(re, 'string'))
10738 re = new RegExp(re, 'i');
10739
10740 // Set style info on selected element
10741 if ((e = t.getSelectedElement()) && !ed.settings.force_span_wrappers)
10742 set(e, 1);
10743 else {
10744 // Generate wrappers and set styles on them
10745 d.execCommand('FontName', false, '__');
10746 each(dom.select('span,font'), function(n) {
10747 var sp, e;
10748
10749 if (dom.getAttrib(n, 'face') == '__' || n.style.fontFamily === '__') {
10750 sp = dom.create(nn, {mce_new : '1'});
10751
10752 set(sp);
10753
10754 each (n.childNodes, function(n) {
10755 sp.appendChild(n.cloneNode(true));
10756 });
10757
10758 dom.replace(sp, n);
10759 }
10760 });
10761 }
10762
10763 // Remove wrappers inside new ones
10764 each(dom.select(nn).reverse(), function(n) {
10765 var p = n.parentNode;
10766
10767 // Check if it's an old span in a new wrapper
10768 if (!dom.getAttrib(n, 'mce_new')) {
10769 // Find new wrapper
10770 p = dom.getParent(n, '*[mce_new]');
10771
10772 if (p)
10773 dom.remove(n, 1);
10774 }
10775 });
10776
10777 // Merge wrappers with parent wrappers
10778 each(dom.select(nn).reverse(), function(n) {
10779 var p = n.parentNode;
10780
10781 if (!p || !dom.getAttrib(n, 'mce_new'))
10782 return;
10783
10784 if (ed.settings.force_span_wrappers && p.nodeName != 'SPAN')
10785 return;
10786
10787 // Has parent of the same type and only child
10788 if (p.nodeName == nn.toUpperCase() && p.childNodes.length == 1)
10789 return dom.remove(p, 1);
10790
10791 // Has parent that is more suitable to have the class and only child
10792 if (n.nodeType == 1 && (!re || !re.test(p.nodeName)) && p.childNodes.length == 1) {
10793 set(p); // Set style info on parent instead
10794 dom.setAttrib(n, 'class', '');
10795 }
10796 });
10797
10798 // Remove empty wrappers
10799 each(dom.select(nn).reverse(), function(n) {
10800 if (dom.getAttrib(n, 'mce_new') || (dom.getAttribs(n).length <= 1 && n.className === '')) {
10801 if (!dom.getAttrib(n, 'class') && !dom.getAttrib(n, 'style'))
10802 return dom.remove(n, 1);
10803
10804 dom.setAttrib(n, 'mce_new', ''); // Remove mce_new marker
10805 }
10806 });
10807
10808 s.moveToBookmark(b);
10809 },
10810
10811 queryStateJustify : function(c, v) {
10812 var ed = this.editor, n = ed.selection.getNode(), dom = ed.dom;
10813
10814 if (n && n.nodeName == 'IMG') {
10815 if (dom.getStyle(n, 'float') == v)
10816 return 1;
10817
10818 return n.parentNode.style.textAlign == v;
10819 }
10820
10821 n = dom.getParent(ed.selection.getStart(), function(n) {
10822 return n.nodeType == 1 && n.style.textAlign;
10823 });
10824
10825 if (v == 'full')
10826 v = 'justify';
10827
10828 if (ed.settings.inline_styles)
10829 return (n && n.style.textAlign == v);
10830
10831 return this._queryState(c);
10832 },
10833
10834 ForeColor : function(ui, v) {
10835 var ed = this.editor;
10836
10837 if (ed.settings.convert_fonts_to_spans) {
10838 this._applyInlineStyle('span', {style : {color : v}});
10839 return;
10840 } else
10841 ed.getDoc().execCommand('ForeColor', false, v);
10842 },
10843
10844 HiliteColor : function(ui, val) {
10845 var t = this, ed = t.editor, d = ed.getDoc();
10846
10847 if (ed.settings.convert_fonts_to_spans) {
10848 this._applyInlineStyle('span', {style : {backgroundColor : val}});
10849 return;
10850 }
10851
10852 function set(s) {
10853 if (!isGecko)
10854 return;
10855
10856 try {
10857 // Try new Gecko method
10858 d.execCommand("styleWithCSS", 0, s);
10859 } catch (ex) {
10860 // Use old
10861 d.execCommand("useCSS", 0, !s);
10862 }
10863 };
10864
10865 if (isGecko || isOpera) {
10866 set(true);
10867 d.execCommand('hilitecolor', false, val);
10868 set(false);
10869 } else
10870 d.execCommand('BackColor', false, val);
10871 },
10872
10873 FormatBlock : function(ui, val) {
10874 var t = this, ed = t.editor, s = ed.selection, dom = ed.dom, bl, nb, b;
10875
10876 function isBlock(n) {
10877 return /^(P|DIV|H[1-6]|ADDRESS|BLOCKQUOTE|PRE)$/.test(n.nodeName);
10878 };
10879
10880 bl = dom.getParent(s.getNode(), function(n) {
10881 return isBlock(n);
10882 });
10883
10884 // IE has an issue where it removes the parent div if you change format on the paragrah in <div><p>Content</p></div>
10885 // FF and Opera doesn't change parent DIV elements if you switch format
10886 if (bl) {
10887 if ((isIE && isBlock(bl.parentNode)) || bl.nodeName == 'DIV') {
10888 // Rename block element
10889 nb = ed.dom.create(val);
10890
10891 each(dom.getAttribs(bl), function(v) {
10892 dom.setAttrib(nb, v.nodeName, dom.getAttrib(bl, v.nodeName));
10893 });
10894
10895 b = s.getBookmark();
10896 dom.replace(nb, bl, 1);
10897 s.moveToBookmark(b);
10898 ed.nodeChanged();
10899 return;
10900 }
10901 }
10902
10903 val = ed.settings.forced_root_block ? (val || '<p>') : val;
10904
10905 if (val.indexOf('<') == -1)
10906 val = '<' + val + '>';
10907
10908 if (tinymce.isGecko)
10909 val = val.replace(/<(div|blockquote|code|dt|dd|dl|samp)>/gi, '$1');
10910
10911 ed.getDoc().execCommand('FormatBlock', false, val);
10912 },
10913
10914 mceCleanup : function() {
10915 var ed = this.editor, s = ed.selection, b = s.getBookmark();
10916 ed.setContent(ed.getContent());
10917 s.moveToBookmark(b);
10918 },
10919
10920 mceRemoveNode : function(ui, val) {
10921 var ed = this.editor, s = ed.selection, b, n = val || s.getNode();
10922
10923 // Make sure that the body node isn't removed
10924 if (n == ed.getBody())
10925 return;
10926
10927 b = s.getBookmark();
10928 ed.dom.remove(n, 1);
10929 s.moveToBookmark(b);
10930 ed.nodeChanged();
10931 },
10932
10933 mceSelectNodeDepth : function(ui, val) {
10934 var ed = this.editor, s = ed.selection, c = 0;
10935
10936 ed.dom.getParent(s.getNode(), function(n) {
10937 if (n.nodeType == 1 && c++ == val) {
10938 s.select(n);
10939 ed.nodeChanged();
10940 return false;
10941 }
10942 }, ed.getBody());
10943 },
10944
10945 mceSelectNode : function(u, v) {
10946 this.editor.selection.select(v);
10947 },
10948
10949 mceInsertContent : function(ui, val) {
10950 this.editor.selection.setContent(val);
10951 },
10952
10953 mceInsertRawHTML : function(ui, val) {
10954 var ed = this.editor;
10955
10956 ed.selection.setContent('tiny_mce_marker');
10957 ed.setContent(ed.getContent().replace(/tiny_mce_marker/g, val));
10958 },
10959
10960 mceRepaint : function() {
10961 var s, b, e = this.editor;
10962
10963 if (tinymce.isGecko) {
10964 try {
10965 s = e.selection;
10966 b = s.getBookmark(true);
10967
10968 if (s.getSel())
10969 s.getSel().selectAllChildren(e.getBody());
10970
10971 s.collapse(true);
10972 s.moveToBookmark(b);
10973 } catch (ex) {
10974 // Ignore
10975 }
10976 }
10977 },
10978
10979 queryStateUnderline : function() {
10980 var ed = this.editor, n = ed.selection.getNode();
10981
10982 if (n && n.nodeName == 'A')
10983 return false;
10984
10985 return this._queryState('Underline');
10986 },
10987
10988 queryStateOutdent : function() {
10989 var ed = this.editor, n;
10990
10991 if (ed.settings.inline_styles) {
10992 if ((n = ed.dom.getParent(ed.selection.getStart(), ed.dom.isBlock)) && parseInt(n.style.paddingLeft) > 0)
10993 return true;
10994
10995 if ((n = ed.dom.getParent(ed.selection.getEnd(), ed.dom.isBlock)) && parseInt(n.style.paddingLeft) > 0)
10996 return true;
10997 }
10998
10999 return this.queryStateInsertUnorderedList() || this.queryStateInsertOrderedList() || (!ed.settings.inline_styles && !!ed.dom.getParent(ed.selection.getNode(), 'BLOCKQUOTE'));
11000 },
11001
11002 queryStateInsertUnorderedList : function() {
11003 return this.editor.dom.getParent(this.editor.selection.getNode(), 'UL');
11004 },
11005
11006 queryStateInsertOrderedList : function() {
11007 return this.editor.dom.getParent(this.editor.selection.getNode(), 'OL');
11008 },
11009
11010 queryStatemceBlockQuote : function() {
11011 return !!this.editor.dom.getParent(this.editor.selection.getStart(), function(n) {return n.nodeName === 'BLOCKQUOTE';});
11012 },
11013
11014 _applyInlineStyle : function(na, at, op) {
11015 var t = this, ed = t.editor, dom = ed.dom, bm, lo = {}, kh, found;
11016
11017 na = na.toUpperCase();
11018
11019 if (op && op.check_classes && at['class'])
11020 op.check_classes.push(at['class']);
11021
11022 function removeEmpty() {
11023 each(dom.select(na).reverse(), function(n) {
11024 var c = 0;
11025
11026 // Check if there is any attributes
11027 each(dom.getAttribs(n), function(an) {
11028 if (an.nodeName.substring(0, 1) != '_' && dom.getAttrib(n, an.nodeName) != '') {
11029 //console.log(dom.getOuterHTML(n), dom.getAttrib(n, an.nodeName));
11030 c++;
11031 }
11032 });
11033
11034 // No attributes then remove the element and keep the children
11035 if (c == 0)
11036 dom.remove(n, 1);
11037 });
11038 };
11039
11040 function replaceFonts() {
11041 var bm;
11042
11043 each(dom.select('span,font'), function(n) {
11044 if (n.style.fontFamily == 'mceinline' || n.face == 'mceinline') {
11045 if (!bm)
11046 bm = ed.selection.getBookmark();
11047
11048 at._mce_new = '1';
11049 dom.replace(dom.create(na, at), n, 1);
11050 }
11051 });
11052
11053 // Remove redundant elements
11054 each(dom.select(na + '[_mce_new]'), function(n) {
11055 function removeStyle(n) {
11056 if (n.nodeType == 1) {
11057 each(at.style, function(v, k) {
11058 dom.setStyle(n, k, '');
11059 });
11060
11061 // Remove spans with the same class or marked classes
11062 if (at['class'] && n.className && op) {
11063 each(op.check_classes, function(c) {
11064 if (dom.hasClass(n, c))
11065 dom.removeClass(n, c);
11066 });
11067 }
11068 }
11069 };
11070
11071 // Remove specified style information from child elements
11072 each(dom.select(na, n), removeStyle);
11073
11074 // Remove the specified style information on parent if current node is only child (IE)
11075 if (n.parentNode && n.parentNode.nodeType == 1 && n.parentNode.childNodes.length == 1)
11076 removeStyle(n.parentNode);
11077
11078 // Remove the child elements style info if a parent already has it
11079 dom.getParent(n.parentNode, function(pn) {
11080 if (pn.nodeType == 1) {
11081 if (at.style) {
11082 each(at.style, function(v, k) {
11083 var sv;
11084
11085 if (!lo[k] && (sv = dom.getStyle(pn, k))) {
11086 if (sv === v)
11087 dom.setStyle(n, k, '');
11088
11089 lo[k] = 1;
11090 }
11091 });
11092 }
11093
11094 // Remove spans with the same class or marked classes
11095 if (at['class'] && pn.className && op) {
11096 each(op.check_classes, function(c) {
11097 if (dom.hasClass(pn, c))
11098 dom.removeClass(n, c);
11099 });
11100 }
11101 }
11102
11103 return false;
11104 });
11105
11106 n.removeAttribute('_mce_new');
11107 });
11108
11109 removeEmpty();
11110 ed.selection.moveToBookmark(bm);
11111
11112 return !!bm;
11113 };
11114
11115 // Create inline elements
11116 ed.focus();
11117 ed.getDoc().execCommand('FontName', false, 'mceinline');
11118 replaceFonts();
11119
11120 if (kh = t._applyInlineStyle.keyhandler) {
11121 ed.onKeyUp.remove(kh);
11122 ed.onKeyPress.remove(kh);
11123 ed.onKeyDown.remove(kh);
11124 ed.onSetContent.remove(t._applyInlineStyle.chandler);
11125 }
11126
11127 if (ed.selection.isCollapsed()) {
11128 // IE will format the current word so this code can't be executed on that browser
11129 if (!isIE) {
11130 each(dom.getParents(ed.selection.getNode(), 'span'), function(n) {
11131 each(at.style, function(v, k) {
11132 var kv;
11133
11134 if (kv = dom.getStyle(n, k)) {
11135 if (kv == v) {
11136 dom.setStyle(n, k, '');
11137 found = 2;
11138 return false;
11139 }
11140
11141 found = 1;
11142 return false;
11143 }
11144 });
11145
11146 if (found)
11147 return false;
11148 });
11149
11150 if (found == 2) {
11151 bm = ed.selection.getBookmark();
11152
11153 removeEmpty();
11154
11155 ed.selection.moveToBookmark(bm);
11156
11157 // Node change needs to be detached since the onselect event
11158 // for the select box will run the onclick handler after onselect call. Todo: Add a nicer fix!
11159 window.setTimeout(function() {
11160 ed.nodeChanged();
11161 }, 1);
11162
11163 return;
11164 }
11165 }
11166
11167 // Start collecting styles
11168 t._pendingStyles = tinymce.extend(t._pendingStyles || {}, at.style);
11169
11170 t._applyInlineStyle.chandler = ed.onSetContent.add(function() {
11171 delete t._pendingStyles;
11172 });
11173
11174 t._applyInlineStyle.keyhandler = kh = function(e) {
11175 // Use pending styles
11176 if (t._pendingStyles) {
11177 at.style = t._pendingStyles;
11178 delete t._pendingStyles;
11179 }
11180
11181 if (replaceFonts()) {
11182 ed.onKeyDown.remove(t._applyInlineStyle.keyhandler);
11183 ed.onKeyPress.remove(t._applyInlineStyle.keyhandler);
11184 }
11185
11186 if (e.type == 'keyup')
11187 ed.onKeyUp.remove(t._applyInlineStyle.keyhandler);
11188 };
11189
11190 ed.onKeyDown.add(kh);
11191 ed.onKeyPress.add(kh);
11192 ed.onKeyUp.add(kh);
11193 } else
11194 t._pendingStyles = 0;
11195 }
11196 });
11197 })(tinymce);(function(tinymce) {
11198 tinymce.create('tinymce.UndoManager', {
11199 index : 0,
11200 data : null,
11201 typing : 0,
11202
11203 UndoManager : function(ed) {
11204 var t = this, Dispatcher = tinymce.util.Dispatcher;
11205
11206 t.editor = ed;
11207 t.data = [];
11208 t.onAdd = new Dispatcher(this);
11209 t.onUndo = new Dispatcher(this);
11210 t.onRedo = new Dispatcher(this);
11211 },
11212
11213 add : function(l) {
11214 var t = this, i, ed = t.editor, b, s = ed.settings, la;
11215
11216 l = l || {};
11217 l.content = l.content || ed.getContent({format : 'raw', no_events : 1});
11218
11219 // Add undo level if needed
11220 l.content = l.content.replace(/^\s*|\s*$/g, '');
11221 la = t.data[t.index > 0 && (t.index == 0 || t.index == t.data.length) ? t.index - 1 : t.index];
11222 if (!l.initial && la && l.content == la.content)
11223 return null;
11224
11225 // Time to compress
11226 if (s.custom_undo_redo_levels) {
11227 if (t.data.length > s.custom_undo_redo_levels) {
11228 for (i = 0; i < t.data.length - 1; i++)
11229 t.data[i] = t.data[i + 1];
11230
11231 t.data.length--;
11232 t.index = t.data.length;
11233 }
11234 }
11235
11236 if (s.custom_undo_redo_restore_selection && !l.initial)
11237 l.bookmark = b = l.bookmark || ed.selection.getBookmark();
11238
11239 if (t.index < t.data.length)
11240 t.index++;
11241
11242 // Only initial marked undo levels should be allowed as first item
11243 // This to workaround a bug with Firefox and the blur event
11244 if (t.data.length === 0 && !l.initial)
11245 return null;
11246
11247 // Add level
11248 t.data.length = t.index + 1;
11249 t.data[t.index++] = l;
11250
11251 if (l.initial)
11252 t.index = 0;
11253
11254 // Set initial bookmark use first real undo level
11255 if (t.data.length == 2 && t.data[0].initial)
11256 t.data[0].bookmark = b;
11257
11258 t.onAdd.dispatch(t, l);
11259 ed.isNotDirty = 0;
11260
11261 //console.dir(t.data);
11262
11263 return l;
11264 },
11265
11266 undo : function() {
11267 var t = this, ed = t.editor, l = l, i;
11268
11269 if (t.typing) {
11270 t.add();
11271 t.typing = 0;
11272 }
11273
11274 if (t.index > 0) {
11275 // If undo on last index then take snapshot
11276 if (t.index == t.data.length && t.index > 1) {
11277 i = t.index;
11278 t.typing = 0;
11279
11280 if (!t.add())
11281 t.index = i;
11282
11283 --t.index;
11284 }
11285
11286 l = t.data[--t.index];
11287 ed.setContent(l.content, {format : 'raw'});
11288 ed.selection.moveToBookmark(l.bookmark);
11289
11290 t.onUndo.dispatch(t, l);
11291 }
11292
11293 return l;
11294 },
11295
11296 redo : function() {
11297 var t = this, ed = t.editor, l = null;
11298
11299 if (t.index < t.data.length - 1) {
11300 l = t.data[++t.index];
11301 ed.setContent(l.content, {format : 'raw'});
11302 ed.selection.moveToBookmark(l.bookmark);
11303
11304 t.onRedo.dispatch(t, l);
11305 }
11306
11307 return l;
11308 },
11309
11310 clear : function() {
11311 var t = this;
11312
11313 t.data = [];
11314 t.index = 0;
11315 t.typing = 0;
11316 t.add({initial : true});
11317 },
11318
11319 hasUndo : function() {
11320 return this.index != 0 || this.typing;
11321 },
11322
11323 hasRedo : function() {
11324 return this.index < this.data.length - 1;
11325 }
11326
11327 });
11328 })(tinymce);
11329 (function(tinymce) {
11330 // Shorten names
11331 var Event, isIE, isGecko, isOpera, each, extend;
11332
11333 Event = tinymce.dom.Event;
11334 isIE = tinymce.isIE;
11335 isGecko = tinymce.isGecko;
11336 isOpera = tinymce.isOpera;
11337 each = tinymce.each;
11338 extend = tinymce.extend;
11339
11340 tinymce.create('tinymce.ForceBlocks', {
11341 ForceBlocks : function(ed) {
11342 var t = this, s = ed.settings, elm;
11343
11344 t.editor = ed;
11345 t.dom = ed.dom;
11346 elm = (s.forced_root_block || 'p').toLowerCase();
11347 s.element = elm.toUpperCase();
11348
11349 ed.onPreInit.add(t.setup, t);
11350
11351 t.reOpera = new RegExp('(\\u00a0|&#160;|&nbsp;)<\/' + elm + '>', 'gi');
11352 t.rePadd = new RegExp('<p( )([^>]+)><\\\/p>|<p( )([^>]+)\\\/>|<p( )([^>]+)>\\s+<\\\/p>|<p><\\\/p>|<p\\\/>|<p>\\s+<\\\/p>'.replace(/p/g, elm), 'gi');
11353 t.reNbsp2BR1 = new RegExp('<p( )([^>]+)>[\\s\\u00a0]+<\\\/p>|<p>[\\s\\u00a0]+<\\\/p>'.replace(/p/g, elm), 'gi');
11354 t.reNbsp2BR2 = new RegExp('<%p()([^>]+)>(&nbsp;|&#160;)<\\\/%p>|<%p>(&nbsp;|&#160;)<\\\/%p>'.replace(/%p/g, elm), 'gi');
11355 t.reBR2Nbsp = new RegExp('<p( )([^>]+)>\\s*<br \\\/>\\s*<\\\/p>|<p>\\s*<br \\\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi');
11356 t.reTrailBr = new RegExp('\\s*<br \\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi');
11357
11358 function padd(ed, o) {
11359 if (isOpera)
11360 o.content = o.content.replace(t.reOpera, '</' + elm + '>');
11361
11362 o.content = o.content.replace(t.rePadd, '<' + elm + '$1$2$3$4$5$6>\u00a0</' + elm + '>');
11363
11364 if (!isIE && !isOpera && o.set) {
11365 // Use &nbsp; instead of BR in padded paragraphs
11366 o.content = o.content.replace(t.reNbsp2BR1, '<' + elm + '$1$2><br /></' + elm + '>');
11367 o.content = o.content.replace(t.reNbsp2BR2, '<' + elm + '$1$2><br /></' + elm + '>');
11368 } else {
11369 o.content = o.content.replace(t.reBR2Nbsp, '<' + elm + '$1$2>\u00a0</' + elm + '>');
11370 o.content = o.content.replace(t.reTrailBr, '</' + elm + '>');
11371 }
11372 };
11373
11374 ed.onBeforeSetContent.add(padd);
11375 ed.onPostProcess.add(padd);
11376
11377 if (s.forced_root_block) {
11378 ed.onInit.add(t.forceRoots, t);
11379 ed.onSetContent.add(t.forceRoots, t);
11380 ed.onBeforeGetContent.add(t.forceRoots, t);
11381 }
11382 },
11383
11384 setup : function() {
11385 var t = this, ed = t.editor, s = ed.settings;
11386
11387 // Force root blocks when typing and when getting output
11388 if (s.forced_root_block) {
11389 ed.onKeyUp.add(t.forceRoots, t);
11390 ed.onPreProcess.add(t.forceRoots, t);
11391 }
11392
11393 if (s.force_br_newlines) {
11394 // Force IE to produce BRs on enter
11395 if (isIE) {
11396 ed.onKeyPress.add(function(ed, e) {
11397 var n, s = ed.selection;
11398
11399 if (e.keyCode == 13 && s.getNode().nodeName != 'LI') {
11400 s.setContent('<br id="__" /> ', {format : 'raw'});
11401 n = ed.dom.get('__');
11402 n.removeAttribute('id');
11403 s.select(n);
11404 s.collapse();
11405 return Event.cancel(e);
11406 }
11407 });
11408 }
11409
11410 return;
11411 }
11412
11413 if (!isIE && s.force_p_newlines) {
11414 /* ed.onPreProcess.add(function(ed, o) {
11415 each(ed.dom.select('br', o.node), function(n) {
11416 var p = n.parentNode;
11417
11418 // Replace <p><br /></p> with <p>&nbsp;</p>
11419 if (p && p.nodeName == 'p' && (p.childNodes.length == 1 || p.lastChild == n)) {
11420 p.replaceChild(ed.getDoc().createTextNode('\u00a0'), n);
11421 }
11422 });
11423 });*/
11424
11425 ed.onKeyPress.add(function(ed, e) {
11426 if (e.keyCode == 13 && !e.shiftKey) {
11427 if (!t.insertPara(e))
11428 Event.cancel(e);
11429 }
11430 });
11431
11432 if (isGecko) {
11433 ed.onKeyDown.add(function(ed, e) {
11434 if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey)
11435 t.backspaceDelete(e, e.keyCode == 8);
11436 });
11437 }
11438 }
11439
11440 function ren(rn, na) {
11441 var ne = ed.dom.create(na);
11442
11443 each(rn.attributes, function(a) {
11444 if (a.specified && a.nodeValue)
11445 ne.setAttribute(a.nodeName.toLowerCase(), a.nodeValue);
11446 });
11447
11448 each(rn.childNodes, function(n) {
11449 ne.appendChild(n.cloneNode(true));
11450 });
11451
11452 rn.parentNode.replaceChild(ne, rn);
11453
11454 return ne;
11455 };
11456
11457 // Replaces IE:s auto generated paragraphs with the specified element name
11458 if (isIE && s.element != 'P') {
11459 ed.onKeyPress.add(function(ed, e) {
11460 t.lastElm = ed.selection.getNode().nodeName;
11461 });
11462
11463 ed.onKeyUp.add(function(ed, e) {
11464 var bl, sel = ed.selection, n = sel.getNode(), b = ed.getBody();
11465
11466 if (b.childNodes.length === 1 && n.nodeName == 'P') {
11467 n = ren(n, s.element);
11468 sel.select(n);
11469 sel.collapse();
11470 ed.nodeChanged();
11471 } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') {
11472 bl = ed.dom.getParent(n, 'P');
11473
11474 if (bl) {
11475 ren(bl, s.element);
11476 ed.nodeChanged();
11477 }
11478 }
11479 });
11480 }
11481 },
11482
11483 find : function(n, t, s) {
11484 var ed = this.editor, w = ed.getDoc().createTreeWalker(n, 4, null, false), c = -1;
11485
11486 while (n = w.nextNode()) {
11487 c++;
11488
11489 // Index by node
11490 if (t == 0 && n == s)
11491 return c;
11492
11493 // Node by index
11494 if (t == 1 && c == s)
11495 return n;
11496 }
11497
11498 return -1;
11499 },
11500
11501 forceRoots : function(ed, e) {
11502 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;
11503 var nx, bl, bp, sp, le, nl = b.childNodes, i, n, eid;
11504
11505 // Fix for bug #1863847
11506 //if (e && e.keyCode == 13)
11507 // return true;
11508
11509 // Wrap non blocks into blocks
11510 for (i = nl.length - 1; i >= 0; i--) {
11511 nx = nl[i];
11512
11513 // Is text or non block element
11514 if (nx.nodeType == 3 || (!t.dom.isBlock(nx) && nx.nodeType != 8)) {
11515 if (!bl) {
11516 // Create new block but ignore whitespace
11517 if (nx.nodeType != 3 || /[^\s]/g.test(nx.nodeValue)) {
11518 // Store selection
11519 if (si == -2 && r) {
11520 if (!isIE) {
11521 // If selection is element then mark it
11522 if (r.startContainer.nodeType == 1 && (n = r.startContainer.childNodes[r.startOffset]) && n.nodeType == 1) {
11523 // Save the id of the selected element
11524 eid = n.getAttribute("id");
11525 n.setAttribute("id", "__mce");
11526 } else {
11527 // If element is inside body, might not be the case in contentEdiable mode
11528 if (ed.dom.getParent(r.startContainer, function(e) {return e === b;})) {
11529 so = r.startOffset;
11530 eo = r.endOffset;
11531 si = t.find(b, 0, r.startContainer);
11532 ei = t.find(b, 0, r.endContainer);
11533 }
11534 }
11535 } else {
11536 tr = d.body.createTextRange();
11537 tr.moveToElementText(b);
11538 tr.collapse(1);
11539 bp = tr.move('character', c) * -1;
11540
11541 tr = r.duplicate();
11542 tr.collapse(1);
11543 sp = tr.move('character', c) * -1;
11544
11545 tr = r.duplicate();
11546 tr.collapse(0);
11547 le = (tr.move('character', c) * -1) - sp;
11548
11549 si = sp - bp;
11550 ei = le;
11551 }
11552 }
11553
11554 bl = ed.dom.create(ed.settings.forced_root_block);
11555 bl.appendChild(nx.cloneNode(1));
11556 nx.parentNode.replaceChild(bl, nx);
11557 }
11558 } else {
11559 if (bl.hasChildNodes())
11560 bl.insertBefore(nx, bl.firstChild);
11561 else
11562 bl.appendChild(nx);
11563 }
11564 } else
11565 bl = null; // Time to create new block
11566 }
11567
11568 // Restore selection
11569 if (si != -2) {
11570 if (!isIE) {
11571 bl = b.getElementsByTagName(ed.settings.element)[0];
11572 r = d.createRange();
11573
11574 // Select last location or generated block
11575 if (si != -1)
11576 r.setStart(t.find(b, 1, si), so);
11577 else
11578 r.setStart(bl, 0);
11579
11580 // Select last location or generated block
11581 if (ei != -1)
11582 r.setEnd(t.find(b, 1, ei), eo);
11583 else
11584 r.setEnd(bl, 0);
11585
11586 if (s) {
11587 s.removeAllRanges();
11588 s.addRange(r);
11589 }
11590 } else {
11591 try {
11592 r = s.createRange();
11593 r.moveToElementText(b);
11594 r.collapse(1);
11595 r.moveStart('character', si);
11596 r.moveEnd('character', ei);
11597 r.select();
11598 } catch (ex) {
11599 // Ignore
11600 }
11601 }
11602 } else if (!isIE && (n = ed.dom.get('__mce'))) {
11603 // Restore the id of the selected element
11604 if (eid)
11605 n.setAttribute('id', eid);
11606 else
11607 n.removeAttribute('id');
11608
11609 // Move caret before selected element
11610 r = d.createRange();
11611 r.setStartBefore(n);
11612 r.setEndBefore(n);
11613 se.setRng(r);
11614 }
11615 },
11616
11617 getParentBlock : function(n) {
11618 var d = this.dom;
11619
11620 return d.getParent(n, d.isBlock);
11621 },
11622
11623 insertPara : function(e) {
11624 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;
11625 var rb, ra, dir, sn, so, en, eo, sb, eb, bn, bef, aft, sc, ec, n, vp = dom.getViewPort(ed.getWin()), y, ch, car;
11626
11627 function isEmpty(n) {
11628 n = n.innerHTML;
11629 n = n.replace(/<(img|hr|table)/gi, '-'); // Keep these convert them to - chars
11630 n = n.replace(/<[^>]+>/g, ''); // Remove all tags
11631
11632 return n.replace(/[ \t\r\n]+/g, '') == '';
11633 };
11634
11635 // If root blocks are forced then use Operas default behavior since it's really good
11636 // Removed due to bug: #1853816
11637 // if (se.forced_root_block && isOpera)
11638 // return true;
11639
11640 // Setup before range
11641 rb = d.createRange();
11642
11643 // If is before the first block element and in body, then move it into first block element
11644 rb.setStart(s.anchorNode, s.anchorOffset);
11645 rb.collapse(true);
11646
11647 // Setup after range
11648 ra = d.createRange();
11649
11650 // If is before the first block element and in body, then move it into first block element
11651 ra.setStart(s.focusNode, s.focusOffset);
11652 ra.collapse(true);
11653
11654 // Setup start/end points
11655 dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0;
11656 sn = dir ? s.anchorNode : s.focusNode;
11657 so = dir ? s.anchorOffset : s.focusOffset;
11658 en = dir ? s.focusNode : s.anchorNode;
11659 eo = dir ? s.focusOffset : s.anchorOffset;
11660
11661 // If selection is in empty table cell
11662 if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) {
11663 dom.remove(sn.firstChild); // Remove BR
11664
11665 // Create two new block elements
11666 ed.dom.add(sn, se.element, null, '<br />');
11667 aft = ed.dom.add(sn, se.element, null, '<br />');
11668
11669 // Move caret into the last one
11670 r = d.createRange();
11671 r.selectNodeContents(aft);
11672 r.collapse(1);
11673 ed.selection.setRng(r);
11674
11675 return false;
11676 }
11677
11678 // If the caret is in an invalid location in FF we need to move it into the first block
11679 if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) {
11680 sn = en = sn.firstChild;
11681 so = eo = 0;
11682 rb = d.createRange();
11683 rb.setStart(sn, 0);
11684 ra = d.createRange();
11685 ra.setStart(en, 0);
11686 }
11687
11688 // Never use body as start or end node
11689 sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes
11690 sn = sn.nodeName == "BODY" ? sn.firstChild : sn;
11691 en = en.nodeName == "HTML" ? d.body : en; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes
11692 en = en.nodeName == "BODY" ? en.firstChild : en;
11693
11694 // Get start and end blocks
11695 sb = t.getParentBlock(sn);
11696 eb = t.getParentBlock(en);
11697 bn = sb ? sb.nodeName : se.element; // Get block name to create
11698
11699 // Return inside list use default browser behavior
11700 if (t.dom.getParent(sb, 'OL,UL,PRE'))
11701 return true;
11702
11703 // If caption or absolute layers then always generate new blocks within
11704 if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {
11705 bn = se.element;
11706 sb = null;
11707 }
11708
11709 // If caption or absolute layers then always generate new blocks within
11710 if (eb && (eb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {
11711 bn = se.element;
11712 eb = null;
11713 }
11714
11715 // Use P instead
11716 if (/(TD|TABLE|TH|CAPTION)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(dom.getStyle(sb, 'float', 1)))) {
11717 bn = se.element;
11718 sb = eb = null;
11719 }
11720
11721 // Setup new before and after blocks
11722 bef = (sb && sb.nodeName == bn) ? sb.cloneNode(0) : ed.dom.create(bn);
11723 aft = (eb && eb.nodeName == bn) ? eb.cloneNode(0) : ed.dom.create(bn);
11724
11725 // Remove id from after clone
11726 aft.removeAttribute('id');
11727
11728 // Is header and cursor is at the end, then force paragraph under
11729 if (/^(H[1-6])$/.test(bn) && sn.nodeValue && so == sn.nodeValue.length)
11730 aft = ed.dom.create(se.element);
11731
11732 // Find start chop node
11733 n = sc = sn;
11734 do {
11735 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))
11736 break;
11737
11738 sc = n;
11739 } while ((n = n.previousSibling ? n.previousSibling : n.parentNode));
11740
11741 // Find end chop node
11742 n = ec = en;
11743 do {
11744 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))
11745 break;
11746
11747 ec = n;
11748 } while ((n = n.nextSibling ? n.nextSibling : n.parentNode));
11749
11750 // Place first chop part into before block element
11751 if (sc.nodeName == bn)
11752 rb.setStart(sc, 0);
11753 else
11754 rb.setStartBefore(sc);
11755
11756 rb.setEnd(sn, so);
11757 bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari
11758
11759 // Place secnd chop part within new block element
11760 try {
11761 ra.setEndAfter(ec);
11762 } catch(ex) {
11763 //console.debug(s.focusNode, s.focusOffset);
11764 }
11765
11766 ra.setStart(en, eo);
11767 aft.appendChild(ra.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari
11768
11769 // Create range around everything
11770 r = d.createRange();
11771 if (!sc.previousSibling && sc.parentNode.nodeName == bn) {
11772 r.setStartBefore(sc.parentNode);
11773 } else {
11774 if (rb.startContainer.nodeName == bn && rb.startOffset == 0)
11775 r.setStartBefore(rb.startContainer);
11776 else
11777 r.setStart(rb.startContainer, rb.startOffset);
11778 }
11779
11780 if (!ec.nextSibling && ec.parentNode.nodeName == bn)
11781 r.setEndAfter(ec.parentNode);
11782 else
11783 r.setEnd(ra.endContainer, ra.endOffset);
11784
11785 // Delete and replace it with new block elements
11786 r.deleteContents();
11787
11788 if (isOpera)
11789 ed.getWin().scrollTo(0, vp.y);
11790
11791 // Never wrap blocks in blocks
11792 if (bef.firstChild && bef.firstChild.nodeName == bn)
11793 bef.innerHTML = bef.firstChild.innerHTML;
11794
11795 if (aft.firstChild && aft.firstChild.nodeName == bn)
11796 aft.innerHTML = aft.firstChild.innerHTML;
11797
11798 // Padd empty blocks
11799 if (isEmpty(bef))
11800 bef.innerHTML = '<br />';
11801
11802 function appendStyles(e, en) {
11803 var nl = [], nn, n, i;
11804
11805 e.innerHTML = '';
11806
11807 // Make clones of style elements
11808 if (se.keep_styles) {
11809 n = en;
11810 do {
11811 // We only want style specific elements
11812 if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) {
11813 nn = n.cloneNode(false);
11814 dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique
11815 nl.push(nn);
11816 }
11817 } while (n = n.parentNode);
11818 }
11819
11820 // Append style elements to aft
11821 if (nl.length > 0) {
11822 for (i = nl.length - 1, nn = e; i >= 0; i--)
11823 nn = nn.appendChild(nl[i]);
11824
11825 // Padd most inner style element
11826 nl[0].innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there
11827 return nl[0]; // Move caret to most inner element
11828 } else
11829 e.innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there
11830 };
11831
11832 // Fill empty afterblook with current style
11833 if (isEmpty(aft))
11834 car = appendStyles(aft, en);
11835
11836 // Opera needs this one backwards for older versions
11837 if (isOpera && parseFloat(opera.version()) < 9.5) {
11838 r.insertNode(bef);
11839 r.insertNode(aft);
11840 } else {
11841 r.insertNode(aft);
11842 r.insertNode(bef);
11843 }
11844
11845 // Normalize
11846 aft.normalize();
11847 bef.normalize();
11848
11849 function first(n) {
11850 return d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false).nextNode() || n;
11851 };
11852
11853 // Move cursor and scroll into view
11854 r = d.createRange();
11855 r.selectNodeContents(isGecko ? first(car || aft) : car || aft);
11856 r.collapse(1);
11857 s.removeAllRanges();
11858 s.addRange(r);
11859
11860 // 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
11861 y = ed.dom.getPos(aft).y;
11862 ch = aft.clientHeight;
11863
11864 // Is element within viewport
11865 if (y < vp.y || y + ch > vp.y + vp.h) {
11866 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
11867 //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));
11868 }
11869
11870 return false;
11871 },
11872
11873 backspaceDelete : function(e, bs) {
11874 var t = this, ed = t.editor, b = ed.getBody(), n, se = ed.selection, r = se.getRng(), sc = r.startContainer, n, w, tn;
11875
11876 // The caret sometimes gets stuck in Gecko if you delete empty paragraphs
11877 // This workaround removes the element by hand and moves the caret to the previous element
11878 if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) {
11879 if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) {
11880 // Find previous block element
11881 n = sc;
11882 while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ;
11883
11884 if (n) {
11885 if (sc != b.firstChild) {
11886 // Find last text node
11887 w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
11888 while (tn = w.nextNode())
11889 n = tn;
11890
11891 // Place caret at the end of last text node
11892 r = ed.getDoc().createRange();
11893 r.setStart(n, n.nodeValue ? n.nodeValue.length : 0);
11894 r.setEnd(n, n.nodeValue ? n.nodeValue.length : 0);
11895 se.setRng(r);
11896
11897 // Remove the target container
11898 ed.dom.remove(sc);
11899 }
11900
11901 return Event.cancel(e);
11902 }
11903 }
11904 }
11905
11906 // Gecko generates BR elements here and there, we don't like those so lets remove them
11907 function handler(e) {
11908 var pr;
11909
11910 e = e.target;
11911
11912 // A new BR was created in a block element, remove it
11913 if (e && e.parentNode && e.nodeName == 'BR' && (n = t.getParentBlock(e))) {
11914 pr = e.previousSibling;
11915
11916 Event.remove(b, 'DOMNodeInserted', handler);
11917
11918 // Is there whitespace at the end of the node before then we might need the pesky BR
11919 // to place the caret at a correct location see bug: #2013943
11920 if (pr && pr.nodeType == 3 && /\s+$/.test(pr.nodeValue))
11921 return;
11922
11923 // Only remove BR elements that got inserted in the middle of the text
11924 if (e.previousSibling || e.nextSibling)
11925 ed.dom.remove(e);
11926 }
11927 };
11928
11929 // Listen for new nodes
11930 Event._add(b, 'DOMNodeInserted', handler);
11931
11932 // Remove listener
11933 window.setTimeout(function() {
11934 Event._remove(b, 'DOMNodeInserted', handler);
11935 }, 1);
11936 }
11937 });
11938 })(tinymce);
11939 (function(tinymce) {
11940 // Shorten names
11941 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;
11942
11943 tinymce.create('tinymce.ControlManager', {
11944 ControlManager : function(ed, s) {
11945 var t = this, i;
11946
11947 s = s || {};
11948 t.editor = ed;
11949 t.controls = {};
11950 t.onAdd = new tinymce.util.Dispatcher(t);
11951 t.onPostRender = new tinymce.util.Dispatcher(t);
11952 t.prefix = s.prefix || ed.id + '_';
11953 t._cls = {};
11954
11955 t.onPostRender.add(function() {
11956 each(t.controls, function(c) {
11957 c.postRender();
11958 });
11959 });
11960 },
11961
11962 get : function(id) {
11963 return this.controls[this.prefix + id] || this.controls[id];
11964 },
11965
11966 setActive : function(id, s) {
11967 var c = null;
11968
11969 if (c = this.get(id))
11970 c.setActive(s);
11971
11972 return c;
11973 },
11974
11975 setDisabled : function(id, s) {
11976 var c = null;
11977
11978 if (c = this.get(id))
11979 c.setDisabled(s);
11980
11981 return c;
11982 },
11983
11984 add : function(c) {
11985 var t = this;
11986
11987 if (c) {
11988 t.controls[c.id] = c;
11989 t.onAdd.dispatch(c, t);
11990 }
11991
11992 return c;
11993 },
11994
11995 createControl : function(n) {
11996 var c, t = this, ed = t.editor;
11997
11998 each(ed.plugins, function(p) {
11999 if (p.createControl) {
12000 c = p.createControl(n, t);
12001
12002 if (c)
12003 return false;
12004 }
12005 });
12006
12007 switch (n) {
12008 case "|":
12009 case "separator":
12010 return t.createSeparator();
12011 }
12012
12013 if (!c && ed.buttons && (c = ed.buttons[n]))
12014 return t.createButton(n, c);
12015
12016 return t.add(c);
12017 },
12018
12019 createDropMenu : function(id, s, cc) {
12020 var t = this, ed = t.editor, c, bm, v, cls;
12021
12022 s = extend({
12023 'class' : 'mceDropDown',
12024 constrain : ed.settings.constrain_menus
12025 }, s);
12026
12027 s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin';
12028 if (v = ed.getParam('skin_variant'))
12029 s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);
12030
12031 id = t.prefix + id;
12032 cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;
12033 c = t.controls[id] = new cls(id, s);
12034 c.onAddItem.add(function(c, o) {
12035 var s = o.settings;
12036
12037 s.title = ed.getLang(s.title, s.title);
12038
12039 if (!s.onclick) {
12040 s.onclick = function(v) {
12041 ed.execCommand(s.cmd, s.ui || false, s.value);
12042 };
12043 }
12044 });
12045
12046 ed.onRemove.add(function() {
12047 c.destroy();
12048 });
12049
12050 // Fix for bug #1897785, #1898007
12051 if (tinymce.isIE) {
12052 c.onShowMenu.add(function() {
12053 bm = ed.selection.getBookmark(1);
12054 });
12055
12056 c.onHideMenu.add(function() {
12057 if (bm) {
12058 ed.selection.moveToBookmark(bm);
12059 bm = 0;
12060 }
12061 });
12062 }
12063
12064 return t.add(c);
12065 },
12066
12067 createListBox : function(id, s, cc) {
12068 var t = this, ed = t.editor, cmd, c, cls;
12069
12070 if (t.get(id))
12071 return null;
12072
12073 s.title = ed.translate(s.title);
12074 s.scope = s.scope || ed;
12075
12076 if (!s.onselect) {
12077 s.onselect = function(v) {
12078 ed.execCommand(s.cmd, s.ui || false, v || s.value);
12079 };
12080 }
12081
12082 s = extend({
12083 title : s.title,
12084 'class' : 'mce_' + id,
12085 scope : s.scope,
12086 control_manager : t
12087 }, s);
12088
12089 id = t.prefix + id;
12090
12091 if (ed.settings.use_native_selects)
12092 c = new tinymce.ui.NativeListBox(id, s);
12093 else {
12094 cls = cc || t._cls.listbox || tinymce.ui.ListBox;
12095 c = new cls(id, s);
12096 }
12097
12098 t.controls[id] = c;
12099
12100 // Fix focus problem in Safari
12101 if (tinymce.isWebKit) {
12102 c.onPostRender.add(function(c, n) {
12103 // Store bookmark on mousedown
12104 Event.add(n, 'mousedown', function() {
12105 ed.bookmark = ed.selection.getBookmark('simple');
12106 });
12107
12108 // Restore on focus, since it might be lost
12109 Event.add(n, 'focus', function() {
12110 ed.selection.moveToBookmark(ed.bookmark);
12111 ed.bookmark = null;
12112 });
12113 });
12114 }
12115
12116 if (c.hideMenu)
12117 ed.onMouseDown.add(c.hideMenu, c);
12118
12119 return t.add(c);
12120 },
12121
12122 createButton : function(id, s, cc) {
12123 var t = this, ed = t.editor, o, c, cls;
12124
12125 if (t.get(id))
12126 return null;
12127
12128 s.title = ed.translate(s.title);
12129 s.label = ed.translate(s.label);
12130 s.scope = s.scope || ed;
12131
12132 if (!s.onclick && !s.menu_button) {
12133 s.onclick = function() {
12134 ed.execCommand(s.cmd, s.ui || false, s.value);
12135 };
12136 }
12137
12138 s = extend({
12139 title : s.title,
12140 'class' : 'mce_' + id,
12141 unavailable_prefix : ed.getLang('unavailable', ''),
12142 scope : s.scope,
12143 control_manager : t
12144 }, s);
12145
12146 id = t.prefix + id;
12147
12148 if (s.menu_button) {
12149 cls = cc || t._cls.menubutton || tinymce.ui.MenuButton;
12150 c = new cls(id, s);
12151 ed.onMouseDown.add(c.hideMenu, c);
12152 } else {
12153 cls = t._cls.button || tinymce.ui.Button;
12154 c = new cls(id, s);
12155 }
12156
12157 return t.add(c);
12158 },
12159
12160 createMenuButton : function(id, s, cc) {
12161 s = s || {};
12162 s.menu_button = 1;
12163
12164 return this.createButton(id, s, cc);
12165 },
12166
12167 createSplitButton : function(id, s, cc) {
12168 var t = this, ed = t.editor, cmd, c, cls;
12169
12170 if (t.get(id))
12171 return null;
12172
12173 s.title = ed.translate(s.title);
12174 s.scope = s.scope || ed;
12175
12176 if (!s.onclick) {
12177 s.onclick = function(v) {
12178 ed.execCommand(s.cmd, s.ui || false, v || s.value);
12179 };
12180 }
12181
12182 if (!s.onselect) {
12183 s.onselect = function(v) {
12184 ed.execCommand(s.cmd, s.ui || false, v || s.value);
12185 };
12186 }
12187
12188 s = extend({
12189 title : s.title,
12190 'class' : 'mce_' + id,
12191 scope : s.scope,
12192 control_manager : t
12193 }, s);
12194
12195 id = t.prefix + id;
12196 cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton;
12197 c = t.add(new cls(id, s));
12198 ed.onMouseDown.add(c.hideMenu, c);
12199
12200 return c;
12201 },
12202
12203 createColorSplitButton : function(id, s, cc) {
12204 var t = this, ed = t.editor, cmd, c, cls, bm;
12205
12206 if (t.get(id))
12207 return null;
12208
12209 s.title = ed.translate(s.title);
12210 s.scope = s.scope || ed;
12211
12212 if (!s.onclick) {
12213 s.onclick = function(v) {
12214 if (tinymce.isIE)
12215 bm = ed.selection.getBookmark(1);
12216
12217 ed.execCommand(s.cmd, s.ui || false, v || s.value);
12218 };
12219 }
12220
12221 if (!s.onselect) {
12222 s.onselect = function(v) {
12223 ed.execCommand(s.cmd, s.ui || false, v || s.value);
12224 };
12225 }
12226
12227 s = extend({
12228 title : s.title,
12229 'class' : 'mce_' + id,
12230 'menu_class' : ed.getParam('skin') + 'Skin',
12231 scope : s.scope,
12232 more_colors_title : ed.getLang('more_colors')
12233 }, s);
12234
12235 id = t.prefix + id;
12236 cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton;
12237 c = new cls(id, s);
12238 ed.onMouseDown.add(c.hideMenu, c);
12239
12240 // Remove the menu element when the editor is removed
12241 ed.onRemove.add(function() {
12242 c.destroy();
12243 });
12244
12245 // Fix for bug #1897785, #1898007
12246 if (tinymce.isIE) {
12247 c.onHideMenu.add(function() {
12248 if (bm) {
12249 ed.selection.moveToBookmark(bm);
12250 bm = 0;
12251 }
12252 });
12253 }
12254
12255 return t.add(c);
12256 },
12257
12258 createToolbar : function(id, s, cc) {
12259 var c, t = this, cls;
12260
12261 id = t.prefix + id;
12262 cls = cc || t._cls.toolbar || tinymce.ui.Toolbar;
12263 c = new cls(id, s);
12264
12265 if (t.get(id))
12266 return null;
12267
12268 return t.add(c);
12269 },
12270
12271 createSeparator : function(cc) {
12272 var cls = cc || this._cls.separator || tinymce.ui.Separator;
12273
12274 return new cls();
12275 },
12276
12277 setControlType : function(n, c) {
12278 return this._cls[n.toLowerCase()] = c;
12279 },
12280
12281 destroy : function() {
12282 each(this.controls, function(c) {
12283 c.destroy();
12284 });
12285
12286 this.controls = null;
12287 }
12288
12289 });
12290 })(tinymce);
12291 (function(tinymce) {
12292 var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;
12293
12294 tinymce.create('tinymce.WindowManager', {
12295 WindowManager : function(ed) {
12296 var t = this;
12297
12298 t.editor = ed;
12299 t.onOpen = new Dispatcher(t);
12300 t.onClose = new Dispatcher(t);
12301 t.params = {};
12302 t.features = {};
12303 },
12304
12305 open : function(s, p) {
12306 var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u;
12307
12308 // Default some options
12309 s = s || {};
12310 p = p || {};
12311 sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window
12312 sh = isOpera ? vp.h : screen.height;
12313 s.name = s.name || 'mc_' + new Date().getTime();
12314 s.width = parseInt(s.width || 320);
12315 s.height = parseInt(s.height || 240);
12316 s.resizable = true;
12317 s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0);
12318 s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0);
12319 p.inline = false;
12320 p.mce_width = s.width;
12321 p.mce_height = s.height;
12322 p.mce_auto_focus = s.auto_focus;
12323
12324 if (mo) {
12325 if (isIE) {
12326 s.center = true;
12327 s.help = false;
12328 s.dialogWidth = s.width + 'px';
12329 s.dialogHeight = s.height + 'px';
12330 s.scroll = s.scrollbars || false;
12331 }
12332 }
12333
12334 // Build features string
12335 each(s, function(v, k) {
12336 if (tinymce.is(v, 'boolean'))
12337 v = v ? 'yes' : 'no';
12338
12339 if (!/^(name|url)$/.test(k)) {
12340 if (isIE && mo)
12341 f += (f ? ';' : '') + k + ':' + v;
12342 else
12343 f += (f ? ',' : '') + k + '=' + v;
12344 }
12345 });
12346
12347 t.features = s;
12348 t.params = p;
12349 t.onOpen.dispatch(t, s, p);
12350
12351 u = s.url || s.file;
12352 u = tinymce._addVer(u);
12353
12354 try {
12355 if (isIE && mo) {
12356 w = 1;
12357 window.showModalDialog(u, window, f);
12358 } else
12359 w = window.open(u, s.name, f);
12360 } catch (ex) {
12361 // Ignore
12362 }
12363
12364 if (!w)
12365 alert(t.editor.getLang('popup_blocked'));
12366 },
12367
12368 close : function(w) {
12369 w.close();
12370 this.onClose.dispatch(this);
12371 },
12372
12373 createInstance : function(cl, a, b, c, d, e) {
12374 var f = tinymce.resolve(cl);
12375
12376 return new f(a, b, c, d, e);
12377 },
12378
12379 confirm : function(t, cb, s, w) {
12380 w = w || window;
12381
12382 cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t))));
12383 },
12384
12385 alert : function(tx, cb, s, w) {
12386 var t = this;
12387
12388 w = w || window;
12389 w.alert(t._decode(t.editor.getLang(tx, tx)));
12390
12391 if (cb)
12392 cb.call(s || t);
12393 },
12394
12395 // Internal functions
12396
12397 _decode : function(s) {
12398 return tinymce.DOM.decode(s).replace(/\\n/g, '\n');
12399 }
12400
12401 });
12402 }(tinymce));(function(tinymce) {
12403 tinymce.CommandManager = function() {
12404 var execCommands = {}, queryStateCommands = {}, queryValueCommands = {};
12405
12406 function add(collection, cmd, func, scope) {
12407 if (typeof(cmd) == 'string')
12408 cmd = [cmd];
12409
12410 tinymce.each(cmd, function(cmd) {
12411 collection[cmd.toLowerCase()] = {func : func, scope : scope};
12412 });
12413 };
12414
12415 tinymce.extend(this, {
12416 add : function(cmd, func, scope) {
12417 add(execCommands, cmd, func, scope);
12418 },
12419
12420 addQueryStateHandler : function(cmd, func, scope) {
12421 add(queryStateCommands, cmd, func, scope);
12422 },
12423
12424 addQueryValueHandler : function(cmd, func, scope) {
12425 add(queryValueCommands, cmd, func, scope);
12426 },
12427
12428 execCommand : function(scope, cmd, ui, value, args) {
12429 if (cmd = execCommands[cmd.toLowerCase()]) {
12430 if (cmd.func.call(scope || cmd.scope, ui, value, args) !== false)
12431 return true;
12432 }
12433 },
12434
12435 queryCommandValue : function() {
12436 if (cmd = queryValueCommands[cmd.toLowerCase()])
12437 return cmd.func.call(scope || cmd.scope, ui, value, args);
12438 },
12439
12440 queryCommandState : function() {
12441 if (cmd = queryStateCommands[cmd.toLowerCase()])
12442 return cmd.func.call(scope || cmd.scope, ui, value, args);
12443 }
12444 });
12445 };
12446
12447 tinymce.GlobalCommands = new tinymce.CommandManager();
12448 })(tinymce);(function(tinymce) {
12449 function processRange(dom, start, end, callback) {
12450 var ancestor, n, startPoint, endPoint, sib;
12451
12452 function findEndPoint(n, c) {
12453 do {
12454 if (n.parentNode == c)
12455 return n;
12456
12457 n = n.parentNode;
12458 } while(n);
12459 };
12460
12461 function process(n) {
12462 callback(n);
12463 tinymce.walk(n, callback, 'childNodes');
12464 };
12465
12466 // Find common ancestor and end points
12467 ancestor = dom.findCommonAncestor(start, end);
12468 startPoint = findEndPoint(start, ancestor) || start;
12469 endPoint = findEndPoint(end, ancestor) || end;
12470
12471 // Process left leaf
12472 for (n = start; n && n != startPoint; n = n.parentNode) {
12473 for (sib = n.nextSibling; sib; sib = sib.nextSibling)
12474 process(sib);
12475 }
12476
12477 // Process middle from start to end point
12478 if (startPoint != endPoint) {
12479 for (n = startPoint.nextSibling; n && n != endPoint; n = n.nextSibling)
12480 process(n);
12481 } else
12482 process(startPoint);
12483
12484 // Process right leaf
12485 for (n = end; n && n != endPoint; n = n.parentNode) {
12486 for (sib = n.previousSibling; sib; sib = sib.previousSibling)
12487 process(sib);
12488 }
12489 };
12490
12491 tinymce.GlobalCommands.add('RemoveFormat', function() {
12492 var ed = this, dom = ed.dom, s = ed.selection, r = s.getRng(1), nodes = [], bm, start, end, sc, so, ec, eo, n;
12493
12494 function findFormatRoot(n) {
12495 var sp;
12496
12497 dom.getParent(n, function(n) {
12498 if (dom.is(n, ed.getParam('removeformat_selector')))
12499 sp = n;
12500
12501 return dom.isBlock(n);
12502 }, ed.getBody())
12503
12504 return sp;
12505 };
12506
12507 function collect(n) {
12508 if (dom.is(n, ed.getParam('removeformat_selector')))
12509 nodes.push(n);
12510 };
12511
12512 function walk(n) {
12513 collect(n);
12514 tinymce.walk(n, collect, 'childNodes');
12515 };
12516
12517 bm = s.getBookmark();
12518 sc = r.startContainer;
12519 ec = r.endContainer;
12520 so = r.startOffset;
12521 eo = r.endOffset;
12522 sc = sc.nodeType == 1 ? sc.childNodes[so] : sc;
12523 ec = ec.nodeType == 1 ? ec.childNodes[eo - 1] : ec;
12524
12525 // Same container
12526 if (sc == ec) { // TEXT_NODE
12527 start = findFormatRoot(sc);
12528
12529 // Handle single text node
12530 if (sc.nodeType == 3) {
12531 if (start && start.nodeType == 1) { // ELEMENT
12532 n = sc.splitText(so);
12533 n.splitText(eo - so);
12534 dom.split(start, n);
12535
12536 s.moveToBookmark(bm);
12537 }
12538
12539 return;
12540 }
12541
12542 // Handle single element
12543 walk(dom.split(start, sc) || sc);
12544 } else {
12545 // Find start/end format root
12546 start = findFormatRoot(sc);
12547 end = findFormatRoot(ec);
12548
12549 // Split start text node
12550 if (start) {
12551 if (sc.nodeType == 3) { // TEXT
12552 // Since IE doesn't support white space nodes in the DOM we need to
12553 // add this invisible character so that the splitText function can split the contents
12554 if (so == sc.nodeValue.length)
12555 sc.nodeValue += '\uFEFF'; // Yet another pesky IE fix
12556
12557 sc = sc.splitText(so);
12558 }
12559 }
12560
12561 // Split end text node
12562 if (end) {
12563 if (ec.nodeType == 3) // TEXT
12564 ec.splitText(eo);
12565 }
12566
12567 // If the start and end format root is the same then we need to wrap
12568 // the end node in a span since the split calls might change the reference
12569 // Example: <p><b><em>x[yz<span>---</span>12]3</em></b></p>
12570 if (start && start == end)
12571 dom.replace(dom.create('span', {id : '__end'}, ec.cloneNode(true)), ec);
12572
12573 // Split all start containers down to the format root
12574 if (start)
12575 start = dom.split(start, sc);
12576 else
12577 start = sc;
12578
12579 // If there is a span wrapper use that one instead
12580 if (n = dom.get('__end')) {
12581 ec = n;
12582 end = findFormatRoot(ec);
12583 }
12584
12585 // Split all end containers down to the format root
12586 if (end)
12587 end = dom.split(end, ec);
12588 else
12589 end = ec;
12590
12591 // Collect nodes in between
12592 processRange(dom, start, end, collect);
12593
12594 // Remove invisible character for IE workaround if we find it
12595 if (sc.nodeValue == '\uFEFF')
12596 sc.nodeValue = '';
12597
12598 // Process start/end container elements
12599 walk(ec);
12600 walk(sc);
12601 }
12602
12603 // Remove all collected nodes
12604 tinymce.each(nodes, function(n) {
12605 dom.remove(n, 1);
12606 });
12607
12608 // Remove leftover wrapper
12609 dom.remove('__end', 1);
12610
12611 s.moveToBookmark(bm);
12612 });
12613 })(tinymce);
12614 (function(tinymce) {
12615 tinymce.GlobalCommands.add('mceBlockQuote', function() {
12616 var ed = this, s = ed.selection, dom = ed.dom, sb, eb, n, bm, bq, r, bq2, i, nl;
12617
12618 function getBQ(e) {
12619 return dom.getParent(e, function(n) {return n.nodeName === 'BLOCKQUOTE';});
12620 };
12621
12622 // Get start/end block
12623 sb = dom.getParent(s.getStart(), dom.isBlock);
12624 eb = dom.getParent(s.getEnd(), dom.isBlock);
12625
12626 // Remove blockquote(s)
12627 if (bq = getBQ(sb)) {
12628 if (sb != eb || sb.childNodes.length > 1 || (sb.childNodes.length == 1 && sb.firstChild.nodeName != 'BR'))
12629 bm = s.getBookmark();
12630
12631 // Move all elements after the end block into new bq
12632 if (getBQ(eb)) {
12633 bq2 = bq.cloneNode(false);
12634
12635 while (n = eb.nextSibling)
12636 bq2.appendChild(n.parentNode.removeChild(n));
12637 }
12638
12639 // Add new bq after
12640 if (bq2)
12641 dom.insertAfter(bq2, bq);
12642
12643 // Move all selected blocks after the current bq
12644 nl = s.getSelectedBlocks(sb, eb);
12645 for (i = nl.length - 1; i >= 0; i--) {
12646 dom.insertAfter(nl[i], bq);
12647 }
12648
12649 // Empty bq, then remove it
12650 if (/^\s*$/.test(bq.innerHTML))
12651 dom.remove(bq, 1); // Keep children so boomark restoration works correctly
12652
12653 // Empty bq, then remote it
12654 if (bq2 && /^\s*$/.test(bq2.innerHTML))
12655 dom.remove(bq2, 1); // Keep children so boomark restoration works correctly
12656
12657 if (!bm) {
12658 // Move caret inside empty block element
12659 if (!tinymce.isIE) {
12660 r = ed.getDoc().createRange();
12661 r.setStart(sb, 0);
12662 r.setEnd(sb, 0);
12663 s.setRng(r);
12664 } else {
12665 s.select(sb);
12666 s.collapse(0);
12667
12668 // IE misses the empty block some times element so we must move back the caret
12669 if (dom.getParent(s.getStart(), dom.isBlock) != sb) {
12670 r = s.getRng();
12671 r.move('character', -1);
12672 r.select();
12673 }
12674 }
12675 } else
12676 ed.selection.moveToBookmark(bm);
12677
12678 return;
12679 }
12680
12681 // Since IE can start with a totally empty document we need to add the first bq and paragraph
12682 if (tinymce.isIE && !sb && !eb) {
12683 ed.getDoc().execCommand('Indent');
12684 n = getBQ(s.getNode());
12685 n.style.margin = n.dir = ''; // IE adds margin and dir to bq
12686 return;
12687 }
12688
12689 if (!sb || !eb)
12690 return;
12691
12692 // If empty paragraph node then do not use bookmark
12693 if (sb != eb || sb.childNodes.length > 1 || (sb.childNodes.length == 1 && sb.firstChild.nodeName != 'BR'))
12694 bm = s.getBookmark();
12695
12696 // Move selected block elements into a bq
12697 tinymce.each(s.getSelectedBlocks(getBQ(s.getStart()), getBQ(s.getEnd())), function(e) {
12698 // Found existing BQ add to this one
12699 if (e.nodeName == 'BLOCKQUOTE' && !bq) {
12700 bq = e;
12701 return;
12702 }
12703
12704 // No BQ found, create one
12705 if (!bq) {
12706 bq = dom.create('blockquote');
12707 e.parentNode.insertBefore(bq, e);
12708 }
12709
12710 // Add children from existing BQ
12711 if (e.nodeName == 'BLOCKQUOTE' && bq) {
12712 n = e.firstChild;
12713
12714 while (n) {
12715 bq.appendChild(n.cloneNode(true));
12716 n = n.nextSibling;
12717 }
12718
12719 dom.remove(e);
12720 return;
12721 }
12722
12723 // Add non BQ element to BQ
12724 bq.appendChild(dom.remove(e));
12725 });
12726
12727 if (!bm) {
12728 // Move caret inside empty block element
12729 if (!tinymce.isIE) {
12730 r = ed.getDoc().createRange();
12731 r.setStart(sb, 0);
12732 r.setEnd(sb, 0);
12733 s.setRng(r);
12734 } else {
12735 s.select(sb);
12736 s.collapse(1);
12737 }
12738 } else
12739 s.moveToBookmark(bm);
12740 });
12741 })(tinymce);
12742 (function(tinymce) {
12743 tinymce.each(['Cut', 'Copy', 'Paste'], function(cmd) {
12744 tinymce.GlobalCommands.add(cmd, function() {
12745 var ed = this, doc = ed.getDoc();
12746
12747 try {
12748 doc.execCommand(cmd, false, null);
12749
12750 // On WebKit the command will just be ignored if it's not enabled
12751 if (!doc.queryCommandSupported(cmd))
12752 throw 'Error';
12753 } catch (ex) {
12754 ed.windowManager.alert(ed.getLang('clipboard_no_support'));
12755 }
12756 });
12757 });
12758 })(tinymce);
12759 (function(tinymce) {
12760 tinymce.GlobalCommands.add('InsertHorizontalRule', function() {
12761 if (tinymce.isOpera)
12762 return this.getDoc().execCommand('InsertHorizontalRule', false, '');
12763
12764 this.selection.setContent('<hr />');
12765 });
12766 })(tinymce);
12767 (function() {
12768 var cmds = tinymce.GlobalCommands;
12769
12770 cmds.add(['mceEndUndoLevel', 'mceAddUndoLevel'], function() {
12771 this.undoManager.add();
12772 });
12773
12774 cmds.add('Undo', function() {
12775 var ed = this;
12776
12777 if (ed.settings.custom_undo_redo) {
12778 ed.undoManager.undo();
12779 ed.nodeChanged();
12780 return true;
12781 }
12782
12783 return false; // Run browser command
12784 });
12785
12786 cmds.add('Redo', function() {
12787 var ed = this;
12788
12789 if (ed.settings.custom_undo_redo) {
12790 ed.undoManager.redo();
12791 ed.nodeChanged();
12792 return true;
12793 }
12794
12795 return false; // Run browser command
12796 });
12797 })();