fix salary JS calculation, ordering classement
[auf_rh_dae.git] / project / assets / js / tiny_mce / tiny_mce_src.js
1 (function(win) {
2 var whiteSpaceRe = /^\s*|\s*$/g,
3 undefined, isRegExpBroken = 'B'.replace(/A(.)|B/, '$1') === '$1';
4
5 var tinymce = {
6 majorVersion : '3',
7
8 minorVersion : '3.9.4',
9
10 releaseDate : '2011-04-13',
11
12 _init : function() {
13 var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
14
15 t.isOpera = win.opera && opera.buildNumber;
16
17 t.isWebKit = /WebKit/.test(ua);
18
19 t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName);
20
21 t.isIE6 = t.isIE && /MSIE [56]/.test(ua);
22
23 t.isIE9 = t.isIE && !/MSIE [5678]/.test(ua);
24
25 t.isGecko = !t.isWebKit && /Gecko/.test(ua);
26
27 t.isMac = ua.indexOf('Mac') != -1;
28
29 t.isAir = /adobeair/i.test(ua);
30
31 t.isIDevice = /(iPad|iPhone)/.test(ua);
32
33 // TinyMCE .NET webcontrol might be setting the values for TinyMCE
34 if (win.tinyMCEPreInit) {
35 t.suffix = tinyMCEPreInit.suffix;
36 t.baseURL = tinyMCEPreInit.base;
37 t.query = tinyMCEPreInit.query;
38 return;
39 }
40
41 // Get suffix and base
42 t.suffix = '';
43
44 // If base element found, add that infront of baseURL
45 nl = d.getElementsByTagName('base');
46 for (i=0; i<nl.length; i++) {
47 if (v = nl[i].href) {
48 // Host only value like http://site.com or http://site.com:8008
49 if (/^https?:\/\/[^\/]+$/.test(v))
50 v += '/';
51
52 base = v ? v.match(/.*\//)[0] : ''; // Get only directory
53 }
54 }
55
56 function getBase(n) {
57 if (n.src && /tiny_mce(|_gzip|_jquery|_prototype|_full)(_dev|_src)?.js/.test(n.src)) {
58 if (/_(src|dev)\.js/g.test(n.src))
59 t.suffix = '_src';
60
61 if ((p = n.src.indexOf('?')) != -1)
62 t.query = n.src.substring(p + 1);
63
64 t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));
65
66 // If path to script is relative and a base href was found add that one infront
67 // the src property will always be an absolute one on non IE browsers and IE 8
68 // so this logic will basically only be executed on older IE versions
69 if (base && t.baseURL.indexOf('://') == -1 && t.baseURL.indexOf('/') !== 0)
70 t.baseURL = base + t.baseURL;
71
72 return t.baseURL;
73 }
74
75 return null;
76 };
77
78 // Check document
79 nl = d.getElementsByTagName('script');
80 for (i=0; i<nl.length; i++) {
81 if (getBase(nl[i]))
82 return;
83 }
84
85 // Check head
86 n = d.getElementsByTagName('head')[0];
87 if (n) {
88 nl = n.getElementsByTagName('script');
89 for (i=0; i<nl.length; i++) {
90 if (getBase(nl[i]))
91 return;
92 }
93 }
94
95 return;
96 },
97
98 is : function(o, t) {
99 if (!t)
100 return o !== undefined;
101
102 if (t == 'array' && (o.hasOwnProperty && o instanceof Array))
103 return true;
104
105 return typeof(o) == t;
106 },
107
108 each : function(o, cb, s) {
109 var n, l;
110
111 if (!o)
112 return 0;
113
114 s = s || o;
115
116 if (o.length !== undefined) {
117 // Indexed arrays, needed for Safari
118 for (n=0, l = o.length; n < l; n++) {
119 if (cb.call(s, o[n], n, o) === false)
120 return 0;
121 }
122 } else {
123 // Hashtables
124 for (n in o) {
125 if (o.hasOwnProperty(n)) {
126 if (cb.call(s, o[n], n, o) === false)
127 return 0;
128 }
129 }
130 }
131
132 return 1;
133 },
134
135
136 map : function(a, f) {
137 var o = [];
138
139 tinymce.each(a, function(v) {
140 o.push(f(v));
141 });
142
143 return o;
144 },
145
146 grep : function(a, f) {
147 var o = [];
148
149 tinymce.each(a, function(v) {
150 if (!f || f(v))
151 o.push(v);
152 });
153
154 return o;
155 },
156
157 inArray : function(a, v) {
158 var i, l;
159
160 if (a) {
161 for (i = 0, l = a.length; i < l; i++) {
162 if (a[i] === v)
163 return i;
164 }
165 }
166
167 return -1;
168 },
169
170 extend : function(o, e) {
171 var i, l, a = arguments;
172
173 for (i = 1, l = a.length; i < l; i++) {
174 e = a[i];
175
176 tinymce.each(e, function(v, n) {
177 if (v !== undefined)
178 o[n] = v;
179 });
180 }
181
182 return o;
183 },
184
185
186 trim : function(s) {
187 return (s ? '' + s : '').replace(whiteSpaceRe, '');
188 },
189
190 create : function(s, p) {
191 var t = this, sp, ns, cn, scn, c, de = 0;
192
193 // Parse : <prefix> <class>:<super class>
194 s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);
195 cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name
196
197 // Create namespace for new class
198 ns = t.createNS(s[3].replace(/\.\w+$/, ''));
199
200 // Class already exists
201 if (ns[cn])
202 return;
203
204 // Make pure static class
205 if (s[2] == 'static') {
206 ns[cn] = p;
207
208 if (this.onCreate)
209 this.onCreate(s[2], s[3], ns[cn]);
210
211 return;
212 }
213
214 // Create default constructor
215 if (!p[cn]) {
216 p[cn] = function() {};
217 de = 1;
218 }
219
220 // Add constructor and methods
221 ns[cn] = p[cn];
222 t.extend(ns[cn].prototype, p);
223
224 // Extend
225 if (s[5]) {
226 sp = t.resolve(s[5]).prototype;
227 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name
228
229 // Extend constructor
230 c = ns[cn];
231 if (de) {
232 // Add passthrough constructor
233 ns[cn] = function() {
234 return sp[scn].apply(this, arguments);
235 };
236 } else {
237 // Add inherit constructor
238 ns[cn] = function() {
239 this.parent = sp[scn];
240 return c.apply(this, arguments);
241 };
242 }
243 ns[cn].prototype[cn] = ns[cn];
244
245 // Add super methods
246 t.each(sp, function(f, n) {
247 ns[cn].prototype[n] = sp[n];
248 });
249
250 // Add overridden methods
251 t.each(p, function(f, n) {
252 // Extend methods if needed
253 if (sp[n]) {
254 ns[cn].prototype[n] = function() {
255 this.parent = sp[n];
256 return f.apply(this, arguments);
257 };
258 } else {
259 if (n != cn)
260 ns[cn].prototype[n] = f;
261 }
262 });
263 }
264
265 // Add static methods
266 t.each(p['static'], function(f, n) {
267 ns[cn][n] = f;
268 });
269
270 if (this.onCreate)
271 this.onCreate(s[2], s[3], ns[cn].prototype);
272 },
273
274 walk : function(o, f, n, s) {
275 s = s || this;
276
277 if (o) {
278 if (n)
279 o = o[n];
280
281 tinymce.each(o, function(o, i) {
282 if (f.call(s, o, i, n) === false)
283 return false;
284
285 tinymce.walk(o, f, n, s);
286 });
287 }
288 },
289
290 createNS : function(n, o) {
291 var i, v;
292
293 o = o || win;
294
295 n = n.split('.');
296 for (i=0; i<n.length; i++) {
297 v = n[i];
298
299 if (!o[v])
300 o[v] = {};
301
302 o = o[v];
303 }
304
305 return o;
306 },
307
308 resolve : function(n, o) {
309 var i, l;
310
311 o = o || win;
312
313 n = n.split('.');
314 for (i = 0, l = n.length; i < l; i++) {
315 o = o[n[i]];
316
317 if (!o)
318 break;
319 }
320
321 return o;
322 },
323
324 addUnload : function(f, s) {
325 var t = this;
326
327 f = {func : f, scope : s || this};
328
329 if (!t.unloads) {
330 function unload() {
331 var li = t.unloads, o, n;
332
333 if (li) {
334 // Call unload handlers
335 for (n in li) {
336 o = li[n];
337
338 if (o && o.func)
339 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy
340 }
341
342 // Detach unload function
343 if (win.detachEvent) {
344 win.detachEvent('onbeforeunload', fakeUnload);
345 win.detachEvent('onunload', unload);
346 } else if (win.removeEventListener)
347 win.removeEventListener('unload', unload, false);
348
349 // Destroy references
350 t.unloads = o = li = w = unload = 0;
351
352 // Run garbarge collector on IE
353 if (win.CollectGarbage)
354 CollectGarbage();
355 }
356 };
357
358 function fakeUnload() {
359 var d = document;
360
361 // Is there things still loading, then do some magic
362 if (d.readyState == 'interactive') {
363 function stop() {
364 // Prevent memory leak
365 d.detachEvent('onstop', stop);
366
367 // Call unload handler
368 if (unload)
369 unload();
370
371 d = 0;
372 };
373
374 // Fire unload when the currently loading page is stopped
375 if (d)
376 d.attachEvent('onstop', stop);
377
378 // Remove onstop listener after a while to prevent the unload function
379 // to execute if the user presses cancel in an onbeforeunload
380 // confirm dialog and then presses the browser stop button
381 win.setTimeout(function() {
382 if (d)
383 d.detachEvent('onstop', stop);
384 }, 0);
385 }
386 };
387
388 // Attach unload handler
389 if (win.attachEvent) {
390 win.attachEvent('onunload', unload);
391 win.attachEvent('onbeforeunload', fakeUnload);
392 } else if (win.addEventListener)
393 win.addEventListener('unload', unload, false);
394
395 // Setup initial unload handler array
396 t.unloads = [f];
397 } else
398 t.unloads.push(f);
399
400 return f;
401 },
402
403 removeUnload : function(f) {
404 var u = this.unloads, r = null;
405
406 tinymce.each(u, function(o, i) {
407 if (o && o.func == f) {
408 u.splice(i, 1);
409 r = f;
410 return false;
411 }
412 });
413
414 return r;
415 },
416
417 explode : function(s, d) {
418 return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;
419 },
420
421 _addVer : function(u) {
422 var v;
423
424 if (!this.query)
425 return u;
426
427 v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;
428
429 if (u.indexOf('#') == -1)
430 return u + v;
431
432 return u.replace('#', v + '#');
433 },
434
435 // Fix function for IE 9 where regexps isn't working correctly
436 // Todo: remove me once MS fixes the bug
437 _replace : function(find, replace, str) {
438 // On IE9 we have to fake $x replacement
439 if (isRegExpBroken) {
440 return str.replace(find, function() {
441 var val = replace, args = arguments, i;
442
443 for (i = 0; i < args.length - 2; i++) {
444 if (args[i] === undefined) {
445 val = val.replace(new RegExp('\\$' + i, 'g'), '');
446 } else {
447 val = val.replace(new RegExp('\\$' + i, 'g'), args[i]);
448 }
449 }
450
451 return val;
452 });
453 }
454
455 return str.replace(find, replace);
456 }
457
458 };
459
460 // Initialize the API
461 tinymce._init();
462
463 // Expose tinymce namespace to the global namespace (window)
464 win.tinymce = win.tinyMCE = tinymce;
465 })(window);
466
467
468
469 tinymce.create('tinymce.util.Dispatcher', {
470 scope : null,
471 listeners : null,
472
473 Dispatcher : function(s) {
474 this.scope = s || this;
475 this.listeners = [];
476 },
477
478 add : function(cb, s) {
479 this.listeners.push({cb : cb, scope : s || this.scope});
480
481 return cb;
482 },
483
484 addToTop : function(cb, s) {
485 this.listeners.unshift({cb : cb, scope : s || this.scope});
486
487 return cb;
488 },
489
490 remove : function(cb) {
491 var l = this.listeners, o = null;
492
493 tinymce.each(l, function(c, i) {
494 if (cb == c.cb) {
495 o = cb;
496 l.splice(i, 1);
497 return false;
498 }
499 });
500
501 return o;
502 },
503
504 dispatch : function() {
505 var s, a = arguments, i, li = this.listeners, c;
506
507 // Needs to be a real loop since the listener count might change while looping
508 // And this is also more efficient
509 for (i = 0; i<li.length; i++) {
510 c = li[i];
511 s = c.cb.apply(c.scope, a);
512
513 if (s === false)
514 break;
515 }
516
517 return s;
518 }
519
520 });
521
522 (function() {
523 var each = tinymce.each;
524
525 tinymce.create('tinymce.util.URI', {
526 URI : function(u, s) {
527 var t = this, o, a, b;
528
529 // Trim whitespace
530 u = tinymce.trim(u);
531
532 // Default settings
533 s = t.settings = s || {};
534
535 // Strange app protocol or local anchor
536 if (/^(mailto|tel|news|javascript|about|data):/i.test(u) || /^\s*#/.test(u)) {
537 t.source = u;
538 return;
539 }
540
541 // Absolute path with no host, fake host and protocol
542 if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)
543 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;
544
545 // Relative path http:// or protocol relative //path
546 if (!/^\w*:?\/\//.test(u))
547 u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u);
548
549 // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
550 u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
551 u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);
552 each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {
553 var s = u[i];
554
555 // Zope 3 workaround, they use @@something
556 if (s)
557 s = s.replace(/\(mce_at\)/g, '@@');
558
559 t[v] = s;
560 });
561
562 if (b = s.base_uri) {
563 if (!t.protocol)
564 t.protocol = b.protocol;
565
566 if (!t.userInfo)
567 t.userInfo = b.userInfo;
568
569 if (!t.port && t.host == 'mce_host')
570 t.port = b.port;
571
572 if (!t.host || t.host == 'mce_host')
573 t.host = b.host;
574
575 t.source = '';
576 }
577
578 //t.path = t.path || '/';
579 },
580
581 setPath : function(p) {
582 var t = this;
583
584 p = /^(.*?)\/?(\w+)?$/.exec(p);
585
586 // Update path parts
587 t.path = p[0];
588 t.directory = p[1];
589 t.file = p[2];
590
591 // Rebuild source
592 t.source = '';
593 t.getURI();
594 },
595
596 toRelative : function(u) {
597 var t = this, o;
598
599 if (u === "./")
600 return u;
601
602 u = new tinymce.util.URI(u, {base_uri : t});
603
604 // Not on same domain/port or protocol
605 if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)
606 return u.getURI();
607
608 o = t.toRelPath(t.path, u.path);
609
610 // Add query
611 if (u.query)
612 o += '?' + u.query;
613
614 // Add anchor
615 if (u.anchor)
616 o += '#' + u.anchor;
617
618 return o;
619 },
620
621 toAbsolute : function(u, nh) {
622 var u = new tinymce.util.URI(u, {base_uri : this});
623
624 return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0);
625 },
626
627 toRelPath : function(base, path) {
628 var items, bp = 0, out = '', i, l;
629
630 // Split the paths
631 base = base.substring(0, base.lastIndexOf('/'));
632 base = base.split('/');
633 items = path.split('/');
634
635 if (base.length >= items.length) {
636 for (i = 0, l = base.length; i < l; i++) {
637 if (i >= items.length || base[i] != items[i]) {
638 bp = i + 1;
639 break;
640 }
641 }
642 }
643
644 if (base.length < items.length) {
645 for (i = 0, l = items.length; i < l; i++) {
646 if (i >= base.length || base[i] != items[i]) {
647 bp = i + 1;
648 break;
649 }
650 }
651 }
652
653 if (bp == 1)
654 return path;
655
656 for (i = 0, l = base.length - (bp - 1); i < l; i++)
657 out += "../";
658
659 for (i = bp - 1, l = items.length; i < l; i++) {
660 if (i != bp - 1)
661 out += "/" + items[i];
662 else
663 out += items[i];
664 }
665
666 return out;
667 },
668
669 toAbsPath : function(base, path) {
670 var i, nb = 0, o = [], tr, outPath;
671
672 // Split paths
673 tr = /\/$/.test(path) ? '/' : '';
674 base = base.split('/');
675 path = path.split('/');
676
677 // Remove empty chunks
678 each(base, function(k) {
679 if (k)
680 o.push(k);
681 });
682
683 base = o;
684
685 // Merge relURLParts chunks
686 for (i = path.length - 1, o = []; i >= 0; i--) {
687 // Ignore empty or .
688 if (path[i].length == 0 || path[i] == ".")
689 continue;
690
691 // Is parent
692 if (path[i] == '..') {
693 nb++;
694 continue;
695 }
696
697 // Move up
698 if (nb > 0) {
699 nb--;
700 continue;
701 }
702
703 o.push(path[i]);
704 }
705
706 i = base.length - nb;
707
708 // If /a/b/c or /
709 if (i <= 0)
710 outPath = o.reverse().join('/');
711 else
712 outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');
713
714 // Add front / if it's needed
715 if (outPath.indexOf('/') !== 0)
716 outPath = '/' + outPath;
717
718 // Add traling / if it's needed
719 if (tr && outPath.lastIndexOf('/') !== outPath.length - 1)
720 outPath += tr;
721
722 return outPath;
723 },
724
725 getURI : function(nh) {
726 var s, t = this;
727
728 // Rebuild source
729 if (!t.source || nh) {
730 s = '';
731
732 if (!nh) {
733 if (t.protocol)
734 s += t.protocol + '://';
735
736 if (t.userInfo)
737 s += t.userInfo + '@';
738
739 if (t.host)
740 s += t.host;
741
742 if (t.port)
743 s += ':' + t.port;
744 }
745
746 if (t.path)
747 s += t.path;
748
749 if (t.query)
750 s += '?' + t.query;
751
752 if (t.anchor)
753 s += '#' + t.anchor;
754
755 t.source = s;
756 }
757
758 return t.source;
759 }
760 });
761 })();
762
763 (function() {
764 var each = tinymce.each;
765
766 tinymce.create('static tinymce.util.Cookie', {
767 getHash : function(n) {
768 var v = this.get(n), h;
769
770 if (v) {
771 each(v.split('&'), function(v) {
772 v = v.split('=');
773 h = h || {};
774 h[unescape(v[0])] = unescape(v[1]);
775 });
776 }
777
778 return h;
779 },
780
781 setHash : function(n, v, e, p, d, s) {
782 var o = '';
783
784 each(v, function(v, k) {
785 o += (!o ? '' : '&') + escape(k) + '=' + escape(v);
786 });
787
788 this.set(n, o, e, p, d, s);
789 },
790
791 get : function(n) {
792 var c = document.cookie, e, p = n + "=", b;
793
794 // Strict mode
795 if (!c)
796 return;
797
798 b = c.indexOf("; " + p);
799
800 if (b == -1) {
801 b = c.indexOf(p);
802
803 if (b != 0)
804 return null;
805 } else
806 b += 2;
807
808 e = c.indexOf(";", b);
809
810 if (e == -1)
811 e = c.length;
812
813 return unescape(c.substring(b + p.length, e));
814 },
815
816 set : function(n, v, e, p, d, s) {
817 document.cookie = n + "=" + escape(v) +
818 ((e) ? "; expires=" + e.toGMTString() : "") +
819 ((p) ? "; path=" + escape(p) : "") +
820 ((d) ? "; domain=" + d : "") +
821 ((s) ? "; secure" : "");
822 },
823
824 remove : function(n, p) {
825 var d = new Date();
826
827 d.setTime(d.getTime() - 1000);
828
829 this.set(n, '', d, p, d);
830 }
831 });
832 })();
833
834 tinymce.create('static tinymce.util.JSON', {
835 serialize : function(o) {
836 var i, v, s = tinymce.util.JSON.serialize, t;
837
838 if (o == null)
839 return 'null';
840
841 t = typeof o;
842
843 if (t == 'string') {
844 v = '\bb\tt\nn\ff\rr\""\'\'\\\\';
845
846 return '"' + o.replace(/([\u0080-\uFFFF\x00-\x1f\"])/g, function(a, b) {
847 i = v.indexOf(b);
848
849 if (i + 1)
850 return '\\' + v.charAt(i + 1);
851
852 a = b.charCodeAt().toString(16);
853
854 return '\\u' + '0000'.substring(a.length) + a;
855 }) + '"';
856 }
857
858 if (t == 'object') {
859 if (o.hasOwnProperty && o instanceof Array) {
860 for (i=0, v = '['; i<o.length; i++)
861 v += (i > 0 ? ',' : '') + s(o[i]);
862
863 return v + ']';
864 }
865
866 v = '{';
867
868 for (i in o)
869 v += typeof o[i] != 'function' ? (v.length > 1 ? ',"' : '"') + i + '":' + s(o[i]) : '';
870
871 return v + '}';
872 }
873
874 return '' + o;
875 },
876
877 parse : function(s) {
878 try {
879 return eval('(' + s + ')');
880 } catch (ex) {
881 // Ignore
882 }
883 }
884
885 });
886
887 tinymce.create('static tinymce.util.XHR', {
888 send : function(o) {
889 var x, t, w = window, c = 0;
890
891 // Default settings
892 o.scope = o.scope || this;
893 o.success_scope = o.success_scope || o.scope;
894 o.error_scope = o.error_scope || o.scope;
895 o.async = o.async === false ? false : true;
896 o.data = o.data || '';
897
898 function get(s) {
899 x = 0;
900
901 try {
902 x = new ActiveXObject(s);
903 } catch (ex) {
904 }
905
906 return x;
907 };
908
909 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');
910
911 if (x) {
912 if (x.overrideMimeType)
913 x.overrideMimeType(o.content_type);
914
915 x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);
916
917 if (o.content_type)
918 x.setRequestHeader('Content-Type', o.content_type);
919
920 x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
921
922 x.send(o.data);
923
924 function ready() {
925 if (!o.async || x.readyState == 4 || c++ > 10000) {
926 if (o.success && c < 10000 && x.status == 200)
927 o.success.call(o.success_scope, '' + x.responseText, x, o);
928 else if (o.error)
929 o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);
930
931 x = null;
932 } else
933 w.setTimeout(ready, 10);
934 };
935
936 // Syncronous request
937 if (!o.async)
938 return ready();
939
940 // Wait for response, onReadyStateChange can not be used since it leaks memory in IE
941 t = w.setTimeout(ready, 10);
942 }
943 }
944 });
945
946 (function() {
947 var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;
948
949 tinymce.create('tinymce.util.JSONRequest', {
950 JSONRequest : function(s) {
951 this.settings = extend({
952 }, s);
953 this.count = 0;
954 },
955
956 send : function(o) {
957 var ecb = o.error, scb = o.success;
958
959 o = extend(this.settings, o);
960
961 o.success = function(c, x) {
962 c = JSON.parse(c);
963
964 if (typeof(c) == 'undefined') {
965 c = {
966 error : 'JSON Parse error.'
967 };
968 }
969
970 if (c.error)
971 ecb.call(o.error_scope || o.scope, c.error, x);
972 else
973 scb.call(o.success_scope || o.scope, c.result);
974 };
975
976 o.error = function(ty, x) {
977 ecb.call(o.error_scope || o.scope, ty, x);
978 };
979
980 o.data = JSON.serialize({
981 id : o.id || 'c' + (this.count++),
982 method : o.method,
983 params : o.params
984 });
985
986 // JSON content type for Ruby on rails. Bug: #1883287
987 o.content_type = 'application/json';
988
989 XHR.send(o);
990 },
991
992 'static' : {
993 sendRPC : function(o) {
994 return new tinymce.util.JSONRequest().send(o);
995 }
996 }
997 });
998 }());
999 (function(tinymce) {
1000 // Shorten names
1001 var each = tinymce.each,
1002 is = tinymce.is,
1003 isWebKit = tinymce.isWebKit,
1004 isIE = tinymce.isIE,
1005 blockRe = /^(H[1-6R]|P|DIV|ADDRESS|PRE|FORM|T(ABLE|BODY|HEAD|FOOT|H|R|D)|LI|OL|UL|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|NOSCRIPT|MENU|ISINDEX|SAMP)$/,
1006 boolAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'),
1007 mceAttribs = makeMap('src,href,style,coords,shape'),
1008 encodedChars = {'&' : '&amp;', '"' : '&quot;', '<' : '&lt;', '>' : '&gt;'},
1009 encodeCharsRe = /[<>&\"]/g,
1010 simpleSelectorRe = /^([a-z0-9],?)+$/i,
1011 tagRegExp = /<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)(\s*\/?)>/g,
1012 attrRegExp = /(\w+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;
1013
1014 function makeMap(str) {
1015 var map = {}, i;
1016
1017 str = str.split(',');
1018 for (i = str.length; i >= 0; i--)
1019 map[str[i]] = 1;
1020
1021 return map;
1022 };
1023
1024 tinymce.create('tinymce.dom.DOMUtils', {
1025 doc : null,
1026 root : null,
1027 files : null,
1028 pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,
1029 props : {
1030 "for" : "htmlFor",
1031 "class" : "className",
1032 className : "className",
1033 checked : "checked",
1034 disabled : "disabled",
1035 maxlength : "maxLength",
1036 readonly : "readOnly",
1037 selected : "selected",
1038 value : "value",
1039 id : "id",
1040 name : "name",
1041 type : "type"
1042 },
1043
1044 DOMUtils : function(d, s) {
1045 var t = this, globalStyle;
1046
1047 t.doc = d;
1048 t.win = window;
1049 t.files = {};
1050 t.cssFlicker = false;
1051 t.counter = 0;
1052 t.stdMode = d.documentMode >= 8;
1053 t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat" || t.stdMode;
1054
1055 t.settings = s = tinymce.extend({
1056 keep_values : false,
1057 hex_colors : 1,
1058 process_html : 1
1059 }, s);
1060
1061 // Fix IE6SP2 flicker and check it failed for pre SP2
1062 if (tinymce.isIE6) {
1063 try {
1064 d.execCommand('BackgroundImageCache', false, true);
1065 } catch (e) {
1066 t.cssFlicker = true;
1067 }
1068 }
1069
1070 // Build styles list
1071 if (s.valid_styles) {
1072 t._styles = {};
1073
1074 // Convert styles into a rule list
1075 each(s.valid_styles, function(value, key) {
1076 t._styles[key] = tinymce.explode(value);
1077 });
1078 }
1079
1080 tinymce.addUnload(t.destroy, t);
1081 },
1082
1083 getRoot : function() {
1084 var t = this, s = t.settings;
1085
1086 return (s && t.get(s.root_element)) || t.doc.body;
1087 },
1088
1089 getViewPort : function(w) {
1090 var d, b;
1091
1092 w = !w ? this.win : w;
1093 d = w.document;
1094 b = this.boxModel ? d.documentElement : d.body;
1095
1096 // Returns viewport size excluding scrollbars
1097 return {
1098 x : w.pageXOffset || b.scrollLeft,
1099 y : w.pageYOffset || b.scrollTop,
1100 w : w.innerWidth || b.clientWidth,
1101 h : w.innerHeight || b.clientHeight
1102 };
1103 },
1104
1105 getRect : function(e) {
1106 var p, t = this, sr;
1107
1108 e = t.get(e);
1109 p = t.getPos(e);
1110 sr = t.getSize(e);
1111
1112 return {
1113 x : p.x,
1114 y : p.y,
1115 w : sr.w,
1116 h : sr.h
1117 };
1118 },
1119
1120 getSize : function(e) {
1121 var t = this, w, h;
1122
1123 e = t.get(e);
1124 w = t.getStyle(e, 'width');
1125 h = t.getStyle(e, 'height');
1126
1127 // Non pixel value, then force offset/clientWidth
1128 if (w.indexOf('px') === -1)
1129 w = 0;
1130
1131 // Non pixel value, then force offset/clientWidth
1132 if (h.indexOf('px') === -1)
1133 h = 0;
1134
1135 return {
1136 w : parseInt(w) || e.offsetWidth || e.clientWidth,
1137 h : parseInt(h) || e.offsetHeight || e.clientHeight
1138 };
1139 },
1140
1141 getParent : function(n, f, r) {
1142 return this.getParents(n, f, r, false);
1143 },
1144
1145 getParents : function(n, f, r, c) {
1146 var t = this, na, se = t.settings, o = [];
1147
1148 n = t.get(n);
1149 c = c === undefined;
1150
1151 if (se.strict_root)
1152 r = r || t.getRoot();
1153
1154 // Wrap node name as func
1155 if (is(f, 'string')) {
1156 na = f;
1157
1158 if (f === '*') {
1159 f = function(n) {return n.nodeType == 1;};
1160 } else {
1161 f = function(n) {
1162 return t.is(n, na);
1163 };
1164 }
1165 }
1166
1167 while (n) {
1168 if (n == r || !n.nodeType || n.nodeType === 9)
1169 break;
1170
1171 if (!f || f(n)) {
1172 if (c)
1173 o.push(n);
1174 else
1175 return n;
1176 }
1177
1178 n = n.parentNode;
1179 }
1180
1181 return c ? o : null;
1182 },
1183
1184 get : function(e) {
1185 var n;
1186
1187 if (e && this.doc && typeof(e) == 'string') {
1188 n = e;
1189 e = this.doc.getElementById(e);
1190
1191 // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick
1192 if (e && e.id !== n)
1193 return this.doc.getElementsByName(n)[1];
1194 }
1195
1196 return e;
1197 },
1198
1199 getNext : function(node, selector) {
1200 return this._findSib(node, selector, 'nextSibling');
1201 },
1202
1203 getPrev : function(node, selector) {
1204 return this._findSib(node, selector, 'previousSibling');
1205 },
1206
1207
1208 select : function(pa, s) {
1209 var t = this;
1210
1211 return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []);
1212 },
1213
1214 is : function(n, selector) {
1215 var i;
1216
1217 // If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance
1218 if (n.length === undefined) {
1219 // Simple all selector
1220 if (selector === '*')
1221 return n.nodeType == 1;
1222
1223 // Simple selector just elements
1224 if (simpleSelectorRe.test(selector)) {
1225 selector = selector.toLowerCase().split(/,/);
1226 n = n.nodeName.toLowerCase();
1227
1228 for (i = selector.length - 1; i >= 0; i--) {
1229 if (selector[i] == n)
1230 return true;
1231 }
1232
1233 return false;
1234 }
1235 }
1236
1237 return tinymce.dom.Sizzle.matches(selector, n.nodeType ? [n] : n).length > 0;
1238 },
1239
1240
1241 add : function(p, n, a, h, c) {
1242 var t = this;
1243
1244 return this.run(p, function(p) {
1245 var e, k;
1246
1247 e = is(n, 'string') ? t.doc.createElement(n) : n;
1248 t.setAttribs(e, a);
1249
1250 if (h) {
1251 if (h.nodeType)
1252 e.appendChild(h);
1253 else
1254 t.setHTML(e, h);
1255 }
1256
1257 return !c ? p.appendChild(e) : e;
1258 });
1259 },
1260
1261 create : function(n, a, h) {
1262 return this.add(this.doc.createElement(n), n, a, h, 1);
1263 },
1264
1265 createHTML : function(n, a, h) {
1266 var o = '', t = this, k;
1267
1268 o += '<' + n;
1269
1270 for (k in a) {
1271 if (a.hasOwnProperty(k))
1272 o += ' ' + k + '="' + t.encode(a[k]) + '"';
1273 }
1274
1275 // A call to tinymce.is doesn't work for some odd reason on IE9 possible bug inside their JS runtime
1276 if (typeof(h) != "undefined")
1277 return o + '>' + h + '</' + n + '>';
1278
1279 return o + ' />';
1280 },
1281
1282 remove : function(node, keep_children) {
1283 return this.run(node, function(node) {
1284 var parent, child;
1285
1286 parent = node.parentNode;
1287
1288 if (!parent)
1289 return null;
1290
1291 if (keep_children) {
1292 while (child = node.firstChild) {
1293 // IE 8 will crash if you don't remove completely empty text nodes
1294 if (!tinymce.isIE || child.nodeType !== 3 || child.nodeValue)
1295 parent.insertBefore(child, node);
1296 else
1297 node.removeChild(child);
1298 }
1299 }
1300
1301 return parent.removeChild(node);
1302 });
1303 },
1304
1305 setStyle : function(n, na, v) {
1306 var t = this;
1307
1308 return t.run(n, function(e) {
1309 var s, i;
1310
1311 s = e.style;
1312
1313 // Camelcase it, if needed
1314 na = na.replace(/-(\D)/g, function(a, b){
1315 return b.toUpperCase();
1316 });
1317
1318 // Default px suffix on these
1319 if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v)))
1320 v += 'px';
1321
1322 switch (na) {
1323 case 'opacity':
1324 // IE specific opacity
1325 if (isIE) {
1326 s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")";
1327
1328 if (!n.currentStyle || !n.currentStyle.hasLayout)
1329 s.display = 'inline-block';
1330 }
1331
1332 // Fix for older browsers
1333 s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || '';
1334 break;
1335
1336 case 'float':
1337 isIE ? s.styleFloat = v : s.cssFloat = v;
1338 break;
1339
1340 default:
1341 s[na] = v || '';
1342 }
1343
1344 // Force update of the style data
1345 if (t.settings.update_styles)
1346 t.setAttrib(e, '_mce_style');
1347 });
1348 },
1349
1350 getStyle : function(n, na, c) {
1351 n = this.get(n);
1352
1353 if (!n)
1354 return false;
1355
1356 // Gecko
1357 if (this.doc.defaultView && c) {
1358 // Remove camelcase
1359 na = na.replace(/[A-Z]/g, function(a){
1360 return '-' + a;
1361 });
1362
1363 try {
1364 return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);
1365 } catch (ex) {
1366 // Old safari might fail
1367 return null;
1368 }
1369 }
1370
1371 // Camelcase it, if needed
1372 na = na.replace(/-(\D)/g, function(a, b){
1373 return b.toUpperCase();
1374 });
1375
1376 if (na == 'float')
1377 na = isIE ? 'styleFloat' : 'cssFloat';
1378
1379 // IE & Opera
1380 if (n.currentStyle && c)
1381 return n.currentStyle[na];
1382
1383 return n.style[na];
1384 },
1385
1386 setStyles : function(e, o) {
1387 var t = this, s = t.settings, ol;
1388
1389 ol = s.update_styles;
1390 s.update_styles = 0;
1391
1392 each(o, function(v, n) {
1393 t.setStyle(e, n, v);
1394 });
1395
1396 // Update style info
1397 s.update_styles = ol;
1398 if (s.update_styles)
1399 t.setAttrib(e, s.cssText);
1400 },
1401
1402 removeAllAttribs: function(e) {
1403 return this.run(e, function(e) {
1404 var attrs = e.attributes;
1405 for (var i = attrs.length - 1; i >= 0; i--) {
1406 e.removeAttributeNode(attrs.item(i));
1407 }
1408 });
1409 },
1410
1411 setAttrib : function(e, n, v) {
1412 var t = this;
1413
1414 // Whats the point
1415 if (!e || !n)
1416 return;
1417
1418 // Strict XML mode
1419 if (t.settings.strict)
1420 n = n.toLowerCase();
1421
1422 return this.run(e, function(e) {
1423 var s = t.settings;
1424
1425 switch (n) {
1426 case "style":
1427 if (!is(v, 'string')) {
1428 each(v, function(v, n) {
1429 t.setStyle(e, n, v);
1430 });
1431
1432 return;
1433 }
1434
1435 // No mce_style for elements with these since they might get resized by the user
1436 if (s.keep_values) {
1437 if (v && !t._isRes(v))
1438 e.setAttribute('_mce_style', v, 2);
1439 else
1440 e.removeAttribute('_mce_style', 2);
1441 }
1442
1443 e.style.cssText = v;
1444 break;
1445
1446 case "class":
1447 e.className = v || ''; // Fix IE null bug
1448 break;
1449
1450 case "src":
1451 case "href":
1452 if (s.keep_values) {
1453 if (s.url_converter)
1454 v = s.url_converter.call(s.url_converter_scope || t, v, n, e);
1455
1456 t.setAttrib(e, '_mce_' + n, v, 2);
1457 }
1458
1459 break;
1460
1461 case "shape":
1462 e.setAttribute('_mce_style', v);
1463 break;
1464 }
1465
1466 if (is(v) && v !== null && v.length !== 0)
1467 e.setAttribute(n, '' + v, 2);
1468 else
1469 e.removeAttribute(n, 2);
1470 });
1471 },
1472
1473 setAttribs : function(e, o) {
1474 var t = this;
1475
1476 return this.run(e, function(e) {
1477 each(o, function(v, n) {
1478 t.setAttrib(e, n, v);
1479 });
1480 });
1481 },
1482
1483 getAttrib : function(e, n, dv) {
1484 var v, t = this;
1485
1486 e = t.get(e);
1487
1488 if (!e || e.nodeType !== 1)
1489 return false;
1490
1491 if (!is(dv))
1492 dv = '';
1493
1494 // Try the mce variant for these
1495 if (/^(src|href|style|coords|shape)$/.test(n)) {
1496 v = e.getAttribute("_mce_" + n);
1497
1498 if (v)
1499 return v;
1500 }
1501
1502 if (isIE && t.props[n]) {
1503 v = e[t.props[n]];
1504 v = v && v.nodeValue ? v.nodeValue : v;
1505 }
1506
1507 if (!v)
1508 v = e.getAttribute(n, 2);
1509
1510 // Check boolean attribs
1511 if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(n)) {
1512 if (e[t.props[n]] === true && v === '')
1513 return n;
1514
1515 return v ? n : '';
1516 }
1517
1518 // Inner input elements will override attributes on form elements
1519 if (e.nodeName === "FORM" && e.getAttributeNode(n))
1520 return e.getAttributeNode(n).nodeValue;
1521
1522 if (n === 'style') {
1523 v = v || e.style.cssText;
1524
1525 if (v) {
1526 v = t.serializeStyle(t.parseStyle(v), e.nodeName);
1527
1528 if (t.settings.keep_values && !t._isRes(v))
1529 e.setAttribute('_mce_style', v);
1530 }
1531 }
1532
1533 // Remove Apple and WebKit stuff
1534 if (isWebKit && n === "class" && v)
1535 v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, '');
1536
1537 // Handle IE issues
1538 if (isIE) {
1539 switch (n) {
1540 case 'rowspan':
1541 case 'colspan':
1542 // IE returns 1 as default value
1543 if (v === 1)
1544 v = '';
1545
1546 break;
1547
1548 case 'size':
1549 // IE returns +0 as default value for size
1550 if (v === '+0' || v === 20 || v === 0)
1551 v = '';
1552
1553 break;
1554
1555 case 'width':
1556 case 'height':
1557 case 'vspace':
1558 case 'checked':
1559 case 'disabled':
1560 case 'readonly':
1561 if (v === 0)
1562 v = '';
1563
1564 break;
1565
1566 case 'hspace':
1567 // IE returns -1 as default value
1568 if (v === -1)
1569 v = '';
1570
1571 break;
1572
1573 case 'maxlength':
1574 case 'tabindex':
1575 // IE returns default value
1576 if (v === 32768 || v === 2147483647 || v === '32768')
1577 v = '';
1578
1579 break;
1580
1581 case 'multiple':
1582 case 'compact':
1583 case 'noshade':
1584 case 'nowrap':
1585 if (v === 65535)
1586 return n;
1587
1588 return dv;
1589
1590 case 'shape':
1591 v = v.toLowerCase();
1592 break;
1593
1594 default:
1595 // IE has odd anonymous function for event attributes
1596 if (n.indexOf('on') === 0 && v)
1597 v = tinymce._replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1', '' + v);
1598 }
1599 }
1600
1601 return (v !== undefined && v !== null && v !== '') ? '' + v : dv;
1602 },
1603
1604 getPos : function(n, ro) {
1605 var t = this, x = 0, y = 0, e, d = t.doc, r;
1606
1607 n = t.get(n);
1608 ro = ro || d.body;
1609
1610 if (n) {
1611 // Use getBoundingClientRect on IE, Opera has it but it's not perfect
1612 if (isIE && !t.stdMode) {
1613 n = n.getBoundingClientRect();
1614 e = t.boxModel ? d.documentElement : d.body;
1615 x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border
1616 x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x;
1617
1618 return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x};
1619 }
1620
1621 r = n;
1622 while (r && r != ro && r.nodeType) {
1623 x += r.offsetLeft || 0;
1624 y += r.offsetTop || 0;
1625 r = r.offsetParent;
1626 }
1627
1628 r = n.parentNode;
1629 while (r && r != ro && r.nodeType) {
1630 x -= r.scrollLeft || 0;
1631 y -= r.scrollTop || 0;
1632 r = r.parentNode;
1633 }
1634 }
1635
1636 return {x : x, y : y};
1637 },
1638
1639 parseStyle : function(st) {
1640 var t = this, s = t.settings, o = {};
1641
1642 if (!st)
1643 return o;
1644
1645 function compress(p, s, ot) {
1646 var t, r, b, l;
1647
1648 // Get values and check it it needs compressing
1649 t = o[p + '-top' + s];
1650 if (!t)
1651 return;
1652
1653 r = o[p + '-right' + s];
1654 if (t != r)
1655 return;
1656
1657 b = o[p + '-bottom' + s];
1658 if (r != b)
1659 return;
1660
1661 l = o[p + '-left' + s];
1662 if (b != l)
1663 return;
1664
1665 // Compress
1666 o[ot] = l;
1667 delete o[p + '-top' + s];
1668 delete o[p + '-right' + s];
1669 delete o[p + '-bottom' + s];
1670 delete o[p + '-left' + s];
1671 };
1672
1673 function canCompress(key) {
1674 var split, value = o[key];
1675 if (!value || value.indexOf(' ') < 0) {
1676 return;
1677 }
1678 split = value.split(' ');
1679 if (each(split, function(v) { return v === split[0]; })) {
1680 o[key] = split[0];
1681 return true;
1682 } else {
1683 return false;
1684 }
1685 }
1686
1687 function compress2(ta, a, b, c) {
1688 var t;
1689
1690 if (!canCompress(a))
1691 return;
1692
1693 if (!canCompress(b))
1694 return;
1695
1696 if (!canCompress(c))
1697 return;
1698
1699 // Compress
1700 o[ta] = o[a] + ' ' + o[b] + ' ' + o[c];
1701 delete o[a];
1702 delete o[b];
1703 delete o[c];
1704 };
1705
1706 st = st.replace(/&(#?[a-z0-9]+);/g, '&$1_MCE_SEMI_'); // Protect entities
1707
1708 each(st.split(';'), function(v) {
1709 var sv, ur = [];
1710
1711 if (v) {
1712 v = v.replace(/_MCE_SEMI_/g, ';'); // Restore entities
1713 v = v.replace(/url\([^\)]+\)/g, function(v) {ur.push(v);return 'url(' + ur.length + ')';});
1714 v = v.split(':');
1715 sv = tinymce.trim(v[1]);
1716 sv = sv.replace(/url\(([^\)]+)\)/g, function(a, b) {return ur[parseInt(b) - 1];});
1717
1718 sv = sv.replace(/rgb\([^\)]+\)/g, function(v) {
1719 return t.toHex(v);
1720 });
1721
1722 if (s.url_converter) {
1723 sv = sv.replace(/url\([\'\"]?([^\)\'\"]+)[\'\"]?\)/g, function(x, c) {
1724 return 'url(' + s.url_converter.call(s.url_converter_scope || t, t.decode(c), 'style', null) + ')';
1725 });
1726 }
1727
1728 o[tinymce.trim(v[0]).toLowerCase()] = sv;
1729 }
1730 });
1731
1732 compress("border", "", "border");
1733 compress("border", "-width", "border-width");
1734 compress("border", "-color", "border-color");
1735 compress("border", "-style", "border-style");
1736 compress("padding", "", "padding");
1737 compress("margin", "", "margin");
1738 compress2('border', 'border-width', 'border-style', 'border-color');
1739
1740 if (isIE) {
1741 // Remove pointless border
1742 if (o.border == 'medium none')
1743 o.border = '';
1744 }
1745
1746 return o;
1747 },
1748
1749 serializeStyle : function(o, name) {
1750 var t = this, s = '';
1751
1752 function add(v, k) {
1753 if (k && v) {
1754 // Remove browser specific styles like -moz- or -webkit-
1755 if (k.indexOf('-') === 0)
1756 return;
1757
1758 switch (k) {
1759 case 'font-weight':
1760 // Opera will output bold as 700
1761 if (v == 700)
1762 v = 'bold';
1763
1764 break;
1765
1766 case 'color':
1767 case 'background-color':
1768 v = v.toLowerCase();
1769 break;
1770 }
1771
1772 s += (s ? ' ' : '') + k + ': ' + v + ';';
1773 }
1774 };
1775
1776 // Validate style output
1777 if (name && t._styles) {
1778 each(t._styles['*'], function(name) {
1779 add(o[name], name);
1780 });
1781
1782 each(t._styles[name.toLowerCase()], function(name) {
1783 add(o[name], name);
1784 });
1785 } else
1786 each(o, add);
1787
1788 return s;
1789 },
1790
1791 loadCSS : function(u) {
1792 var t = this, d = t.doc, head;
1793
1794 if (!u)
1795 u = '';
1796
1797 head = t.select('head')[0];
1798
1799 each(u.split(','), function(u) {
1800 var link;
1801
1802 if (t.files[u])
1803 return;
1804
1805 t.files[u] = true;
1806 link = t.create('link', {rel : 'stylesheet', href : tinymce._addVer(u)});
1807
1808 // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug
1809 // This fix seems to resolve that issue by realcing the document ones a stylesheet finishes loading
1810 // It's ugly but it seems to work fine.
1811 if (isIE && d.documentMode && d.recalc) {
1812 link.onload = function() {
1813 d.recalc();
1814 link.onload = null;
1815 };
1816 }
1817
1818 head.appendChild(link);
1819 });
1820 },
1821
1822 addClass : function(e, c) {
1823 return this.run(e, function(e) {
1824 var o;
1825
1826 if (!c)
1827 return 0;
1828
1829 if (this.hasClass(e, c))
1830 return e.className;
1831
1832 o = this.removeClass(e, c);
1833
1834 return e.className = (o != '' ? (o + ' ') : '') + c;
1835 });
1836 },
1837
1838 removeClass : function(e, c) {
1839 var t = this, re;
1840
1841 return t.run(e, function(e) {
1842 var v;
1843
1844 if (t.hasClass(e, c)) {
1845 if (!re)
1846 re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g");
1847
1848 v = e.className.replace(re, ' ');
1849 v = tinymce.trim(v != ' ' ? v : '');
1850
1851 e.className = v;
1852
1853 // Empty class attr
1854 if (!v) {
1855 e.removeAttribute('class');
1856 e.removeAttribute('className');
1857 }
1858
1859 return v;
1860 }
1861
1862 return e.className;
1863 });
1864 },
1865
1866 hasClass : function(n, c) {
1867 n = this.get(n);
1868
1869 if (!n || !c)
1870 return false;
1871
1872 return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1;
1873 },
1874
1875 show : function(e) {
1876 return this.setStyle(e, 'display', 'block');
1877 },
1878
1879 hide : function(e) {
1880 return this.setStyle(e, 'display', 'none');
1881 },
1882
1883 isHidden : function(e) {
1884 e = this.get(e);
1885
1886 return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none';
1887 },
1888
1889 uniqueId : function(p) {
1890 return (!p ? 'mce_' : p) + (this.counter++);
1891 },
1892
1893 setHTML : function(e, h) {
1894 var t = this;
1895
1896 return this.run(e, function(e) {
1897 var x, i, nl, n, p, x;
1898
1899 h = t.processHTML(h);
1900
1901 if (isIE) {
1902 function set() {
1903 // Remove all child nodes
1904 while (e.firstChild)
1905 e.removeChild(e.firstChild);
1906
1907 try {
1908 // IE will remove comments from the beginning
1909 // unless you padd the contents with something
1910 e.innerHTML = '<br />' + h;
1911 e.removeChild(e.firstChild);
1912 } catch (ex) {
1913 // 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
1914 // This seems to fix this problem
1915
1916 // Create new div with HTML contents and a BR infront to keep comments
1917 x = t.create('div');
1918 x.innerHTML = '<br />' + h;
1919
1920 // Add all children from div to target
1921 each (x.childNodes, function(n, i) {
1922 // Skip br element
1923 if (i)
1924 e.appendChild(n);
1925 });
1926 }
1927 };
1928
1929 // IE has a serious bug when it comes to paragraphs it can produce an invalid
1930 // DOM tree if contents like this <p><ul><li>Item 1</li></ul></p> is inserted
1931 // It seems to be that IE doesn't like a root block element placed inside another root block element
1932 if (t.settings.fix_ie_paragraphs)
1933 h = h.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi, '<p$1 _mce_keep="true">&nbsp;</p>');
1934
1935 set();
1936
1937 if (t.settings.fix_ie_paragraphs) {
1938 // Check for odd paragraphs this is a sign of a broken DOM
1939 nl = e.getElementsByTagName("p");
1940 for (i = nl.length - 1, x = 0; i >= 0; i--) {
1941 n = nl[i];
1942
1943 if (!n.hasChildNodes()) {
1944 if (!n._mce_keep) {
1945 x = 1; // Is broken
1946 break;
1947 }
1948
1949 n.removeAttribute('_mce_keep');
1950 }
1951 }
1952 }
1953
1954 // Time to fix the madness IE left us
1955 if (x) {
1956 // So if we replace the p elements with divs and mark them and then replace them back to paragraphs
1957 // after we use innerHTML we can fix the DOM tree
1958 h = h.replace(/<p ([^>]+)>|<p>/ig, '<div $1 _mce_tmp="1">');
1959 h = h.replace(/<\/p>/gi, '</div>');
1960
1961 // Set the new HTML with DIVs
1962 set();
1963
1964 // Replace all DIV elements with the _mce_tmp attibute back to paragraphs
1965 // This is needed since IE has a annoying bug see above for details
1966 // This is a slow process but it has to be done. :(
1967 if (t.settings.fix_ie_paragraphs) {
1968 nl = e.getElementsByTagName("DIV");
1969 for (i = nl.length - 1; i >= 0; i--) {
1970 n = nl[i];
1971
1972 // Is it a temp div
1973 if (n.hasAttribute("_mce_tmp")) {
1974 // Create new paragraph
1975 p = t.doc.createElement('p');
1976
1977 // Copy all attributes
1978 n.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi, function(a, b) {
1979 var v;
1980
1981 if (b !== '_mce_tmp') {
1982 v = n.getAttribute(b);
1983
1984 if (!v && b === 'class')
1985 v = n.className;
1986
1987 p.setAttribute(b, v);
1988 }
1989 });
1990
1991 // Append all children to new paragraph
1992 for (x = 0; x<n.childNodes.length; x++)
1993 p.appendChild(n.childNodes[x].cloneNode(true));
1994
1995 // Replace div with new paragraph
1996 n.swapNode(p);
1997 }
1998 }
1999 }
2000 }
2001 } else
2002 e.innerHTML = h;
2003
2004 return h;
2005 });
2006 },
2007
2008 processHTML : function(h) {
2009 var t = this, s = t.settings, codeBlocks = [];
2010
2011 if (!s.process_html)
2012 return h;
2013
2014 if (isIE) {
2015 h = h.replace(/&apos;/g, '&#39;'); // IE can't handle apos
2016 h = h.replace(/\s+(disabled|checked|readonly|selected)\s*=\s*[\"\']?(false|0)[\"\']?/gi, ''); // IE doesn't handle default values correct
2017 }
2018
2019 // Force tags open, and on IE9 replace $1$2 that got left behind due to bugs in their RegExp engine
2020 h = tinymce._replace(/<a( )([^>]+)\/>|<a\/>/gi, '<a$1$2></a>', h); // Force open
2021
2022 // Store away src and href in _mce_src and mce_href since browsers mess them up
2023 if (s.keep_values) {
2024 // Wrap scripts and styles in comments for serialization purposes
2025 if (/<script|noscript|style/i.test(h)) {
2026 function trim(s) {
2027 // Remove prefix and suffix code for element
2028 s = s.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n');
2029 s = s.replace(/^[\r\n]*|[\r\n]*$/g, '');
2030 s = s.replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, '');
2031 s = s.replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, '');
2032
2033 return s;
2034 };
2035
2036 // Wrap the script contents in CDATA and keep them from executing
2037 h = h.replace(/<script([^>]+|)>([\s\S]*?)<\/script>/gi, function(v, attribs, text) {
2038 // Force type attribute
2039 if (!attribs)
2040 attribs = ' type="text/javascript"';
2041
2042 // Convert the src attribute of the scripts
2043 attribs = attribs.replace(/src=\"([^\"]+)\"?/i, function(a, url) {
2044 if (s.url_converter)
2045 url = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(url), 'src', 'script'));
2046
2047 return '_mce_src="' + url + '"';
2048 });
2049
2050 // Wrap text contents
2051 if (tinymce.trim(text)) {
2052 codeBlocks.push(trim(text));
2053 text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n// -->';
2054 }
2055
2056 return '<mce:script' + attribs + '>' + text + '</mce:script>';
2057 });
2058
2059 // Wrap style elements
2060 h = h.replace(/<style([^>]+|)>([\s\S]*?)<\/style>/gi, function(v, attribs, text) {
2061 // Wrap text contents
2062 if (text) {
2063 codeBlocks.push(trim(text));
2064 text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n-->';
2065 }
2066
2067 return '<mce:style' + attribs + '>' + text + '</mce:style><style ' + attribs + ' _mce_bogus="1">' + text + '</style>';
2068 });
2069
2070 // Wrap noscript elements
2071 h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {
2072 return '<mce:noscript' + attribs + '><!--' + t.encode(text).replace(/--/g, '&#45;&#45;') + '--></mce:noscript>';
2073 });
2074 }
2075
2076 h = tinymce._replace(/<!\[CDATA\[([\s\S]+)\]\]>/g, '<!--[CDATA[$1]]-->', h);
2077
2078 // This function processes the attributes in the HTML string to force boolean
2079 // attributes to the attr="attr" format and convert style, src and href to _mce_ versions
2080 function processTags(html) {
2081 return html.replace(tagRegExp, function(match, elm_name, attrs, end) {
2082 return '<' + elm_name + attrs.replace(attrRegExp, function(match, name, value, val2, val3) {
2083 var mceValue;
2084
2085 name = name.toLowerCase();
2086 value = value || val2 || val3 || "";
2087
2088 // Treat boolean attributes
2089 if (boolAttrs[name]) {
2090 // false or 0 is treated as a missing attribute
2091 if (value === 'false' || value === '0')
2092 return;
2093
2094 return name + '="' + name + '"';
2095 }
2096
2097 // Is attribute one that needs special treatment
2098 if (mceAttribs[name] && attrs.indexOf('_mce_' + name) == -1) {
2099 mceValue = t.decode(value);
2100
2101 // Convert URLs to relative/absolute ones
2102 if (s.url_converter && (name == "src" || name == "href"))
2103 mceValue = s.url_converter.call(s.url_converter_scope || t, mceValue, name, elm_name);
2104
2105 // Process styles lowercases them and compresses them
2106 if (name == 'style')
2107 mceValue = t.serializeStyle(t.parseStyle(mceValue), name);
2108
2109 return name + '="' + value + '"' + ' _mce_' + name + '="' + t.encode(mceValue) + '"';
2110 }
2111
2112 return match;
2113 }) + end + '>';
2114 });
2115 };
2116
2117 h = processTags(h);
2118
2119 // Restore script blocks
2120 h = h.replace(/MCE_SCRIPT:([0-9]+)/g, function(val, idx) {
2121 return codeBlocks[idx];
2122 });
2123 }
2124
2125 return h;
2126 },
2127
2128 getOuterHTML : function(e) {
2129 var d;
2130
2131 e = this.get(e);
2132
2133 if (!e)
2134 return null;
2135
2136 if (e.outerHTML !== undefined)
2137 return e.outerHTML;
2138
2139 d = (e.ownerDocument || this.doc).createElement("body");
2140 d.appendChild(e.cloneNode(true));
2141
2142 return d.innerHTML;
2143 },
2144
2145 setOuterHTML : function(e, h, d) {
2146 var t = this;
2147
2148 function setHTML(e, h, d) {
2149 var n, tp;
2150
2151 tp = d.createElement("body");
2152 tp.innerHTML = h;
2153
2154 n = tp.lastChild;
2155 while (n) {
2156 t.insertAfter(n.cloneNode(true), e);
2157 n = n.previousSibling;
2158 }
2159
2160 t.remove(e);
2161 };
2162
2163 return this.run(e, function(e) {
2164 e = t.get(e);
2165
2166 // Only set HTML on elements
2167 if (e.nodeType == 1) {
2168 d = d || e.ownerDocument || t.doc;
2169
2170 if (isIE) {
2171 try {
2172 // Try outerHTML for IE it sometimes produces an unknown runtime error
2173 if (isIE && e.nodeType == 1)
2174 e.outerHTML = h;
2175 else
2176 setHTML(e, h, d);
2177 } catch (ex) {
2178 // Fix for unknown runtime error
2179 setHTML(e, h, d);
2180 }
2181 } else
2182 setHTML(e, h, d);
2183 }
2184 });
2185 },
2186
2187 decode : function(s) {
2188 var e, n, v;
2189
2190 // Look for entities to decode
2191 if (/&[\w#]+;/.test(s)) {
2192 // Decode the entities using a div element not super efficient but less code
2193 e = this.doc.createElement("div");
2194 e.innerHTML = s;
2195 n = e.firstChild;
2196 v = '';
2197
2198 if (n) {
2199 do {
2200 v += n.nodeValue;
2201 } while (n = n.nextSibling);
2202 }
2203
2204 return v || s;
2205 }
2206
2207 return s;
2208 },
2209
2210 encode : function(str) {
2211 return ('' + str).replace(encodeCharsRe, function(chr) {
2212 return encodedChars[chr];
2213 });
2214 },
2215
2216 insertAfter : function(node, reference_node) {
2217 reference_node = this.get(reference_node);
2218
2219 return this.run(node, function(node) {
2220 var parent, nextSibling;
2221
2222 parent = reference_node.parentNode;
2223 nextSibling = reference_node.nextSibling;
2224
2225 if (nextSibling)
2226 parent.insertBefore(node, nextSibling);
2227 else
2228 parent.appendChild(node);
2229
2230 return node;
2231 });
2232 },
2233
2234 isBlock : function(n) {
2235 if (n.nodeType && n.nodeType !== 1)
2236 return false;
2237
2238 n = n.nodeName || n;
2239
2240 return blockRe.test(n);
2241 },
2242
2243 replace : function(n, o, k) {
2244 var t = this;
2245
2246 if (is(o, 'array'))
2247 n = n.cloneNode(true);
2248
2249 return t.run(o, function(o) {
2250 if (k) {
2251 each(tinymce.grep(o.childNodes), function(c) {
2252 n.appendChild(c);
2253 });
2254 }
2255
2256 return o.parentNode.replaceChild(n, o);
2257 });
2258 },
2259
2260 rename : function(elm, name) {
2261 var t = this, newElm;
2262
2263 if (elm.nodeName != name.toUpperCase()) {
2264 // Rename block element
2265 newElm = t.create(name);
2266
2267 // Copy attribs to new block
2268 each(t.getAttribs(elm), function(attr_node) {
2269 t.setAttrib(newElm, attr_node.nodeName, t.getAttrib(elm, attr_node.nodeName));
2270 });
2271
2272 // Replace block
2273 t.replace(newElm, elm, 1);
2274 }
2275
2276 return newElm || elm;
2277 },
2278
2279 findCommonAncestor : function(a, b) {
2280 var ps = a, pe;
2281
2282 while (ps) {
2283 pe = b;
2284
2285 while (pe && ps != pe)
2286 pe = pe.parentNode;
2287
2288 if (ps == pe)
2289 break;
2290
2291 ps = ps.parentNode;
2292 }
2293
2294 if (!ps && a.ownerDocument)
2295 return a.ownerDocument.documentElement;
2296
2297 return ps;
2298 },
2299
2300 toHex : function(s) {
2301 var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);
2302
2303 function hex(s) {
2304 s = parseInt(s).toString(16);
2305
2306 return s.length > 1 ? s : '0' + s; // 0 -> 00
2307 };
2308
2309 if (c) {
2310 s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);
2311
2312 return s;
2313 }
2314
2315 return s;
2316 },
2317
2318 getClasses : function() {
2319 var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;
2320
2321 if (t.classes)
2322 return t.classes;
2323
2324 function addClasses(s) {
2325 // IE style imports
2326 each(s.imports, function(r) {
2327 addClasses(r);
2328 });
2329
2330 each(s.cssRules || s.rules, function(r) {
2331 // Real type or fake it on IE
2332 switch (r.type || 1) {
2333 // Rule
2334 case 1:
2335 if (r.selectorText) {
2336 each(r.selectorText.split(','), function(v) {
2337 v = v.replace(/^\s*|\s*$|^\s\./g, "");
2338
2339 // Is internal or it doesn't contain a class
2340 if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v))
2341 return;
2342
2343 // Remove everything but class name
2344 ov = v;
2345 v = tinymce._replace(/.*\.([a-z0-9_\-]+).*/i, '$1', v);
2346
2347 // Filter classes
2348 if (f && !(v = f(v, ov)))
2349 return;
2350
2351 if (!lo[v]) {
2352 cl.push({'class' : v});
2353 lo[v] = 1;
2354 }
2355 });
2356 }
2357 break;
2358
2359 // Import
2360 case 3:
2361 addClasses(r.styleSheet);
2362 break;
2363 }
2364 });
2365 };
2366
2367 try {
2368 each(t.doc.styleSheets, addClasses);
2369 } catch (ex) {
2370 // Ignore
2371 }
2372
2373 if (cl.length > 0)
2374 t.classes = cl;
2375
2376 return cl;
2377 },
2378
2379 run : function(e, f, s) {
2380 var t = this, o;
2381
2382 if (t.doc && typeof(e) === 'string')
2383 e = t.get(e);
2384
2385 if (!e)
2386 return false;
2387
2388 s = s || this;
2389 if (!e.nodeType && (e.length || e.length === 0)) {
2390 o = [];
2391
2392 each(e, function(e, i) {
2393 if (e) {
2394 if (typeof(e) == 'string')
2395 e = t.doc.getElementById(e);
2396
2397 o.push(f.call(s, e, i));
2398 }
2399 });
2400
2401 return o;
2402 }
2403
2404 return f.call(s, e);
2405 },
2406
2407 getAttribs : function(n) {
2408 var o;
2409
2410 n = this.get(n);
2411
2412 if (!n)
2413 return [];
2414
2415 if (isIE) {
2416 o = [];
2417
2418 // Object will throw exception in IE
2419 if (n.nodeName == 'OBJECT')
2420 return n.attributes;
2421
2422 // IE doesn't keep the selected attribute if you clone option elements
2423 if (n.nodeName === 'OPTION' && this.getAttrib(n, 'selected'))
2424 o.push({specified : 1, nodeName : 'selected'});
2425
2426 // It's crazy that this is faster in IE but it's because it returns all attributes all the time
2427 n.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi, '').replace(/[\w:\-]+/gi, function(a) {
2428 o.push({specified : 1, nodeName : a});
2429 });
2430
2431 return o;
2432 }
2433
2434 return n.attributes;
2435 },
2436
2437 destroy : function(s) {
2438 var t = this;
2439
2440 if (t.events)
2441 t.events.destroy();
2442
2443 t.win = t.doc = t.root = t.events = null;
2444
2445 // Manual destroy then remove unload handler
2446 if (!s)
2447 tinymce.removeUnload(t.destroy);
2448 },
2449
2450 createRng : function() {
2451 var d = this.doc;
2452
2453 return d.createRange ? d.createRange() : new tinymce.dom.Range(this);
2454 },
2455
2456 nodeIndex : function(node, normalized) {
2457 var idx = 0, lastNodeType, lastNode, nodeType, nodeValueExists;
2458
2459 if (node) {
2460 for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {
2461 nodeType = node.nodeType;
2462
2463 // Normalize text nodes
2464 if (normalized && nodeType == 3) {
2465 // ensure that text nodes that have been removed are handled correctly in Internet Explorer.
2466 // (the nodeValue attribute will not exist, and will error here).
2467 nodeValueExists = false;
2468 try {nodeValueExists == node.nodeValue.length} catch (c) {}
2469 if (nodeType == lastNodeType || !nodeValueExists)
2470 continue;
2471 }
2472 idx++;
2473 lastNodeType = nodeType;
2474 }
2475 }
2476
2477 return idx;
2478 },
2479
2480 split : function(pe, e, re) {
2481 var t = this, r = t.createRng(), bef, aft, pa;
2482
2483 // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sense
2484 // but we don't want that in our code since it serves no purpose for the end user
2485 // For example if this is chopped:
2486 // <p>text 1<span><b>CHOP</b></span>text 2</p>
2487 // would produce:
2488 // <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>
2489 // this function will then trim of empty edges and produce:
2490 // <p>text 1</p><b>CHOP</b><p>text 2</p>
2491 function trim(node) {
2492 var i, children = node.childNodes;
2493
2494 if (node.nodeType == 1 && node.getAttribute('_mce_type') == 'bookmark')
2495 return;
2496
2497 for (i = children.length - 1; i >= 0; i--)
2498 trim(children[i]);
2499
2500 if (node.nodeType != 9) {
2501 // Keep non whitespace text nodes
2502 if (node.nodeType == 3 && node.nodeValue.length > 0) {
2503 // If parent element isn't a block or there isn't any useful contents for example "<p> </p>"
2504 if (!t.isBlock(node.parentNode) || tinymce.trim(node.nodeValue).length > 0)
2505 return;
2506 }
2507
2508 if (node.nodeType == 1) {
2509 // If the only child is a bookmark then move it up
2510 children = node.childNodes;
2511 if (children.length == 1 && children[0] && children[0].nodeType == 1 && children[0].getAttribute('_mce_type') == 'bookmark')
2512 node.parentNode.insertBefore(children[0], node);
2513
2514 // Keep non empty elements or img, hr etc
2515 if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName))
2516 return;
2517 }
2518
2519 t.remove(node);
2520 }
2521
2522 return node;
2523 };
2524
2525 if (pe && e) {
2526 // Get before chunk
2527 r.setStart(pe.parentNode, t.nodeIndex(pe));
2528 r.setEnd(e.parentNode, t.nodeIndex(e));
2529 bef = r.extractContents();
2530
2531 // Get after chunk
2532 r = t.createRng();
2533 r.setStart(e.parentNode, t.nodeIndex(e) + 1);
2534 r.setEnd(pe.parentNode, t.nodeIndex(pe) + 1);
2535 aft = r.extractContents();
2536
2537 // Insert before chunk
2538 pa = pe.parentNode;
2539 pa.insertBefore(trim(bef), pe);
2540
2541 // Insert middle chunk
2542 if (re)
2543 pa.replaceChild(re, e);
2544 else
2545 pa.insertBefore(e, pe);
2546
2547 // Insert after chunk
2548 pa.insertBefore(trim(aft), pe);
2549 t.remove(pe);
2550
2551 return re || e;
2552 }
2553 },
2554
2555 bind : function(target, name, func, scope) {
2556 var t = this;
2557
2558 if (!t.events)
2559 t.events = new tinymce.dom.EventUtils();
2560
2561 return t.events.add(target, name, func, scope || this);
2562 },
2563
2564 unbind : function(target, name, func) {
2565 var t = this;
2566
2567 if (!t.events)
2568 t.events = new tinymce.dom.EventUtils();
2569
2570 return t.events.remove(target, name, func);
2571 },
2572
2573
2574 _findSib : function(node, selector, name) {
2575 var t = this, f = selector;
2576
2577 if (node) {
2578 // If expression make a function of it using is
2579 if (is(f, 'string')) {
2580 f = function(node) {
2581 return t.is(node, selector);
2582 };
2583 }
2584
2585 // Loop all siblings
2586 for (node = node[name]; node; node = node[name]) {
2587 if (f(node))
2588 return node;
2589 }
2590 }
2591
2592 return null;
2593 },
2594
2595 _isRes : function(c) {
2596 // Is live resizble element
2597 return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c);
2598 }
2599
2600 /*
2601 walk : function(n, f, s) {
2602 var d = this.doc, w;
2603
2604 if (d.createTreeWalker) {
2605 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
2606
2607 while ((n = w.nextNode()) != null)
2608 f.call(s || this, n);
2609 } else
2610 tinymce.walk(n, f, 'childNodes', s);
2611 }
2612 */
2613
2614 /*
2615 toRGB : function(s) {
2616 var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);
2617
2618 if (c) {
2619 // #FFF -> #FFFFFF
2620 if (!is(c[3]))
2621 c[3] = c[2] = c[1];
2622
2623 return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";
2624 }
2625
2626 return s;
2627 }
2628 */
2629 });
2630
2631 tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});
2632 })(tinymce);
2633
2634 (function(ns) {
2635 // Range constructor
2636 function Range(dom) {
2637 var t = this,
2638 doc = dom.doc,
2639 EXTRACT = 0,
2640 CLONE = 1,
2641 DELETE = 2,
2642 TRUE = true,
2643 FALSE = false,
2644 START_OFFSET = 'startOffset',
2645 START_CONTAINER = 'startContainer',
2646 END_CONTAINER = 'endContainer',
2647 END_OFFSET = 'endOffset',
2648 extend = tinymce.extend,
2649 nodeIndex = dom.nodeIndex;
2650
2651 extend(t, {
2652 // Inital states
2653 startContainer : doc,
2654 startOffset : 0,
2655 endContainer : doc,
2656 endOffset : 0,
2657 collapsed : TRUE,
2658 commonAncestorContainer : doc,
2659
2660 // Range constants
2661 START_TO_START : 0,
2662 START_TO_END : 1,
2663 END_TO_END : 2,
2664 END_TO_START : 3,
2665
2666 // Public methods
2667 setStart : setStart,
2668 setEnd : setEnd,
2669 setStartBefore : setStartBefore,
2670 setStartAfter : setStartAfter,
2671 setEndBefore : setEndBefore,
2672 setEndAfter : setEndAfter,
2673 collapse : collapse,
2674 selectNode : selectNode,
2675 selectNodeContents : selectNodeContents,
2676 compareBoundaryPoints : compareBoundaryPoints,
2677 deleteContents : deleteContents,
2678 extractContents : extractContents,
2679 cloneContents : cloneContents,
2680 insertNode : insertNode,
2681 surroundContents : surroundContents,
2682 cloneRange : cloneRange
2683 });
2684
2685 function setStart(n, o) {
2686 _setEndPoint(TRUE, n, o);
2687 };
2688
2689 function setEnd(n, o) {
2690 _setEndPoint(FALSE, n, o);
2691 };
2692
2693 function setStartBefore(n) {
2694 setStart(n.parentNode, nodeIndex(n));
2695 };
2696
2697 function setStartAfter(n) {
2698 setStart(n.parentNode, nodeIndex(n) + 1);
2699 };
2700
2701 function setEndBefore(n) {
2702 setEnd(n.parentNode, nodeIndex(n));
2703 };
2704
2705 function setEndAfter(n) {
2706 setEnd(n.parentNode, nodeIndex(n) + 1);
2707 };
2708
2709 function collapse(ts) {
2710 if (ts) {
2711 t[END_CONTAINER] = t[START_CONTAINER];
2712 t[END_OFFSET] = t[START_OFFSET];
2713 } else {
2714 t[START_CONTAINER] = t[END_CONTAINER];
2715 t[START_OFFSET] = t[END_OFFSET];
2716 }
2717
2718 t.collapsed = TRUE;
2719 };
2720
2721 function selectNode(n) {
2722 setStartBefore(n);
2723 setEndAfter(n);
2724 };
2725
2726 function selectNodeContents(n) {
2727 setStart(n, 0);
2728 setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);
2729 };
2730
2731 function compareBoundaryPoints(h, r) {
2732 var sc = t[START_CONTAINER], so = t[START_OFFSET], ec = t[END_CONTAINER], eo = t[END_OFFSET],
2733 rsc = r.startContainer, rso = r.startOffset, rec = r.endContainer, reo = r.endOffset;
2734
2735 // Check START_TO_START
2736 if (h === 0)
2737 return _compareBoundaryPoints(sc, so, rsc, rso);
2738
2739 // Check START_TO_END
2740 if (h === 1)
2741 return _compareBoundaryPoints(ec, eo, rsc, rso);
2742
2743 // Check END_TO_END
2744 if (h === 2)
2745 return _compareBoundaryPoints(ec, eo, rec, reo);
2746
2747 // Check END_TO_START
2748 if (h === 3)
2749 return _compareBoundaryPoints(sc, so, rec, reo);
2750 };
2751
2752 function deleteContents() {
2753 _traverse(DELETE);
2754 };
2755
2756 function extractContents() {
2757 return _traverse(EXTRACT);
2758 };
2759
2760 function cloneContents() {
2761 return _traverse(CLONE);
2762 };
2763
2764 function insertNode(n) {
2765 var startContainer = this[START_CONTAINER],
2766 startOffset = this[START_OFFSET], nn, o;
2767
2768 // Node is TEXT_NODE or CDATA
2769 if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {
2770 if (!startOffset) {
2771 // At the start of text
2772 startContainer.parentNode.insertBefore(n, startContainer);
2773 } else if (startOffset >= startContainer.nodeValue.length) {
2774 // At the end of text
2775 dom.insertAfter(n, startContainer);
2776 } else {
2777 // Middle, need to split
2778 nn = startContainer.splitText(startOffset);
2779 startContainer.parentNode.insertBefore(n, nn);
2780 }
2781 } else {
2782 // Insert element node
2783 if (startContainer.childNodes.length > 0)
2784 o = startContainer.childNodes[startOffset];
2785
2786 if (o)
2787 startContainer.insertBefore(n, o);
2788 else
2789 startContainer.appendChild(n);
2790 }
2791 };
2792
2793 function surroundContents(n) {
2794 var f = t.extractContents();
2795
2796 t.insertNode(n);
2797 n.appendChild(f);
2798 t.selectNode(n);
2799 };
2800
2801 function cloneRange() {
2802 return extend(new Range(dom), {
2803 startContainer : t[START_CONTAINER],
2804 startOffset : t[START_OFFSET],
2805 endContainer : t[END_CONTAINER],
2806 endOffset : t[END_OFFSET],
2807 collapsed : t.collapsed,
2808 commonAncestorContainer : t.commonAncestorContainer
2809 });
2810 };
2811
2812 // Private methods
2813
2814 function _getSelectedNode(container, offset) {
2815 var child;
2816
2817 if (container.nodeType == 3 /* TEXT_NODE */)
2818 return container;
2819
2820 if (offset < 0)
2821 return container;
2822
2823 child = container.firstChild;
2824 while (child && offset > 0) {
2825 --offset;
2826 child = child.nextSibling;
2827 }
2828
2829 if (child)
2830 return child;
2831
2832 return container;
2833 };
2834
2835 function _isCollapsed() {
2836 return (t[START_CONTAINER] == t[END_CONTAINER] && t[START_OFFSET] == t[END_OFFSET]);
2837 };
2838
2839 function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {
2840 var c, offsetC, n, cmnRoot, childA, childB;
2841
2842 // In the first case the boundary-points have the same container. A is before B
2843 // if its offset is less than the offset of B, A is equal to B if its offset is
2844 // equal to the offset of B, and A is after B if its offset is greater than the
2845 // offset of B.
2846 if (containerA == containerB) {
2847 if (offsetA == offsetB)
2848 return 0; // equal
2849
2850 if (offsetA < offsetB)
2851 return -1; // before
2852
2853 return 1; // after
2854 }
2855
2856 // In the second case a child node C of the container of A is an ancestor
2857 // container of B. In this case, A is before B if the offset of A is less than or
2858 // equal to the index of the child node C and A is after B otherwise.
2859 c = containerB;
2860 while (c && c.parentNode != containerA)
2861 c = c.parentNode;
2862
2863 if (c) {
2864 offsetC = 0;
2865 n = containerA.firstChild;
2866
2867 while (n != c && offsetC < offsetA) {
2868 offsetC++;
2869 n = n.nextSibling;
2870 }
2871
2872 if (offsetA <= offsetC)
2873 return -1; // before
2874
2875 return 1; // after
2876 }
2877
2878 // In the third case a child node C of the container of B is an ancestor container
2879 // of A. In this case, A is before B if the index of the child node C is less than