summaryrefslogtreecommitdiff
path: root/public/javascripts/tiny_mce/tiny_mce_src.js
diff options
context:
space:
mode:
authorerdgeist <erdgeist@erdgeist.org>2026-06-27 16:58:53 +0200
committererdgeist <erdgeist@erdgeist.org>2026-06-27 16:58:53 +0200
commit420506e58fdfc84f1a5bede0a01dedf0af3bb4f3 (patch)
tree57726b40e8aa9ccf80f874f39d3facefc0331420 /public/javascripts/tiny_mce/tiny_mce_src.js
parent241d5e91b2b6716e2861cc77d319c3d3568343a8 (diff)
Stage 7: Rails 7.2 → 8.1 on Ruby 3.2.11
- Bump Rails to 8.1.3 (Ruby unchanged at 3.2.11, new gemset rails8-upgrade) - config.load_defaults 8.1; merge app:update diffs for all environment files - Remove routing-filter 0.7.0; replace with native scope '(:locale)' in routes.rb and default_url_options in ApplicationController - Delete config/initializers/routing_filter_rails71_patch.rb - Replace vendored TinyMCE 3.x (~200 files) with tinymce-rails ~> 8.3; migrate admin_interface.js from jQuery .tinymce()/advanced theme to tinymce.init(); add config/tinymce.yml; note: TinyMCE 7+ is GPL - rails-i18n ~> 8.0 added explicitly (previously indirect dependency) - awesome_nested_set, acts-as-taggable-on pinned to git main/master (gemspec activerecord < 8.1 ceiling; no functional incompatibility; repin to version once upstream releases updated gemspecs) - globalize ~> 7.0, libxml-ruby ~> 5.0, nokogiri ~> 1.18, pg ~> 1.5 - sass-rails, coffee-rails, uglifier moved from :assets group to main (Sprockets 4 convention; :assets group no longer meaningful) - Node: head, draft, lock_owner marked belongs_to optional: true - Page: node, user, editor marked belongs_to optional: true - Static assets in public/images/ and public/javascripts/ referenced via plain HTML tags; Rails 8 load_defaults raises on pipeline helpers for undeclared assets - sessions_controller_test.rb: remove stale require and dead rescue_action - users_controller_test.rb: assert button[type=submit] not input[type=submit] (Rails 8 button_to renders <button> not <input>) - test_helper.rb: node.reload after children.create! (awesome_nested_set 3.9.0 does not refresh parent in memory after callback) - 129 runs, 339 assertions, 3 failures, 0 errors — identical baseline to 7.2
Diffstat (limited to 'public/javascripts/tiny_mce/tiny_mce_src.js')
-rw-r--r--public/javascripts/tiny_mce/tiny_mce_src.js12428
1 files changed, 0 insertions, 12428 deletions
diff --git a/public/javascripts/tiny_mce/tiny_mce_src.js b/public/javascripts/tiny_mce/tiny_mce_src.js
deleted file mode 100644
index 3fe768b..0000000
--- a/public/javascripts/tiny_mce/tiny_mce_src.js
+++ /dev/null
@@ -1,12428 +0,0 @@
1var tinymce = {
2 majorVersion : '3',
3 minorVersion : '2.7',
4 releaseDate : '2009-09-22',
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 t.isOpera = w.opera && opera.buildNumber;
10
11 t.isWebKit = /WebKit/.test(ua);
12
13 t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName);
14
15 t.isIE6 = t.isIE && /MSIE [56]/.test(ua);
16
17 t.isGecko = !t.isWebKit && /Gecko/.test(ua);
18
19 t.isMac = ua.indexOf('Mac') != -1;
20
21 t.isAir = /adobeair/i.test(ua);
22
23 // TinyMCE .NET webcontrol might be setting the values for TinyMCE
24 if (w.tinyMCEPreInit) {
25 t.suffix = tinyMCEPreInit.suffix;
26 t.baseURL = tinyMCEPreInit.base;
27 t.query = tinyMCEPreInit.query;
28 return;
29 }
30
31 // Get suffix and base
32 t.suffix = '';
33
34 // If base element found, add that infront of baseURL
35 nl = d.getElementsByTagName('base');
36 for (i=0; i<nl.length; i++) {
37 if (v = nl[i].href) {
38 // Host only value like http://site.com or http://site.com:8008
39 if (/^https?:\/\/[^\/]+$/.test(v))
40 v += '/';
41
42 base = v ? v.match(/.*\//)[0] : ''; // Get only directory
43 }
44 }
45
46 function getBase(n) {
47 if (n.src && /tiny_mce(|_gzip|_jquery|_prototype)(_dev|_src)?.js/.test(n.src)) {
48 if (/_(src|dev)\.js/g.test(n.src))
49 t.suffix = '_src';
50
51 if ((p = n.src.indexOf('?')) != -1)
52 t.query = n.src.substring(p + 1);
53
54 t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));
55
56 // If path to script is relative and a base href was found add that one infront
57 // the src property will always be an absolute one on non IE browsers and IE 8
58 // so this logic will basically only be executed on older IE versions
59 if (base && t.baseURL.indexOf('://') == -1 && t.baseURL.indexOf('/') !== 0)
60 t.baseURL = base + t.baseURL;
61
62 return t.baseURL;
63 }
64
65 return null;
66 };
67
68 // Check document
69 nl = d.getElementsByTagName('script');
70 for (i=0; i<nl.length; i++) {
71 if (getBase(nl[i]))
72 return;
73 }
74
75 // Check head
76 n = d.getElementsByTagName('head')[0];
77 if (n) {
78 nl = n.getElementsByTagName('script');
79 for (i=0; i<nl.length; i++) {
80 if (getBase(nl[i]))
81 return;
82 }
83 }
84
85 return;
86 },
87
88 is : function(o, t) {
89 var n = typeof(o);
90
91 if (!t)
92 return n != 'undefined';
93
94 if (t == 'array' && (o.hasOwnProperty && o instanceof Array))
95 return true;
96
97 return n == t;
98 },
99
100 each : function(o, cb, s) {
101 var n, l;
102
103 if (!o)
104 return 0;
105
106 s = s || o;
107
108 if (typeof(o.length) != 'undefined') {
109 // Indexed arrays, needed for Safari
110 for (n=0, l = o.length; n<l; n++) {
111 if (cb.call(s, o[n], n, o) === false)
112 return 0;
113 }
114 } else {
115 // Hashtables
116 for (n in o) {
117 if (o.hasOwnProperty(n)) {
118 if (cb.call(s, o[n], n, o) === false)
119 return 0;
120 }
121 }
122 }
123
124 return 1;
125 },
126
127
128 trim : function(s) {
129 return (s ? '' + s : '').replace(/^\s*|\s*$/g, '');
130 },
131
132 create : function(s, p) {
133 var t = this, sp, ns, cn, scn, c, de = 0;
134
135 // Parse : <prefix> <class>:<super class>
136 s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);
137 cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name
138
139 // Create namespace for new class
140 ns = t.createNS(s[3].replace(/\.\w+$/, ''));
141
142 // Class already exists
143 if (ns[cn])
144 return;
145
146 // Make pure static class
147 if (s[2] == 'static') {
148 ns[cn] = p;
149
150 if (this.onCreate)
151 this.onCreate(s[2], s[3], ns[cn]);
152
153 return;
154 }
155
156 // Create default constructor
157 if (!p[cn]) {
158 p[cn] = function() {};
159 de = 1;
160 }
161
162 // Add constructor and methods
163 ns[cn] = p[cn];
164 t.extend(ns[cn].prototype, p);
165
166 // Extend
167 if (s[5]) {
168 sp = t.resolve(s[5]).prototype;
169 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name
170
171 // Extend constructor
172 c = ns[cn];
173 if (de) {
174 // Add passthrough constructor
175 ns[cn] = function() {
176 return sp[scn].apply(this, arguments);
177 };
178 } else {
179 // Add inherit constructor
180 ns[cn] = function() {
181 this.parent = sp[scn];
182 return c.apply(this, arguments);
183 };
184 }
185 ns[cn].prototype[cn] = ns[cn];
186
187 // Add super methods
188 t.each(sp, function(f, n) {
189 ns[cn].prototype[n] = sp[n];
190 });
191
192 // Add overridden methods
193 t.each(p, function(f, n) {
194 // Extend methods if needed
195 if (sp[n]) {
196 ns[cn].prototype[n] = function() {
197 this.parent = sp[n];
198 return f.apply(this, arguments);
199 };
200 } else {
201 if (n != cn)
202 ns[cn].prototype[n] = f;
203 }
204 });
205 }
206
207 // Add static methods
208 t.each(p['static'], function(f, n) {
209 ns[cn][n] = f;
210 });
211
212 if (this.onCreate)
213 this.onCreate(s[2], s[3], ns[cn].prototype);
214 },
215
216 walk : function(o, f, n, s) {
217 s = s || this;
218
219 if (o) {
220 if (n)
221 o = o[n];
222
223 tinymce.each(o, function(o, i) {
224 if (f.call(s, o, i, n) === false)
225 return false;
226
227 tinymce.walk(o, f, n, s);
228 });
229 }
230 },
231
232 createNS : function(n, o) {
233 var i, v;
234
235 o = o || window;
236
237 n = n.split('.');
238 for (i=0; i<n.length; i++) {
239 v = n[i];
240
241 if (!o[v])
242 o[v] = {};
243
244 o = o[v];
245 }
246
247 return o;
248 },
249
250 resolve : function(n, o) {
251 var i, l;
252
253 o = o || window;
254
255 n = n.split('.');
256 for (i = 0, l = n.length; i < l; i++) {
257 o = o[n[i]];
258
259 if (!o)
260 break;
261 }
262
263 return o;
264 },
265
266 addUnload : function(f, s) {
267 var t = this, w = window;
268
269 f = {func : f, scope : s || this};
270
271 if (!t.unloads) {
272 function unload() {
273 var li = t.unloads, o, n;
274
275 if (li) {
276 // Call unload handlers
277 for (n in li) {
278 o = li[n];
279
280 if (o && o.func)
281 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy
282 }
283
284 // Detach unload function
285 if (w.detachEvent) {
286 w.detachEvent('onbeforeunload', fakeUnload);
287 w.detachEvent('onunload', unload);
288 } else if (w.removeEventListener)
289 w.removeEventListener('unload', unload, false);
290
291 // Destroy references
292 t.unloads = o = li = w = unload = 0;
293
294 // Run garbarge collector on IE
295 if (window.CollectGarbage)
296 window.CollectGarbage();
297 }
298 };
299
300 function fakeUnload() {
301 var d = document;
302
303 // Is there things still loading, then do some magic
304 if (d.readyState == 'interactive') {
305 function stop() {
306 // Prevent memory leak
307 d.detachEvent('onstop', stop);
308
309 // Call unload handler
310 if (unload)
311 unload();
312
313 d = 0;
314 };
315
316 // Fire unload when the currently loading page is stopped
317 if (d)
318 d.attachEvent('onstop', stop);
319
320 // Remove onstop listener after a while to prevent the unload function
321 // to execute if the user presses cancel in an onbeforeunload
322 // confirm dialog and then presses the browser stop button
323 window.setTimeout(function() {
324 if (d)
325 d.detachEvent('onstop', stop);
326 }, 0);
327 }
328 };
329
330 // Attach unload handler
331 if (w.attachEvent) {
332 w.attachEvent('onunload', unload);
333 w.attachEvent('onbeforeunload', fakeUnload);
334 } else if (w.addEventListener)
335 w.addEventListener('unload', unload, false);
336
337 // Setup initial unload handler array
338 t.unloads = [f];
339 } else
340 t.unloads.push(f);
341
342 return f;
343 },
344
345 removeUnload : function(f) {
346 var u = this.unloads, r = null;
347
348 tinymce.each(u, function(o, i) {
349 if (o && o.func == f) {
350 u.splice(i, 1);
351 r = f;
352 return false;
353 }
354 });
355
356 return r;
357 },
358
359 explode : function(s, d) {
360 return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;
361 },
362
363 _addVer : function(u) {
364 var v;
365
366 if (!this.query)
367 return u;
368
369 v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;
370
371 if (u.indexOf('#') == -1)
372 return u + v;
373
374 return u.replace('#', v + '#');
375 }
376
377 };
378
379// Required for GZip AJAX loading
380window.tinymce = tinymce;
381
382// Initialize the API
383tinymce._init();
384
385(function($, tinymce) {
386 var is = tinymce.is;
387
388 if (!window.jQuery)
389 return alert("Load jQuery first!");
390
391 // Patch in core NS functions
392 tinymce.extend = $.extend;
393 tinymce.extend(tinymce, {
394 map : $.map,
395 grep : function(a, f) {return $.grep(a, f || function(){return 1;});},
396 inArray : function(a, v) {return $.inArray(v, a || []);}
397
398 /* Didn't iterate stylesheets
399 each : function(o, cb, s) {
400 if (!o)
401 return 0;
402
403 var r = 1;
404
405 $.each(o, function(nr, el){
406 if (cb.call(s, el, nr, o) === false) {
407 r = 0;
408 return false;
409 }
410 });
411
412 return r;
413 }*/
414 });
415
416 // Patch in functions in various clases
417 // Add a "#ifndefjquery" statement around each core API function you add below
418 var patches = {
419 'tinymce.dom.DOMUtils' : {
420 /*
421 addClass : function(e, c) {
422 if (is(e, 'array') && is(e[0], 'string'))
423 e = e.join(',#');
424 return (e && $(is(e, 'string') ? '#' + e : e)
425 .addClass(c)
426 .attr('class')) || false;
427 },
428
429 hasClass : function(n, c) {
430 return $(is(n, 'string') ? '#' + n : n).hasClass(c);
431 },
432
433 removeClass : function(e, c) {
434 if (!e)
435 return false;
436
437 var r = [];
438
439 $(is(e, 'string') ? '#' + e : e)
440 .removeClass(c)
441 .each(function(){
442 r.push(this.className);
443 });
444
445 return r.length == 1 ? r[0] : r;
446 },
447 */
448
449 select : function(pattern, scope) {
450 var t = this;
451
452 return $.find(pattern, t.get(scope) || t.get(t.settings.root_element) || t.doc, []);
453 },
454
455 is : function(n, patt) {
456 return $(this.get(n)).is(patt);
457 }
458
459 /*
460 show : function(e) {
461 if (is(e, 'array') && is(e[0], 'string'))
462 e = e.join(',#');
463
464 $(is(e, 'string') ? '#' + e : e).css('display', 'block');
465 },
466
467 hide : function(e) {
468 if (is(e, 'array') && is(e[0], 'string'))
469 e = e.join(',#');
470
471 $(is(e, 'string') ? '#' + e : e).css('display', 'none');
472 },
473
474 isHidden : function(e) {
475 return $(is(e, 'string') ? '#' + e : e).is(':hidden');
476 },
477
478 insertAfter : function(n, e) {
479 return $(is(e, 'string') ? '#' + e : e).after(n);
480 },
481
482 replace : function(o, n, k) {
483 n = $(is(n, 'string') ? '#' + n : n);
484
485 if (k)
486 n.children().appendTo(o);
487
488 n.replaceWith(o);
489 },
490
491 setStyle : function(n, na, v) {
492 if (is(n, 'array') && is(n[0], 'string'))
493 n = n.join(',#');
494
495 $(is(n, 'string') ? '#' + n : n).css(na, v);
496 },
497
498 getStyle : function(n, na, c) {
499 return $(is(n, 'string') ? '#' + n : n).css(na);
500 },
501
502 setStyles : function(e, o) {
503 if (is(e, 'array') && is(e[0], 'string'))
504 e = e.join(',#');
505 $(is(e, 'string') ? '#' + e : e).css(o);
506 },
507
508 setAttrib : function(e, n, v) {
509 var t = this, s = t.settings;
510
511 if (is(e, 'array') && is(e[0], 'string'))
512 e = e.join(',#');
513
514 e = $(is(e, 'string') ? '#' + e : e);
515
516 switch (n) {
517 case "style":
518 e.each(function(i, v){
519 if (s.keep_values)
520 $(v).attr('mce_style', v);
521
522 v.style.cssText = v;
523 });
524 break;
525
526 case "class":
527 e.each(function(){
528 this.className = v;
529 });
530 break;
531
532 case "src":
533 case "href":
534 e.each(function(i, v){
535 if (s.keep_values) {
536 if (s.url_converter)
537 v = s.url_converter.call(s.url_converter_scope || t, v, n, v);
538
539 t.setAttrib(v, 'mce_' + n, v);
540 }
541 });
542
543 break;
544 }
545
546 if (v !== null && v.length !== 0)
547 e.attr(n, '' + v);
548 else
549 e.removeAttr(n);
550 },
551
552 setAttribs : function(e, o) {
553 var t = this;
554
555 $.each(o, function(n, v){
556 t.setAttrib(e,n,v);
557 });
558 }
559 */
560 }
561
562/*
563 'tinymce.dom.Event' : {
564 add : function (o, n, f, s) {
565 var lo, cb;
566
567 cb = function(e) {
568 e.target = e.target || this;
569 f.call(s || this, e);
570 };
571
572 if (is(o, 'array') && is(o[0], 'string'))
573 o = o.join(',#');
574 o = $(is(o, 'string') ? '#' + o : o);
575 if (n == 'init') {
576 o.ready(cb, s);
577 } else {
578 if (s) {
579 o.bind(n, s, cb);
580 } else {
581 o.bind(n, cb);
582 }
583 }
584
585 lo = this._jqLookup || (this._jqLookup = []);
586 lo.push({func : f, cfunc : cb});
587
588 return cb;
589 },
590
591 remove : function(o, n, f) {
592 // Find cfunc
593 $(this._jqLookup).each(function() {
594 if (this.func === f)
595 f = this.cfunc;
596 });
597
598 if (is(o, 'array') && is(o[0], 'string'))
599 o = o.join(',#');
600
601 $(is(o, 'string') ? '#' + o : o).unbind(n,f);
602
603 return true;
604 }
605 }
606*/
607 };
608
609 // Patch functions after a class is created
610 tinymce.onCreate = function(ty, c, p) {
611 tinymce.extend(p, patches[c]);
612 };
613})(jQuery, tinymce);
614
615tinymce.create('tinymce.util.Dispatcher', {
616 scope : null,
617 listeners : null,
618
619 Dispatcher : function(s) {
620 this.scope = s || this;
621 this.listeners = [];
622 },
623
624 add : function(cb, s) {
625 this.listeners.push({cb : cb, scope : s || this.scope});
626
627 return cb;
628 },
629
630 addToTop : function(cb, s) {
631 this.listeners.unshift({cb : cb, scope : s || this.scope});
632
633 return cb;
634 },
635
636 remove : function(cb) {
637 var l = this.listeners, o = null;
638
639 tinymce.each(l, function(c, i) {
640 if (cb == c.cb) {
641 o = cb;
642 l.splice(i, 1);
643 return false;
644 }
645 });
646
647 return o;
648 },
649
650 dispatch : function() {
651 var s, a = arguments, i, li = this.listeners, c;
652
653 // Needs to be a real loop since the listener count might change while looping
654 // And this is also more efficient
655 for (i = 0; i<li.length; i++) {
656 c = li[i];
657 s = c.cb.apply(c.scope, a);
658
659 if (s === false)
660 break;
661 }
662
663 return s;
664 }
665
666 });
667(function() {
668 var each = tinymce.each;
669
670 tinymce.create('tinymce.util.URI', {
671 URI : function(u, s) {
672 var t = this, o, a, b;
673
674 // Trim whitespace
675 u = tinymce.trim(u);
676
677 // Default settings
678 s = t.settings = s || {};
679
680 // Strange app protocol or local anchor
681 if (/^(mailto|tel|news|javascript|about|data):/i.test(u) || /^\s*#/.test(u)) {
682 t.source = u;
683 return;
684 }
685
686 // Absolute path with no host, fake host and protocol
687 if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)
688 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;
689
690 // Relative path http:// or protocol relative //path
691 if (!/^\w*:?\/\//.test(u))
692 u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u);
693
694 // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
695 u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
696 u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);
697 each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {
698 var s = u[i];
699
700 // Zope 3 workaround, they use @@something
701 if (s)
702 s = s.replace(/\(mce_at\)/g, '@@');
703
704 t[v] = s;
705 });
706
707 if (b = s.base_uri) {
708 if (!t.protocol)
709 t.protocol = b.protocol;
710
711 if (!t.userInfo)
712 t.userInfo = b.userInfo;
713
714 if (!t.port && t.host == 'mce_host')
715 t.port = b.port;
716
717 if (!t.host || t.host == 'mce_host')
718 t.host = b.host;
719
720 t.source = '';
721 }
722
723 //t.path = t.path || '/';
724 },
725
726 setPath : function(p) {
727 var t = this;
728
729 p = /^(.*?)\/?(\w+)?$/.exec(p);
730
731 // Update path parts
732 t.path = p[0];
733 t.directory = p[1];
734 t.file = p[2];
735
736 // Rebuild source
737 t.source = '';
738 t.getURI();
739 },
740
741 toRelative : function(u) {
742 var t = this, o;
743
744 if (u === "./")
745 return u;
746
747 u = new tinymce.util.URI(u, {base_uri : t});
748
749 // Not on same domain/port or protocol
750 if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)
751 return u.getURI();
752
753 o = t.toRelPath(t.path, u.path);
754
755 // Add query
756 if (u.query)
757 o += '?' + u.query;
758
759 // Add anchor
760 if (u.anchor)
761 o += '#' + u.anchor;
762
763 return o;
764 },
765
766 toAbsolute : function(u, nh) {
767 var u = new tinymce.util.URI(u, {base_uri : this});
768
769 return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0);
770 },
771
772 toRelPath : function(base, path) {
773 var items, bp = 0, out = '', i, l;
774
775 // Split the paths
776 base = base.substring(0, base.lastIndexOf('/'));
777 base = base.split('/');
778 items = path.split('/');
779
780 if (base.length >= items.length) {
781 for (i = 0, l = base.length; i < l; i++) {
782 if (i >= items.length || base[i] != items[i]) {
783 bp = i + 1;
784 break;
785 }
786 }
787 }
788
789 if (base.length < items.length) {
790 for (i = 0, l = items.length; i < l; i++) {
791 if (i >= base.length || base[i] != items[i]) {
792 bp = i + 1;
793 break;
794 }
795 }
796 }
797
798 if (bp == 1)
799 return path;
800
801 for (i = 0, l = base.length - (bp - 1); i < l; i++)
802 out += "../";
803
804 for (i = bp - 1, l = items.length; i < l; i++) {
805 if (i != bp - 1)
806 out += "/" + items[i];
807 else
808 out += items[i];
809 }
810
811 return out;
812 },
813
814 toAbsPath : function(base, path) {
815 var i, nb = 0, o = [], tr, outPath;
816
817 // Split paths
818 tr = /\/$/.test(path) ? '/' : '';
819 base = base.split('/');
820 path = path.split('/');
821
822 // Remove empty chunks
823 each(base, function(k) {
824 if (k)
825 o.push(k);
826 });
827
828 base = o;
829
830 // Merge relURLParts chunks
831 for (i = path.length - 1, o = []; i >= 0; i--) {
832 // Ignore empty or .
833 if (path[i].length == 0 || path[i] == ".")
834 continue;
835
836 // Is parent
837 if (path[i] == '..') {
838 nb++;
839 continue;
840 }
841
842 // Move up
843 if (nb > 0) {
844 nb--;
845 continue;
846 }
847
848 o.push(path[i]);
849 }
850
851 i = base.length - nb;
852
853 // If /a/b/c or /
854 if (i <= 0)
855 outPath = o.reverse().join('/');
856 else
857 outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');
858
859 // Add front / if it's needed
860 if (outPath.indexOf('/') !== 0)
861 outPath = '/' + outPath;
862
863 // Add traling / if it's needed
864 if (tr && outPath.lastIndexOf('/') !== outPath.length - 1)
865 outPath += tr;
866
867 return outPath;
868 },
869
870 getURI : function(nh) {
871 var s, t = this;
872
873 // Rebuild source
874 if (!t.source || nh) {
875 s = '';
876
877 if (!nh) {
878 if (t.protocol)
879 s += t.protocol + '://';
880
881 if (t.userInfo)
882 s += t.userInfo + '@';
883
884 if (t.host)
885 s += t.host;
886
887 if (t.port)
888 s += ':' + t.port;
889 }
890
891 if (t.path)
892 s += t.path;
893
894 if (t.query)
895 s += '?' + t.query;
896
897 if (t.anchor)
898 s += '#' + t.anchor;
899
900 t.source = s;
901 }
902
903 return t.source;
904 }
905 });
906})();
907(function() {
908 var each = tinymce.each;
909
910 tinymce.create('static tinymce.util.Cookie', {
911 getHash : function(n) {
912 var v = this.get(n), h;
913
914 if (v) {
915 each(v.split('&'), function(v) {
916 v = v.split('=');
917 h = h || {};
918 h[unescape(v[0])] = unescape(v[1]);
919 });
920 }
921
922 return h;
923 },
924
925 setHash : function(n, v, e, p, d, s) {
926 var o = '';
927
928 each(v, function(v, k) {
929 o += (!o ? '' : '&') + escape(k) + '=' + escape(v);
930 });
931
932 this.set(n, o, e, p, d, s);
933 },
934
935 get : function(n) {
936 var c = document.cookie, e, p = n + "=", b;
937
938 // Strict mode
939 if (!c)
940 return;
941
942 b = c.indexOf("; " + p);
943
944 if (b == -1) {
945 b = c.indexOf(p);
946
947 if (b != 0)
948 return null;
949 } else
950 b += 2;
951
952 e = c.indexOf(";", b);
953
954 if (e == -1)
955 e = c.length;
956
957 return unescape(c.substring(b + p.length, e));
958 },
959
960 set : function(n, v, e, p, d, s) {
961 document.cookie = n + "=" + escape(v) +
962 ((e) ? "; expires=" + e.toGMTString() : "") +
963 ((p) ? "; path=" + escape(p) : "") +
964 ((d) ? "; domain=" + d : "") +
965 ((s) ? "; secure" : "");
966 },
967
968 remove : function(n, p) {
969 var d = new Date();
970
971 d.setTime(d.getTime() - 1000);
972
973 this.set(n, '', d, p, d);
974 }
975 });
976})();
977tinymce.create('static tinymce.util.JSON', {
978 serialize : function(o) {
979 var i, v, s = tinymce.util.JSON.serialize, t;
980
981 if (o == null)
982 return 'null';
983
984 t = typeof o;
985
986 if (t == 'string') {
987 v = '\bb\tt\nn\ff\rr\""\'\'\\\\';
988
989 return '"' + o.replace(/([\u0080-\uFFFF\x00-\x1f\"])/g, function(a, b) {
990 i = v.indexOf(b);
991
992 if (i + 1)
993 return '\\' + v.charAt(i + 1);
994
995 a = b.charCodeAt().toString(16);
996
997 return '\\u' + '0000'.substring(a.length) + a;
998 }) + '"';
999 }
1000
1001 if (t == 'object') {
1002 if (o.hasOwnProperty && o instanceof Array) {
1003 for (i=0, v = '['; i<o.length; i++)
1004 v += (i > 0 ? ',' : '') + s(o[i]);
1005
1006 return v + ']';
1007 }
1008
1009 v = '{';
1010
1011 for (i in o)
1012 v += typeof o[i] != 'function' ? (v.length > 1 ? ',"' : '"') + i + '":' + s(o[i]) : '';
1013
1014 return v + '}';
1015 }
1016
1017 return '' + o;
1018 },
1019
1020 parse : function(s) {
1021 try {
1022 return eval('(' + s + ')');
1023 } catch (ex) {
1024 // Ignore
1025 }
1026 }
1027
1028 });
1029tinymce.create('static tinymce.util.XHR', {
1030 send : function(o) {
1031 var x, t, w = window, c = 0;
1032
1033 // Default settings
1034 o.scope = o.scope || this;
1035 o.success_scope = o.success_scope || o.scope;
1036 o.error_scope = o.error_scope || o.scope;
1037 o.async = o.async === false ? false : true;
1038 o.data = o.data || '';
1039
1040 function get(s) {
1041 x = 0;
1042
1043 try {
1044 x = new ActiveXObject(s);
1045 } catch (ex) {
1046 }
1047
1048 return x;
1049 };
1050
1051 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');
1052
1053 if (x) {
1054 if (x.overrideMimeType)
1055 x.overrideMimeType(o.content_type);
1056
1057 x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);
1058
1059 if (o.content_type)
1060 x.setRequestHeader('Content-Type', o.content_type);
1061
1062 x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
1063
1064 x.send(o.data);
1065
1066 function ready() {
1067 if (!o.async || x.readyState == 4 || c++ > 10000) {
1068 if (o.success && c < 10000 && x.status == 200)
1069 o.success.call(o.success_scope, '' + x.responseText, x, o);
1070 else if (o.error)
1071 o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);
1072
1073 x = null;
1074 } else
1075 w.setTimeout(ready, 10);
1076 };
1077
1078 // Syncronous request
1079 if (!o.async)
1080 return ready();
1081
1082 // Wait for response, onReadyStateChange can not be used since it leaks memory in IE
1083 t = w.setTimeout(ready, 10);
1084 }
1085 }
1086});
1087(function() {
1088 var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;
1089
1090 tinymce.create('tinymce.util.JSONRequest', {
1091 JSONRequest : function(s) {
1092 this.settings = extend({
1093 }, s);
1094 this.count = 0;
1095 },
1096
1097 send : function(o) {
1098 var ecb = o.error, scb = o.success;
1099
1100 o = extend(this.settings, o);
1101
1102 o.success = function(c, x) {
1103 c = JSON.parse(c);
1104
1105 if (typeof(c) == 'undefined') {
1106 c = {
1107 error : 'JSON Parse error.'
1108 };
1109 }
1110
1111 if (c.error)
1112 ecb.call(o.error_scope || o.scope, c.error, x);
1113 else
1114 scb.call(o.success_scope || o.scope, c.result);
1115 };
1116
1117 o.error = function(ty, x) {
1118 ecb.call(o.error_scope || o.scope, ty, x);
1119 };
1120
1121 o.data = JSON.serialize({
1122 id : o.id || 'c' + (this.count++),
1123 method : o.method,
1124 params : o.params
1125 });
1126
1127 // JSON content type for Ruby on rails. Bug: #1883287
1128 o.content_type = 'application/json';
1129
1130 XHR.send(o);
1131 },
1132
1133 'static' : {
1134 sendRPC : function(o) {
1135 return new tinymce.util.JSONRequest().send(o);
1136 }
1137 }
1138 });
1139}());(function(tinymce) {
1140 // Shorten names
1141 var each = tinymce.each, is = tinymce.is;
1142 var isWebKit = tinymce.isWebKit, isIE = tinymce.isIE;
1143
1144 tinymce.create('tinymce.dom.DOMUtils', {
1145 doc : null,
1146 root : null,
1147 files : null,
1148 pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,
1149 props : {
1150 "for" : "htmlFor",
1151 "class" : "className",
1152 className : "className",
1153 checked : "checked",
1154 disabled : "disabled",
1155 maxlength : "maxLength",
1156 readonly : "readOnly",
1157 selected : "selected",
1158 value : "value",
1159 id : "id",
1160 name : "name",
1161 type : "type"
1162 },
1163
1164 DOMUtils : function(d, s) {
1165 var t = this;
1166
1167 t.doc = d;
1168 t.win = window;
1169 t.files = {};
1170 t.cssFlicker = false;
1171 t.counter = 0;
1172 t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat";
1173 t.stdMode = d.documentMode === 8;
1174
1175 t.settings = s = tinymce.extend({
1176 keep_values : false,
1177 hex_colors : 1,
1178 process_html : 1
1179 }, s);
1180
1181 // Fix IE6SP2 flicker and check it failed for pre SP2
1182 if (tinymce.isIE6) {
1183 try {
1184 d.execCommand('BackgroundImageCache', false, true);
1185 } catch (e) {
1186 t.cssFlicker = true;
1187 }
1188 }
1189
1190 tinymce.addUnload(t.destroy, t);
1191 },
1192
1193 getRoot : function() {
1194 var t = this, s = t.settings;
1195
1196 return (s && t.get(s.root_element)) || t.doc.body;
1197 },
1198
1199 getViewPort : function(w) {
1200 var d, b;
1201
1202 w = !w ? this.win : w;
1203 d = w.document;
1204 b = this.boxModel ? d.documentElement : d.body;
1205
1206 // Returns viewport size excluding scrollbars
1207 return {
1208 x : w.pageXOffset || b.scrollLeft,
1209 y : w.pageYOffset || b.scrollTop,
1210 w : w.innerWidth || b.clientWidth,
1211 h : w.innerHeight || b.clientHeight
1212 };
1213 },
1214
1215 getRect : function(e) {
1216 var p, t = this, sr;
1217
1218 e = t.get(e);
1219 p = t.getPos(e);
1220 sr = t.getSize(e);
1221
1222 return {
1223 x : p.x,
1224 y : p.y,
1225 w : sr.w,
1226 h : sr.h
1227 };
1228 },
1229
1230 getSize : function(e) {
1231 var t = this, w, h;
1232
1233 e = t.get(e);
1234 w = t.getStyle(e, 'width');
1235 h = t.getStyle(e, 'height');
1236
1237 // Non pixel value, then force offset/clientWidth
1238 if (w.indexOf('px') === -1)
1239 w = 0;
1240
1241 // Non pixel value, then force offset/clientWidth
1242 if (h.indexOf('px') === -1)
1243 h = 0;
1244
1245 return {
1246 w : parseInt(w) || e.offsetWidth || e.clientWidth,
1247 h : parseInt(h) || e.offsetHeight || e.clientHeight
1248 };
1249 },
1250
1251 getParent : function(n, f, r) {
1252 return this.getParents(n, f, r, false);
1253 },
1254
1255 getParents : function(n, f, r, c) {
1256 var t = this, na, se = t.settings, o = [];
1257
1258 n = t.get(n);
1259 c = c === undefined;
1260
1261 if (se.strict_root)
1262 r = r || t.getRoot();
1263
1264 // Wrap node name as func
1265 if (is(f, 'string')) {
1266 na = f;
1267
1268 if (f === '*') {
1269 f = function(n) {return n.nodeType == 1;};
1270 } else {
1271 f = function(n) {
1272 return t.is(n, na);
1273 };
1274 }
1275 }
1276
1277 while (n) {
1278 if (n == r || !n.nodeType || n.nodeType === 9)
1279 break;
1280
1281 if (!f || f(n)) {
1282 if (c)
1283 o.push(n);
1284 else
1285 return n;
1286 }
1287
1288 n = n.parentNode;
1289 }
1290
1291 return c ? o : null;
1292 },
1293
1294 get : function(e) {
1295 var n;
1296
1297 if (e && this.doc && typeof(e) == 'string') {
1298 n = e;
1299 e = this.doc.getElementById(e);
1300
1301 // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick
1302 if (e && e.id !== n)
1303 return this.doc.getElementsByName(n)[1];
1304 }
1305
1306 return e;
1307 },
1308
1309 getNext : function(node, selector) {
1310 return this._findSib(node, selector, 'nextSibling');
1311 },
1312
1313 getPrev : function(node, selector) {
1314 return this._findSib(node, selector, 'previousSibling');
1315 },
1316
1317
1318 add : function(p, n, a, h, c) {
1319 var t = this;
1320
1321 return this.run(p, function(p) {
1322 var e, k;
1323
1324 e = is(n, 'string') ? t.doc.createElement(n) : n;
1325 t.setAttribs(e, a);
1326
1327 if (h) {
1328 if (h.nodeType)
1329 e.appendChild(h);
1330 else
1331 t.setHTML(e, h);
1332 }
1333
1334 return !c ? p.appendChild(e) : e;
1335 });
1336 },
1337
1338 create : function(n, a, h) {
1339 return this.add(this.doc.createElement(n), n, a, h, 1);
1340 },
1341
1342 createHTML : function(n, a, h) {
1343 var o = '', t = this, k;
1344
1345 o += '<' + n;
1346
1347 for (k in a) {
1348 if (a.hasOwnProperty(k))
1349 o += ' ' + k + '="' + t.encode(a[k]) + '"';
1350 }
1351
1352 if (tinymce.is(h))
1353 return o + '>' + h + '</' + n + '>';
1354
1355 return o + ' />';
1356 },
1357
1358 remove : function(n, k) {
1359 var t = this;
1360
1361 return this.run(n, function(n) {
1362 var p, g, i;
1363
1364 p = n.parentNode;
1365
1366 if (!p)
1367 return null;
1368
1369 if (k) {
1370 for (i = n.childNodes.length - 1; i >= 0; i--)
1371 t.insertAfter(n.childNodes[i], n);
1372
1373 //each(n.childNodes, function(c) {
1374 // p.insertBefore(c.cloneNode(true), n);
1375 //});
1376 }
1377
1378 // Fix IE psuedo leak
1379 if (t.fixPsuedoLeaks) {
1380 p = n.cloneNode(true);
1381 k = 'IELeakGarbageBin';
1382 g = t.get(k) || t.add(t.doc.body, 'div', {id : k, style : 'display:none'});
1383 g.appendChild(n);
1384 g.innerHTML = '';
1385
1386 return p;
1387 }
1388
1389 return p.removeChild(n);
1390 });
1391 },
1392
1393 setStyle : function(n, na, v) {
1394 var t = this;
1395
1396 return t.run(n, function(e) {
1397 var s, i;
1398
1399 s = e.style;
1400
1401 // Camelcase it, if needed
1402 na = na.replace(/-(\D)/g, function(a, b){
1403 return b.toUpperCase();
1404 });
1405
1406 // Default px suffix on these
1407 if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v)))
1408 v += 'px';
1409
1410 switch (na) {
1411 case 'opacity':
1412 // IE specific opacity
1413 if (isIE) {
1414 s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")";
1415
1416 if (!n.currentStyle || !n.currentStyle.hasLayout)
1417 s.display = 'inline-block';
1418 }
1419
1420 // Fix for older browsers
1421 s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || '';
1422 break;
1423
1424 case 'float':
1425 isIE ? s.styleFloat = v : s.cssFloat = v;
1426 break;
1427
1428 default:
1429 s[na] = v || '';
1430 }
1431
1432 // Force update of the style data
1433 if (t.settings.update_styles)
1434 t.setAttrib(e, 'mce_style');
1435 });
1436 },
1437
1438 getStyle : function(n, na, c) {
1439 n = this.get(n);
1440
1441 if (!n)
1442 return false;
1443
1444 // Gecko
1445 if (this.doc.defaultView && c) {
1446 // Remove camelcase
1447 na = na.replace(/[A-Z]/g, function(a){
1448 return '-' + a;
1449 });
1450
1451 try {
1452 return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);
1453 } catch (ex) {
1454 // Old safari might fail
1455 return null;
1456 }
1457 }
1458
1459 // Camelcase it, if needed
1460 na = na.replace(/-(\D)/g, function(a, b){
1461 return b.toUpperCase();
1462 });
1463
1464 if (na == 'float')
1465 na = isIE ? 'styleFloat' : 'cssFloat';
1466
1467 // IE & Opera
1468 if (n.currentStyle && c)
1469 return n.currentStyle[na];
1470
1471 return n.style[na];
1472 },
1473
1474 setStyles : function(e, o) {
1475 var t = this, s = t.settings, ol;
1476
1477 ol = s.update_styles;
1478 s.update_styles = 0;
1479
1480 each(o, function(v, n) {
1481 t.setStyle(e, n, v);
1482 });
1483
1484 // Update style info
1485 s.update_styles = ol;
1486 if (s.update_styles)
1487 t.setAttrib(e, s.cssText);
1488 },
1489
1490 setAttrib : function(e, n, v) {
1491 var t = this;
1492
1493 // Whats the point
1494 if (!e || !n)
1495 return;
1496
1497 // Strict XML mode
1498 if (t.settings.strict)
1499 n = n.toLowerCase();
1500
1501 return this.run(e, function(e) {
1502 var s = t.settings;
1503
1504 switch (n) {
1505 case "style":
1506 if (!is(v, 'string')) {
1507 each(v, function(v, n) {
1508 t.setStyle(e, n, v);
1509 });
1510
1511 return;
1512 }
1513
1514 // No mce_style for elements with these since they might get resized by the user
1515 if (s.keep_values) {
1516 if (v && !t._isRes(v))
1517 e.setAttribute('mce_style', v, 2);
1518 else
1519 e.removeAttribute('mce_style', 2);
1520 }
1521
1522 e.style.cssText = v;
1523 break;
1524
1525 case "class":
1526 e.className = v || ''; // Fix IE null bug
1527 break;
1528
1529 case "src":
1530 case "href":
1531 if (s.keep_values) {
1532 if (s.url_converter)
1533 v = s.url_converter.call(s.url_converter_scope || t, v, n, e);
1534
1535 t.setAttrib(e, 'mce_' + n, v, 2);
1536 }
1537
1538 break;
1539
1540 case "shape":
1541 e.setAttribute('mce_style', v);
1542 break;
1543 }
1544
1545 if (is(v) && v !== null && v.length !== 0)
1546 e.setAttribute(n, '' + v, 2);
1547 else
1548 e.removeAttribute(n, 2);
1549 });
1550 },
1551
1552 setAttribs : function(e, o) {
1553 var t = this;
1554
1555 return this.run(e, function(e) {
1556 each(o, function(v, n) {
1557 t.setAttrib(e, n, v);
1558 });
1559 });
1560 },
1561
1562 getAttrib : function(e, n, dv) {
1563 var v, t = this;
1564
1565 e = t.get(e);
1566
1567 if (!e || e.nodeType !== 1)
1568 return false;
1569
1570 if (!is(dv))
1571 dv = '';
1572
1573 // Try the mce variant for these
1574 if (/^(src|href|style|coords|shape)$/.test(n)) {
1575 v = e.getAttribute("mce_" + n);
1576
1577 if (v)
1578 return v;
1579 }
1580
1581 if (isIE && t.props[n]) {
1582 v = e[t.props[n]];
1583 v = v && v.nodeValue ? v.nodeValue : v;
1584 }
1585
1586 if (!v)
1587 v = e.getAttribute(n, 2);
1588
1589 // Check boolean attribs
1590 if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(n)) {
1591 if (e[t.props[n]] === true && v === '')
1592 return n;
1593
1594 return v ? n : '';
1595 }
1596
1597 // Inner input elements will override attributes on form elements
1598 if (e.nodeName === "FORM" && e.getAttributeNode(n))
1599 return e.getAttributeNode(n).nodeValue;
1600
1601 if (n === 'style') {
1602 v = v || e.style.cssText;
1603
1604 if (v) {
1605 v = t.serializeStyle(t.parseStyle(v));
1606
1607 if (t.settings.keep_values && !t._isRes(v))
1608 e.setAttribute('mce_style', v);
1609 }
1610 }
1611
1612 // Remove Apple and WebKit stuff
1613 if (isWebKit && n === "class" && v)
1614 v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, '');
1615
1616 // Handle IE issues
1617 if (isIE) {
1618 switch (n) {
1619 case 'rowspan':
1620 case 'colspan':
1621 // IE returns 1 as default value
1622 if (v === 1)
1623 v = '';
1624
1625 break;
1626
1627 case 'size':
1628 // IE returns +0 as default value for size
1629 if (v === '+0' || v === 20 || v === 0)
1630 v = '';
1631
1632 break;
1633
1634 case 'width':
1635 case 'height':
1636 case 'vspace':
1637 case 'checked':
1638 case 'disabled':
1639 case 'readonly':
1640 if (v === 0)
1641 v = '';
1642
1643 break;
1644
1645 case 'hspace':
1646 // IE returns -1 as default value
1647 if (v === -1)
1648 v = '';
1649
1650 break;
1651
1652 case 'maxlength':
1653 case 'tabindex':
1654 // IE returns default value
1655 if (v === 32768 || v === 2147483647 || v === '32768')
1656 v = '';
1657
1658 break;
1659
1660 case 'multiple':
1661 case 'compact':
1662 case 'noshade':
1663 case 'nowrap':
1664 if (v === 65535)
1665 return n;
1666
1667 return dv;
1668
1669 case 'shape':
1670 v = v.toLowerCase();
1671 break;
1672
1673 default:
1674 // IE has odd anonymous function for event attributes
1675 if (n.indexOf('on') === 0 && v)
1676 v = ('' + v).replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1');
1677 }
1678 }
1679
1680 return (v !== undefined && v !== null && v !== '') ? '' + v : dv;
1681 },
1682
1683 getPos : function(n, ro) {
1684 var t = this, x = 0, y = 0, e, d = t.doc, r;
1685
1686 n = t.get(n);
1687 ro = ro || d.body;
1688
1689 if (n) {
1690 // Use getBoundingClientRect on IE, Opera has it but it's not perfect
1691 if (isIE && !t.stdMode) {
1692 n = n.getBoundingClientRect();
1693 e = t.boxModel ? d.documentElement : d.body;
1694 x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border
1695 x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x;
1696 n.top += t.win.self != t.win.top ? 2 : 0; // IE adds some strange extra cord if used in a frameset
1697
1698 return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x};
1699 }
1700
1701 r = n;
1702 while (r && r != ro && r.nodeType) {
1703 x += r.offsetLeft || 0;
1704 y += r.offsetTop || 0;
1705 r = r.offsetParent;
1706 }
1707
1708 r = n.parentNode;
1709 while (r && r != ro && r.nodeType) {
1710 x -= r.scrollLeft || 0;
1711 y -= r.scrollTop || 0;
1712 r = r.parentNode;
1713 }
1714 }
1715
1716 return {x : x, y : y};
1717 },
1718
1719 parseStyle : function(st) {
1720 var t = this, s = t.settings, o = {};
1721
1722 if (!st)
1723 return o;
1724
1725 function compress(p, s, ot) {
1726 var t, r, b, l;
1727
1728 // Get values and check it it needs compressing
1729 t = o[p + '-top' + s];
1730 if (!t)
1731 return;
1732
1733 r = o[p + '-right' + s];
1734 if (t != r)
1735 return;
1736
1737 b = o[p + '-bottom' + s];
1738 if (r != b)
1739 return;
1740
1741 l = o[p + '-left' + s];
1742 if (b != l)
1743 return;
1744
1745 // Compress
1746 o[ot] = l;
1747 delete o[p + '-top' + s];
1748 delete o[p + '-right' + s];
1749 delete o[p + '-bottom' + s];
1750 delete o[p + '-left' + s];
1751 };
1752
1753 function compress2(ta, a, b, c) {
1754 var t;
1755
1756 t = o[a];
1757 if (!t)
1758 return;
1759
1760 t = o[b];
1761 if (!t)
1762 return;
1763
1764 t = o[c];
1765 if (!t)
1766 return;
1767
1768 // Compress
1769 o[ta] = o[a] + ' ' + o[b] + ' ' + o[c];
1770 delete o[a];
1771 delete o[b];
1772 delete o[c];
1773 };
1774
1775 st = st.replace(/&(#?[a-z0-9]+);/g, '&$1_MCE_SEMI_'); // Protect entities
1776
1777 each(st.split(';'), function(v) {
1778 var sv, ur = [];
1779
1780 if (v) {
1781 v = v.replace(/_MCE_SEMI_/g, ';'); // Restore entities
1782 v = v.replace(/url\([^\)]+\)/g, function(v) {ur.push(v);return 'url(' + ur.length + ')';});
1783 v = v.split(':');
1784 sv = tinymce.trim(v[1]);
1785 sv = sv.replace(/url\(([^\)]+)\)/g, function(a, b) {return ur[parseInt(b) - 1];});
1786
1787 sv = sv.replace(/rgb\([^\)]+\)/g, function(v) {
1788 return t.toHex(v);
1789 });
1790
1791 if (s.url_converter) {
1792 sv = sv.replace(/url\([\'\"]?([^\)\'\"]+)[\'\"]?\)/g, function(x, c) {
1793 return 'url(' + s.url_converter.call(s.url_converter_scope || t, t.decode(c), 'style', null) + ')';
1794 });
1795 }
1796
1797 o[tinymce.trim(v[0]).toLowerCase()] = sv;
1798 }
1799 });
1800
1801 compress("border", "", "border");
1802 compress("border", "-width", "border-width");
1803 compress("border", "-color", "border-color");
1804 compress("border", "-style", "border-style");
1805 compress("padding", "", "padding");
1806 compress("margin", "", "margin");
1807 compress2('border', 'border-width', 'border-style', 'border-color');
1808
1809 if (isIE) {
1810 // Remove pointless border
1811 if (o.border == 'medium none')
1812 o.border = '';
1813 }
1814
1815 return o;
1816 },
1817
1818 serializeStyle : function(o) {
1819 var s = '';
1820
1821 each(o, function(v, k) {
1822 if (k && v) {
1823 if (tinymce.isGecko && k.indexOf('-moz-') === 0)
1824 return;
1825
1826 switch (k) {
1827 case 'color':
1828 case 'background-color':
1829 v = v.toLowerCase();
1830 break;
1831 }
1832
1833 s += (s ? ' ' : '') + k + ': ' + v + ';';
1834 }
1835 });
1836
1837 return s;
1838 },
1839
1840 loadCSS : function(u) {
1841 var t = this, d = t.doc, head;
1842
1843 if (!u)
1844 u = '';
1845
1846 head = t.select('head')[0];
1847
1848 each(u.split(','), function(u) {
1849 var link;
1850
1851 if (t.files[u])
1852 return;
1853
1854 t.files[u] = true;
1855 link = t.create('link', {rel : 'stylesheet', href : tinymce._addVer(u)});
1856
1857 // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug
1858 // This fix seems to resolve that issue by realcing the document ones a stylesheet finishes loading
1859 // It's ugly but it seems to work fine.
1860 if (isIE && d.documentMode) {
1861 link.onload = function() {
1862 d.recalc();
1863 link.onload = null;
1864 };
1865 }
1866
1867 head.appendChild(link);
1868 });
1869 },
1870
1871 addClass : function(e, c) {
1872 return this.run(e, function(e) {
1873 var o;
1874
1875 if (!c)
1876 return 0;
1877
1878 if (this.hasClass(e, c))
1879 return e.className;
1880
1881 o = this.removeClass(e, c);
1882
1883 return e.className = (o != '' ? (o + ' ') : '') + c;
1884 });
1885 },
1886
1887 removeClass : function(e, c) {
1888 var t = this, re;
1889
1890 return t.run(e, function(e) {
1891 var v;
1892
1893 if (t.hasClass(e, c)) {
1894 if (!re)
1895 re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g");
1896
1897 v = e.className.replace(re, ' ');
1898
1899 return e.className = tinymce.trim(v != ' ' ? v : '');
1900 }
1901
1902 return e.className;
1903 });
1904 },
1905
1906 hasClass : function(n, c) {
1907 n = this.get(n);
1908
1909 if (!n || !c)
1910 return false;
1911
1912 return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1;
1913 },
1914
1915 show : function(e) {
1916 return this.setStyle(e, 'display', 'block');
1917 },
1918
1919 hide : function(e) {
1920 return this.setStyle(e, 'display', 'none');
1921 },
1922
1923 isHidden : function(e) {
1924 e = this.get(e);
1925
1926 return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none';
1927 },
1928
1929 uniqueId : function(p) {
1930 return (!p ? 'mce_' : p) + (this.counter++);
1931 },
1932
1933 setHTML : function(e, h) {
1934 var t = this;
1935
1936 return this.run(e, function(e) {
1937 var x, i, nl, n, p, x;
1938
1939 h = t.processHTML(h);
1940
1941 if (isIE) {
1942 function set() {
1943 try {
1944 // IE will remove comments from the beginning
1945 // unless you padd the contents with something
1946 e.innerHTML = '<br />' + h;
1947 e.removeChild(e.firstChild);
1948 } catch (ex) {
1949 // 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
1950 // This seems to fix this problem
1951
1952 // Remove all child nodes
1953 while (e.firstChild)
1954 e.firstChild.removeNode();
1955
1956 // Create new div with HTML contents and a BR infront to keep comments
1957 x = t.create('div');
1958 x.innerHTML = '<br />' + h;
1959
1960 // Add all children from div to target
1961 each (x.childNodes, function(n, i) {
1962 // Skip br element
1963 if (i)
1964 e.appendChild(n);
1965 });
1966 }
1967 };
1968
1969 // IE has a serious bug when it comes to paragraphs it can produce an invalid
1970 // DOM tree if contents like this <p><ul><li>Item 1</li></ul></p> is inserted
1971 // It seems to be that IE doesn't like a root block element placed inside another root block element
1972 if (t.settings.fix_ie_paragraphs)
1973 h = h.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi, '<p$1 mce_keep="true">&nbsp;</p>');
1974
1975 set();
1976
1977 if (t.settings.fix_ie_paragraphs) {
1978 // Check for odd paragraphs this is a sign of a broken DOM
1979 nl = e.getElementsByTagName("p");
1980 for (i = nl.length - 1, x = 0; i >= 0; i--) {
1981 n = nl[i];
1982
1983 if (!n.hasChildNodes()) {
1984 if (!n.mce_keep) {
1985 x = 1; // Is broken
1986 break;
1987 }
1988
1989 n.removeAttribute('mce_keep');
1990 }
1991 }
1992 }
1993
1994 // Time to fix the madness IE left us
1995 if (x) {
1996 // So if we replace the p elements with divs and mark them and then replace them back to paragraphs
1997 // after we use innerHTML we can fix the DOM tree
1998 h = h.replace(/<p ([^>]+)>|<p>/ig, '<div $1 mce_tmp="1">');
1999 h = h.replace(/<\/p>/g, '</div>');
2000
2001 // Set the new HTML with DIVs
2002 set();
2003
2004 // Replace all DIV elements with he mce_tmp attibute back to paragraphs
2005 // This is needed since IE has a annoying bug see above for details
2006 // This is a slow process but it has to be done. :(
2007 if (t.settings.fix_ie_paragraphs) {
2008 nl = e.getElementsByTagName("DIV");
2009 for (i = nl.length - 1; i >= 0; i--) {
2010 n = nl[i];
2011
2012 // Is it a temp div
2013 if (n.mce_tmp) {
2014 // Create new paragraph
2015 p = t.doc.createElement('p');
2016
2017 // Copy all attributes
2018 n.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi, function(a, b) {
2019 var v;
2020
2021 if (b !== 'mce_tmp') {
2022 v = n.getAttribute(b);
2023
2024 if (!v && b === 'class')
2025 v = n.className;
2026
2027 p.setAttribute(b, v);
2028 }
2029 });
2030
2031 // Append all children to new paragraph
2032 for (x = 0; x<n.childNodes.length; x++)
2033 p.appendChild(n.childNodes[x].cloneNode(true));
2034
2035 // Replace div with new paragraph
2036 n.swapNode(p);
2037 }
2038 }
2039 }
2040 }
2041 } else
2042 e.innerHTML = h;
2043
2044 return h;
2045 });
2046 },
2047
2048 processHTML : function(h) {
2049 var t = this, s = t.settings, codeBlocks = [];
2050
2051 if (!s.process_html)
2052 return h;
2053
2054 // Convert strong and em to b and i in FF since it can't handle them
2055 if (tinymce.isGecko) {
2056 h = h.replace(/<(\/?)strong>|<strong( [^>]+)>/gi, '<$1b$2>');
2057 h = h.replace(/<(\/?)em>|<em( [^>]+)>/gi, '<$1i$2>');
2058 } else if (isIE) {
2059 h = h.replace(/&apos;/g, '&#39;'); // IE can't handle apos
2060 h = h.replace(/\s+(disabled|checked|readonly|selected)\s*=\s*[\"\']?(false|0)[\"\']?/gi, ''); // IE doesn't handle default values correct
2061 }
2062
2063 // Fix some issues
2064 h = h.replace(/<a( )([^>]+)\/>|<a\/>/gi, '<a$1$2></a>'); // Force open
2065
2066 // Store away src and href in mce_src and mce_href since browsers mess them up
2067 if (s.keep_values) {
2068 // Wrap scripts and styles in comments for serialization purposes
2069 if (/<script|noscript|style/i.test(h)) {
2070 function trim(s) {
2071 // Remove prefix and suffix code for element
2072 s = s.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n');
2073 s = s.replace(/^[\r\n]*|[\r\n]*$/g, '');
2074 s = s.replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, '');
2075 s = s.replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, '');
2076
2077 return s;
2078 };
2079
2080 // Wrap the script contents in CDATA and keep them from executing
2081 h = h.replace(/<script([^>]+|)>([\s\S]*?)<\/script>/gi, function(v, attribs, text) {
2082 // Force type attribute
2083 if (!attribs)
2084 attribs = ' type="text/javascript"';
2085
2086 // Convert the src attribute of the scripts
2087 attribs = attribs.replace(/src=\"([^\"]+)\"?/i, function(a, url) {
2088 if (s.url_converter)
2089 url = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(url), 'src', 'script'));
2090
2091 return 'mce_src="' + url + '"';
2092 });
2093
2094 // Wrap text contents
2095 if (tinymce.trim(text)) {
2096 codeBlocks.push(trim(text));
2097 text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n// -->';
2098 }
2099
2100 return '<mce:script' + attribs + '>' + text + '</mce:script>';
2101 });
2102
2103 // Wrap style elements
2104 h = h.replace(/<style([^>]+|)>([\s\S]*?)<\/style>/gi, function(v, attribs, text) {
2105 // Wrap text contents
2106 if (text) {
2107 codeBlocks.push(trim(text));
2108 text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n-->';
2109 }
2110
2111 return '<mce:style' + attribs + '>' + text + '</mce:style><style ' + attribs + ' mce_bogus="1">' + text + '</style>';
2112 });
2113
2114 // Wrap noscript elements
2115 h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {
2116 return '<mce:noscript' + attribs + '><!--' + t.encode(text).replace(/--/g, '&#45;&#45;') + '--></mce:noscript>';
2117 });
2118 }
2119
2120 h = h.replace(/<!\[CDATA\[([\s\S]+)\]\]>/g, '<!--[CDATA[$1]]-->');
2121
2122 // Remove false bool attributes and force attributes into xhtml style attr="attr"
2123 h = h.replace(/<([\w:]+) [^>]*(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)[^>]*>/gi, function(val) {
2124 function handle(val, name, value) {
2125 // Remove false/0 attribs
2126 if (value === 'false' || value === '0')
2127 return '';
2128
2129 return ' ' + name + '="' + name + '"';
2130 };
2131
2132 val = val.replace(/ (checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)=[\"]([^\"]+)[\"]/gi, handle); // W3C
2133 val = val.replace(/ (checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)=[\']([^\']+)[\']/gi, handle); // W3C
2134 val = val.replace(/ (checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)=([^\s\"\'>]+)/gi, handle); // IE
2135 val = val.replace(/ (checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)([\s>])/gi, ' $1="$1"$2'); // Force attr="attr"
2136
2137 return val;
2138 });
2139
2140 // Process all tags with src, href or style
2141 h = h.replace(/<([\w:]+) [^>]*(src|href|style|shape|coords)[^>]*>/gi, function(a, n) {
2142 function handle(m, b, c) {
2143 var u = c;
2144
2145 // Tag already got a mce_ version
2146 if (a.indexOf('mce_' + b) != -1)
2147 return m;
2148
2149 if (b == 'style') {
2150 // No mce_style for elements with these since they might get resized by the user
2151 if (t._isRes(c))
2152 return m;
2153
2154 // Parse and serialize the style to convert for example uppercase styles like "BORDER: 1px"
2155 u = t.encode(t.serializeStyle(t.parseStyle(u)));
2156 } else if (b != 'coords' && b != 'shape') {
2157 if (s.url_converter)
2158 u = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), b, n));
2159 }
2160
2161 return ' ' + b + '="' + c + '" mce_' + b + '="' + u + '"';
2162 };
2163
2164 a = a.replace(/ (src|href|style|coords|shape)=[\"]([^\"]+)[\"]/gi, handle); // W3C
2165 a = a.replace(/ (src|href|style|coords|shape)=[\']([^\']+)[\']/gi, handle); // W3C
2166
2167 return a.replace(/ (src|href|style|coords|shape)=([^\s\"\'>]+)/gi, handle); // IE
2168 });
2169
2170 // Restore script blocks
2171 h = h.replace(/MCE_SCRIPT:([0-9]+)/g, function(val, idx) {
2172 return codeBlocks[idx];
2173 });
2174 }
2175
2176 return h;
2177 },
2178
2179 getOuterHTML : function(e) {
2180 var d;
2181
2182 e = this.get(e);
2183
2184 if (!e)
2185 return null;
2186
2187 if (e.outerHTML !== undefined)
2188 return e.outerHTML;
2189
2190 d = (e.ownerDocument || this.doc).createElement("body");
2191 d.appendChild(e.cloneNode(true));
2192
2193 return d.innerHTML;
2194 },
2195
2196 setOuterHTML : function(e, h, d) {
2197 var t = this;
2198
2199 function setHTML(e, h, d) {
2200 var n, tp;
2201
2202 tp = d.createElement("body");
2203 tp.innerHTML = h;
2204
2205 n = tp.lastChild;
2206 while (n) {
2207 t.insertAfter(n.cloneNode(true), e);
2208 n = n.previousSibling;
2209 }
2210
2211 t.remove(e);
2212 };
2213
2214 return this.run(e, function(e) {
2215 e = t.get(e);
2216
2217 // Only set HTML on elements
2218 if (e.nodeType == 1) {
2219 d = d || e.ownerDocument || t.doc;
2220
2221 if (isIE) {
2222 try {
2223 // Try outerHTML for IE it sometimes produces an unknown runtime error
2224 if (isIE && e.nodeType == 1)
2225 e.outerHTML = h;
2226 else
2227 setHTML(e, h, d);
2228 } catch (ex) {
2229 // Fix for unknown runtime error
2230 setHTML(e, h, d);
2231 }
2232 } else
2233 setHTML(e, h, d);
2234 }
2235 });
2236 },
2237
2238 decode : function(s) {
2239 var e, n, v;
2240
2241 // Look for entities to decode
2242 if (/&[^;]+;/.test(s)) {
2243 // Decode the entities using a div element not super efficient but less code
2244 e = this.doc.createElement("div");
2245 e.innerHTML = s;
2246 n = e.firstChild;
2247 v = '';
2248
2249 if (n) {
2250 do {
2251 v += n.nodeValue;
2252 } while (n.nextSibling);
2253 }
2254
2255 return v || s;
2256 }
2257
2258 return s;
2259 },
2260
2261 encode : function(s) {
2262 return s ? ('' + s).replace(/[<>&\"]/g, function (c, b) {
2263 switch (c) {
2264 case '&':
2265 return '&amp;';
2266
2267 case '"':
2268 return '&quot;';
2269
2270 case '<':
2271 return '&lt;';
2272
2273 case '>':
2274 return '&gt;';
2275 }
2276
2277 return c;
2278 }) : s;
2279 },
2280
2281 insertAfter : function(n, r) {
2282 var t = this;
2283
2284 r = t.get(r);
2285
2286 return this.run(n, function(n) {
2287 var p, ns;
2288
2289 p = r.parentNode;
2290 ns = r.nextSibling;
2291
2292 if (ns)
2293 p.insertBefore(n, ns);
2294 else
2295 p.appendChild(n);
2296
2297 return n;
2298 });
2299 },
2300
2301 isBlock : function(n) {
2302 if (n.nodeType && n.nodeType !== 1)
2303 return false;
2304
2305 n = n.nodeName || n;
2306
2307 return /^(H[1-6]|HR|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TH|TBODY|TR|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP)$/.test(n);
2308 },
2309
2310 replace : function(n, o, k) {
2311 var t = this;
2312
2313 if (is(o, 'array'))
2314 n = n.cloneNode(true);
2315
2316 return t.run(o, function(o) {
2317 if (k) {
2318 each(o.childNodes, function(c) {
2319 n.appendChild(c.cloneNode(true));
2320 });
2321 }
2322
2323 // Fix IE psuedo leak for elements since replacing elements if fairly common
2324 // Will break parentNode for some unknown reason
2325 if (t.fixPsuedoLeaks && o.nodeType === 1) {
2326 o.parentNode.insertBefore(n, o);
2327 t.remove(o);
2328 return n;
2329 }
2330
2331 return o.parentNode.replaceChild(n, o);
2332 });
2333 },
2334
2335 findCommonAncestor : function(a, b) {
2336 var ps = a, pe;
2337
2338 while (ps) {
2339 pe = b;
2340
2341 while (pe && ps != pe)
2342 pe = pe.parentNode;
2343
2344 if (ps == pe)
2345 break;
2346
2347 ps = ps.parentNode;
2348 }
2349
2350 if (!ps && a.ownerDocument)
2351 return a.ownerDocument.documentElement;
2352
2353 return ps;
2354 },
2355
2356 toHex : function(s) {
2357 var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);
2358
2359 function hex(s) {
2360 s = parseInt(s).toString(16);
2361
2362 return s.length > 1 ? s : '0' + s; // 0 -> 00
2363 };
2364
2365 if (c) {
2366 s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);
2367
2368 return s;
2369 }
2370
2371 return s;
2372 },
2373
2374 getClasses : function() {
2375 var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;
2376
2377 if (t.classes)
2378 return t.classes;
2379
2380 function addClasses(s) {
2381 // IE style imports
2382 each(s.imports, function(r) {
2383 addClasses(r);
2384 });
2385
2386 each(s.cssRules || s.rules, function(r) {
2387 // Real type or fake it on IE
2388 switch (r.type || 1) {
2389 // Rule
2390 case 1:
2391 if (r.selectorText) {
2392 each(r.selectorText.split(','), function(v) {
2393 v = v.replace(/^\s*|\s*$|^\s\./g, "");
2394
2395 // Is internal or it doesn't contain a class
2396 if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v))
2397 return;
2398
2399 // Remove everything but class name
2400 ov = v;
2401 v = v.replace(/.*\.([a-z0-9_\-]+).*/i, '$1');
2402
2403 // Filter classes
2404 if (f && !(v = f(v, ov)))
2405 return;
2406
2407 if (!lo[v]) {
2408 cl.push({'class' : v});
2409 lo[v] = 1;
2410 }
2411 });
2412 }
2413 break;
2414
2415 // Import
2416 case 3:
2417 addClasses(r.styleSheet);
2418 break;
2419 }
2420 });
2421 };
2422
2423 try {
2424 each(t.doc.styleSheets, addClasses);
2425 } catch (ex) {
2426 // Ignore
2427 }
2428
2429 if (cl.length > 0)
2430 t.classes = cl;
2431
2432 return cl;
2433 },
2434
2435 run : function(e, f, s) {
2436 var t = this, o;
2437
2438 if (t.doc && typeof(e) === 'string')
2439 e = t.get(e);
2440
2441 if (!e)
2442 return false;
2443
2444 s = s || this;
2445 if (!e.nodeType && (e.length || e.length === 0)) {
2446 o = [];
2447
2448 each(e, function(e, i) {
2449 if (e) {
2450 if (typeof(e) == 'string')
2451 e = t.doc.getElementById(e);
2452
2453 o.push(f.call(s, e, i));
2454 }
2455 });
2456
2457 return o;
2458 }
2459
2460 return f.call(s, e);
2461 },
2462
2463 getAttribs : function(n) {
2464 var o;
2465
2466 n = this.get(n);
2467
2468 if (!n)
2469 return [];
2470
2471 if (isIE) {
2472 o = [];
2473
2474 // Object will throw exception in IE
2475 if (n.nodeName == 'OBJECT')
2476 return n.attributes;
2477
2478 // IE doesn't keep the selected attribute if you clone option elements
2479 if (n.nodeName === 'OPTION' && this.getAttrib(n, 'selected'))
2480 o.push({specified : 1, nodeName : 'selected'});
2481
2482 // It's crazy that this is faster in IE but it's because it returns all attributes all the time
2483 n.cloneNode(false).outerHTML.replace(/<\/?[\w:]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=\w+|>/gi, '').replace(/[\w:]+/gi, function(a) {
2484 o.push({specified : 1, nodeName : a});
2485 });
2486
2487 return o;
2488 }
2489
2490 return n.attributes;
2491 },
2492
2493 destroy : function(s) {
2494 var t = this;
2495
2496 if (t.events)
2497 t.events.destroy();
2498
2499 t.win = t.doc = t.root = t.events = null;
2500
2501 // Manual destroy then remove unload handler
2502 if (!s)
2503 tinymce.removeUnload(t.destroy);
2504 },
2505
2506 createRng : function() {
2507 var d = this.doc;
2508
2509 return d.createRange ? d.createRange() : new tinymce.dom.Range(this);
2510 },
2511
2512 split : function(pe, e, re) {
2513 var t = this, r = t.createRng(), bef, aft, pa;
2514
2515 // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sence
2516 // but we don't want that in our code since it serves no purpose
2517 // For example if this is chopped:
2518 // <p>text 1<span><b>CHOP</b></span>text 2</p>
2519 // would produce:
2520 // <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>
2521 // this function will then trim of empty edges and produce:
2522 // <p>text 1</p><b>CHOP</b><p>text 2</p>
2523 function trimEdge(n, na) {
2524 n = n[na];
2525
2526 if (n && n[na] && n[na].nodeType == 1 && isEmpty(n[na]))
2527 t.remove(n[na]);
2528 };
2529
2530 function isEmpty(n) {
2531 n = t.getOuterHTML(n);
2532 n = n.replace(/<(img|hr|table)/gi, '-'); // Keep these convert them to - chars
2533 n = n.replace(/<[^>]+>/g, ''); // Remove all tags
2534
2535 return n.replace(/[ \t\r\n]+|&nbsp;|&#160;/g, '') == '';
2536 };
2537
2538 // Added until Gecko can create real HTML documents using implementation.createHTMLDocument
2539 // this is to future proof it if Gecko decides to implement the error checking for range methods.
2540 function nodeIndex(n) {
2541 var i = 0;
2542
2543 while (n.previousSibling) {
2544 i++;
2545 n = n.previousSibling;
2546 }
2547
2548 return i;
2549 };
2550
2551 if (pe && e) {
2552 // Get before chunk
2553 r.setStart(pe.parentNode, nodeIndex(pe));
2554 r.setEnd(e.parentNode, nodeIndex(e));
2555 bef = r.extractContents();
2556
2557 // Get after chunk
2558 r = t.createRng();
2559 r.setStart(e.parentNode, nodeIndex(e) + 1);
2560 r.setEnd(pe.parentNode, nodeIndex(pe) + 1);
2561 aft = r.extractContents();
2562
2563 // Insert chunks and remove parent
2564 pa = pe.parentNode;
2565
2566 // Remove right side edge of the before contents
2567 trimEdge(bef, 'lastChild');
2568
2569 if (!isEmpty(bef))
2570 pa.insertBefore(bef, pe);
2571
2572 if (re)
2573 pa.replaceChild(re, e);
2574 else
2575 pa.insertBefore(e, pe);
2576
2577 // Remove left site edge of the after contents
2578 trimEdge(aft, 'firstChild');
2579
2580 if (!isEmpty(aft))
2581 pa.insertBefore(aft, pe);
2582
2583 t.remove(pe);
2584
2585 return re || e;
2586 }
2587 },
2588
2589 bind : function(target, name, func, scope) {
2590 var t = this;
2591
2592 if (!t.events)
2593 t.events = new tinymce.dom.EventUtils();
2594
2595 return t.events.add(target, name, func, scope || this);
2596 },
2597
2598 unbind : function(target, name, func) {
2599 var t = this;
2600
2601 if (!t.events)
2602 t.events = new tinymce.dom.EventUtils();
2603
2604 return t.events.remove(target, name, func);
2605 },
2606
2607
2608 _findSib : function(node, selector, name) {
2609 var t = this, f = selector;
2610
2611 if (node) {
2612 // If expression make a function of it using is
2613 if (is(f, 'string')) {
2614 f = function(node) {
2615 return t.is(node, selector);
2616 };
2617 }
2618
2619 // Loop all siblings
2620 for (node = node[name]; node; node = node[name]) {
2621 if (f(node))
2622 return node;
2623 }
2624 }
2625
2626 return null;
2627 },
2628
2629 _isRes : function(c) {
2630 // Is live resizble element
2631 return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c);
2632 }
2633
2634 /*
2635 walk : function(n, f, s) {
2636 var d = this.doc, w;
2637
2638 if (d.createTreeWalker) {
2639 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
2640
2641 while ((n = w.nextNode()) != null)
2642 f.call(s || this, n);
2643 } else
2644 tinymce.walk(n, f, 'childNodes', s);
2645 }
2646 */
2647
2648 /*
2649 toRGB : function(s) {
2650 var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);
2651
2652 if (c) {
2653 // #FFF -> #FFFFFF
2654 if (!is(c[3]))
2655 c[3] = c[2] = c[1];
2656
2657 return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";
2658 }
2659
2660 return s;
2661 }
2662 */
2663 });
2664
2665 tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});
2666})(tinymce);
2667(function(ns) {
2668 // Traverse constants
2669 var EXTRACT = 0, CLONE = 1, DELETE = 2, extend = tinymce.extend;
2670
2671 function indexOf(child, parent) {
2672 var i, node;
2673
2674 if (child.parentNode != parent)
2675 return -1;
2676
2677 for (node = parent.firstChild, i = 0; node != child; node = node.nextSibling)
2678 i++;
2679
2680 return i;
2681 };
2682
2683 function nodeIndex(n) {
2684 var i = 0;
2685
2686 while (n.previousSibling) {
2687 i++;
2688 n = n.previousSibling;
2689 }
2690
2691 return i;
2692 };
2693
2694 function getSelectedNode(container, offset) {
2695 var child;
2696
2697 if (container.nodeType == 3 /* TEXT_NODE */)
2698 return container;
2699
2700 if (offset < 0)
2701 return container;
2702
2703 child = container.firstChild;
2704 while (child != null && offset > 0) {
2705 --offset;
2706 child = child.nextSibling;
2707 }
2708
2709 if (child != null)
2710 return child;
2711
2712 return container;
2713 };
2714
2715 // Range constructor
2716 function Range(dom) {
2717 var d = dom.doc;
2718
2719 extend(this, {
2720 dom : dom,
2721
2722 // Inital states
2723 startContainer : d,
2724 startOffset : 0,
2725 endContainer : d,
2726 endOffset : 0,
2727 collapsed : true,
2728 commonAncestorContainer : d,
2729
2730 // Range constants
2731 START_TO_START : 0,
2732 START_TO_END : 1,
2733 END_TO_END : 2,
2734 END_TO_START : 3
2735 });
2736 };
2737
2738 // Add range methods
2739 extend(Range.prototype, {
2740 setStart : function(n, o) {
2741 this._setEndPoint(true, n, o);
2742 },
2743
2744 setEnd : function(n, o) {
2745 this._setEndPoint(false, n, o);
2746 },
2747
2748 setStartBefore : function(n) {
2749 this.setStart(n.parentNode, nodeIndex(n));
2750 },
2751
2752 setStartAfter : function(n) {
2753 this.setStart(n.parentNode, nodeIndex(n) + 1);
2754 },
2755
2756 setEndBefore : function(n) {
2757 this.setEnd(n.parentNode, nodeIndex(n));
2758 },
2759
2760 setEndAfter : function(n) {
2761 this.setEnd(n.parentNode, nodeIndex(n) + 1);
2762 },
2763
2764 collapse : function(ts) {
2765 var t = this;
2766
2767 if (ts) {
2768 t.endContainer = t.startContainer;
2769 t.endOffset = t.startOffset;
2770 } else {
2771 t.startContainer = t.endContainer;
2772 t.startOffset = t.endOffset;
2773 }
2774
2775 t.collapsed = true;
2776 },
2777
2778 selectNode : function(n) {
2779 this.setStartBefore(n);
2780 this.setEndAfter(n);
2781 },
2782
2783 selectNodeContents : function(n) {
2784 this.setStart(n, 0);
2785 this.setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);
2786 },
2787
2788 compareBoundaryPoints : function(h, r) {
2789 var t = this, sc = t.startContainer, so = t.startOffset, ec = t.endContainer, eo = t.endOffset;
2790
2791 // Check START_TO_START
2792 if (h === 0)
2793 return t._compareBoundaryPoints(sc, so, sc, so);
2794
2795 // Check START_TO_END
2796 if (h === 1)
2797 return t._compareBoundaryPoints(sc, so, ec, eo);
2798
2799 // Check END_TO_END
2800 if (h === 2)
2801 return t._compareBoundaryPoints(ec, eo, ec, eo);
2802
2803 // Check END_TO_START
2804 if (h === 3)
2805 return t._compareBoundaryPoints(ec, eo, sc, so);
2806 },
2807
2808 deleteContents : function() {
2809 this._traverse(DELETE);
2810 },
2811
2812 extractContents : function() {
2813 return this._traverse(EXTRACT);
2814 },
2815
2816 cloneContents : function() {
2817 return this._traverse(CLONE);
2818 },
2819
2820 insertNode : function(n) {
2821 var t = this, nn, o;
2822
2823 // Node is TEXT_NODE or CDATA
2824 if (n.nodeType === 3 || n.nodeType === 4) {
2825 nn = t.startContainer.splitText(t.startOffset);
2826 t.startContainer.parentNode.insertBefore(n, nn);
2827 } else {
2828 // Insert element node
2829 if (t.startContainer.childNodes.length > 0)
2830 o = t.startContainer.childNodes[t.startOffset];
2831
2832 t.startContainer.insertBefore(n, o);
2833 }
2834 },
2835
2836 surroundContents : function(n) {
2837 var t = this, f = t.extractContents();
2838
2839 t.insertNode(n);
2840 n.appendChild(f);
2841 t.selectNode(n);
2842 },
2843
2844 cloneRange : function() {
2845 var t = this;
2846
2847 return extend(new Range(t.dom), {
2848 startContainer : t.startContainer,
2849 startOffset : t.startOffset,
2850 endContainer : t.endContainer,
2851 endOffset : t.endOffset,
2852 collapsed : t.collapsed,
2853 commonAncestorContainer : t.commonAncestorContainer
2854 });
2855 },
2856
2857/*
2858 detach : function() {
2859 // Not implemented
2860 },
2861*/
2862 // Internal methods
2863
2864 _isCollapsed : function() {
2865 return (this.startContainer == this.endContainer && this.startOffset == this.endOffset);
2866 },
2867
2868 _compareBoundaryPoints : function (containerA, offsetA, containerB, offsetB) {
2869 var c, offsetC, n, cmnRoot, childA, childB;
2870
2871 // In the first case the boundary-points have the same container. A is before B
2872 // if its offset is less than the offset of B, A is equal to B if its offset is
2873 // equal to the offset of B, and A is after B if its offset is greater than the
2874 // offset of B.
2875 if (containerA == containerB) {
2876 if (offsetA == offsetB) {
2877 return 0; // equal
2878 } else if (offsetA < offsetB) {
2879 return -1; // before
2880 } else {
2881 return 1; // after
2882 }
2883 }
2884
2885 // In the second case a child node C of the container of A is an ancestor
2886 // container of B. In this case, A is before B if the offset of A is less than or
2887 // equal to the index of the child node C and A is after B otherwise.
2888 c = containerB;
2889 while (c && c.parentNode != containerA) {
2890 c = c.parentNode;
2891 }
2892 if (c) {
2893 offsetC = 0;
2894 n = containerA.firstChild;
2895
2896 while (n != c && offsetC < offsetA) {
2897 offsetC++;
2898 n = n.nextSibling;
2899 }
2900
2901 if (offsetA <= offsetC) {
2902 return -1; // before
2903 } else {
2904 return 1; // after
2905 }
2906 }
2907
2908 // In the third case a child node C of the container of B is an ancestor container
2909 // of A. In this case, A is before B if the index of the child node C is less than
2910 // the offset of B and A is after B otherwise.
2911 c = containerA;
2912 while (c && c.parentNode != containerB) {
2913 c = c.parentNode;
2914 }
2915
2916 if (c) {
2917 offsetC = 0;
2918 n = containerB.firstChild;
2919
2920 while (n != c && offsetC < offsetB) {
2921 offsetC++;
2922 n = n.nextSibling;
2923 }
2924
2925 if (offsetC < offsetB) {
2926 return -1; // before
2927 } else {
2928 return 1; // after
2929 }
2930 }
2931
2932 // In the fourth case, none of three other cases hold: the containers of A and B
2933 // are siblings or descendants of sibling nodes. In this case, A is before B if
2934 // the container of A is before the container of B in a pre-order traversal of the
2935 // Ranges' context tree and A is after B otherwise.
2936 cmnRoot = this.dom.findCommonAncestor(containerA, containerB);
2937 childA = containerA;
2938
2939 while (childA && childA.parentNode != cmnRoot) {
2940 childA = childA.parentNode;
2941 }
2942
2943 if (!childA) {
2944 childA = cmnRoot;
2945 }
2946
2947 childB = containerB;
2948 while (childB && childB.parentNode != cmnRoot) {
2949 childB = childB.parentNode;
2950 }
2951
2952 if (!childB) {
2953 childB = cmnRoot;
2954 }
2955
2956 if (childA == childB) {
2957 return 0; // equal
2958 }
2959
2960 n = cmnRoot.firstChild;
2961 while (n) {
2962 if (n == childA) {
2963 return -1; // before
2964 }
2965
2966 if (n == childB) {
2967 return 1; // after
2968 }
2969
2970 n = n.nextSibling;
2971 }
2972 },
2973
2974 _setEndPoint : function(st, n, o) {
2975 var t = this, ec, sc;
2976
2977 if (st) {
2978 t.startContainer = n;
2979 t.startOffset = o;
2980 } else {
2981 t.endContainer = n;
2982 t.endOffset = o;
2983 }
2984
2985 // If one boundary-point of a Range is set to have a root container
2986 // other than the current one for the Range, the Range is collapsed to
2987 // the new position. This enforces the restriction that both boundary-
2988 // points of a Range must have the same root container.
2989 ec = t.endContainer;
2990 while (ec.parentNode)
2991 ec = ec.parentNode;
2992
2993 sc = t.startContainer;
2994 while (sc.parentNode)
2995 sc = sc.parentNode;
2996
2997 if (sc != ec) {
2998 t.collapse(st);
2999 } else {
3000 // The start position of a Range is guaranteed to never be after the
3001 // end position. To enforce this restriction, if the start is set to
3002 // be at a position after the end, the Range is collapsed to that
3003 // position.
3004 if (t._compareBoundaryPoints(t.startContainer, t.startOffset, t.endContainer, t.endOffset) > 0)
3005 t.collapse(st);
3006 }
3007
3008 t.collapsed = t._isCollapsed();
3009 t.commonAncestorContainer = t.dom.findCommonAncestor(t.startContainer, t.endContainer);
3010 },
3011
3012 // This code is heavily "inspired" by the Apache Xerces implementation. I hope they don't mind. :)
3013
3014 _traverse : function(how) {
3015 var t = this, c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;
3016
3017 if (t.startContainer == t.endContainer)
3018 return t._traverseSameContainer(how);
3019
3020 for (c = t.endContainer, p = c.parentNode; p != null; c = p, p = p.parentNode) {
3021 if (p == t.startContainer)
3022 return t._traverseCommonStartContainer(c, how);
3023
3024 ++endContainerDepth;
3025 }
3026
3027 for (c = t.startContainer, p = c.parentNode; p != null; c = p, p = p.parentNode) {
3028 if (p == t.endContainer)
3029 return t._traverseCommonEndContainer(c, how);
3030
3031 ++startContainerDepth;
3032 }
3033
3034 depthDiff = startContainerDepth - endContainerDepth;
3035
3036 startNode = t.startContainer;
3037 while (depthDiff > 0) {
3038 startNode = startNode.parentNode;
3039 depthDiff--;
3040 }
3041
3042 endNode = t.endContainer;
3043 while (depthDiff < 0) {
3044 endNode = endNode.parentNode;
3045 depthDiff++;
3046 }
3047
3048 // ascend the ancestor hierarchy until we have a common parent.
3049 for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {
3050 startNode = sp;
3051 endNode = ep;
3052 }
3053
3054 return t._traverseCommonAncestors(startNode, endNode, how);
3055 },
3056
3057 _traverseSameContainer : function(how) {
3058 var t = this, frag, s, sub, n, cnt, sibling, xferNode;
3059
3060 if (how != DELETE)
3061 frag = t.dom.doc.createDocumentFragment();
3062
3063 // If selection is empty, just return the fragment
3064 if (t.startOffset == t.endOffset)
3065 return frag;
3066
3067 // Text node needs special case handling
3068 if (t.startContainer.nodeType == 3 /* TEXT_NODE */) {
3069 // get the substring
3070 s = t.startContainer.nodeValue;
3071 sub = s.substring(t.startOffset, t.endOffset);
3072
3073 // set the original text node to its new value
3074 if (how != CLONE) {
3075 t.startContainer.deleteData(t.startOffset, t.endOffset - t.startOffset);
3076
3077 // Nothing is partially selected, so collapse to start point
3078 t.collapse(true);
3079 }
3080
3081 if (how == DELETE)
3082 return null;
3083
3084 frag.appendChild(t.dom.doc.createTextNode(sub));
3085 return frag;
3086 }
3087
3088 // Copy nodes between the start/end offsets.
3089 n = getSelectedNode(t.startContainer, t.startOffset);
3090 cnt = t.endOffset - t.startOffset;
3091
3092 while (cnt > 0) {
3093 sibling = n.nextSibling;
3094 xferNode = t._traverseFullySelected(n, how);
3095
3096 if (frag)
3097 frag.appendChild( xferNode );
3098
3099 --cnt;
3100 n = sibling;
3101 }
3102
3103 // Nothing is partially selected, so collapse to start point
3104 if (how != CLONE)
3105 t.collapse(true);
3106
3107 return frag;
3108 },
3109
3110 _traverseCommonStartContainer : function(endAncestor, how) {
3111 var t = this, frag, n, endIdx, cnt, sibling, xferNode;
3112
3113 if (how != DELETE)
3114 frag = t.dom.doc.createDocumentFragment();
3115
3116 n = t._traverseRightBoundary(endAncestor, how);
3117
3118 if (frag)
3119 frag.appendChild(n);
3120
3121 endIdx = indexOf(endAncestor, t.startContainer);
3122 cnt = endIdx - t.startOffset;
3123
3124 if (cnt <= 0) {
3125 // Collapse to just before the endAncestor, which
3126 // is partially selected.
3127 if (how != CLONE) {
3128 t.setEndBefore(endAncestor);
3129 t.collapse(false);
3130 }
3131
3132 return frag;
3133 }
3134
3135 n = endAncestor.previousSibling;
3136 while (cnt > 0) {
3137 sibling = n.previousSibling;
3138 xferNode = t._traverseFullySelected(n, how);
3139
3140 if (frag)
3141 frag.insertBefore(xferNode, frag.firstChild);
3142
3143 --cnt;
3144 n = sibling;
3145 }
3146
3147 // Collapse to just before the endAncestor, which
3148 // is partially selected.
3149 if (how != CLONE) {
3150 t.setEndBefore(endAncestor);
3151 t.collapse(false);
3152 }
3153
3154 return frag;
3155 },
3156
3157 _traverseCommonEndContainer : function(startAncestor, how) {
3158 var t = this, frag, startIdx, n, cnt, sibling, xferNode;
3159
3160 if (how != DELETE)
3161 frag = t.dom.doc.createDocumentFragment();
3162
3163 n = t._traverseLeftBoundary(startAncestor, how);
3164 if (frag)
3165 frag.appendChild(n);
3166
3167 startIdx = indexOf(startAncestor, t.endContainer);
3168 ++startIdx; // Because we already traversed it....
3169
3170 cnt = t.endOffset - startIdx;
3171 n = startAncestor.nextSibling;
3172 while (cnt > 0) {
3173 sibling = n.nextSibling;
3174 xferNode = t._traverseFullySelected(n, how);
3175
3176 if (frag)
3177 frag.appendChild(xferNode);
3178
3179 --cnt;
3180 n = sibling;
3181 }
3182
3183 if (how != CLONE) {
3184 t.setStartAfter(startAncestor);
3185 t.collapse(true);
3186 }
3187
3188 return frag;
3189 },
3190
3191 _traverseCommonAncestors : function(startAncestor, endAncestor, how) {
3192 var t = this, n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;
3193
3194 if (how != DELETE)
3195 frag = t.dom.doc.createDocumentFragment();
3196
3197 n = t._traverseLeftBoundary(startAncestor, how);
3198 if (frag)
3199 frag.appendChild(n);
3200
3201 commonParent = startAncestor.parentNode;
3202 startOffset = indexOf(startAncestor, commonParent);
3203 endOffset = indexOf(endAncestor, commonParent);
3204 ++startOffset;
3205
3206 cnt = endOffset - startOffset;
3207 sibling = startAncestor.nextSibling;
3208
3209 while (cnt > 0) {
3210 nextSibling = sibling.nextSibling;
3211 n = t._traverseFullySelected(sibling, how);
3212
3213 if (frag)
3214 frag.appendChild(n);
3215
3216 sibling = nextSibling;
3217 --cnt;
3218 }
3219
3220 n = t._traverseRightBoundary(endAncestor, how);
3221
3222 if (frag)
3223 frag.appendChild(n);
3224
3225 if (how != CLONE) {
3226 t.setStartAfter(startAncestor);
3227 t.collapse(true);
3228 }
3229
3230 return frag;
3231 },
3232
3233 _traverseRightBoundary : function(root, how) {
3234 var t = this, next = getSelectedNode(t.endContainer, t.endOffset - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent;
3235 var isFullySelected = next != t.endContainer;
3236
3237 if (next == root)
3238 return t._traverseNode(next, isFullySelected, false, how);
3239
3240 parent = next.parentNode;
3241 clonedParent = t._traverseNode(parent, false, false, how);
3242
3243 while (parent != null) {
3244 while (next != null) {
3245 prevSibling = next.previousSibling;
3246 clonedChild = t._traverseNode(next, isFullySelected, false, how);
3247
3248 if (how != DELETE)
3249 clonedParent.insertBefore(clonedChild, clonedParent.firstChild);
3250
3251 isFullySelected = true;
3252 next = prevSibling;
3253 }
3254
3255 if (parent == root)
3256 return clonedParent;
3257
3258 next = parent.previousSibling;
3259 parent = parent.parentNode;
3260
3261 clonedGrandParent = t._traverseNode(parent, false, false, how);
3262
3263 if (how != DELETE)
3264 clonedGrandParent.appendChild(clonedParent);
3265
3266 clonedParent = clonedGrandParent;
3267 }
3268
3269 // should never occur
3270 return null;
3271 },
3272
3273 _traverseLeftBoundary : function(root, how) {
3274 var t = this, next = getSelectedNode(t.startContainer, t.startOffset);
3275 var isFullySelected = next != t.startContainer, parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;
3276
3277 if (next == root)
3278 return t._traverseNode(next, isFullySelected, true, how);
3279
3280 parent = next.parentNode;
3281 clonedParent = t._traverseNode(parent, false, true, how);
3282
3283 while (parent != null) {
3284 while (next != null) {
3285 nextSibling = next.nextSibling;
3286 clonedChild = t._traverseNode(next, isFullySelected, true, how);
3287
3288 if (how != DELETE)
3289 clonedParent.appendChild(clonedChild);
3290
3291 isFullySelected = true;
3292 next = nextSibling;
3293 }
3294
3295 if (parent == root)
3296 return clonedParent;
3297
3298 next = parent.nextSibling;
3299 parent = parent.parentNode;
3300
3301 clonedGrandParent = t._traverseNode(parent, false, true, how);
3302
3303 if (how != DELETE)
3304 clonedGrandParent.appendChild(clonedParent);
3305
3306 clonedParent = clonedGrandParent;
3307 }
3308
3309 // should never occur
3310 return null;
3311 },
3312
3313 _traverseNode : function(n, isFullySelected, isLeft, how) {
3314 var t = this, txtValue, newNodeValue, oldNodeValue, offset, newNode;
3315
3316 if (isFullySelected)
3317 return t._traverseFullySelected(n, how);
3318
3319 if (n.nodeType == 3 /* TEXT_NODE */) {
3320 txtValue = n.nodeValue;
3321
3322 if (isLeft) {
3323 offset = t.startOffset;
3324 newNodeValue = txtValue.substring(offset);
3325 oldNodeValue = txtValue.substring(0, offset);
3326 } else {
3327 offset = t.endOffset;
3328 newNodeValue = txtValue.substring(0, offset);
3329 oldNodeValue = txtValue.substring(offset);
3330 }
3331
3332 if (how != CLONE)
3333 n.nodeValue = oldNodeValue;
3334
3335 if (how == DELETE)
3336 return null;
3337
3338 newNode = n.cloneNode(false);
3339 newNode.nodeValue = newNodeValue;
3340
3341 return newNode;
3342 }
3343
3344 if (how == DELETE)
3345 return null;
3346
3347 return n.cloneNode(false);
3348 },
3349
3350 _traverseFullySelected : function(n, how) {
3351 var t = this;
3352
3353 if (how != DELETE)
3354 return how == CLONE ? n.cloneNode(true) : n;
3355
3356 n.parentNode.removeChild(n);
3357 return null;
3358 }
3359 });
3360
3361 ns.Range = Range;
3362})(tinymce.dom);
3363(function() {
3364 function Selection(selection) {
3365 var t = this, invisibleChar = '\uFEFF', range, lastIERng;
3366
3367 function compareRanges(rng1, rng2) {
3368 if (rng1 && rng2) {
3369 // Both are control ranges and the selected element matches
3370 if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))
3371 return 1;
3372
3373 // Both are text ranges and the range matches
3374 if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1))
3375 return 1;
3376 }
3377
3378 return 0;
3379 };
3380
3381 function getRange() {
3382 var dom = selection.dom, ieRange = selection.getRng(), domRange = dom.createRng(), startPos, endPos, element, sc, ec, collapsed;
3383
3384 function findIndex(element) {
3385 var nl = element.parentNode.childNodes, i;
3386
3387 for (i = nl.length - 1; i >= 0; i--) {
3388 if (nl[i] == element)
3389 return i;
3390 }
3391
3392 return -1;
3393 };
3394
3395 function findEndPoint(start) {
3396 var rng = ieRange.duplicate(), parent, i, nl, n, offset = 0, index = 0, pos, tmpRng;
3397
3398 // Insert marker character
3399 rng.collapse(start);
3400 parent = rng.parentElement();
3401 rng.pasteHTML(invisibleChar); // Needs to be a pasteHTML instead of .text = since IE has a bug with nodeValue
3402
3403 // Find marker character
3404 nl = parent.childNodes;
3405 for (i = 0; i < nl.length; i++) {
3406 n = nl[i];
3407
3408 // Calculate node index excluding text node fragmentation
3409 if (i > 0 && (n.nodeType !== 3 || nl[i - 1].nodeType !== 3))
3410 index++;
3411
3412 // If text node then calculate offset
3413 if (n.nodeType === 3) {
3414 // Look for marker
3415 pos = n.nodeValue.indexOf(invisibleChar);
3416 if (pos !== -1) {
3417 offset += pos;
3418 break;
3419 }
3420
3421 offset += n.nodeValue.length;
3422 } else
3423 offset = 0;
3424 }
3425
3426 // Remove marker character
3427 rng.moveStart('character', -1);
3428 rng.text = '';
3429
3430 return {index : index, offset : offset, parent : parent};
3431 };
3432
3433 // If selection is outside the current document just return an empty range
3434 element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();
3435 if (element.ownerDocument != dom.doc)
3436 return domRange;
3437
3438 // Handle control selection or text selection of a image
3439 if (ieRange.item || !element.hasChildNodes()) {
3440 domRange.setStart(element.parentNode, findIndex(element));
3441 domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);
3442
3443 return domRange;
3444 }
3445
3446 // Check collapsed state
3447 collapsed = selection.isCollapsed();
3448
3449 // Find start and end pos index and offset
3450 startPos = findEndPoint(true);
3451 endPos = findEndPoint(false);
3452
3453 // Normalize the elements to avoid fragmented dom
3454 startPos.parent.normalize();
3455 endPos.parent.normalize();
3456
3457 // Set start container and offset
3458 sc = startPos.parent.childNodes[Math.min(startPos.index, startPos.parent.childNodes.length - 1)];
3459
3460 if (sc.nodeType != 3)
3461 domRange.setStart(startPos.parent, startPos.index);
3462 else
3463 domRange.setStart(startPos.parent.childNodes[startPos.index], startPos.offset);
3464
3465 // Set end container and offset
3466 ec = endPos.parent.childNodes[Math.min(endPos.index, endPos.parent.childNodes.length - 1)];
3467
3468 if (ec.nodeType != 3) {
3469 if (!collapsed)
3470 endPos.index++;
3471
3472 domRange.setEnd(endPos.parent, endPos.index);
3473 } else
3474 domRange.setEnd(endPos.parent.childNodes[endPos.index], endPos.offset);
3475
3476 // If not collapsed then make sure offsets are valid
3477 if (!collapsed) {
3478 sc = domRange.startContainer;
3479 if (sc.nodeType == 1)
3480 domRange.setStart(sc, Math.min(domRange.startOffset, sc.childNodes.length));
3481
3482 ec = domRange.endContainer;
3483 if (ec.nodeType == 1)
3484 domRange.setEnd(ec, Math.min(domRange.endOffset, ec.childNodes.length));
3485 }
3486
3487 // Restore selection to new range
3488 t.addRange(domRange);
3489
3490 return domRange;
3491 };
3492
3493 this.addRange = function(rng) {
3494 var ieRng, body = selection.dom.doc.body, startPos, endPos, sc, so, ec, eo;
3495
3496 // Setup some shorter versions
3497 sc = rng.startContainer;
3498 so = rng.startOffset;
3499 ec = rng.endContainer;
3500 eo = rng.endOffset;
3501 ieRng = body.createTextRange();
3502
3503 // Find element
3504 sc = sc.nodeType == 1 ? sc.childNodes[Math.min(so, sc.childNodes.length - 1)] : sc;
3505 ec = ec.nodeType == 1 ? ec.childNodes[Math.min(so == eo ? eo : eo - 1, ec.childNodes.length - 1)] : ec;
3506
3507 // Single element selection
3508 if (sc == ec && sc.nodeType == 1) {
3509 // Make control selection for some elements
3510 if (/^(IMG|TABLE)$/.test(sc.nodeName) && so != eo) {
3511 ieRng = body.createControlRange();
3512 ieRng.addElement(sc);
3513 } else {
3514 ieRng = body.createTextRange();
3515
3516 // Padd empty elements with invisible character
3517 if (!sc.hasChildNodes() && sc.canHaveHTML)
3518 sc.innerHTML = invisibleChar;
3519
3520 // Select element contents
3521 ieRng.moveToElementText(sc);
3522
3523 // If it's only containing a padding remove it so the caret remains
3524 if (sc.innerHTML == invisibleChar) {
3525 ieRng.collapse(true);
3526 sc.removeChild(sc.firstChild);
3527 }
3528 }
3529
3530 if (so == eo)
3531 ieRng.collapse(eo <= rng.endContainer.childNodes.length - 1);
3532
3533 ieRng.select();
3534
3535 return;
3536 }
3537
3538 function getCharPos(container, offset) {
3539 var nodeVal, rng, pos;
3540
3541 if (container.nodeType != 3)
3542 return -1;
3543
3544 nodeVal = container.nodeValue;
3545 rng = body.createTextRange();
3546
3547 // Insert marker at offset position
3548 container.nodeValue = nodeVal.substring(0, offset) + invisibleChar + nodeVal.substring(offset);
3549
3550 // Find char pos of marker and remove it
3551 rng.moveToElementText(container.parentNode);
3552 rng.findText(invisibleChar);
3553 pos = Math.abs(rng.moveStart('character', -0xFFFFF));
3554 container.nodeValue = nodeVal;
3555
3556 return pos;
3557 };
3558
3559 // Collapsed range
3560 if (rng.collapsed) {
3561 pos = getCharPos(sc, so);
3562
3563 ieRng = body.createTextRange();
3564 ieRng.move('character', pos);
3565 ieRng.select();
3566
3567 return;
3568 } else {
3569 // If same text container
3570 if (sc == ec && sc.nodeType == 3) {
3571 startPos = getCharPos(sc, so);
3572
3573 ieRng = body.createTextRange();
3574 ieRng.move('character', startPos);
3575 ieRng.moveEnd('character', eo - so);
3576 ieRng.select();
3577
3578 return;
3579 }
3580
3581 // Get caret positions
3582 startPos = getCharPos(sc, so);
3583 endPos = getCharPos(ec, eo);
3584 ieRng = body.createTextRange();
3585
3586 // Move start of range to start character position or start element
3587 if (startPos == -1) {
3588 ieRng.moveToElementText(sc);
3589 startPos = 0;
3590 } else
3591 ieRng.move('character', startPos);
3592
3593 // Move end of range to end character position or end element
3594 tmpRng = body.createTextRange();
3595
3596 if (endPos == -1)
3597 tmpRng.moveToElementText(ec);
3598 else
3599 tmpRng.move('character', endPos);
3600
3601 ieRng.setEndPoint('EndToEnd', tmpRng);
3602 ieRng.select();
3603
3604 return;
3605 }
3606 };
3607
3608 this.getRangeAt = function() {
3609 // Setup new range if the cache is empty
3610 if (!range || !compareRanges(lastIERng, selection.getRng())) {
3611 range = getRange();
3612
3613 // Store away text range for next call
3614 lastIERng = selection.getRng();
3615 }
3616
3617 // Return cached range
3618 return range;
3619 };
3620
3621 this.destroy = function() {
3622 // Destroy cached range and last IE range to avoid memory leaks
3623 lastIERng = range = null;
3624 };
3625 };
3626
3627 // Expose the selection object
3628 tinymce.dom.TridentSelection = Selection;
3629})();
3630(function(tinymce) {
3631 // Shorten names
3632 var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event;
3633
3634 tinymce.create('tinymce.dom.EventUtils', {
3635 EventUtils : function() {
3636 this.inits = [];
3637 this.events = [];
3638 },
3639
3640 add : function(o, n, f, s) {
3641 var cb, t = this, el = t.events, r;
3642
3643 if (n instanceof Array) {
3644 r = [];
3645
3646 each(n, function(n) {
3647 r.push(t.add(o, n, f, s));
3648 });
3649
3650 return r;
3651 }
3652
3653 // Handle array
3654 if (o && o.hasOwnProperty && o instanceof Array) {
3655 r = [];
3656
3657 each(o, function(o) {
3658 o = DOM.get(o);
3659 r.push(t.add(o, n, f, s));
3660 });
3661
3662 return r;
3663 }
3664
3665 o = DOM.get(o);
3666
3667 if (!o)
3668 return;
3669
3670 // Setup event callback
3671 cb = function(e) {
3672 // Is all events disabled
3673 if (t.disabled)
3674 return;
3675
3676 e = e || window.event;
3677
3678 // Patch in target, preventDefault and stopPropagation in IE it's W3C valid
3679 if (e && isIE) {
3680 if (!e.target)
3681 e.target = e.srcElement;
3682
3683 // Patch in preventDefault, stopPropagation methods for W3C compatibility
3684 tinymce.extend(e, t._stoppers);
3685 }
3686
3687 if (!s)
3688 return f(e);
3689
3690 return f.call(s, e);
3691 };
3692
3693 if (n == 'unload') {
3694 tinymce.unloads.unshift({func : cb});
3695 return cb;
3696 }
3697
3698 if (n == 'init') {
3699 if (t.domLoaded)
3700 cb();
3701 else
3702 t.inits.push(cb);
3703
3704 return cb;
3705 }
3706
3707 // Store away listener reference
3708 el.push({
3709 obj : o,
3710 name : n,
3711 func : f,
3712 cfunc : cb,
3713 scope : s
3714 });
3715
3716 t._add(o, n, cb);
3717
3718 return f;
3719 },
3720
3721 remove : function(o, n, f) {
3722 var t = this, a = t.events, s = false, r;
3723
3724 // Handle array
3725 if (o && o.hasOwnProperty && o instanceof Array) {
3726 r = [];
3727
3728 each(o, function(o) {
3729 o = DOM.get(o);
3730 r.push(t.remove(o, n, f));
3731 });
3732
3733 return r;
3734 }
3735
3736 o = DOM.get(o);
3737
3738 each(a, function(e, i) {
3739 if (e.obj == o && e.name == n && (!f || (e.func == f || e.cfunc == f))) {
3740 a.splice(i, 1);
3741 t._remove(o, n, e.cfunc);
3742 s = true;
3743 return false;
3744 }
3745 });
3746
3747 return s;
3748 },
3749
3750 clear : function(o) {
3751 var t = this, a = t.events, i, e;
3752
3753 if (o) {
3754 o = DOM.get(o);
3755
3756 for (i = a.length - 1; i >= 0; i--) {
3757 e = a[i];
3758
3759 if (e.obj === o) {
3760 t._remove(e.obj, e.name, e.cfunc);
3761 e.obj = e.cfunc = null;
3762 a.splice(i, 1);
3763 }
3764 }
3765 }
3766 },
3767
3768 cancel : function(e) {
3769 if (!e)
3770 return false;
3771
3772 this.stop(e);
3773
3774 return this.prevent(e);
3775 },
3776
3777 stop : function(e) {
3778 if (e.stopPropagation)
3779 e.stopPropagation();
3780 else
3781 e.cancelBubble = true;
3782
3783 return false;
3784 },
3785
3786 prevent : function(e) {
3787 if (e.preventDefault)
3788 e.preventDefault();
3789 else
3790 e.returnValue = false;
3791
3792 return false;
3793 },
3794
3795 destroy : function() {
3796 var t = this;
3797
3798 each(t.events, function(e, i) {
3799 t._remove(e.obj, e.name, e.cfunc);
3800 e.obj = e.cfunc = null;
3801 });
3802
3803 t.events = [];
3804 t = null;
3805 },
3806
3807 _add : function(o, n, f) {
3808 if (o.attachEvent)
3809 o.attachEvent('on' + n, f);
3810 else if (o.addEventListener)
3811 o.addEventListener(n, f, false);
3812 else
3813 o['on' + n] = f;
3814 },
3815
3816 _remove : function(o, n, f) {
3817 if (o) {
3818 try {
3819 if (o.detachEvent)
3820 o.detachEvent('on' + n, f);
3821 else if (o.removeEventListener)
3822 o.removeEventListener(n, f, false);
3823 else
3824 o['on' + n] = null;
3825 } catch (ex) {
3826 // Might fail with permission denined on IE so we just ignore that
3827 }
3828 }
3829 },
3830
3831 _pageInit : function(win) {
3832 var t = this;
3833
3834 // Keep it from running more than once
3835 if (t.domLoaded)
3836 return;
3837
3838 t.domLoaded = true;
3839
3840 each(t.inits, function(c) {
3841 c();
3842 });
3843
3844 t.inits = [];
3845 },
3846
3847 _wait : function(win) {
3848 var t = this, doc = win.document;
3849
3850 // No need since the document is already loaded
3851 if (win.tinyMCE_GZ && tinyMCE_GZ.loaded) {
3852 t.domLoaded = 1;
3853 return;
3854 }
3855
3856 // Use IE method
3857 if (doc.attachEvent) {
3858 doc.attachEvent("onreadystatechange", function() {
3859 if (doc.readyState === "complete") {
3860 doc.detachEvent("onreadystatechange", arguments.callee);
3861 t._pageInit(win);
3862 }
3863 });
3864
3865 if (doc.documentElement.doScroll && win == win.top) {
3866 (function() {
3867 if (t.domLoaded)
3868 return;
3869
3870 try {
3871 // If IE is used, use the trick by Diego Perini
3872 // http://javascript.nwbox.com/IEContentLoaded/
3873 doc.documentElement.doScroll("left");
3874 } catch (ex) {
3875 setTimeout(arguments.callee, 0);
3876 return;
3877 }
3878
3879 t._pageInit(win);
3880 })();
3881 }
3882 } else if (doc.addEventListener) {
3883 t._add(win, 'DOMContentLoaded', function() {
3884 t._pageInit(win);
3885 });
3886 }
3887
3888 t._add(win, 'load', function() {
3889 t._pageInit(win);
3890 });
3891 },
3892
3893 _stoppers : {
3894 preventDefault : function() {
3895 this.returnValue = false;
3896 },
3897
3898 stopPropagation : function() {
3899 this.cancelBubble = true;
3900 }
3901 }
3902 });
3903
3904 Event = tinymce.dom.Event = new tinymce.dom.EventUtils();
3905
3906 // Dispatch DOM content loaded event for IE and Safari
3907 Event._wait(window);
3908
3909 tinymce.addUnload(function() {
3910 Event.destroy();
3911 });
3912})(tinymce);
3913(function(tinymce) {
3914 var each = tinymce.each;
3915
3916 tinymce.create('tinymce.dom.Element', {
3917 Element : function(id, s) {
3918 var t = this, dom, el;
3919
3920 s = s || {};
3921 t.id = id;
3922 t.dom = dom = s.dom || tinymce.DOM;
3923 t.settings = s;
3924
3925 // Only IE leaks DOM references, this is a lot faster
3926 if (!tinymce.isIE)
3927 el = t.dom.get(t.id);
3928
3929 each([
3930 'getPos',
3931 'getRect',
3932 'getParent',
3933 'add',
3934 'setStyle',
3935 'getStyle',
3936 'setStyles',
3937 'setAttrib',
3938 'setAttribs',
3939 'getAttrib',
3940 'addClass',
3941 'removeClass',
3942 'hasClass',
3943 'getOuterHTML',
3944 'setOuterHTML',
3945 'remove',
3946 'show',
3947 'hide',
3948 'isHidden',
3949 'setHTML',
3950 'get'
3951 ], function(k) {
3952 t[k] = function() {
3953 var a = [id], i;
3954
3955 for (i = 0; i < arguments.length; i++)
3956 a.push(arguments[i]);
3957
3958 a = dom[k].apply(dom, a);
3959 t.update(k);
3960
3961 return a;
3962 };
3963 });
3964 },
3965
3966 on : function(n, f, s) {
3967 return tinymce.dom.Event.add(this.id, n, f, s);
3968 },
3969
3970 getXY : function() {
3971 return {
3972 x : parseInt(this.getStyle('left')),
3973 y : parseInt(this.getStyle('top'))
3974 };
3975 },
3976
3977 getSize : function() {
3978 var n = this.dom.get(this.id);
3979
3980 return {
3981 w : parseInt(this.getStyle('width') || n.clientWidth),
3982 h : parseInt(this.getStyle('height') || n.clientHeight)
3983 };
3984 },
3985
3986 moveTo : function(x, y) {
3987 this.setStyles({left : x, top : y});
3988 },
3989
3990 moveBy : function(x, y) {
3991 var p = this.getXY();
3992
3993 this.moveTo(p.x + x, p.y + y);
3994 },
3995
3996 resizeTo : function(w, h) {
3997 this.setStyles({width : w, height : h});
3998 },
3999
4000 resizeBy : function(w, h) {
4001 var s = this.getSize();
4002
4003 this.resizeTo(s.w + w, s.h + h);
4004 },
4005
4006 update : function(k) {
4007 var t = this, b, dom = t.dom;
4008
4009 if (tinymce.isIE6 && t.settings.blocker) {
4010 k = k || '';
4011
4012 // Ignore getters
4013 if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0)
4014 return;
4015
4016 // Remove blocker on remove
4017 if (k == 'remove') {
4018 dom.remove(t.blocker);
4019 return;
4020 }
4021
4022 if (!t.blocker) {
4023 t.blocker = dom.uniqueId();
4024 b = dom.add(t.settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'});
4025 dom.setStyle(b, 'opacity', 0);
4026 } else
4027 b = dom.get(t.blocker);
4028
4029 dom.setStyle(b, 'left', t.getStyle('left', 1));
4030 dom.setStyle(b, 'top', t.getStyle('top', 1));
4031 dom.setStyle(b, 'width', t.getStyle('width', 1));
4032 dom.setStyle(b, 'height', t.getStyle('height', 1));
4033 dom.setStyle(b, 'display', t.getStyle('display', 1));
4034 dom.setStyle(b, 'zIndex', parseInt(t.getStyle('zIndex', 1) || 0) - 1);
4035 }
4036 }
4037 });
4038})(tinymce);
4039(function(tinymce) {
4040 function trimNl(s) {
4041 return s.replace(/[\n\r]+/g, '');
4042 };
4043
4044 // Shorten names
4045 var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each;
4046
4047 tinymce.create('tinymce.dom.Selection', {
4048 Selection : function(dom, win, serializer) {
4049 var t = this;
4050
4051 t.dom = dom;
4052 t.win = win;
4053 t.serializer = serializer;
4054
4055 // Add events
4056 each([
4057 'onBeforeSetContent',
4058 'onBeforeGetContent',
4059 'onSetContent',
4060 'onGetContent'
4061 ], function(e) {
4062 t[e] = new tinymce.util.Dispatcher(t);
4063 });
4064
4065 // No W3C Range support
4066 if (!t.win.getSelection)
4067 t.tridentSel = new tinymce.dom.TridentSelection(t);
4068
4069 // Prevent leaks
4070 tinymce.addUnload(t.destroy, t);
4071 },
4072
4073 getContent : function(s) {
4074 var t = this, r = t.getRng(), e = t.dom.create("body"), se = t.getSel(), wb, wa, n;
4075
4076 s = s || {};
4077 wb = wa = '';
4078 s.get = true;
4079 s.format = s.format || 'html';
4080 t.onBeforeGetContent.dispatch(t, s);
4081
4082 if (s.format == 'text')
4083 return t.isCollapsed() ? '' : (r.text || (se.toString ? se.toString() : ''));
4084
4085 if (r.cloneContents) {
4086 n = r.cloneContents();
4087
4088 if (n)
4089 e.appendChild(n);
4090 } else if (is(r.item) || is(r.htmlText))
4091 e.innerHTML = r.item ? r.item(0).outerHTML : r.htmlText;
4092 else
4093 e.innerHTML = r.toString();
4094
4095 // Keep whitespace before and after
4096 if (/^\s/.test(e.innerHTML))
4097 wb = ' ';
4098
4099 if (/\s+$/.test(e.innerHTML))
4100 wa = ' ';
4101
4102 s.getInner = true;
4103
4104 s.content = t.isCollapsed() ? '' : wb + t.serializer.serialize(e, s) + wa;
4105 t.onGetContent.dispatch(t, s);
4106
4107 return s.content;
4108 },
4109
4110 setContent : function(h, s) {
4111 var t = this, r = t.getRng(), c, d = t.win.document;
4112
4113 s = s || {format : 'html'};
4114 s.set = true;
4115 h = s.content = t.dom.processHTML(h);
4116
4117 // Dispatch before set content event
4118 t.onBeforeSetContent.dispatch(t, s);
4119 h = s.content;
4120
4121 if (r.insertNode) {
4122 // Make caret marker since insertNode places the caret in the beginning of text after insert
4123 h += '<span id="__caret">_</span>';
4124
4125 // Delete and insert new node
4126 r.deleteContents();
4127 r.insertNode(t.getRng().createContextualFragment(h));
4128
4129 // Move to caret marker
4130 c = t.dom.get('__caret');
4131
4132 // Make sure we wrap it compleatly, Opera fails with a simple select call
4133 r = d.createRange();
4134 r.setStartBefore(c);
4135 r.setEndAfter(c);
4136 t.setRng(r);
4137
4138 // Delete the marker, and hopefully the caret gets placed in the right location
4139 // Removed this since it seems to remove &nbsp; in FF and simply deleting it
4140 // doesn't seem to affect the caret position in any browser
4141 //d.execCommand('Delete', false, null);
4142
4143 // Remove the caret position
4144 t.dom.remove('__caret');
4145 } else {
4146 if (r.item) {
4147 // Delete content and get caret text selection
4148 d.execCommand('Delete', false, null);
4149 r = t.getRng();
4150 }
4151
4152 r.pasteHTML(h);
4153 }
4154
4155 // Dispatch set content event
4156 t.onSetContent.dispatch(t, s);
4157 },
4158
4159 getStart : function() {
4160 var t = this, r = t.getRng(), e;
4161
4162 if (isIE) {
4163 if (r.item)
4164 return r.item(0);
4165
4166 r = r.duplicate();
4167 r.collapse(1);
4168 e = r.parentElement();
4169
4170 if (e && e.nodeName == 'BODY')
4171 return e.firstChild;
4172
4173 return e;
4174 } else {
4175 e = r.startContainer;
4176
4177 if (e.nodeName == 'BODY')
4178 return e.firstChild;
4179
4180 return t.dom.getParent(e, '*');
4181 }
4182 },
4183
4184 getEnd : function() {
4185 var t = this, r = t.getRng(), e;
4186
4187 if (isIE) {
4188 if (r.item)
4189 return r.item(0);
4190
4191 r = r.duplicate();
4192 r.collapse(0);
4193 e = r.parentElement();
4194
4195 if (e && e.nodeName == 'BODY')
4196 return e.lastChild;
4197
4198 return e;
4199 } else {
4200 e = r.endContainer;
4201
4202 if (e.nodeName == 'BODY')
4203 return e.lastChild;
4204
4205 return t.dom.getParent(e, '*');
4206 }
4207 },
4208
4209 getBookmark : function(si) {
4210 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;
4211 sx = vp.x;
4212 sy = vp.y;
4213
4214 // Simple bookmark fast but not as persistent
4215 if (si)
4216 return {rng : r, scrollX : sx, scrollY : sy};
4217
4218 // Handle IE
4219 if (isIE) {
4220 // Control selection
4221 if (r.item) {
4222 e = r.item(0);
4223
4224 each(t.dom.select(e.nodeName), function(n, i) {
4225 if (e == n) {
4226 sp = i;
4227 return false;
4228 }
4229 });
4230
4231 return {
4232 tag : e.nodeName,
4233 index : sp,
4234 scrollX : sx,
4235 scrollY : sy
4236 };
4237 }
4238
4239 // Text selection
4240 tr = t.dom.doc.body.createTextRange();
4241 tr.moveToElementText(ro);
4242 tr.collapse(true);
4243 bp = Math.abs(tr.move('character', c));
4244
4245 tr = r.duplicate();
4246 tr.collapse(true);
4247 sp = Math.abs(tr.move('character', c));
4248
4249 tr = r.duplicate();
4250 tr.collapse(false);
4251 le = Math.abs(tr.move('character', c)) - sp;
4252
4253 return {
4254 start : sp - bp,
4255 length : le,
4256 scrollX : sx,
4257 scrollY : sy
4258 };
4259 }
4260
4261 // Handle W3C
4262 e = t.getNode();
4263 s = t.getSel();
4264
4265 if (!s)
4266 return null;
4267
4268 // Image selection
4269 if (e && e.nodeName == 'IMG') {
4270 return {
4271 scrollX : sx,
4272 scrollY : sy
4273 };
4274 }
4275
4276 // Text selection
4277
4278 function getPos(r, sn, en) {
4279 var w = t.dom.doc.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {};
4280
4281 while ((n = w.nextNode()) != null) {
4282 if (n == sn)
4283 d.start = p;
4284
4285 if (n == en) {
4286 d.end = p;
4287 return d;
4288 }
4289
4290 p += trimNl(n.nodeValue || '').length;
4291 }
4292
4293 return null;
4294 };
4295
4296 // Caret or selection
4297 if (s.anchorNode == s.focusNode && s.anchorOffset == s.focusOffset) {
4298 e = getPos(ro, s.anchorNode, s.focusNode);
4299
4300 if (!e)
4301 return {scrollX : sx, scrollY : sy};
4302
4303 // Count whitespace before
4304 trimNl(s.anchorNode.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;});
4305
4306 return {
4307 start : Math.max(e.start + s.anchorOffset - wb, 0),
4308 end : Math.max(e.end + s.focusOffset - wb, 0),
4309 scrollX : sx,
4310 scrollY : sy,
4311 beg : s.anchorOffset - wb == 0
4312 };
4313 } else {
4314 e = getPos(ro, r.startContainer, r.endContainer);
4315
4316 // Count whitespace before start and end container
4317 //(r.startContainer.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;});
4318 //(r.endContainer.nodeValue || '').replace(/^\s+/, function(a) {wa = a.length;});
4319
4320 if (!e)
4321 return {scrollX : sx, scrollY : sy};
4322
4323 return {
4324 start : Math.max(e.start + r.startOffset - wb, 0),
4325 end : Math.max(e.end + r.endOffset - wa, 0),
4326 scrollX : sx,
4327 scrollY : sy,
4328 beg : r.startOffset - wb == 0
4329 };
4330 }
4331 },
4332
4333 moveToBookmark : function(b) {
4334 var t = this, r = t.getRng(), s = t.getSel(), ro = t.dom.getRoot(), sd, nvl, nv;
4335
4336 function getPos(r, sp, ep) {
4337 var w = t.dom.doc.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {}, o, v, wa, wb;
4338
4339 while ((n = w.nextNode()) != null) {
4340 wa = wb = 0;
4341
4342 nv = n.nodeValue || '';
4343 //nv.replace(/^\s+[^\s]/, function(a) {wb = a.length - 1;});
4344 //nv.replace(/[^\s]\s+$/, function(a) {wa = a.length - 1;});
4345
4346 nvl = trimNl(nv).length;
4347 p += nvl;
4348
4349 if (p >= sp && !d.startNode) {
4350 o = sp - (p - nvl);
4351
4352 // Fix for odd quirk in FF
4353 if (b.beg && o >= nvl)
4354 continue;
4355
4356 d.startNode = n;
4357 d.startOffset = o + wb;
4358 }
4359
4360 if (p >= ep) {
4361 d.endNode = n;
4362 d.endOffset = ep - (p - nvl) + wb;
4363 return d;
4364 }
4365 }
4366
4367 return null;
4368 };
4369
4370 if (!b)
4371 return false;
4372
4373 t.win.scrollTo(b.scrollX, b.scrollY);
4374
4375 // Handle explorer
4376 if (isIE) {
4377 t.tridentSel.destroy();
4378
4379 // Handle simple
4380 if (r = b.rng) {
4381 try {
4382 r.select();
4383 } catch (ex) {
4384 // Ignore
4385 }
4386
4387 return true;
4388 }
4389
4390 t.win.focus();
4391
4392 // Handle control bookmark
4393 if (b.tag) {
4394 r = ro.createControlRange();
4395
4396 each(t.dom.select(b.tag), function(n, i) {
4397 if (i == b.index)
4398 r.addElement(n);
4399 });
4400 } else {
4401 // Try/catch needed since this operation breaks when TinyMCE is placed in hidden divs/tabs
4402 try {
4403 // Incorrect bookmark
4404 if (b.start < 0)
4405 return true;
4406
4407 r = s.createRange();
4408 r.moveToElementText(ro);
4409 r.collapse(true);
4410 r.moveStart('character', b.start);
4411 r.moveEnd('character', b.length);
4412 } catch (ex2) {
4413 return true;
4414 }
4415 }
4416
4417 try {
4418 r.select();
4419 } catch (ex) {
4420 // Needed for some odd IE bug #1843306
4421 }
4422
4423 return true;
4424 }
4425
4426 // Handle W3C
4427 if (!s)
4428 return false;
4429
4430 // Handle simple
4431 if (b.rng) {
4432 s.removeAllRanges();
4433 s.addRange(b.rng);
4434 } else {
4435 if (is(b.start) && is(b.end)) {
4436 try {
4437 sd = getPos(ro, b.start, b.end);
4438
4439 if (sd) {
4440 r = t.dom.doc.createRange();
4441 r.setStart(sd.startNode, sd.startOffset);
4442 r.setEnd(sd.endNode, sd.endOffset);
4443 s.removeAllRanges();
4444 s.addRange(r);
4445 }
4446
4447 if (!tinymce.isOpera)
4448 t.win.focus();
4449 } catch (ex) {
4450 // Ignore
4451 }
4452 }
4453 }
4454 },
4455
4456 select : function(n, c) {
4457 var t = this, r = t.getRng(), s = t.getSel(), b, fn, ln, d = t.win.document;
4458
4459 function find(n, start) {
4460 var walker, o;
4461
4462 if (n) {
4463 walker = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
4464
4465 // Find first/last non empty text node
4466 while (n = walker.nextNode()) {
4467 o = n;
4468
4469 if (tinymce.trim(n.nodeValue).length != 0) {
4470 if (start)
4471 return n;
4472 else
4473 o = n;
4474 }
4475 }
4476 }
4477
4478 return o;
4479 };
4480
4481 if (isIE) {
4482 try {
4483 b = d.body;
4484
4485 if (/^(IMG|TABLE)$/.test(n.nodeName)) {
4486 r = b.createControlRange();
4487 r.addElement(n);
4488 } else {
4489 r = b.createTextRange();
4490 r.moveToElementText(n);
4491 }
4492
4493 r.select();
4494 } catch (ex) {
4495 // Throws illigal agrument in IE some times
4496 }
4497 } else {
4498 if (c) {
4499 fn = find(n, 1) || t.dom.select('br:first', n)[0];
4500 ln = find(n, 0) || t.dom.select('br:last', n)[0];
4501
4502 if (fn && ln) {
4503 r = d.createRange();
4504
4505 if (fn.nodeName == 'BR')
4506 r.setStartBefore(fn);
4507 else
4508 r.setStart(fn, 0);
4509
4510 if (ln.nodeName == 'BR')
4511 r.setEndBefore(ln);
4512 else
4513 r.setEnd(ln, ln.nodeValue.length);
4514 } else
4515 r.selectNode(n);
4516 } else
4517 r.selectNode(n);
4518
4519 t.setRng(r);
4520 }
4521
4522 return n;
4523 },
4524
4525 isCollapsed : function() {
4526 var t = this, r = t.getRng(), s = t.getSel();
4527
4528 if (!r || r.item)
4529 return false;
4530
4531 return !s || r.boundingWidth == 0 || r.collapsed;
4532 },
4533
4534 collapse : function(b) {
4535 var t = this, r = t.getRng(), n;
4536
4537 // Control range on IE
4538 if (r.item) {
4539 n = r.item(0);
4540 r = this.win.document.body.createTextRange();
4541 r.moveToElementText(n);
4542 }
4543
4544 r.collapse(!!b);
4545 t.setRng(r);
4546 },
4547
4548 getSel : function() {
4549 var t = this, w = this.win;
4550
4551 return w.getSelection ? w.getSelection() : w.document.selection;
4552 },
4553
4554 getRng : function(w3c) {
4555 var t = this, s, r;
4556
4557 // Found tridentSel object then we need to use that one
4558 if (w3c && t.tridentSel)
4559 return t.tridentSel.getRangeAt(0);
4560
4561 try {
4562 if (s = t.getSel())
4563 r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : t.win.document.createRange());
4564 } catch (ex) {
4565 // IE throws unspecified error here if TinyMCE is placed in a frame/iframe
4566 }
4567
4568 // No range found then create an empty one
4569 // This can occur when the editor is placed in a hidden container element on Gecko
4570 // Or on IE when there was an exception
4571 if (!r)
4572 r = isIE ? t.win.document.body.createTextRange() : t.win.document.createRange();
4573
4574 return r;
4575 },
4576
4577 setRng : function(r) {
4578 var s, t = this;
4579
4580 if (!t.tridentSel) {
4581 s = t.getSel();
4582
4583 if (s) {
4584 s.removeAllRanges();
4585 s.addRange(r);
4586 }
4587 } else {
4588 // Is W3C Range
4589 if (r.cloneRange) {
4590 t.tridentSel.addRange(r);
4591 return;
4592 }
4593
4594 // Is IE specific range
4595 try {
4596 r.select();
4597 } catch (ex) {
4598 // Needed for some odd IE bug #1843306
4599 }
4600 }
4601 },
4602
4603 setNode : function(n) {
4604 var t = this;
4605
4606 t.setContent(t.dom.getOuterHTML(n));
4607
4608 return n;
4609 },
4610
4611 getNode : function() {
4612 var t = this, r = t.getRng(), s = t.getSel(), e;
4613
4614 if (!isIE) {
4615 // Range maybe lost after the editor is made visible again
4616 if (!r)
4617 return t.dom.getRoot();
4618
4619 e = r.commonAncestorContainer;
4620
4621 // Handle selection a image or other control like element such as anchors
4622 if (!r.collapsed) {
4623 // If the anchor node is a element instead of a text node then return this element
4624 if (tinymce.isWebKit && s.anchorNode && s.anchorNode.nodeType == 1)
4625 return s.anchorNode.childNodes[s.anchorOffset];
4626
4627 if (r.startContainer == r.endContainer) {
4628 if (r.startOffset - r.endOffset < 2) {
4629 if (r.startContainer.hasChildNodes())
4630 e = r.startContainer.childNodes[r.startOffset];
4631 }
4632 }
4633 }
4634
4635 return t.dom.getParent(e, '*');
4636 }
4637
4638 return r.item ? r.item(0) : r.parentElement();
4639 },
4640
4641 getSelectedBlocks : function(st, en) {
4642 var t = this, dom = t.dom, sb, eb, n, bl = [];
4643
4644 sb = dom.getParent(st || t.getStart(), dom.isBlock);
4645 eb = dom.getParent(en || t.getEnd(), dom.isBlock);
4646
4647 if (sb)
4648 bl.push(sb);
4649
4650 if (sb && eb && sb != eb) {
4651 n = sb;
4652
4653 while ((n = n.nextSibling) && n != eb) {
4654 if (dom.isBlock(n))
4655 bl.push(n);
4656 }
4657 }
4658
4659 if (eb && sb != eb)
4660 bl.push(eb);
4661
4662 return bl;
4663 },
4664
4665 destroy : function(s) {
4666 var t = this;
4667
4668 t.win = null;
4669
4670 if (t.tridentSel)
4671 t.tridentSel.destroy();
4672
4673 // Manual destroy then remove unload handler
4674 if (!s)
4675 tinymce.removeUnload(t.destroy);
4676 }
4677 });
4678})(tinymce);
4679(function(tinymce) {
4680 tinymce.create('tinymce.dom.XMLWriter', {
4681 node : null,
4682
4683 XMLWriter : function(s) {
4684 // Get XML document
4685 function getXML() {
4686 var i = document.implementation;
4687
4688 if (!i || !i.createDocument) {
4689 // Try IE objects
4690 try {return new ActiveXObject('MSXML2.DOMDocument');} catch (ex) {}
4691 try {return new ActiveXObject('Microsoft.XmlDom');} catch (ex) {}
4692 } else
4693 return i.createDocument('', '', null);
4694 };
4695
4696 this.doc = getXML();
4697
4698 // Since Opera and WebKit doesn't escape > into &gt; we need to do it our self to normalize the output for all browsers
4699 this.valid = tinymce.isOpera || tinymce.isWebKit;
4700
4701 this.reset();
4702 },
4703
4704 reset : function() {
4705 var t = this, d = t.doc;
4706
4707 if (d.firstChild)
4708 d.removeChild(d.firstChild);
4709
4710 t.node = d.appendChild(d.createElement("html"));
4711 },
4712
4713 writeStartElement : function(n) {
4714 var t = this;
4715
4716 t.node = t.node.appendChild(t.doc.createElement(n));
4717 },
4718
4719 writeAttribute : function(n, v) {
4720 if (this.valid)
4721 v = v.replace(/>/g, '%MCGT%');
4722
4723 this.node.setAttribute(n, v);
4724 },
4725
4726 writeEndElement : function() {
4727 this.node = this.node.parentNode;
4728 },
4729
4730 writeFullEndElement : function() {
4731 var t = this, n = t.node;
4732
4733 n.appendChild(t.doc.createTextNode(""));
4734 t.node = n.parentNode;
4735 },
4736
4737 writeText : function(v) {
4738 if (this.valid)
4739 v = v.replace(/>/g, '%MCGT%');
4740
4741 this.node.appendChild(this.doc.createTextNode(v));
4742 },
4743
4744 writeCDATA : function(v) {
4745 this.node.appendChild(this.doc.createCDATASection(v));
4746 },
4747
4748 writeComment : function(v) {
4749 // Fix for bug #2035694
4750 if (tinymce.isIE)
4751 v = v.replace(/^\-|\-$/g, ' ');
4752
4753 this.node.appendChild(this.doc.createComment(v.replace(/\-\-/g, ' ')));
4754 },
4755
4756 getContent : function() {
4757 var h;
4758
4759 h = this.doc.xml || new XMLSerializer().serializeToString(this.doc);
4760 h = h.replace(/<\?[^?]+\?>|<html>|<\/html>|<html\/>|<!DOCTYPE[^>]+>/g, '');
4761 h = h.replace(/ ?\/>/g, ' />');
4762
4763 if (this.valid)
4764 h = h.replace(/\%MCGT%/g, '&gt;');
4765
4766 return h;
4767 }
4768 });
4769})(tinymce);
4770(function(tinymce) {
4771 tinymce.create('tinymce.dom.StringWriter', {
4772 str : null,
4773 tags : null,
4774 count : 0,
4775 settings : null,
4776 indent : null,
4777
4778 StringWriter : function(s) {
4779 this.settings = tinymce.extend({
4780 indent_char : ' ',
4781 indentation : 0
4782 }, s);
4783
4784 this.reset();
4785 },
4786
4787 reset : function() {
4788 this.indent = '';
4789 this.str = "";
4790 this.tags = [];
4791 this.count = 0;
4792 },
4793
4794 writeStartElement : function(n) {
4795 this._writeAttributesEnd();
4796 this.writeRaw('<' + n);
4797 this.tags.push(n);
4798 this.inAttr = true;
4799 this.count++;
4800 this.elementCount = this.count;
4801 },
4802
4803 writeAttribute : function(n, v) {
4804 var t = this;
4805
4806 t.writeRaw(" " + t.encode(n) + '="' + t.encode(v) + '"');
4807 },
4808
4809 writeEndElement : function() {
4810 var n;
4811
4812 if (this.tags.length > 0) {
4813 n = this.tags.pop();
4814
4815 if (this._writeAttributesEnd(1))
4816 this.writeRaw('</' + n + '>');
4817
4818 if (this.settings.indentation > 0)
4819 this.writeRaw('\n');
4820 }
4821 },
4822
4823 writeFullEndElement : function() {
4824 if (this.tags.length > 0) {
4825 this._writeAttributesEnd();
4826 this.writeRaw('</' + this.tags.pop() + '>');
4827
4828 if (this.settings.indentation > 0)
4829 this.writeRaw('\n');
4830 }
4831 },
4832
4833 writeText : function(v) {
4834 this._writeAttributesEnd();
4835 this.writeRaw(this.encode(v));
4836 this.count++;
4837 },
4838
4839 writeCDATA : function(v) {
4840 this._writeAttributesEnd();
4841 this.writeRaw('<![CDATA[' + v + ']]>');
4842 this.count++;
4843 },
4844
4845 writeComment : function(v) {
4846 this._writeAttributesEnd();
4847 this.writeRaw('<!-- ' + v + '-->');
4848 this.count++;
4849 },
4850
4851 writeRaw : function(v) {
4852 this.str += v;
4853 },
4854
4855 encode : function(s) {
4856 return s.replace(/[<>&"]/g, function(v) {
4857 switch (v) {
4858 case '<':
4859 return '&lt;';
4860
4861 case '>':
4862 return '&gt;';
4863
4864 case '&':
4865 return '&amp;';
4866
4867 case '"':
4868 return '&quot;';
4869 }
4870
4871 return v;
4872 });
4873 },
4874
4875 getContent : function() {
4876 return this.str;
4877 },
4878
4879 _writeAttributesEnd : function(s) {
4880 if (!this.inAttr)
4881 return;
4882
4883 this.inAttr = false;
4884
4885 if (s && this.elementCount == this.count) {
4886 this.writeRaw(' />');
4887 return false;
4888 }
4889
4890 this.writeRaw('>');
4891
4892 return true;
4893 }
4894 });
4895})(tinymce);
4896(function(tinymce) {
4897 // Shorten names
4898 var extend = tinymce.extend, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, isIE = tinymce.isIE, isGecko = tinymce.isGecko;
4899
4900 function wildcardToRE(s) {
4901 return s.replace(/([?+*])/g, '.$1');
4902 };
4903
4904 tinymce.create('tinymce.dom.Serializer', {
4905 Serializer : function(s) {
4906 var t = this;
4907
4908 t.key = 0;
4909 t.onPreProcess = new Dispatcher(t);
4910 t.onPostProcess = new Dispatcher(t);
4911
4912 try {
4913 t.writer = new tinymce.dom.XMLWriter();
4914 } catch (ex) {
4915 // IE might throw exception if ActiveX is disabled so we then switch to the slightly slower StringWriter
4916 t.writer = new tinymce.dom.StringWriter();
4917 }
4918
4919 // Default settings
4920 t.settings = s = extend({
4921 dom : tinymce.DOM,
4922 valid_nodes : 0,
4923 node_filter : 0,
4924 attr_filter : 0,
4925 invalid_attrs : /^(mce_|_moz_|sizset|sizcache)/,
4926 closed : /^(br|hr|input|meta|img|link|param|area)$/,
4927 entity_encoding : 'named',
4928 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',
4929 valid_elements : '*[*]',
4930 extended_valid_elements : 0,
4931 valid_child_elements : 0,
4932 invalid_elements : 0,
4933 fix_table_elements : 1,
4934 fix_list_elements : true,
4935 fix_content_duplication : true,
4936 convert_fonts_to_spans : false,
4937 font_size_classes : 0,
4938 font_size_style_values : 0,
4939 apply_source_formatting : 0,
4940 indent_mode : 'simple',
4941 indent_char : '\t',
4942 indent_levels : 1,
4943 remove_linebreaks : 1,
4944 remove_redundant_brs : 1,
4945 element_format : 'xhtml'
4946 }, s);
4947
4948 t.dom = s.dom;
4949
4950 if (s.remove_redundant_brs) {
4951 t.onPostProcess.add(function(se, o) {
4952 // Remove single BR at end of block elements since they get rendered
4953 o.content = o.content.replace(/(<br \/>\s*)+<\/(p|h[1-6]|div|li)>/gi, function(a, b, c) {
4954 // Check if it's a single element
4955 if (/^<br \/>\s*<\//.test(a))
4956 return '</' + c + '>';
4957
4958 return a;
4959 });
4960 });
4961 }
4962
4963 // Remove XHTML element endings i.e. produce crap :) XHTML is better
4964 if (s.element_format == 'html') {
4965 t.onPostProcess.add(function(se, o) {
4966 o.content = o.content.replace(/<([^>]+) \/>/g, '<$1>');
4967 });
4968 }
4969
4970 if (s.fix_list_elements) {
4971 t.onPreProcess.add(function(se, o) {
4972 var nl, x, a = ['ol', 'ul'], i, n, p, r = /^(OL|UL)$/, np;
4973
4974 function prevNode(e, n) {
4975 var a = n.split(','), i;
4976
4977 while ((e = e.previousSibling) != null) {
4978 for (i=0; i<a.length; i++) {
4979 if (e.nodeName == a[i])
4980 return e;
4981 }
4982 }
4983
4984 return null;
4985 };
4986
4987 for (x=0; x<a.length; x++) {
4988 nl = t.dom.select(a[x], o.node);
4989
4990 for (i=0; i<nl.length; i++) {
4991 n = nl[i];
4992 p = n.parentNode;
4993
4994 if (r.test(p.nodeName)) {
4995 np = prevNode(n, 'LI');
4996
4997 if (!np) {
4998 np = t.dom.create('li');
4999 np.innerHTML = '&nbsp;';
5000 np.appendChild(n);
5001 p.insertBefore(np, p.firstChild);
5002 } else
5003 np.appendChild(n);
5004 }
5005 }
5006 }
5007 });
5008 }
5009
5010 if (s.fix_table_elements) {
5011 t.onPreProcess.add(function(se, o) {
5012 // Since Opera will crash if you attach the node to a dynamic document we need to brrowser sniff a specific build
5013 // so Opera users with an older version will have to live with less compaible output not much we can do here
5014 if (!tinymce.isOpera || opera.buildNumber() >= 1767) {
5015 each(t.dom.select('p table', o.node).reverse(), function(n) {
5016 var parent = t.dom.getParent(n.parentNode, 'table,p');
5017
5018 if (parent.nodeName != 'TABLE') {
5019 try {
5020 t.dom.split(parent, n);
5021 } catch (ex) {
5022 // IE can sometimes fire an unknown runtime error so we just ignore it
5023 }
5024 }
5025 });
5026 }
5027 });
5028 }
5029 },
5030
5031 setEntities : function(s) {
5032 var t = this, a, i, l = {}, re = '', v;
5033
5034 // No need to setup more than once
5035 if (t.entityLookup)
5036 return;
5037
5038 // Build regex and lookup array
5039 a = s.split(',');
5040 for (i = 0; i < a.length; i += 2) {
5041 v = a[i];
5042
5043 // Don't add default &amp; &quot; etc.
5044 if (v == 34 || v == 38 || v == 60 || v == 62)
5045 continue;
5046
5047 l[String.fromCharCode(a[i])] = a[i + 1];
5048
5049 v = parseInt(a[i]).toString(16);
5050 re += '\\u' + '0000'.substring(v.length) + v;
5051 }
5052
5053 if (!re) {
5054 t.settings.entity_encoding = 'raw';
5055 return;
5056 }
5057
5058 t.entitiesRE = new RegExp('[' + re + ']', 'g');
5059 t.entityLookup = l;
5060 },
5061
5062 setValidChildRules : function(s) {
5063 this.childRules = null;
5064 this.addValidChildRules(s);
5065 },
5066
5067 addValidChildRules : function(s) {
5068 var t = this, inst, intr, bloc;
5069
5070 if (!s)
5071 return;
5072
5073 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';
5074 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';
5075 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';
5076
5077 each(s.split(','), function(s) {
5078 var p = s.split(/\[|\]/), re;
5079
5080 s = '';
5081 each(p[1].split('|'), function(v) {
5082 if (s)
5083 s += '|';
5084
5085 switch (v) {
5086 case '%itrans':
5087 v = intr;
5088 break;
5089
5090 case '%itrans_na':
5091 v = intr.substring(2);
5092 break;
5093
5094 case '%istrict':
5095 v = inst;
5096 break;
5097
5098 case '%istrict_na':
5099 v = inst.substring(2);
5100 break;
5101
5102 case '%btrans':
5103 v = bloc;
5104 break;
5105
5106 case '%bstrict':
5107 v = bloc;
5108 break;
5109 }
5110
5111 s += v;
5112 });
5113 re = new RegExp('^(' + s.toLowerCase() + ')$', 'i');
5114
5115 each(p[0].split('/'), function(s) {
5116 t.childRules = t.childRules || {};
5117 t.childRules[s] = re;
5118 });
5119 });
5120
5121 // Build regex
5122 s = '';
5123 each(t.childRules, function(v, k) {
5124 if (s)
5125 s += '|';
5126
5127 s += k;
5128 });
5129
5130 t.parentElementsRE = new RegExp('^(' + s.toLowerCase() + ')$', 'i');
5131
5132 /*console.debug(t.parentElementsRE.toString());
5133 each(t.childRules, function(v) {
5134 console.debug(v.toString());
5135 });*/
5136 },
5137
5138 setRules : function(s) {
5139 var t = this;
5140
5141 t._setup();
5142 t.rules = {};
5143 t.wildRules = [];
5144 t.validElements = {};
5145
5146 return t.addRules(s);
5147 },
5148
5149 addRules : function(s) {
5150 var t = this, dr;
5151
5152 if (!s)
5153 return;
5154
5155 t._setup();
5156
5157 each(s.split(','), function(s) {
5158 var p = s.split(/\[|\]/), tn = p[0].split('/'), ra, at, wat, va = [];
5159
5160 // Extend with default rules
5161 if (dr)
5162 at = tinymce.extend([], dr.attribs);
5163
5164 // Parse attributes
5165 if (p.length > 1) {
5166 each(p[1].split('|'), function(s) {
5167 var ar = {}, i;
5168
5169 at = at || [];
5170
5171 // Parse attribute rule
5172 s = s.replace(/::/g, '~');
5173 s = /^([!\-])?([\w*.?~_\-]+|)([=:<])?(.+)?$/.exec(s);
5174 s[2] = s[2].replace(/~/g, ':');
5175
5176 // Add required attributes
5177 if (s[1] == '!') {
5178 ra = ra || [];
5179 ra.push(s[2]);
5180 }
5181
5182 // Remove inherited attributes
5183 if (s[1] == '-') {
5184 for (i = 0; i <at.length; i++) {
5185 if (at[i].name == s[2]) {
5186 at.splice(i, 1);
5187 return;
5188 }
5189 }
5190 }
5191
5192 switch (s[3]) {
5193 // Add default attrib values
5194 case '=':
5195 ar.defaultVal = s[4] || '';
5196 break;
5197
5198 // Add forced attrib values
5199 case ':':
5200 ar.forcedVal = s[4];
5201 break;
5202
5203 // Add validation values
5204 case '<':
5205 ar.validVals = s[4].split('?');
5206 break;
5207 }
5208
5209 if (/[*.?]/.test(s[2])) {
5210 wat = wat || [];
5211 ar.nameRE = new RegExp('^' + wildcardToRE(s[2]) + '$');
5212 wat.push(ar);
5213 } else {
5214 ar.name = s[2];
5215 at.push(ar);
5216 }
5217
5218 va.push(s[2]);
5219 });
5220 }
5221
5222 // Handle element names
5223 each(tn, function(s, i) {
5224 var pr = s.charAt(0), x = 1, ru = {};
5225
5226 // Extend with default rule data
5227 if (dr) {
5228 if (dr.noEmpty)
5229 ru.noEmpty = dr.noEmpty;
5230
5231 if (dr.fullEnd)
5232 ru.fullEnd = dr.fullEnd;
5233
5234 if (dr.padd)
5235 ru.padd = dr.padd;
5236 }
5237
5238 // Handle prefixes
5239 switch (pr) {
5240 case '-':
5241 ru.noEmpty = true;
5242 break;
5243
5244 case '+':
5245 ru.fullEnd = true;
5246 break;
5247
5248 case '#':
5249 ru.padd = true;
5250 break;
5251
5252 default:
5253 x = 0;
5254 }
5255
5256 tn[i] = s = s.substring(x);
5257 t.validElements[s] = 1;
5258
5259 // Add element name or element regex
5260 if (/[*.?]/.test(tn[0])) {
5261 ru.nameRE = new RegExp('^' + wildcardToRE(tn[0]) + '$');
5262 t.wildRules = t.wildRules || {};
5263 t.wildRules.push(ru);
5264 } else {
5265 ru.name = tn[0];
5266
5267 // Store away default rule
5268 if (tn[0] == '@')
5269 dr = ru;
5270
5271 t.rules[s] = ru;
5272 }
5273
5274 ru.attribs = at;
5275
5276 if (ra)
5277 ru.requiredAttribs = ra;
5278
5279 if (wat) {
5280 // Build valid attributes regexp
5281 s = '';
5282 each(va, function(v) {
5283 if (s)
5284 s += '|';
5285
5286 s += '(' + wildcardToRE(v) + ')';
5287 });
5288 ru.validAttribsRE = new RegExp('^' + s.toLowerCase() + '$');
5289 ru.wildAttribs = wat;
5290 }
5291 });
5292 });
5293
5294 // Build valid elements regexp
5295 s = '';
5296 each(t.validElements, function(v, k) {
5297 if (s)
5298 s += '|';
5299
5300 if (k != '@')
5301 s += k;
5302 });
5303 t.validElementsRE = new RegExp('^(' + wildcardToRE(s.toLowerCase()) + ')$');
5304
5305 //console.debug(t.validElementsRE.toString());
5306 //console.dir(t.rules);
5307 //console.dir(t.wildRules);
5308 },
5309
5310 findRule : function(n) {
5311 var t = this, rl = t.rules, i, r;
5312
5313 t._setup();
5314
5315 // Exact match
5316 r = rl[n];
5317 if (r)
5318 return r;
5319
5320 // Try wildcards
5321 rl = t.wildRules;
5322 for (i = 0; i < rl.length; i++) {
5323 if (rl[i].nameRE.test(n))
5324 return rl[i];
5325 }
5326
5327 return null;
5328 },
5329
5330 findAttribRule : function(ru, n) {
5331 var i, wa = ru.wildAttribs;
5332
5333 for (i = 0; i < wa.length; i++) {
5334 if (wa[i].nameRE.test(n))
5335 return wa[i];
5336 }
5337
5338 return null;
5339 },
5340
5341 serialize : function(n, o) {
5342 var h, t = this, doc, oldDoc, impl, selected;
5343
5344 t._setup();
5345 o = o || {};
5346 o.format = o.format || 'html';
5347 t.processObj = o;
5348
5349 // IE looses the selected attribute on option elements so we need to store it
5350 // See: http://support.microsoft.com/kb/829907
5351 if (isIE) {
5352 selected = [];
5353 each(n.getElementsByTagName('option'), function(n) {
5354 var v = t.dom.getAttrib(n, 'selected');
5355
5356 selected.push(v ? v : null);
5357 });
5358 }
5359
5360 n = n.cloneNode(true);
5361
5362 // IE looses the selected attribute on option elements so we need to restore it
5363 if (isIE) {
5364 each(n.getElementsByTagName('option'), function(n, i) {
5365 t.dom.setAttrib(n, 'selected', selected[i]);
5366 });
5367 }
5368
5369 // Nodes needs to be attached to something in WebKit/Opera
5370 // Older builds of Opera crashes if you attach the node to an document created dynamically
5371 // and since we can't feature detect a crash we need to sniff the acutal build number
5372 // This fix will make DOM ranges and make Sizzle happy!
5373 impl = n.ownerDocument.implementation;
5374 if (impl.createHTMLDocument && (tinymce.isOpera && opera.buildNumber() >= 1767)) {
5375 // Create an empty HTML document
5376 doc = impl.createHTMLDocument("");
5377
5378 // Add the element or it's children if it's a body element to the new document
5379 each(n.nodeName == 'BODY' ? n.childNodes : [n], function(node) {
5380 doc.body.appendChild(doc.importNode(node, true));
5381 });
5382
5383 // Grab first child or body element for serialization
5384 if (n.nodeName != 'BODY')
5385 n = doc.body.firstChild;
5386 else
5387 n = doc.body;
5388
5389 // set the new document in DOMUtils so createElement etc works
5390 oldDoc = t.dom.doc;
5391 t.dom.doc = doc;
5392 }
5393
5394 t.key = '' + (parseInt(t.key) + 1);
5395
5396 // Pre process
5397 if (!o.no_events) {
5398 o.node = n;
5399 t.onPreProcess.dispatch(t, o);
5400 }
5401
5402 // Serialize HTML DOM into a string
5403 t.writer.reset();
5404 t._serializeNode(n, o.getInner);
5405
5406 // Post process
5407 o.content = t.writer.getContent();
5408
5409 // Restore the old document if it was changed
5410 if (oldDoc)
5411 t.dom.doc = oldDoc;
5412
5413 if (!o.no_events)
5414 t.onPostProcess.dispatch(t, o);
5415
5416 t._postProcess(o);
5417 o.node = null;
5418
5419 return tinymce.trim(o.content);
5420 },
5421
5422 // Internal functions
5423
5424 _postProcess : function(o) {
5425 var t = this, s = t.settings, h = o.content, sc = [], p;
5426
5427 if (o.format == 'html') {
5428 // Protect some elements
5429 p = t._protect({
5430 content : h,
5431 patterns : [
5432 {pattern : /(<script[^>]*>)(.*?)(<\/script>)/g},
5433 {pattern : /(<noscript[^>]*>)(.*?)(<\/noscript>)/g},
5434 {pattern : /(<style[^>]*>)(.*?)(<\/style>)/g},
5435 {pattern : /(<pre[^>]*>)(.*?)(<\/pre>)/g, encode : 1},
5436 {pattern : /(<!--\[CDATA\[)(.*?)(\]\]-->)/g}
5437 ]
5438 });
5439
5440 h = p.content;
5441
5442 // Entity encode
5443 if (s.entity_encoding !== 'raw')
5444 h = t._encode(h);
5445
5446 // Use BR instead of &nbsp; padded P elements inside editor and use <p>&nbsp;</p> outside editor
5447/* if (o.set)
5448 h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p><br /></p>');
5449 else
5450 h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p>$1</p>');*/
5451
5452 // Since Gecko and Safari keeps whitespace in the DOM we need to
5453 // remove it inorder to match other browsers. But I think Gecko and Safari is right.
5454 // This process is only done when getting contents out from the editor.
5455 if (!o.set) {
5456 // We need to replace paragraph whitespace with an nbsp before indentation to keep the \u00a0 char
5457 h = h.replace(/<p>\s+<\/p>|<p([^>]+)>\s+<\/p>/g, s.entity_encoding == 'numeric' ? '<p$1>&#160;</p>' : '<p$1>&nbsp;</p>');
5458
5459 if (s.remove_linebreaks) {
5460 h = h.replace(/\r?\n|\r/g, ' ');
5461 h = h.replace(/(<[^>]+>)\s+/g, '$1 ');
5462 h = h.replace(/\s+(<\/[^>]+>)/g, ' $1');
5463 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
5464 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
5465 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
5466 }
5467
5468 // Simple indentation
5469 if (s.apply_source_formatting && s.indent_mode == 'simple') {
5470 // Add line breaks before and after block elements
5471 h = h.replace(/<(\/?)(ul|hr|table|meta|link|tbody|tr|object|body|head|html|map)(|[^>]+)>\s*/g, '\n<$1$2$3>\n');
5472 h = h.replace(/\s*<(p|h[1-6]|blockquote|div|title|style|pre|script|td|li|area)(|[^>]+)>/g, '\n<$1$2>');
5473 h = h.replace(/<\/(p|h[1-6]|blockquote|div|title|style|pre|script|td|li)>\s*/g, '</$1>\n');
5474 h = h.replace(/\n\n/g, '\n');
5475 }
5476 }
5477
5478 h = t._unprotect(h, p);
5479
5480 // Restore CDATA sections
5481 h = h.replace(/<!--\[CDATA\[([\s\S]+)\]\]-->/g, '<![CDATA[$1]]>');
5482
5483 // Restore the \u00a0 character if raw mode is enabled
5484 if (s.entity_encoding == 'raw')
5485 h = h.replace(/<p>&nbsp;<\/p>|<p([^>]+)>&nbsp;<\/p>/g, '<p$1>\u00a0</p>');
5486
5487 // Restore noscript elements
5488 h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {
5489 return '<noscript' + attribs + '>' + t.dom.decode(text.replace(/<!--|-->/g, '')) + '</noscript>';
5490 });
5491 }
5492
5493 o.content = h;
5494 },
5495
5496 _serializeNode : function(n, inn) {
5497 var t = this, s = t.settings, w = t.writer, hc, el, cn, i, l, a, at, no, v, nn, ru, ar, iv, closed;
5498
5499 if (!s.node_filter || s.node_filter(n)) {
5500 switch (n.nodeType) {
5501 case 1: // Element
5502 if (n.hasAttribute ? n.hasAttribute('mce_bogus') : n.getAttribute('mce_bogus'))
5503 return;
5504
5505 iv = false;
5506 hc = n.hasChildNodes();
5507 nn = n.getAttribute('mce_name') || n.nodeName.toLowerCase();
5508
5509 // Add correct prefix on IE
5510 if (isIE) {
5511 if (n.scopeName !== 'HTML' && n.scopeName !== 'html')
5512 nn = n.scopeName + ':' + nn;
5513 }
5514
5515 // Remove mce prefix on IE needed for the abbr element
5516 if (nn.indexOf('mce:') === 0)
5517 nn = nn.substring(4);
5518
5519 // Check if valid
5520 if (!t.validElementsRE || !t.validElementsRE.test(nn) || (t.invalidElementsRE && t.invalidElementsRE.test(nn)) || inn) {
5521 iv = true;
5522 break;
5523 }
5524
5525 if (isIE) {
5526 // Fix IE content duplication (DOM can have multiple copies of the same node)
5527 if (s.fix_content_duplication) {
5528 if (n.mce_serialized == t.key)
5529 return;
5530
5531 n.mce_serialized = t.key;
5532 }
5533
5534 // IE sometimes adds a / infront of the node name
5535 if (nn.charAt(0) == '/')
5536 nn = nn.substring(1);
5537 } else if (isGecko) {
5538 // Ignore br elements
5539 if (n.nodeName === 'BR' && n.getAttribute('type') == '_moz')
5540 return;
5541 }
5542
5543 // Check if valid child
5544 if (t.childRules) {
5545 if (t.parentElementsRE.test(t.elementName)) {
5546 if (!t.childRules[t.elementName].test(nn)) {
5547 iv = true;
5548 break;
5549 }
5550 }
5551
5552 t.elementName = nn;
5553 }
5554
5555 ru = t.findRule(nn);
5556 nn = ru.name || nn;
5557 closed = s.closed.test(nn);
5558
5559 // Skip empty nodes or empty node name in IE
5560 if ((!hc && ru.noEmpty) || (isIE && !nn)) {
5561 iv = true;
5562 break;
5563 }
5564
5565 // Check required
5566 if (ru.requiredAttribs) {
5567 a = ru.requiredAttribs;
5568
5569 for (i = a.length - 1; i >= 0; i--) {
5570 if (this.dom.getAttrib(n, a[i]) !== '')
5571 break;
5572 }
5573
5574 // None of the required was there
5575 if (i == -1) {
5576 iv = true;
5577 break;
5578 }
5579 }
5580
5581 w.writeStartElement(nn);
5582
5583 // Add ordered attributes
5584 if (ru.attribs) {
5585 for (i=0, at = ru.attribs, l = at.length; i<l; i++) {
5586 a = at[i];
5587 v = t._getAttrib(n, a);
5588
5589 if (v !== null)
5590 w.writeAttribute(a.name, v);
5591 }
5592 }
5593
5594 // Add wild attributes
5595 if (ru.validAttribsRE) {
5596 at = t.dom.getAttribs(n);
5597 for (i=at.length-1; i>-1; i--) {
5598 no = at[i];
5599
5600 if (no.specified) {
5601 a = no.nodeName.toLowerCase();
5602
5603 if (s.invalid_attrs.test(a) || !ru.validAttribsRE.test(a))
5604 continue;
5605
5606 ar = t.findAttribRule(ru, a);
5607 v = t._getAttrib(n, ar, a);
5608
5609 if (v !== null)
5610 w.writeAttribute(a, v);
5611 }
5612 }
5613 }
5614
5615 // Write text from script
5616 if (nn === 'script' && tinymce.trim(n.innerHTML)) {
5617 w.writeText('// '); // Padd it with a comment so it will parse on older browsers
5618 w.writeCDATA(n.innerHTML.replace(/<!--|-->|<\[CDATA\[|\]\]>/g, '')); // Remove comments and cdata stuctures
5619 hc = false;
5620 break;
5621 }
5622
5623 // Padd empty nodes with a &nbsp;
5624 if (ru.padd) {
5625 // If it has only one bogus child, padd it anyway workaround for <td><br /></td> bug
5626 if (hc && (cn = n.firstChild) && cn.nodeType === 1 && n.childNodes.length === 1) {
5627 if (cn.hasAttribute ? cn.hasAttribute('mce_bogus') : cn.getAttribute('mce_bogus'))
5628 w.writeText('\u00a0');
5629 } else if (!hc)
5630 w.writeText('\u00a0'); // No children then padd it
5631 }
5632
5633 break;
5634
5635 case 3: // Text
5636 // Check if valid child
5637 if (t.childRules && t.parentElementsRE.test(t.elementName)) {
5638 if (!t.childRules[t.elementName].test(n.nodeName))
5639 return;
5640 }
5641
5642 return w.writeText(n.nodeValue);
5643
5644 case 4: // CDATA
5645 return w.writeCDATA(n.nodeValue);
5646
5647 case 8: // Comment
5648 return w.writeComment(n.nodeValue);
5649 }
5650 } else if (n.nodeType == 1)
5651 hc = n.hasChildNodes();
5652
5653 if (hc && !closed) {
5654 cn = n.firstChild;
5655
5656 while (cn) {
5657 t._serializeNode(cn);
5658 t.elementName = nn;
5659 cn = cn.nextSibling;
5660 }
5661 }
5662
5663 // Write element end
5664 if (!iv) {
5665 if (!closed)
5666 w.writeFullEndElement();
5667 else
5668 w.writeEndElement();
5669 }
5670 },
5671
5672 _protect : function(o) {
5673 var t = this;
5674
5675 o.items = o.items || [];
5676
5677 function enc(s) {
5678 return s.replace(/[\r\n\\]/g, function(c) {
5679 if (c === '\n')
5680 return '\\n';
5681 else if (c === '\\')
5682 return '\\\\';
5683
5684 return '\\r';
5685 });
5686 };
5687
5688 function dec(s) {
5689 return s.replace(/\\[\\rn]/g, function(c) {
5690 if (c === '\\n')
5691 return '\n';
5692 else if (c === '\\\\')
5693 return '\\';
5694
5695 return '\r';
5696 });
5697 };
5698
5699 each(o.patterns, function(p) {
5700 o.content = dec(enc(o.content).replace(p.pattern, function(x, a, b, c) {
5701 b = dec(b);
5702
5703 if (p.encode)
5704 b = t._encode(b);
5705
5706 o.items.push(b);
5707 return a + '<!--mce:' + (o.items.length - 1) + '-->' + c;
5708 }));
5709 });
5710
5711 return o;
5712 },
5713
5714 _unprotect : function(h, o) {
5715 h = h.replace(/\<!--mce:([0-9]+)--\>/g, function(a, b) {
5716 return o.items[parseInt(b)];
5717 });
5718
5719 o.items = [];
5720
5721 return h;
5722 },
5723
5724 _encode : function(h) {
5725 var t = this, s = t.settings, l;
5726
5727 // Entity encode
5728 if (s.entity_encoding !== 'raw') {
5729 if (s.entity_encoding.indexOf('named') != -1) {
5730 t.setEntities(s.entities);
5731 l = t.entityLookup;
5732
5733 h = h.replace(t.entitiesRE, function(a) {
5734 var v;
5735
5736 if (v = l[a])
5737 a = '&' + v + ';';
5738
5739 return a;
5740 });
5741 }
5742
5743 if (s.entity_encoding.indexOf('numeric') != -1) {
5744 h = h.replace(/[\u007E-\uFFFF]/g, function(a) {
5745 return '&#' + a.charCodeAt(0) + ';';
5746 });
5747 }
5748 }
5749
5750 return h;
5751 },
5752
5753 _setup : function() {
5754 var t = this, s = this.settings;
5755
5756 if (t.done)
5757 return;
5758
5759 t.done = 1;
5760
5761 t.setRules(s.valid_elements);
5762 t.addRules(s.extended_valid_elements);
5763 t.addValidChildRules(s.valid_child_elements);
5764
5765 if (s.invalid_elements)
5766 t.invalidElementsRE = new RegExp('^(' + wildcardToRE(s.invalid_elements.replace(/,/g, '|').toLowerCase()) + ')$');
5767
5768 if (s.attrib_value_filter)
5769 t.attribValueFilter = s.attribValueFilter;
5770 },
5771
5772 _getAttrib : function(n, a, na) {
5773 var i, v;
5774
5775 na = na || a.name;
5776
5777 if (a.forcedVal && (v = a.forcedVal)) {
5778 if (v === '{$uid}')
5779 return this.dom.uniqueId();
5780
5781 return v;
5782 }
5783
5784 v = this.dom.getAttrib(n, na);
5785
5786 switch (na) {
5787 case 'rowspan':
5788 case 'colspan':
5789 // Whats the point? Remove usless attribute value
5790 if (v == '1')
5791 v = '';
5792
5793 break;
5794 }
5795
5796 if (this.attribValueFilter)
5797 v = this.attribValueFilter(na, v, n);
5798
5799 if (a.validVals) {
5800 for (i = a.validVals.length - 1; i >= 0; i--) {
5801 if (v == a.validVals[i])
5802 break;
5803 }
5804
5805 if (i == -1)
5806 return null;
5807 }
5808
5809 if (v === '' && typeof(a.defaultVal) != 'undefined') {
5810 v = a.defaultVal;
5811
5812 if (v === '{$uid}')
5813 return this.dom.uniqueId();
5814
5815 return v;
5816 } else {
5817 // Remove internal mceItemXX classes when content is extracted from editor
5818 if (na == 'class' && this.processObj.get)
5819 v = v.replace(/\s?mceItem\w+\s?/g, '');
5820 }
5821
5822 if (v === '')
5823 return null;
5824
5825
5826 return v;
5827 }
5828 });
5829})(tinymce);
5830(function(tinymce) {
5831 var each = tinymce.each, Event = tinymce.dom.Event;
5832
5833 tinymce.create('tinymce.dom.ScriptLoader', {
5834 ScriptLoader : function(s) {
5835 this.settings = s || {};
5836 this.queue = [];
5837 this.lookup = {};
5838 },
5839
5840 isDone : function(u) {
5841 return this.lookup[u] ? this.lookup[u].state == 2 : 0;
5842 },
5843
5844 markDone : function(u) {
5845 this.lookup[u] = {state : 2, url : u};
5846 },
5847
5848 add : function(u, cb, s, pr) {
5849 var t = this, lo = t.lookup, o;
5850
5851 if (o = lo[u]) {
5852 // Is loaded fire callback
5853 if (cb && o.state == 2)
5854 cb.call(s || this);
5855
5856 return o;
5857 }
5858
5859 o = {state : 0, url : u, func : cb, scope : s || this};
5860
5861 if (pr)
5862 t.queue.unshift(o);
5863 else
5864 t.queue.push(o);
5865
5866 lo[u] = o;
5867
5868 return o;
5869 },
5870
5871 load : function(u, cb, s) {
5872 var t = this, o;
5873
5874 if (o = t.lookup[u]) {
5875 // Is loaded fire callback
5876 if (cb && o.state == 2)
5877 cb.call(s || t);
5878
5879 return o;
5880 }
5881
5882 function loadScript(u) {
5883 if (Event.domLoaded || t.settings.strict_mode) {
5884 tinymce.util.XHR.send({
5885 url : tinymce._addVer(u),
5886 error : t.settings.error,
5887 async : false,
5888 success : function(co) {
5889 t.eval(co);
5890 }
5891 });
5892 } else
5893 document.write('<script type="text/javascript" src="' + tinymce._addVer(u) + '"></script>');
5894 };
5895
5896 if (!tinymce.is(u, 'string')) {
5897 each(u, function(u) {
5898 loadScript(u);
5899 });
5900
5901 if (cb)
5902 cb.call(s || t);
5903 } else {
5904 loadScript(u);
5905
5906 if (cb)
5907 cb.call(s || t);
5908 }
5909 },
5910
5911 loadQueue : function(cb, s) {
5912 var t = this;
5913
5914 if (!t.queueLoading) {
5915 t.queueLoading = 1;
5916 t.queueCallbacks = [];
5917
5918 t.loadScripts(t.queue, function() {
5919 t.queueLoading = 0;
5920
5921 if (cb)
5922 cb.call(s || t);
5923
5924 each(t.queueCallbacks, function(o) {
5925 o.func.call(o.scope);
5926 });
5927 });
5928 } else if (cb)
5929 t.queueCallbacks.push({func : cb, scope : s || t});
5930 },
5931
5932 eval : function(co) {
5933 var w = window;
5934
5935 // Evaluate script
5936 if (!w.execScript) {
5937 try {
5938 eval.call(w, co);
5939 } catch (ex) {
5940 eval(co, w); // Firefox 3.0a8
5941 }
5942 } else
5943 w.execScript(co); // IE
5944 },
5945
5946 loadScripts : function(sc, cb, s) {
5947 var t = this, lo = t.lookup;
5948
5949 function done(o) {
5950 o.state = 2; // Has been loaded
5951
5952 // Run callback
5953 if (o.func)
5954 o.func.call(o.scope || t);
5955 };
5956
5957 function allDone() {
5958 var l;
5959
5960 // Check if all files are loaded
5961 l = sc.length;
5962 each(sc, function(o) {
5963 o = lo[o.url];
5964
5965 if (o.state === 2) {// It has finished loading
5966 done(o);
5967 l--;
5968 } else
5969 load(o);
5970 });
5971
5972 // They are all loaded
5973 if (l === 0 && cb) {
5974 cb.call(s || t);
5975 cb = 0;
5976 }
5977 };
5978
5979 function load(o) {
5980 if (o.state > 0)
5981 return;
5982
5983 o.state = 1; // Is loading
5984
5985 tinymce.dom.ScriptLoader.loadScript(o.url, function() {
5986 done(o);
5987 allDone();
5988 });
5989
5990 /*
5991 tinymce.util.XHR.send({
5992 url : o.url,
5993 error : t.settings.error,
5994 success : function(co) {
5995 t.eval(co);
5996 done(o);
5997 allDone();
5998 }
5999 });
6000 */
6001 };
6002
6003 each(sc, function(o) {
6004 var u = o.url;
6005
6006 // Add to queue if needed
6007 if (!lo[u]) {
6008 lo[u] = o;
6009 t.queue.push(o);
6010 } else
6011 o = lo[u];
6012
6013 // Is already loading or has been loaded
6014 if (o.state > 0)
6015 return;
6016
6017 if (!Event.domLoaded && !t.settings.strict_mode) {
6018 var ix, ol = '';
6019
6020 // Add onload events
6021 if (cb || o.func) {
6022 o.state = 1; // Is loading
6023
6024 ix = tinymce.dom.ScriptLoader._addOnLoad(function() {
6025 done(o);
6026 allDone();
6027 });
6028
6029 if (tinymce.isIE)
6030 ol = ' onreadystatechange="';
6031 else
6032 ol = ' onload="';
6033
6034 ol += 'tinymce.dom.ScriptLoader._onLoad(this,\'' + u + '\',' + ix + ');"';
6035 }
6036
6037 document.write('<script type="text/javascript" src="' + tinymce._addVer(u) + '"' + ol + '></script>');
6038
6039 if (!o.func)
6040 done(o);
6041 } else
6042 load(o);
6043 });
6044
6045 allDone();
6046 },
6047
6048 // Static methods
6049 'static' : {
6050 _addOnLoad : function(f) {
6051 var t = this;
6052
6053 t._funcs = t._funcs || [];
6054 t._funcs.push(f);
6055
6056 return t._funcs.length - 1;
6057 },
6058
6059 _onLoad : function(e, u, ix) {
6060 if (!tinymce.isIE || e.readyState == 'complete')
6061 this._funcs[ix].call(this);
6062 },
6063
6064 loadScript : function(u, cb) {
6065 var id = tinymce.DOM.uniqueId(), e;
6066
6067 function done() {
6068 Event.clear(id);
6069 tinymce.DOM.remove(id);
6070
6071 if (cb) {
6072 cb.call(document, u);
6073 cb = 0;
6074 }
6075 };
6076
6077 if (tinymce.isIE) {
6078/* Event.add(e, 'readystatechange', function(e) {
6079 if (e.target && e.target.readyState == 'complete')
6080 done();
6081 });*/
6082
6083 tinymce.util.XHR.send({
6084 url : tinymce._addVer(u),
6085 async : false,
6086 success : function(co) {
6087 window.execScript(co);
6088 done();
6089 }
6090 });
6091 } else {
6092 e = tinymce.DOM.create('script', {id : id, type : 'text/javascript', src : tinymce._addVer(u)});
6093 Event.add(e, 'load', done);
6094
6095 // Check for head or body
6096 (document.getElementsByTagName('head')[0] || document.body).appendChild(e);
6097 }
6098 }
6099 }
6100 });
6101
6102 // Global script loader
6103 tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();
6104})(tinymce);
6105(function(tinymce) {
6106 // Shorten class names
6107 var DOM = tinymce.DOM, is = tinymce.is;
6108
6109 tinymce.create('tinymce.ui.Control', {
6110 Control : function(id, s) {
6111 this.id = id;
6112 this.settings = s = s || {};
6113 this.rendered = false;
6114 this.onRender = new tinymce.util.Dispatcher(this);
6115 this.classPrefix = '';
6116 this.scope = s.scope || this;
6117 this.disabled = 0;
6118 this.active = 0;
6119 },
6120
6121 setDisabled : function(s) {
6122 var e;
6123
6124 if (s != this.disabled) {
6125 e = DOM.get(this.id);
6126
6127 // Add accessibility title for unavailable actions
6128 if (e && this.settings.unavailable_prefix) {
6129 if (s) {
6130 this.prevTitle = e.title;
6131 e.title = this.settings.unavailable_prefix + ": " + e.title;
6132 } else
6133 e.title = this.prevTitle;
6134 }
6135
6136 this.setState('Disabled', s);
6137 this.setState('Enabled', !s);
6138 this.disabled = s;
6139 }
6140 },
6141
6142 isDisabled : function() {
6143 return this.disabled;
6144 },
6145
6146 setActive : function(s) {
6147 if (s != this.active) {
6148 this.setState('Active', s);
6149 this.active = s;
6150 }
6151 },
6152
6153 isActive : function() {
6154 return this.active;
6155 },
6156
6157 setState : function(c, s) {
6158 var n = DOM.get(this.id);
6159
6160 c = this.classPrefix + c;
6161
6162 if (s)
6163 DOM.addClass(n, c);
6164 else
6165 DOM.removeClass(n, c);
6166 },
6167
6168 isRendered : function() {
6169 return this.rendered;
6170 },
6171
6172 renderHTML : function() {
6173 },
6174
6175 renderTo : function(n) {
6176 DOM.setHTML(n, this.renderHTML());
6177 },
6178
6179 postRender : function() {
6180 var t = this, b;
6181
6182 // Set pending states
6183 if (is(t.disabled)) {
6184 b = t.disabled;
6185 t.disabled = -1;
6186 t.setDisabled(b);
6187 }
6188
6189 if (is(t.active)) {
6190 b = t.active;
6191 t.active = -1;
6192 t.setActive(b);
6193 }
6194 },
6195
6196 remove : function() {
6197 DOM.remove(this.id);
6198 this.destroy();
6199 },
6200
6201 destroy : function() {
6202 tinymce.dom.Event.clear(this.id);
6203 }
6204 });
6205})(tinymce);tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {
6206 Container : function(id, s) {
6207 this.parent(id, s);
6208
6209 this.controls = [];
6210
6211 this.lookup = {};
6212 },
6213
6214 add : function(c) {
6215 this.lookup[c.id] = c;
6216 this.controls.push(c);
6217
6218 return c;
6219 },
6220
6221 get : function(n) {
6222 return this.lookup[n];
6223 }
6224});
6225
6226tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
6227 Separator : function(id, s) {
6228 this.parent(id, s);
6229 this.classPrefix = 'mceSeparator';
6230 },
6231
6232 renderHTML : function() {
6233 return tinymce.DOM.createHTML('span', {'class' : this.classPrefix});
6234 }
6235});
6236(function(tinymce) {
6237 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;
6238
6239 tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', {
6240 MenuItem : function(id, s) {
6241 this.parent(id, s);
6242 this.classPrefix = 'mceMenuItem';
6243 },
6244
6245 setSelected : function(s) {
6246 this.setState('Selected', s);
6247 this.selected = s;
6248 },
6249
6250 isSelected : function() {
6251 return this.selected;
6252 },
6253
6254 postRender : function() {
6255 var t = this;
6256
6257 t.parent();
6258
6259 // Set pending state
6260 if (is(t.selected))
6261 t.setSelected(t.selected);
6262 }
6263 });
6264})(tinymce);
6265(function(tinymce) {
6266 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;
6267
6268 tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', {
6269 Menu : function(id, s) {
6270 var t = this;
6271
6272 t.parent(id, s);
6273 t.items = {};
6274 t.collapsed = false;
6275 t.menuCount = 0;
6276 t.onAddItem = new tinymce.util.Dispatcher(this);
6277 },
6278
6279 expand : function(d) {
6280 var t = this;
6281
6282 if (d) {
6283 walk(t, function(o) {
6284 if (o.expand)
6285 o.expand();
6286 }, 'items', t);
6287 }
6288
6289 t.collapsed = false;
6290 },
6291
6292 collapse : function(d) {
6293 var t = this;
6294
6295 if (d) {
6296 walk(t, function(o) {
6297 if (o.collapse)
6298 o.collapse();
6299 }, 'items', t);
6300 }
6301
6302 t.collapsed = true;
6303 },
6304
6305 isCollapsed : function() {
6306 return this.collapsed;
6307 },
6308
6309 add : function(o) {
6310 if (!o.settings)
6311 o = new tinymce.ui.MenuItem(o.id || DOM.uniqueId(), o);
6312
6313 this.onAddItem.dispatch(this, o);
6314
6315 return this.items[o.id] = o;
6316 },
6317
6318 addSeparator : function() {
6319 return this.add({separator : true});
6320 },
6321
6322 addMenu : function(o) {
6323 if (!o.collapse)
6324 o = this.createMenu(o);
6325
6326 this.menuCount++;
6327
6328 return this.add(o);
6329 },
6330
6331 hasMenus : function() {
6332 return this.menuCount !== 0;
6333 },
6334
6335 remove : function(o) {
6336 delete this.items[o.id];
6337 },
6338
6339 removeAll : function() {
6340 var t = this;
6341
6342 walk(t, function(o) {
6343 if (o.removeAll)
6344 o.removeAll();
6345 else
6346 o.remove();
6347
6348 o.destroy();
6349 }, 'items', t);
6350
6351 t.items = {};
6352 },
6353
6354 createMenu : function(o) {
6355 var m = new tinymce.ui.Menu(o.id || DOM.uniqueId(), o);
6356
6357 m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem);
6358
6359 return m;
6360 }
6361 });
6362})(tinymce);(function(tinymce) {
6363 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element;
6364
6365 tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', {
6366 DropMenu : function(id, s) {
6367 s = s || {};
6368 s.container = s.container || DOM.doc.body;
6369 s.offset_x = s.offset_x || 0;
6370 s.offset_y = s.offset_y || 0;
6371 s.vp_offset_x = s.vp_offset_x || 0;
6372 s.vp_offset_y = s.vp_offset_y || 0;
6373
6374 if (is(s.icons) && !s.icons)
6375 s['class'] += ' mceNoIcons';
6376
6377 this.parent(id, s);
6378 this.onShowMenu = new tinymce.util.Dispatcher(this);
6379 this.onHideMenu = new tinymce.util.Dispatcher(this);
6380 this.classPrefix = 'mceMenu';
6381 },
6382
6383 createMenu : function(s) {
6384 var t = this, cs = t.settings, m;
6385
6386 s.container = s.container || cs.container;
6387 s.parent = t;
6388 s.constrain = s.constrain || cs.constrain;
6389 s['class'] = s['class'] || cs['class'];
6390 s.vp_offset_x = s.vp_offset_x || cs.vp_offset_x;
6391 s.vp_offset_y = s.vp_offset_y || cs.vp_offset_y;
6392 m = new tinymce.ui.DropMenu(s.id || DOM.uniqueId(), s);
6393
6394 m.onAddItem.add(t.onAddItem.dispatch, t.onAddItem);
6395
6396 return m;
6397 },
6398
6399 update : function() {
6400 var t = this, s = t.settings, tb = DOM.get('menu_' + t.id + '_tbl'), co = DOM.get('menu_' + t.id + '_co'), tw, th;
6401
6402 tw = s.max_width ? Math.min(tb.clientWidth, s.max_width) : tb.clientWidth;
6403 th = s.max_height ? Math.min(tb.clientHeight, s.max_height) : tb.clientHeight;
6404
6405 if (!DOM.boxModel)
6406 t.element.setStyles({width : tw + 2, height : th + 2});
6407 else
6408 t.element.setStyles({width : tw, height : th});
6409
6410 if (s.max_width)
6411 DOM.setStyle(co, 'width', tw);
6412
6413 if (s.max_height) {
6414 DOM.setStyle(co, 'height', th);
6415
6416 if (tb.clientHeight < s.max_height)
6417 DOM.setStyle(co, 'overflow', 'hidden');
6418 }
6419 },
6420
6421 showMenu : function(x, y, px) {
6422 var t = this, s = t.settings, co, vp = DOM.getViewPort(), w, h, mx, my, ot = 2, dm, tb, cp = t.classPrefix;
6423
6424 t.collapse(1);
6425
6426 if (t.isMenuVisible)
6427 return;
6428
6429 if (!t.rendered) {
6430 co = DOM.add(t.settings.container, t.renderNode());
6431
6432 each(t.items, function(o) {
6433 o.postRender();
6434 });
6435
6436 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});
6437 } else
6438 co = DOM.get('menu_' + t.id);
6439
6440 // Move layer out of sight unless it's Opera since it scrolls to top of page due to an bug
6441 if (!tinymce.isOpera)
6442 DOM.setStyles(co, {left : -0xFFFF , top : -0xFFFF});
6443
6444 DOM.show(co);
6445 t.update();
6446
6447 x += s.offset_x || 0;
6448 y += s.offset_y || 0;
6449 vp.w -= 4;
6450 vp.h -= 4;
6451
6452 // Move inside viewport if not submenu
6453 if (s.constrain) {
6454 w = co.clientWidth - ot;
6455 h = co.clientHeight - ot;
6456 mx = vp.x + vp.w;
6457 my = vp.y + vp.h;
6458
6459 if ((x + s.vp_offset_x + w) > mx)
6460 x = px ? px - w : Math.max(0, (mx - s.vp_offset_x) - w);
6461
6462 if ((y + s.vp_offset_y + h) > my)
6463 y = Math.max(0, (my - s.vp_offset_y) - h);
6464 }
6465
6466 DOM.setStyles(co, {left : x , top : y});
6467 t.element.update();
6468
6469 t.isMenuVisible = 1;
6470 t.mouseClickFunc = Event.add(co, 'click', function(e) {
6471 var m;
6472
6473 e = e.target;
6474
6475 if (e && (e = DOM.getParent(e, 'tr')) && !DOM.hasClass(e, cp + 'ItemSub')) {
6476 m = t.items[e.id];
6477
6478 if (m.isDisabled())
6479 return;
6480
6481 dm = t;
6482
6483 while (dm) {
6484 if (dm.hideMenu)
6485 dm.hideMenu();
6486
6487 dm = dm.settings.parent;
6488 }
6489
6490 if (m.settings.onclick)
6491 m.settings.onclick(e);
6492
6493 return Event.cancel(e); // Cancel to fix onbeforeunload problem
6494 }
6495 });
6496
6497 if (t.hasMenus()) {
6498 t.mouseOverFunc = Event.add(co, 'mouseover', function(e) {
6499 var m, r, mi;
6500
6501 e = e.target;
6502 if (e && (e = DOM.getParent(e, 'tr'))) {
6503 m = t.items[e.id];
6504
6505 if (t.lastMenu)
6506 t.lastMenu.collapse(1);
6507
6508 if (m.isDisabled())
6509 return;
6510
6511 if (e && DOM.hasClass(e, cp + 'ItemSub')) {
6512 //p = DOM.getPos(s.container);
6513 r = DOM.getRect(e);
6514 m.showMenu((r.x + r.w - ot), r.y - ot, r.x);
6515 t.lastMenu = m;
6516 DOM.addClass(DOM.get(m.id).firstChild, cp + 'ItemActive');
6517 }
6518 }
6519 });
6520 }
6521
6522 t.onShowMenu.dispatch(t);
6523
6524 if (s.keyboard_focus) {
6525 Event.add(co, 'keydown', t._keyHandler, t);
6526 DOM.select('a', 'menu_' + t.id)[0].focus(); // Select first link
6527 t._focusIdx = 0;
6528 }
6529 },
6530
6531 hideMenu : function(c) {
6532 var t = this, co = DOM.get('menu_' + t.id), e;
6533
6534 if (!t.isMenuVisible)
6535 return;
6536
6537 Event.remove(co, 'mouseover', t.mouseOverFunc);
6538 Event.remove(co, 'click', t.mouseClickFunc);
6539 Event.remove(co, 'keydown', t._keyHandler);
6540 DOM.hide(co);
6541 t.isMenuVisible = 0;
6542
6543 if (!c)
6544 t.collapse(1);
6545
6546 if (t.element)
6547 t.element.hide();
6548
6549 if (e = DOM.get(t.id))
6550 DOM.removeClass(e.firstChild, t.classPrefix + 'ItemActive');
6551
6552 t.onHideMenu.dispatch(t);
6553 },
6554
6555 add : function(o) {
6556 var t = this, co;
6557
6558 o = t.parent(o);
6559
6560 if (t.isRendered && (co = DOM.get('menu_' + t.id)))
6561 t._add(DOM.select('tbody', co)[0], o);
6562
6563 return o;
6564 },
6565
6566 collapse : function(d) {
6567 this.parent(d);
6568 this.hideMenu(1);
6569 },
6570
6571 remove : function(o) {
6572 DOM.remove(o.id);
6573 this.destroy();
6574
6575 return this.parent(o);
6576 },
6577
6578 destroy : function() {
6579 var t = this, co = DOM.get('menu_' + t.id);
6580
6581 Event.remove(co, 'mouseover', t.mouseOverFunc);
6582 Event.remove(co, 'click', t.mouseClickFunc);
6583
6584 if (t.element)
6585 t.element.remove();
6586
6587 DOM.remove(co);
6588 },
6589
6590 renderNode : function() {
6591 var t = this, s = t.settings, n, tb, co, w;
6592
6593 w = DOM.create('div', {id : 'menu_' + t.id, 'class' : s['class'], 'style' : 'position:absolute;left:0;top:0;z-index:200000'});
6594 co = DOM.add(w, 'div', {id : 'menu_' + t.id + '_co', 'class' : t.classPrefix + (s['class'] ? ' ' + s['class'] : '')});
6595 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});
6596
6597 if (s.menu_line)
6598 DOM.add(co, 'span', {'class' : t.classPrefix + 'Line'});
6599
6600// n = DOM.add(co, 'div', {id : 'menu_' + t.id + '_co', 'class' : 'mceMenuContainer'});
6601 n = DOM.add(co, 'table', {id : 'menu_' + t.id + '_tbl', border : 0, cellPadding : 0, cellSpacing : 0});
6602 tb = DOM.add(n, 'tbody');
6603
6604 each(t.items, function(o) {
6605 t._add(tb, o);
6606 });
6607
6608 t.rendered = true;
6609
6610 return w;
6611 },
6612
6613 // Internal functions
6614
6615 _keyHandler : function(e) {
6616 var t = this, kc = e.keyCode;
6617
6618 function focus(d) {
6619 var i = t._focusIdx + d, e = DOM.select('a', 'menu_' + t.id)[i];
6620
6621 if (e) {
6622 t._focusIdx = i;
6623 e.focus();
6624 }
6625 };
6626
6627 switch (kc) {
6628 case 38:
6629 focus(-1); // Select first link
6630 return;
6631
6632 case 40:
6633 focus(1);
6634 return;
6635
6636 case 13:
6637 return;
6638
6639 case 27:
6640 return this.hideMenu();
6641 }
6642 },
6643
6644 _add : function(tb, o) {
6645 var n, s = o.settings, a, ro, it, cp = this.classPrefix, ic;
6646
6647 if (s.separator) {
6648 ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'ItemSeparator'});
6649 DOM.add(ro, 'td', {'class' : cp + 'ItemSeparator'});
6650
6651 if (n = ro.previousSibling)
6652 DOM.addClass(n, 'mceLast');
6653
6654 return;
6655 }
6656
6657 n = ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'Item ' + cp + 'ItemEnabled'});
6658 n = it = DOM.add(n, 'td');
6659 n = a = DOM.add(n, 'a', {href : 'javascript:;', onclick : "return false;", onmousedown : 'return false;'});
6660
6661 DOM.addClass(it, s['class']);
6662// n = DOM.add(n, 'span', {'class' : 'item'});
6663
6664 ic = DOM.add(n, 'span', {'class' : 'mceIcon' + (s.icon ? ' mce_' + s.icon : '')});
6665
6666 if (s.icon_src)
6667 DOM.add(ic, 'img', {src : s.icon_src});
6668
6669 n = DOM.add(n, s.element || 'span', {'class' : 'mceText', title : o.settings.title}, o.settings.title);
6670
6671 if (o.settings.style)
6672 DOM.setAttrib(n, 'style', o.settings.style);
6673
6674 if (tb.childNodes.length == 1)
6675 DOM.addClass(ro, 'mceFirst');
6676
6677 if ((n = ro.previousSibling) && DOM.hasClass(n, cp + 'ItemSeparator'))
6678 DOM.addClass(ro, 'mceFirst');
6679
6680 if (o.collapse)
6681 DOM.addClass(ro, cp + 'ItemSub');
6682
6683 if (n = ro.previousSibling)
6684 DOM.removeClass(n, 'mceLast');
6685
6686 DOM.addClass(ro, 'mceLast');
6687 }
6688 });
6689})(tinymce);(function(tinymce) {
6690 var DOM = tinymce.DOM;
6691
6692 tinymce.create('tinymce.ui.Button:tinymce.ui.Control', {
6693 Button : function(id, s) {
6694 this.parent(id, s);
6695 this.classPrefix = 'mceButton';
6696 },
6697
6698 renderHTML : function() {
6699 var cp = this.classPrefix, s = this.settings, h, l;
6700
6701 l = DOM.encode(s.label || '');
6702 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) + '">';
6703
6704 if (s.image)
6705 h += '<img class="mceIcon" src="' + s.image + '" />' + l + '</a>';
6706 else
6707 h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '') + '</a>';
6708
6709 return h;
6710 },
6711
6712 postRender : function() {
6713 var t = this, s = t.settings;
6714
6715 tinymce.dom.Event.add(t.id, 'click', function(e) {
6716 if (!t.isDisabled())
6717 return s.onclick.call(s.scope, e);
6718 });
6719 }
6720 });
6721})(tinymce);
6722(function(tinymce) {
6723 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;
6724
6725 tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {
6726 ListBox : function(id, s) {
6727 var t = this;
6728
6729 t.parent(id, s);
6730
6731 t.items = [];
6732
6733 t.onChange = new Dispatcher(t);
6734
6735 t.onPostRender = new Dispatcher(t);
6736
6737 t.onAdd = new Dispatcher(t);
6738
6739 t.onRenderMenu = new tinymce.util.Dispatcher(this);
6740
6741 t.classPrefix = 'mceListBox';
6742 },
6743
6744 select : function(va) {
6745 var t = this, fv, f;
6746
6747 if (va == undefined)
6748 return t.selectByIndex(-1);
6749
6750 // Is string or number make function selector
6751 if (va && va.call)
6752 f = va;
6753 else {
6754 f = function(v) {
6755 return v == va;
6756 };
6757 }
6758
6759 // Do we need to do something?
6760 if (va != t.selectedValue) {
6761 // Find item
6762 each(t.items, function(o, i) {
6763 if (f(o.value)) {
6764 fv = 1;
6765 t.selectByIndex(i);
6766 return false;
6767 }
6768 });
6769
6770 if (!fv)
6771 t.selectByIndex(-1);
6772 }
6773 },
6774
6775 selectByIndex : function(idx) {
6776 var t = this, e, o;
6777
6778 if (idx != t.selectedIndex) {
6779 e = DOM.get(t.id + '_text');
6780 o = t.items[idx];
6781
6782 if (o) {
6783 t.selectedValue = o.value;
6784 t.selectedIndex = idx;
6785 DOM.setHTML(e, DOM.encode(o.title));
6786 DOM.removeClass(e, 'mceTitle');
6787 } else {
6788 DOM.setHTML(e, DOM.encode(t.settings.title));
6789 DOM.addClass(e, 'mceTitle');
6790 t.selectedValue = t.selectedIndex = null;
6791 }
6792
6793 e = 0;
6794 }
6795 },
6796
6797 add : function(n, v, o) {
6798 var t = this;
6799
6800 o = o || {};
6801 o = tinymce.extend(o, {
6802 title : n,
6803 value : v
6804 });
6805
6806 t.items.push(o);
6807 t.onAdd.dispatch(t, o);
6808 },
6809
6810 getLength : function() {
6811 return this.items.length;
6812 },
6813
6814 renderHTML : function() {
6815 var h = '', t = this, s = t.settings, cp = t.classPrefix;
6816
6817 h = '<table id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>';
6818 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>';
6819 h += '<td>' + DOM.createHTML('a', {id : t.id + '_open', tabindex : -1, href : 'javascript:;', 'class' : 'mceOpen', onclick : "return false;", onmousedown : 'return false;'}, '<span></span>') + '</td>';
6820 h += '</tr></tbody></table>';
6821
6822 return h;
6823 },
6824
6825 showMenu : function() {
6826 var t = this, p1, p2, e = DOM.get(this.id), m;
6827
6828 if (t.isDisabled() || t.items.length == 0)
6829 return;
6830
6831 if (t.menu && t.menu.isMenuVisible)
6832 return t.hideMenu();
6833
6834 if (!t.isMenuRendered) {
6835 t.renderMenu();
6836 t.isMenuRendered = true;
6837 }
6838
6839 p1 = DOM.getPos(this.settings.menu_container);
6840 p2 = DOM.getPos(e);
6841
6842 m = t.menu;
6843 m.settings.offset_x = p2.x;
6844 m.settings.offset_y = p2.y;
6845 m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus
6846
6847 // Select in menu
6848 if (t.oldID)
6849 m.items[t.oldID].setSelected(0);
6850
6851 each(t.items, function(o) {
6852 if (o.value === t.selectedValue) {
6853 m.items[o.id].setSelected(1);
6854 t.oldID = o.id;
6855 }
6856 });
6857
6858 m.showMenu(0, e.clientHeight);
6859
6860 Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
6861 DOM.addClass(t.id, t.classPrefix + 'Selected');
6862
6863 //DOM.get(t.id + '_text').focus();
6864 },
6865
6866 hideMenu : function(e) {
6867 var t = this;
6868
6869 // Prevent double toogles by canceling the mouse click event to the button
6870 if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open'))
6871 return;
6872
6873 if (!e || !DOM.getParent(e.target, '.mceMenu')) {
6874 DOM.removeClass(t.id, t.classPrefix + 'Selected');
6875 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
6876
6877 if (t.menu)
6878 t.menu.hideMenu();
6879 }
6880 },
6881
6882 renderMenu : function() {
6883 var t = this, m;
6884
6885 m = t.settings.control_manager.createDropMenu(t.id + '_menu', {
6886 menu_line : 1,
6887 'class' : t.classPrefix + 'Menu mceNoIcons',
6888 max_width : 150,
6889 max_height : 150
6890 });
6891
6892 m.onHideMenu.add(t.hideMenu, t);
6893
6894 m.add({
6895 title : t.settings.title,
6896 'class' : 'mceMenuItemTitle',
6897 onclick : function() {
6898 if (t.settings.onselect('') !== false)
6899 t.select(''); // Must be runned after
6900 }
6901 });
6902
6903 each(t.items, function(o) {
6904 o.id = DOM.uniqueId();
6905 o.onclick = function() {
6906 if (t.settings.onselect(o.value) !== false)
6907 t.select(o.value); // Must be runned after
6908 };
6909
6910 m.add(o);
6911 });
6912
6913 t.onRenderMenu.dispatch(t, m);
6914 t.menu = m;
6915 },
6916
6917 postRender : function() {
6918 var t = this, cp = t.classPrefix;
6919
6920 Event.add(t.id, 'click', t.showMenu, t);
6921 Event.add(t.id + '_text', 'focus', function(e) {
6922 if (!t._focused) {
6923 t.keyDownHandler = Event.add(t.id + '_text', 'keydown', function(e) {
6924 var idx = -1, v, kc = e.keyCode;
6925
6926 // Find current index
6927 each(t.items, function(v, i) {
6928 if (t.selectedValue == v.value)
6929 idx = i;
6930 });
6931
6932 // Move up/down
6933 if (kc == 38)
6934 v = t.items[idx - 1];
6935 else if (kc == 40)
6936 v = t.items[idx + 1];
6937 else if (kc == 13) {
6938 // Fake select on enter
6939 v = t.selectedValue;
6940 t.selectedValue = null; // Needs to be null to fake change
6941 t.settings.onselect(v);
6942 return Event.cancel(e);
6943 }
6944
6945 if (v) {
6946 t.hideMenu();
6947 t.select(v.value);
6948 }
6949 });
6950 }
6951
6952 t._focused = 1;
6953 });
6954 Event.add(t.id + '_text', 'blur', function() {Event.remove(t.id + '_text', 'keydown', t.keyDownHandler); t._focused = 0;});
6955
6956 // Old IE doesn't have hover on all elements
6957 if (tinymce.isIE6 || !DOM.boxModel) {
6958 Event.add(t.id, 'mouseover', function() {
6959 if (!DOM.hasClass(t.id, cp + 'Disabled'))
6960 DOM.addClass(t.id, cp + 'Hover');
6961 });
6962
6963 Event.add(t.id, 'mouseout', function() {
6964 if (!DOM.hasClass(t.id, cp + 'Disabled'))
6965 DOM.removeClass(t.id, cp + 'Hover');
6966 });
6967 }
6968
6969 t.onPostRender.dispatch(t, DOM.get(t.id));
6970 },
6971
6972 destroy : function() {
6973 this.parent();
6974
6975 Event.clear(this.id + '_text');
6976 Event.clear(this.id + '_open');
6977 }
6978 });
6979})(tinymce);(function(tinymce) {
6980 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;
6981
6982 tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {
6983 NativeListBox : function(id, s) {
6984 this.parent(id, s);
6985 this.classPrefix = 'mceNativeListBox';
6986 },
6987
6988 setDisabled : function(s) {
6989 DOM.get(this.id).disabled = s;
6990 },
6991
6992 isDisabled : function() {
6993 return DOM.get(this.id).disabled;
6994 },
6995
6996 select : function(va) {
6997 var t = this, fv, f;
6998
6999 if (va == undefined)
7000 return t.selectByIndex(-1);
7001
7002 // Is string or number make function selector
7003 if (va && va.call)
7004 f = va;
7005 else {
7006 f = function(v) {
7007 return v == va;
7008 };
7009 }
7010
7011 // Do we need to do something?
7012 if (va != t.selectedValue) {
7013 // Find item
7014 each(t.items, function(o, i) {
7015 if (f(o.value)) {
7016 fv = 1;
7017 t.selectByIndex(i);
7018 return false;
7019 }
7020 });
7021
7022 if (!fv)
7023 t.selectByIndex(-1);
7024 }
7025 },
7026
7027 selectByIndex : function(idx) {
7028 DOM.get(this.id).selectedIndex = idx + 1;
7029 this.selectedValue = this.items[idx] ? this.items[idx].value : null;
7030 },
7031
7032 add : function(n, v, a) {
7033 var o, t = this;
7034
7035 a = a || {};
7036 a.value = v;
7037
7038 if (t.isRendered())
7039 DOM.add(DOM.get(this.id), 'option', a, n);
7040
7041 o = {
7042 title : n,
7043 value : v,
7044 attribs : a
7045 };
7046
7047 t.items.push(o);
7048 t.onAdd.dispatch(t, o);
7049 },
7050
7051 getLength : function() {
7052 return DOM.get(this.id).options.length - 1;
7053 },
7054
7055 renderHTML : function() {
7056 var h, t = this;
7057
7058 h = DOM.createHTML('option', {value : ''}, '-- ' + t.settings.title + ' --');
7059
7060 each(t.items, function(it) {
7061 h += DOM.createHTML('option', {value : it.value}, it.title);
7062 });
7063
7064 h = DOM.createHTML('select', {id : t.id, 'class' : 'mceNativeListBox'}, h);
7065
7066 return h;
7067 },
7068
7069 postRender : function() {
7070 var t = this, ch;
7071
7072 t.rendered = true;
7073
7074 function onChange(e) {
7075 var v = t.items[e.target.selectedIndex - 1];
7076
7077 if (v && (v = v.value)) {
7078 t.onChange.dispatch(t, v);
7079
7080 if (t.settings.onselect)
7081 t.settings.onselect(v);
7082 }
7083 };
7084
7085 Event.add(t.id, 'change', onChange);
7086
7087 // Accessibility keyhandler
7088 Event.add(t.id, 'keydown', function(e) {
7089 var bf;
7090
7091 Event.remove(t.id, 'change', ch);
7092
7093 bf = Event.add(t.id, 'blur', function() {
7094 Event.add(t.id, 'change', onChange);
7095 Event.remove(t.id, 'blur', bf);
7096 });
7097
7098 if (e.keyCode == 13 || e.keyCode == 32) {
7099 onChange(e);
7100 return Event.cancel(e);
7101 }
7102 });
7103
7104 t.onPostRender.dispatch(t, DOM.get(t.id));
7105 }
7106 });
7107})(tinymce);(function(tinymce) {
7108 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;
7109
7110 tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', {
7111 MenuButton : function(id, s) {
7112 this.parent(id, s);
7113
7114 this.onRenderMenu = new tinymce.util.Dispatcher(this);
7115
7116 s.menu_container = s.menu_container || DOM.doc.body;
7117 },
7118
7119 showMenu : function() {
7120 var t = this, p1, p2, e = DOM.get(t.id), m;
7121
7122 if (t.isDisabled())
7123 return;
7124
7125 if (!t.isMenuRendered) {
7126 t.renderMenu();
7127 t.isMenuRendered = true;
7128 }
7129
7130 if (t.isMenuVisible)
7131 return t.hideMenu();
7132
7133 p1 = DOM.getPos(t.settings.menu_container);
7134 p2 = DOM.getPos(e);
7135
7136 m = t.menu;
7137 m.settings.offset_x = p2.x;
7138 m.settings.offset_y = p2.y;
7139 m.settings.vp_offset_x = p2.x;
7140 m.settings.vp_offset_y = p2.y;
7141 m.settings.keyboard_focus = t._focused;
7142 m.showMenu(0, e.clientHeight);
7143
7144 Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
7145 t.setState('Selected', 1);
7146
7147 t.isMenuVisible = 1;
7148 },
7149
7150 renderMenu : function() {
7151 var t = this, m;
7152
7153 m = t.settings.control_manager.createDropMenu(t.id + '_menu', {
7154 menu_line : 1,
7155 'class' : this.classPrefix + 'Menu',
7156 icons : t.settings.icons
7157 });
7158
7159 m.onHideMenu.add(t.hideMenu, t);
7160
7161 t.onRenderMenu.dispatch(t, m);
7162 t.menu = m;
7163 },
7164
7165 hideMenu : function(e) {
7166 var t = this;
7167
7168 // Prevent double toogles by canceling the mouse click event to the button
7169 if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id || e.id === t.id + '_open';}))
7170 return;
7171
7172 if (!e || !DOM.getParent(e.target, '.mceMenu')) {
7173 t.setState('Selected', 0);
7174 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
7175 if (t.menu)
7176 t.menu.hideMenu();
7177 }
7178
7179 t.isMenuVisible = 0;
7180 },
7181
7182 postRender : function() {
7183 var t = this, s = t.settings;
7184
7185 Event.add(t.id, 'click', function() {
7186 if (!t.isDisabled()) {
7187 if (s.onclick)
7188 s.onclick(t.value);
7189
7190 t.showMenu();
7191 }
7192 });
7193 }
7194 });
7195})(tinymce);
7196(function(tinymce) {
7197 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;
7198
7199 tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', {
7200 SplitButton : function(id, s) {
7201 this.parent(id, s);
7202 this.classPrefix = 'mceSplitButton';
7203 },
7204
7205 renderHTML : function() {
7206 var h, t = this, s = t.settings, h1;
7207
7208 h = '<tbody><tr>';
7209
7210 if (s.image)
7211 h1 = DOM.createHTML('img ', {src : s.image, 'class' : 'mceAction ' + s['class']});
7212 else
7213 h1 = DOM.createHTML('span', {'class' : 'mceAction ' + s['class']}, '');
7214
7215 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>';
7216
7217 h1 = DOM.createHTML('span', {'class' : 'mceOpen ' + s['class']});
7218 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>';
7219
7220 h += '</tr></tbody>';
7221
7222 return DOM.createHTML('table', {id : t.id, 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', onmousedown : 'return false;', title : s.title}, h);
7223 },
7224
7225 postRender : function() {
7226 var t = this, s = t.settings;
7227
7228 if (s.onclick) {
7229 Event.add(t.id + '_action', 'click', function() {
7230 if (!t.isDisabled())
7231 s.onclick(t.value);
7232 });
7233 }
7234
7235 Event.add(t.id + '_open', 'click', t.showMenu, t);
7236 Event.add(t.id + '_open', 'focus', function() {t._focused = 1;});
7237 Event.add(t.id + '_open', 'blur', function() {t._focused = 0;});
7238
7239 // Old IE doesn't have hover on all elements
7240 if (tinymce.isIE6 || !DOM.boxModel) {
7241 Event.add(t.id, 'mouseover', function() {
7242 if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))
7243 DOM.addClass(t.id, 'mceSplitButtonHover');
7244 });
7245
7246 Event.add(t.id, 'mouseout', function() {
7247 if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))
7248 DOM.removeClass(t.id, 'mceSplitButtonHover');
7249 });
7250 }
7251 },
7252
7253 destroy : function() {
7254 this.parent();
7255
7256 Event.clear(this.id + '_action');
7257 Event.clear(this.id + '_open');
7258 }
7259 });
7260})(tinymce);
7261(function(tinymce) {
7262 var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each;
7263
7264 tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', {
7265 ColorSplitButton : function(id, s) {
7266 var t = this;
7267
7268 t.parent(id, s);
7269
7270 t.settings = s = tinymce.extend({
7271 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',
7272 grid_width : 8,
7273 default_color : '#888888'
7274 }, t.settings);
7275
7276 t.onShowMenu = new tinymce.util.Dispatcher(t);
7277
7278 t.onHideMenu = new tinymce.util.Dispatcher(t);
7279
7280 t.value = s.default_color;
7281 },
7282
7283 showMenu : function() {
7284 var t = this, r, p, e, p2;
7285
7286 if (t.isDisabled())
7287 return;
7288
7289 if (!t.isMenuRendered) {
7290 t.renderMenu();
7291 t.isMenuRendered = true;
7292 }
7293
7294 if (t.isMenuVisible)
7295 return t.hideMenu();
7296
7297 e = DOM.get(t.id);
7298 DOM.show(t.id + '_menu');
7299 DOM.addClass(e, 'mceSplitButtonSelected');
7300 p2 = DOM.getPos(e);
7301 DOM.setStyles(t.id + '_menu', {
7302 left : p2.x,
7303 top : p2.y + e.clientHeight,
7304 zIndex : 200000
7305 });
7306 e = 0;
7307
7308 Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
7309 t.onShowMenu.dispatch(t);
7310
7311 if (t._focused) {
7312 t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) {
7313 if (e.keyCode == 27)
7314 t.hideMenu();
7315 });
7316
7317 DOM.select('a', t.id + '_menu')[0].focus(); // Select first link
7318 }
7319
7320 t.isMenuVisible = 1;
7321 },
7322
7323 hideMenu : function(e) {
7324 var t = this;
7325
7326 // Prevent double toogles by canceling the mouse click event to the button
7327 if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';}))
7328 return;
7329
7330 if (!e || !DOM.getParent(e.target, '.mceSplitButtonMenu')) {
7331 DOM.removeClass(t.id, 'mceSplitButtonSelected');
7332 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
7333 Event.remove(t.id + '_menu', 'keydown', t._keyHandler);
7334 DOM.hide(t.id + '_menu');
7335 }
7336
7337 t.onHideMenu.dispatch(t);
7338
7339 t.isMenuVisible = 0;
7340 },
7341
7342 renderMenu : function() {
7343 var t = this, m, i = 0, s = t.settings, n, tb, tr, w;
7344
7345 w = DOM.add(s.menu_container, 'div', {id : t.id + '_menu', 'class' : s['menu_class'] + ' ' + s['class'], style : 'position:absolute;left:0;top:-1000px;'});
7346 m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'});
7347 DOM.add(m, 'span', {'class' : 'mceMenuLine'});
7348
7349 n = DOM.add(m, 'table', {'class' : 'mceColorSplitMenu'});
7350 tb = DOM.add(n, 'tbody');
7351
7352 // Generate color grid
7353 i = 0;
7354 each(is(s.colors, 'array') ? s.colors : s.colors.split(','), function(c) {
7355 c = c.replace(/^#/, '');
7356
7357 if (!i--) {
7358 tr = DOM.add(tb, 'tr');
7359 i = s.grid_width - 1;
7360 }
7361
7362 n = DOM.add(tr, 'td');
7363
7364 n = DOM.add(n, 'a', {
7365 href : 'javascript:;',
7366 style : {
7367 backgroundColor : '#' + c
7368 },
7369 mce_color : '#' + c
7370 });
7371 });
7372
7373 if (s.more_colors_func) {
7374 n = DOM.add(tb, 'tr');
7375 n = DOM.add(n, 'td', {colspan : s.grid_width, 'class' : 'mceMoreColors'});
7376 n = DOM.add(n, 'a', {id : t.id + '_more', href : 'javascript:;', onclick : 'return false;', 'class' : 'mceMoreColors'}, s.more_colors_title);
7377
7378 Event.add(n, 'click', function(e) {
7379 s.more_colors_func.call(s.more_colors_scope || this);
7380 return Event.cancel(e); // Cancel to fix onbeforeunload problem
7381 });
7382 }
7383
7384 DOM.addClass(m, 'mceColorSplitMenu');
7385
7386 Event.add(t.id + '_menu', 'click', function(e) {
7387 var c;
7388
7389 e = e.target;
7390
7391 if (e.nodeName == 'A' && (c = e.getAttribute('mce_color')))
7392 t.setColor(c);
7393
7394 return Event.cancel(e); // Prevent IE auto save warning
7395 });
7396
7397 return w;
7398 },
7399
7400 setColor : function(c) {
7401 var t = this;
7402
7403 DOM.setStyle(t.id + '_preview', 'backgroundColor', c);
7404
7405 t.value = c;
7406 t.hideMenu();
7407 t.settings.onselect(c);
7408 },
7409
7410 postRender : function() {
7411 var t = this, id = t.id;
7412
7413 t.parent();
7414 DOM.add(id + '_action', 'div', {id : id + '_preview', 'class' : 'mceColorPreview'});
7415 DOM.setStyle(t.id + '_preview', 'backgroundColor', t.value);
7416 },
7417
7418 destroy : function() {
7419 this.parent();
7420
7421 Event.clear(this.id + '_menu');
7422 Event.clear(this.id + '_more');
7423 DOM.remove(this.id + '_menu');
7424 }
7425 });
7426})(tinymce);
7427tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
7428 renderHTML : function() {
7429 var t = this, h = '', c, co, dom = tinymce.DOM, s = t.settings, i, pr, nx, cl;
7430
7431 cl = t.controls;
7432 for (i=0; i<cl.length; i++) {
7433 // Get current control, prev control, next control and if the control is a list box or not
7434 co = cl[i];
7435 pr = cl[i - 1];
7436 nx = cl[i + 1];
7437
7438 // Add toolbar start
7439 if (i === 0) {
7440 c = 'mceToolbarStart';
7441
7442 if (co.Button)
7443 c += ' mceToolbarStartButton';
7444 else if (co.SplitButton)
7445 c += ' mceToolbarStartSplitButton';
7446 else if (co.ListBox)
7447 c += ' mceToolbarStartListBox';
7448
7449 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));
7450 }
7451
7452 // Add toolbar end before list box and after the previous button
7453 // This is to fix the o2k7 editor skins
7454 if (pr && co.ListBox) {
7455 if (pr.Button || pr.SplitButton)
7456 h += dom.createHTML('td', {'class' : 'mceToolbarEnd'}, dom.createHTML('span', null, '<!-- IE -->'));
7457 }
7458
7459 // Render control HTML
7460
7461 // IE 8 quick fix, needed to propertly generate a hit area for anchors
7462 if (dom.stdMode)
7463 h += '<td style="position: relative">' + co.renderHTML() + '</td>';
7464 else
7465 h += '<td>' + co.renderHTML() + '</td>';
7466
7467 // Add toolbar start after list box and before the next button
7468 // This is to fix the o2k7 editor skins
7469 if (nx && co.ListBox) {
7470 if (nx.Button || nx.SplitButton)
7471 h += dom.createHTML('td', {'class' : 'mceToolbarStart'}, dom.createHTML('span', null, '<!-- IE -->'));
7472 }
7473 }
7474
7475 c = 'mceToolbarEnd';
7476
7477 if (co.Button)
7478 c += ' mceToolbarEndButton';
7479 else if (co.SplitButton)
7480 c += ' mceToolbarEndSplitButton';
7481 else if (co.ListBox)
7482 c += ' mceToolbarEndListBox';
7483
7484 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));
7485
7486 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>');
7487 }
7488});
7489(function(tinymce) {
7490 var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;
7491
7492 tinymce.create('tinymce.AddOnManager', {
7493 items : [],
7494 urls : {},
7495 lookup : {},
7496
7497 onAdd : new Dispatcher(this),
7498
7499 get : function(n) {
7500 return this.lookup[n];
7501 },
7502
7503 requireLangPack : function(n) {
7504 var u, s = tinymce.EditorManager.settings;
7505
7506 if (s && s.language) {
7507 u = this.urls[n] + '/langs/' + s.language + '.js';
7508
7509 if (!tinymce.dom.Event.domLoaded && !s.strict_mode)
7510 tinymce.ScriptLoader.load(u);
7511 else
7512 tinymce.ScriptLoader.add(u);
7513 }
7514 },
7515
7516 add : function(id, o) {
7517 this.items.push(o);
7518 this.lookup[id] = o;
7519 this.onAdd.dispatch(this, id, o);
7520
7521 return o;
7522 },
7523
7524 load : function(n, u, cb, s) {
7525 var t = this;
7526
7527 if (t.urls[n])
7528 return;
7529
7530 if (u.indexOf('/') != 0 && u.indexOf('://') == -1)
7531 u = tinymce.baseURL + '/' + u;
7532
7533 t.urls[n] = u.substring(0, u.lastIndexOf('/'));
7534 tinymce.ScriptLoader.add(u, cb, s);
7535 }
7536 });
7537
7538 // Create plugin and theme managers
7539 tinymce.PluginManager = new tinymce.AddOnManager();
7540 tinymce.ThemeManager = new tinymce.AddOnManager();
7541}(tinymce));
7542
7543(function(tinymce) {
7544 // Shorten names
7545 var each = tinymce.each, extend = tinymce.extend, DOM = tinymce.DOM, Event = tinymce.dom.Event, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, explode = tinymce.explode;
7546
7547 tinymce.create('static tinymce.EditorManager', {
7548 editors : {},
7549
7550 i18n : {},
7551
7552 activeEditor : null,
7553
7554 preInit : function() {
7555 var t = this, lo = window.location;
7556
7557 // Setup some URLs where the editor API is located and where the document is
7558 tinymce.documentBaseURL = lo.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
7559 if (!/[\/\\]$/.test(tinymce.documentBaseURL))
7560 tinymce.documentBaseURL += '/';
7561
7562 tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL);
7563 tinymce.EditorManager.baseURI = new tinymce.util.URI(tinymce.baseURL);
7564
7565 // Add before unload listener
7566 // This was required since IE was leaking memory if you added and removed beforeunload listeners
7567 // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event
7568 t.onBeforeUnload = new tinymce.util.Dispatcher(t);
7569
7570 // Must be on window or IE will leak if the editor is placed in frame or iframe
7571 Event.add(window, 'beforeunload', function(e) {
7572 t.onBeforeUnload.dispatch(t, e);
7573 });
7574 },
7575
7576 init : function(s) {
7577 var t = this, pl, sl = tinymce.ScriptLoader, c, e, el = [], ed;
7578
7579 function execCallback(se, n, s) {
7580 var f = se[n];
7581
7582 if (!f)
7583 return;
7584
7585 if (tinymce.is(f, 'string')) {
7586 s = f.replace(/\.\w+$/, '');
7587 s = s ? tinymce.resolve(s) : 0;
7588 f = tinymce.resolve(f);
7589 }
7590
7591 return f.apply(s || this, Array.prototype.slice.call(arguments, 2));
7592 };
7593
7594 s = extend({
7595 theme : "simple",
7596 language : "en",
7597 strict_loading_mode : document.contentType == 'application/xhtml+xml'
7598 }, s);
7599
7600 t.settings = s;
7601
7602 // If page not loaded and strict mode isn't enabled then load them
7603 if (!Event.domLoaded && !s.strict_loading_mode) {
7604 // Load language
7605 if (s.language)
7606 sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');
7607
7608 // Load theme
7609 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
7610 ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');
7611
7612 // Load plugins
7613 if (s.plugins) {
7614 pl = explode(s.plugins);
7615
7616 // Load rest if plugins
7617 each(pl, function(v) {
7618 if (v && v.charAt(0) != '-' && !PluginManager.urls[v]) {
7619 // Skip safari plugin for other browsers
7620 if (!tinymce.isWebKit && v == 'safari')
7621 return;
7622
7623 PluginManager.load(v, 'plugins/' + v + '/editor_plugin' + tinymce.suffix + '.js');
7624 }
7625 });
7626 }
7627
7628 sl.loadQueue();
7629 }
7630
7631 // Legacy call
7632 Event.add(document, 'init', function() {
7633 var l, co;
7634
7635 execCallback(s, 'onpageload');
7636
7637 // Verify that it's a valid browser
7638 if (s.browsers) {
7639 l = false;
7640
7641 each(explode(s.browsers), function(v) {
7642 switch (v) {
7643 case 'ie':
7644 case 'msie':
7645 if (tinymce.isIE)
7646 l = true;
7647 break;
7648
7649 case 'gecko':
7650 if (tinymce.isGecko)
7651 l = true;
7652 break;
7653
7654 case 'safari':
7655 case 'webkit':
7656 if (tinymce.isWebKit)
7657 l = true;
7658 break;
7659
7660 case 'opera':
7661 if (tinymce.isOpera)
7662 l = true;
7663
7664 break;
7665 }
7666 });
7667
7668 // Not a valid one
7669 if (!l)
7670 return;
7671 }
7672
7673 switch (s.mode) {
7674 case "exact":
7675 l = s.elements || '';
7676
7677 if(l.length > 0) {
7678 each(explode(l), function(v) {
7679 if (DOM.get(v)) {
7680 ed = new tinymce.Editor(v, s);
7681 el.push(ed);
7682 ed.render(1);
7683 } else {
7684 c = 0;
7685
7686 each(document.forms, function(f) {
7687 each(f.elements, function(e) {
7688 if (e.name === v) {
7689 v = 'mce_editor_' + c;
7690 DOM.setAttrib(e, 'id', v);
7691
7692 ed = new tinymce.Editor(v, s);
7693 el.push(ed);
7694 ed.render(1);
7695 }
7696 });
7697 });
7698 }
7699 });
7700 }
7701 break;
7702
7703 case "textareas":
7704 case "specific_textareas":
7705 function hasClass(n, c) {
7706 return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);
7707 };
7708
7709 each(DOM.select('textarea'), function(v) {
7710 if (s.editor_deselector && hasClass(v, s.editor_deselector))
7711 return;
7712
7713 if (!s.editor_selector || hasClass(v, s.editor_selector)) {
7714 // Can we use the name
7715 e = DOM.get(v.name);
7716 if (!v.id && !e)
7717 v.id = v.name;
7718
7719 // Generate unique name if missing or already exists
7720 if (!v.id || t.get(v.id))
7721 v.id = DOM.uniqueId();
7722
7723 ed = new tinymce.Editor(v.id, s);
7724 el.push(ed);
7725 ed.render(1);
7726 }
7727 });
7728 break;
7729 }
7730
7731 // Call onInit when all editors are initialized
7732 if (s.oninit) {
7733 l = co = 0;
7734
7735 each (el, function(ed) {
7736 co++;
7737
7738 if (!ed.initialized) {
7739 // Wait for it
7740 ed.onInit.add(function() {
7741 l++;
7742
7743 // All done
7744 if (l == co)
7745 execCallback(s, 'oninit');
7746 });
7747 } else
7748 l++;
7749
7750 // All done
7751 if (l == co)
7752 execCallback(s, 'oninit');
7753 });
7754 }
7755 });
7756 },
7757
7758 get : function(id) {
7759 return this.editors[id];
7760 },
7761
7762 getInstanceById : function(id) {
7763 return this.get(id);
7764 },
7765
7766 add : function(e) {
7767 this.editors[e.id] = e;
7768 this._setActive(e);
7769
7770 return e;
7771 },
7772
7773 remove : function(e) {
7774 var t = this;
7775
7776 // Not in the collection
7777 if (!t.editors[e.id])
7778 return null;
7779
7780 delete t.editors[e.id];
7781
7782 // Select another editor since the active one was removed
7783 if (t.activeEditor == e) {
7784 t._setActive(null);
7785
7786 each(t.editors, function(e) {
7787 t._setActive(e);
7788 return false; // Break
7789 });
7790 }
7791
7792 e.destroy();
7793
7794 return e;
7795 },
7796
7797 execCommand : function(c, u, v) {
7798 var t = this, ed = t.get(v), w;
7799
7800 // Manager commands
7801 switch (c) {
7802 case "mceFocus":
7803 ed.focus();
7804 return true;
7805
7806 case "mceAddEditor":
7807 case "mceAddControl":
7808 if (!t.get(v))
7809 new tinymce.Editor(v, t.settings).render();
7810
7811 return true;
7812
7813 case "mceAddFrameControl":
7814 w = v.window;
7815
7816 // Add tinyMCE global instance and tinymce namespace to specified window
7817 w.tinyMCE = tinyMCE;
7818 w.tinymce = tinymce;
7819
7820 tinymce.DOM.doc = w.document;
7821 tinymce.DOM.win = w;
7822
7823 ed = new tinymce.Editor(v.element_id, v);
7824 ed.render();
7825
7826 // Fix IE memory leaks
7827 if (tinymce.isIE) {
7828 function clr() {
7829 ed.destroy();
7830 w.detachEvent('onunload', clr);
7831 w = w.tinyMCE = w.tinymce = null; // IE leak
7832 };
7833
7834 w.attachEvent('onunload', clr);
7835 }
7836
7837 v.page_window = null;
7838
7839 return true;
7840
7841 case "mceRemoveEditor":
7842 case "mceRemoveControl":
7843 if (ed)
7844 ed.remove();
7845
7846 return true;
7847
7848 case 'mceToggleEditor':
7849 if (!ed) {
7850 t.execCommand('mceAddControl', 0, v);
7851 return true;
7852 }
7853
7854 if (ed.isHidden())
7855 ed.show();
7856 else
7857 ed.hide();
7858
7859 return true;
7860 }
7861
7862 // Run command on active editor
7863 if (t.activeEditor)
7864 return t.activeEditor.execCommand(c, u, v);
7865
7866 return false;
7867 },
7868
7869 execInstanceCommand : function(id, c, u, v) {
7870 var ed = this.get(id);
7871
7872 if (ed)
7873 return ed.execCommand(c, u, v);
7874
7875 return false;
7876 },
7877
7878 triggerSave : function() {
7879 each(this.editors, function(e) {
7880 e.save();
7881 });
7882 },
7883
7884 addI18n : function(p, o) {
7885 var lo, i18n = this.i18n;
7886
7887 if (!tinymce.is(p, 'string')) {
7888 each(p, function(o, lc) {
7889 each(o, function(o, g) {
7890 each(o, function(o, k) {
7891 if (g === 'common')
7892 i18n[lc + '.' + k] = o;
7893 else
7894 i18n[lc + '.' + g + '.' + k] = o;
7895 });
7896 });
7897 });
7898 } else {
7899 each(o, function(o, k) {
7900 i18n[p + '.' + k] = o;
7901 });
7902 }
7903 },
7904
7905 // Private methods
7906
7907 _setActive : function(e) {
7908 this.selectedInstance = this.activeEditor = e;
7909 }
7910 });
7911
7912 tinymce.EditorManager.preInit();
7913})(tinymce);
7914
7915var tinyMCE = window.tinyMCE = tinymce.EditorManager;
7916(function(tinymce) {
7917 var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend, Dispatcher = tinymce.util.Dispatcher;
7918 var each = tinymce.each, isGecko = tinymce.isGecko, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit;
7919 var is = tinymce.is, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, EditorManager = tinymce.EditorManager;
7920 var inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;
7921
7922 tinymce.create('tinymce.Editor', {
7923 Editor : function(id, s) {
7924 var t = this;
7925
7926 t.id = t.editorId = id;
7927
7928 t.execCommands = {};
7929 t.queryStateCommands = {};
7930 t.queryValueCommands = {};
7931
7932 t.isNotDirty = false;
7933
7934 t.plugins = {};
7935
7936 // Add events to the editor
7937 each([
7938 'onPreInit',
7939
7940 'onBeforeRenderUI',
7941
7942 'onPostRender',
7943
7944 'onInit',
7945
7946 'onRemove',
7947
7948 'onActivate',
7949
7950 'onDeactivate',
7951
7952 'onClick',
7953
7954 'onEvent',
7955
7956 'onMouseUp',
7957
7958 'onMouseDown',
7959
7960 'onDblClick',
7961
7962 'onKeyDown',
7963
7964 'onKeyUp',
7965
7966 'onKeyPress',
7967
7968 'onContextMenu',
7969
7970 'onSubmit',
7971
7972 'onReset',
7973
7974 'onPaste',
7975
7976 'onPreProcess',
7977
7978 'onPostProcess',
7979
7980 'onBeforeSetContent',
7981
7982 'onBeforeGetContent',
7983
7984 'onSetContent',
7985
7986 'onGetContent',
7987
7988 'onLoadContent',
7989
7990 'onSaveContent',
7991
7992 'onNodeChange',
7993
7994 'onChange',
7995
7996 'onBeforeExecCommand',
7997
7998 'onExecCommand',
7999
8000 'onUndo',
8001
8002 'onRedo',
8003
8004 'onVisualAid',
8005
8006 'onSetProgressState'
8007 ], function(e) {
8008 t[e] = new Dispatcher(t);
8009 });
8010
8011 t.settings = s = extend({
8012 id : id,
8013 language : 'en',
8014 docs_language : 'en',
8015 theme : 'simple',
8016 skin : 'default',
8017 delta_width : 0,
8018 delta_height : 0,
8019 popup_css : '',
8020 plugins : '',
8021 document_base_url : tinymce.documentBaseURL,
8022 add_form_submit_trigger : 1,
8023 submit_patch : 1,
8024 add_unload_trigger : 1,
8025 convert_urls : 1,
8026 relative_urls : 1,
8027 remove_script_host : 1,
8028 table_inline_editing : 0,
8029 object_resizing : 1,
8030 cleanup : 1,
8031 accessibility_focus : 1,
8032 custom_shortcuts : 1,
8033 custom_undo_redo_keyboard_shortcuts : 1,
8034 custom_undo_redo_restore_selection : 1,
8035 custom_undo_redo : 1,
8036 doctype : '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">',
8037 visual_table_class : 'mceItemTable',
8038 visual : 1,
8039 inline_styles : true,
8040 convert_fonts_to_spans : true,
8041 font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',
8042 apply_source_formatting : 1,
8043 directionality : 'ltr',
8044 forced_root_block : 'p',
8045 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,-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|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',
8046 hidden_input : 1,
8047 padd_empty_editor : 1,
8048 render_ui : 1,
8049 init_theme : 1,
8050 force_p_newlines : 1,
8051 indentation : '30px',
8052 keep_styles : 1,
8053 fix_table_elements : 1,
8054 removeformat_selector : 'span,b,strong,em,i,font,u,strike'
8055 }, s);
8056
8057 t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {
8058 base_uri : tinyMCE.baseURI
8059 });
8060
8061 t.baseURI = EditorManager.baseURI;
8062
8063 // Call setup
8064 t.execCallback('setup', t);
8065 },
8066
8067 render : function(nst) {
8068 var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader;
8069
8070 // Page is not loaded yet, wait for it
8071 if (!Event.domLoaded) {
8072 Event.add(document, 'init', function() {
8073 t.render();
8074 });
8075 return;
8076 }
8077
8078 // Force strict loading mode if render us called by user and not internally
8079 if (!nst) {
8080 s.strict_loading_mode = 1;
8081 tinyMCE.settings = s;
8082 }
8083
8084 // Element not found, then skip initialization
8085 if (!t.getElement())
8086 return;
8087
8088 if (s.strict_loading_mode) {
8089 sl.settings.strict_mode = s.strict_loading_mode;
8090 tinymce.DOM.settings.strict = 1;
8091 }
8092
8093 // Add hidden input for non input elements inside form elements
8094 if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))
8095 DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);
8096
8097 if (tinymce.WindowManager)
8098 t.windowManager = new tinymce.WindowManager(t);
8099
8100 if (s.encoding == 'xml') {
8101 t.onGetContent.add(function(ed, o) {
8102 if (o.save)
8103 o.content = DOM.encode(o.content);
8104 });
8105 }
8106
8107 if (s.add_form_submit_trigger) {
8108 t.onSubmit.addToTop(function() {
8109 if (t.initialized) {
8110 t.save();
8111 t.isNotDirty = 1;
8112 }
8113 });
8114 }
8115
8116 if (s.add_unload_trigger) {
8117 t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {
8118 if (t.initialized && !t.destroyed && !t.isHidden())
8119 t.save({format : 'raw', no_events : true});
8120 });
8121 }
8122
8123 tinymce.addUnload(t.destroy, t);
8124
8125 if (s.submit_patch) {
8126 t.onBeforeRenderUI.add(function() {
8127 var n = t.getElement().form;
8128
8129 if (!n)
8130 return;
8131
8132 // Already patched
8133 if (n._mceOldSubmit)
8134 return;
8135
8136 // Check page uses id="submit" or name="submit" for it's submit button
8137 if (!n.submit.nodeType && !n.submit.length) {
8138 t.formElement = n;
8139 n._mceOldSubmit = n.submit;
8140 n.submit = function() {
8141 // Save all instances
8142 EditorManager.triggerSave();
8143 t.isNotDirty = 1;
8144
8145 return t.formElement._mceOldSubmit(t.formElement);
8146 };
8147 }
8148
8149 n = null;
8150 });
8151 }
8152
8153 // Load scripts
8154 function loadScripts() {
8155 if (s.language)
8156 sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');
8157
8158 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
8159 ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');
8160
8161 each(explode(s.plugins), function(p) {
8162 if (p && p.charAt(0) != '-' && !PluginManager.urls[p]) {
8163 // Skip safari plugin for other browsers
8164 if (!isWebKit && p == 'safari')
8165 return;
8166
8167 PluginManager.load(p, 'plugins/' + p + '/editor_plugin' + tinymce.suffix + '.js');
8168 }
8169 });
8170
8171 // Init when que is loaded
8172 sl.loadQueue(function() {
8173 if (!t.removed)
8174 t.init();
8175 });
8176 };
8177
8178 loadScripts();
8179 },
8180
8181 init : function() {
8182 var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re;
8183
8184 EditorManager.add(t);
8185
8186 if (s.theme) {
8187 s.theme = s.theme.replace(/-/, '');
8188 o = ThemeManager.get(s.theme);
8189 t.theme = new o();
8190
8191 if (t.theme.init && s.init_theme)
8192 t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));
8193 }
8194
8195 // Create all plugins
8196 each(explode(s.plugins.replace(/\-/g, '')), function(p) {
8197 var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;
8198
8199 if (c) {
8200 po = new c(t, u);
8201
8202 t.plugins[p] = po;
8203
8204 if (po.init)
8205 po.init(t, u);
8206 }
8207 });
8208
8209 // Setup popup CSS path(s)
8210 if (s.popup_css !== false) {
8211 if (s.popup_css)
8212 s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);
8213 else
8214 s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");
8215 }
8216
8217 if (s.popup_css_add)
8218 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);
8219
8220 t.controlManager = new tinymce.ControlManager(t);
8221
8222 t.undoManager = new tinymce.UndoManager(t);
8223
8224 // Pass through
8225 t.undoManager.onAdd.add(function(um, l) {
8226 if (!l.initial)
8227 return t.onChange.dispatch(t, l, um);
8228 });
8229
8230 t.undoManager.onUndo.add(function(um, l) {
8231 return t.onUndo.dispatch(t, l, um);
8232 });
8233
8234 t.undoManager.onRedo.add(function(um, l) {
8235 return t.onRedo.dispatch(t, l, um);
8236 });
8237
8238 if (s.custom_undo_redo) {
8239 t.onExecCommand.add(function(ed, cmd, ui, val, a) {
8240 if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))
8241 t.undoManager.add();
8242 });
8243 }
8244
8245 t.onExecCommand.add(function(ed, c) {
8246 // Don't refresh the select lists until caret move
8247 if (!/^(FontName|FontSize)$/.test(c))
8248 t.nodeChanged();
8249 });
8250
8251 // Remove ghost selections on images and tables in Gecko
8252 if (isGecko) {
8253 function repaint(a, o) {
8254 if (!o || !o.initial)
8255 t.execCommand('mceRepaint');
8256 };
8257
8258 t.onUndo.add(repaint);
8259 t.onRedo.add(repaint);
8260 t.onSetContent.add(repaint);
8261 }
8262
8263 // Enables users to override the control factory
8264 t.onBeforeRenderUI.dispatch(t, t.controlManager);
8265
8266 // Measure box
8267 if (s.render_ui) {
8268 w = s.width || e.style.width || e.offsetWidth;
8269 h = s.height || e.style.height || e.offsetHeight;
8270 t.orgDisplay = e.style.display;
8271 re = /^[0-9\.]+(|px)$/i;
8272
8273 if (re.test('' + w))
8274 w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);
8275
8276 if (re.test('' + h))
8277 h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);
8278
8279 // Render UI
8280 o = t.theme.renderUI({
8281 targetNode : e,
8282 width : w,
8283 height : h,
8284 deltaWidth : s.delta_width,
8285 deltaHeight : s.delta_height
8286 });
8287
8288 t.editorContainer = o.editorContainer;
8289 }
8290
8291
8292 // User specified a document.domain value
8293 if (document.domain && location.hostname != document.domain)
8294 tinymce.relaxedDomain = document.domain;
8295
8296 // Resize editor
8297 DOM.setStyles(o.sizeContainer || o.editorContainer, {
8298 width : w,
8299 height : h
8300 });
8301
8302 h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');
8303 if (h < 100)
8304 h = 100;
8305
8306 t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">';
8307
8308 // We only need to override paths if we have to
8309 // IE has a bug where it remove site absolute urls to relative ones if this is specified
8310 if (s.document_base_url != tinymce.documentBaseURL)
8311 t.iframeHTML += '<base href="' + t.documentBaseURI.getURI() + '" />';
8312
8313 t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
8314
8315 if (tinymce.relaxedDomain)
8316 t.iframeHTML += '<script type="text/javascript">document.domain = "' + tinymce.relaxedDomain + '";</script>';
8317
8318 bi = s.body_id || 'tinymce';
8319 if (bi.indexOf('=') != -1) {
8320 bi = t.getParam('body_id', '', 'hash');
8321 bi = bi[t.id] || bi;
8322 }
8323
8324 bc = s.body_class || '';
8325 if (bc.indexOf('=') != -1) {
8326 bc = t.getParam('body_class', '', 'hash');
8327 bc = bc[t.id] || '';
8328 }
8329
8330 t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"></body></html>';
8331
8332 // Domain relaxing enabled, then set document domain
8333 if (tinymce.relaxedDomain) {
8334 // We need to write the contents here in IE since multiple writes messes up refresh button and back button
8335 if (isIE || (tinymce.isOpera && parseFloat(opera.version()) >= 9.5))
8336 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();})()';
8337 else if (tinymce.isOpera)
8338 u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";document.close();ed.setupIframe();})()';
8339 }
8340
8341 // Create iframe
8342 n = DOM.add(o.iframeContainer, 'iframe', {
8343 id : t.id + "_ifr",
8344 src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7
8345 frameBorder : '0',
8346 style : {
8347 width : '100%',
8348 height : h
8349 }
8350 });
8351
8352 t.contentAreaContainer = o.iframeContainer;
8353 DOM.get(o.editorContainer).style.display = t.orgDisplay;
8354 DOM.get(t.id).style.display = 'none';
8355
8356 if (!isIE || !tinymce.relaxedDomain)
8357 t.setupIframe();
8358
8359 e = n = o = null; // Cleanup
8360 },
8361
8362 setupIframe : function() {
8363 var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;
8364
8365 // Setup iframe body
8366 if (!isIE || !tinymce.relaxedDomain) {
8367 d.open();
8368 d.write(t.iframeHTML);
8369 d.close();
8370 }
8371
8372 // Design mode needs to be added here Ctrl+A will fail otherwise
8373 if (!isIE) {
8374 try {
8375 if (!s.readonly)
8376 d.designMode = 'On';
8377 } catch (ex) {
8378 // Will fail on Gecko if the editor is placed in an hidden container element
8379 // The design mode will be set ones the editor is focused
8380 }
8381 }
8382
8383 // IE needs to use contentEditable or it will display non secure items for HTTPS
8384 if (isIE) {
8385 // It will not steal focus if we hide it while setting contentEditable
8386 b = t.getBody();
8387 DOM.hide(b);
8388
8389 if (!s.readonly)
8390 b.contentEditable = true;
8391
8392 DOM.show(b);
8393 }
8394
8395 t.dom = new tinymce.dom.DOMUtils(t.getDoc(), {
8396 keep_values : true,
8397 url_converter : t.convertURL,
8398 url_converter_scope : t,
8399 hex_colors : s.force_hex_style_colors,
8400 class_filter : s.class_filter,
8401 update_styles : 1,
8402 fix_ie_paragraphs : 1
8403 });
8404
8405 t.serializer = new tinymce.dom.Serializer(extend(s, {
8406 valid_elements : s.verify_html === false ? '*[*]' : s.valid_elements,
8407 dom : t.dom
8408 }));
8409
8410 t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);
8411
8412 t.forceBlocks = new tinymce.ForceBlocks(t, {
8413 forced_root_block : s.forced_root_block
8414 });
8415 t.editorCommands = new tinymce.EditorCommands(t);
8416
8417 // Pass through
8418 t.serializer.onPreProcess.add(function(se, o) {
8419 return t.onPreProcess.dispatch(t, o, se);
8420 });
8421
8422 t.serializer.onPostProcess.add(function(se, o) {
8423 return t.onPostProcess.dispatch(t, o, se);
8424 });
8425
8426 t.onPreInit.dispatch(t);
8427
8428 if (!s.gecko_spellcheck)
8429 t.getBody().spellcheck = 0;
8430
8431 if (!s.readonly)
8432 t._addEvents();
8433
8434 t.controlManager.onPostRender.dispatch(t, t.controlManager);
8435 t.onPostRender.dispatch(t);
8436
8437 if (s.directionality)
8438 t.getBody().dir = s.directionality;
8439
8440 if (s.nowrap)
8441 t.getBody().style.whiteSpace = "nowrap";
8442
8443 if (s.custom_elements) {
8444 function handleCustom(ed, o) {
8445 each(explode(s.custom_elements), function(v) {
8446 var n;
8447
8448 if (v.indexOf('~') === 0) {
8449 v = v.substring(1);
8450 n = 'span';
8451 } else
8452 n = 'div';
8453
8454 o.content = o.content.replace(new RegExp('<(' + v + ')([^>]*)>', 'g'), '<' + n + ' mce_name="$1"$2>');
8455 o.content = o.content.replace(new RegExp('</(' + v + ')>', 'g'), '</' + n + '>');
8456 });
8457 };
8458
8459 t.onBeforeSetContent.add(handleCustom);
8460 t.onPostProcess.add(function(ed, o) {
8461 if (o.set)
8462 handleCustom(ed, o);
8463 });
8464 }
8465
8466 if (s.handle_node_change_callback) {
8467 t.onNodeChange.add(function(ed, cm, n) {
8468 t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());
8469 });
8470 }
8471
8472 if (s.save_callback) {
8473 t.onSaveContent.add(function(ed, o) {
8474 var h = t.execCallback('save_callback', t.id, o.content, t.getBody());
8475
8476 if (h)
8477 o.content = h;
8478 });
8479 }
8480
8481 if (s.onchange_callback) {
8482 t.onChange.add(function(ed, l) {
8483 t.execCallback('onchange_callback', t, l);
8484 });
8485 }
8486
8487 if (s.convert_newlines_to_brs) {
8488 t.onBeforeSetContent.add(function(ed, o) {
8489 if (o.initial)
8490 o.content = o.content.replace(/\r?\n/g, '<br />');
8491 });
8492 }
8493
8494 if (s.fix_nesting && isIE) {
8495 t.onBeforeSetContent.add(function(ed, o) {
8496 o.content = t._fixNesting(o.content);
8497 });
8498 }
8499
8500 if (s.preformatted) {
8501 t.onPostProcess.add(function(ed, o) {
8502 o.content = o.content.replace(/^\s*<pre.*?>/, '');
8503 o.content = o.content.replace(/<\/pre>\s*$/, '');
8504
8505 if (o.set)
8506 o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';
8507 });
8508 }
8509
8510 if (s.verify_css_classes) {
8511 t.serializer.attribValueFilter = function(n, v) {
8512 var s, cl;
8513
8514 if (n == 'class') {
8515 // Build regexp for classes
8516 if (!t.classesRE) {
8517 cl = t.dom.getClasses();
8518
8519 if (cl.length > 0) {
8520 s = '';
8521
8522 each (cl, function(o) {
8523 s += (s ? '|' : '') + o['class'];
8524 });
8525
8526 t.classesRE = new RegExp('(' + s + ')', 'gi');
8527 }
8528 }
8529
8530 return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';
8531 }
8532
8533 return v;
8534 };
8535 }
8536
8537 if (s.convert_fonts_to_spans)
8538 t._convertFonts();
8539
8540 if (s.inline_styles)
8541 t._convertInlineElements();
8542
8543 if (s.cleanup_callback) {
8544 t.onBeforeSetContent.add(function(ed, o) {
8545 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
8546 });
8547
8548 t.onPreProcess.add(function(ed, o) {
8549 if (o.set)
8550 t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);
8551
8552 if (o.get)
8553 t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);
8554 });
8555
8556 t.onPostProcess.add(function(ed, o) {
8557 if (o.set)
8558 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
8559
8560 if (o.get)
8561 o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);
8562 });
8563 }
8564
8565 if (s.save_callback) {
8566 t.onGetContent.add(function(ed, o) {
8567 if (o.save)
8568 o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());
8569 });
8570 }
8571
8572 if (s.handle_event_callback) {
8573 t.onEvent.add(function(ed, e, o) {
8574 if (t.execCallback('handle_event_callback', e, ed, o) === false)
8575 Event.cancel(e);
8576 });
8577 }
8578
8579 // Add visual aids when new contents is added
8580 t.onSetContent.add(function() {
8581 t.addVisual(t.getBody());
8582 });
8583
8584 // Remove empty contents
8585 if (s.padd_empty_editor) {
8586 t.onPostProcess.add(function(ed, o) {
8587 o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');
8588 });
8589 }
8590
8591 if (isGecko) {
8592 // Fix gecko link bug, when a link is placed at the end of block elements there is
8593 // no way to move the caret behind the link. This fix adds a bogus br element after the link
8594 function fixLinks(ed, o) {
8595 each(ed.dom.select('a'), function(n) {
8596 var pn = n.parentNode;
8597
8598 if (ed.dom.isBlock(pn) && pn.lastChild === n)
8599 ed.dom.add(pn, 'br', {'mce_bogus' : 1});
8600 });
8601 };
8602
8603 t.onExecCommand.add(function(ed, cmd) {
8604 if (cmd === 'CreateLink')
8605 fixLinks(ed);
8606 });
8607
8608 t.onSetContent.add(t.selection.onSetContent.add(fixLinks));
8609
8610 if (!s.readonly) {
8611 try {
8612 // Design mode must be set here once again to fix a bug where
8613 // Ctrl+A/Delete/Backspace didn't work if the editor was added using mceAddControl then removed then added again
8614 d.designMode = 'Off';
8615 d.designMode = 'On';
8616 } catch (ex) {
8617 // Will fail on Gecko if the editor is placed in an hidden container element
8618 // The design mode will be set ones the editor is focused
8619 }
8620 }
8621 }
8622
8623 // A small timeout was needed since firefox will remove. Bug: #1838304
8624 setTimeout(function () {
8625 if (t.removed)
8626 return;
8627
8628 t.load({initial : true, format : (s.cleanup_on_startup ? 'html' : 'raw')});
8629 t.startContent = t.getContent({format : 'raw'});
8630 t.undoManager.add({initial : true});
8631 t.initialized = true;
8632
8633 t.onInit.dispatch(t);
8634 t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());
8635 t.execCallback('init_instance_callback', t);
8636 t.focus(true);
8637 t.nodeChanged({initial : 1});
8638
8639 // Load specified content CSS last
8640 if (s.content_css) {
8641 tinymce.each(explode(s.content_css), function(u) {
8642 t.dom.loadCSS(t.documentBaseURI.toAbsolute(u));
8643 });
8644 }
8645
8646 // Handle auto focus
8647 if (s.auto_focus) {
8648 setTimeout(function () {
8649 var ed = EditorManager.get(s.auto_focus);
8650
8651 ed.selection.select(ed.getBody(), 1);
8652 ed.selection.collapse(1);
8653 ed.getWin().focus();
8654 }, 100);
8655 }
8656 }, 1);
8657
8658 e = null;
8659 },
8660
8661
8662 focus : function(sf) {
8663 var oed, t = this, ce = t.settings.content_editable;
8664
8665 if (!sf) {
8666 // Is not content editable or the selection is outside the area in IE
8667 // the IE statement is needed to avoid bluring if element selections inside layers since
8668 // the layer is like it's own document in IE
8669 if (!ce && (!isIE || t.selection.getNode().ownerDocument != t.getDoc()))
8670 t.getWin().focus();
8671
8672 }
8673
8674 if (EditorManager.activeEditor != t) {
8675 if ((oed = EditorManager.activeEditor) != null)
8676 oed.onDeactivate.dispatch(oed, t);
8677
8678 t.onActivate.dispatch(t, oed);
8679 }
8680
8681 EditorManager._setActive(t);
8682 },
8683
8684 execCallback : function(n) {
8685 var t = this, f = t.settings[n], s;
8686
8687 if (!f)
8688 return;
8689
8690 // Look through lookup
8691 if (t.callbackLookup && (s = t.callbackLookup[n])) {
8692 f = s.func;
8693 s = s.scope;
8694 }
8695
8696 if (is(f, 'string')) {
8697 s = f.replace(/\.\w+$/, '');
8698 s = s ? tinymce.resolve(s) : 0;
8699 f = tinymce.resolve(f);
8700 t.callbackLookup = t.callbackLookup || {};
8701 t.callbackLookup[n] = {func : f, scope : s};
8702 }
8703
8704 return f.apply(s || t, Array.prototype.slice.call(arguments, 1));
8705 },
8706
8707 translate : function(s) {
8708 var c = this.settings.language || 'en', i18n = EditorManager.i18n;
8709
8710 if (!s)
8711 return '';
8712
8713 return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {
8714 return i18n[c + '.' + b] || '{#' + b + '}';
8715 });
8716 },
8717
8718 getLang : function(n, dv) {
8719 return EditorManager.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}');
8720 },
8721
8722 getParam : function(n, dv, ty) {
8723 var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;
8724
8725 if (ty === 'hash') {
8726 o = {};
8727
8728 if (is(v, 'string')) {
8729 each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {
8730 v = v.split('=');
8731
8732 if (v.length > 1)
8733 o[tr(v[0])] = tr(v[1]);
8734 else
8735 o[tr(v[0])] = tr(v);
8736 });
8737 } else
8738 o = v;
8739
8740 return o;
8741 }
8742
8743 return v;
8744 },
8745
8746 nodeChanged : function(o) {
8747 var t = this, s = t.selection, n = s.getNode() || t.getBody();
8748
8749 // Fix for bug #1896577 it seems that this can not be fired while the editor is loading
8750 if (t.initialized) {
8751 t.onNodeChange.dispatch(
8752 t,
8753 o ? o.controlManager || t.controlManager : t.controlManager,
8754 isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n, // Fix for IE initial state
8755 s.isCollapsed(),
8756 o
8757 );
8758 }
8759 },
8760
8761 addButton : function(n, s) {
8762 var t = this;
8763
8764 t.buttons = t.buttons || {};
8765 t.buttons[n] = s;
8766 },
8767
8768 addCommand : function(n, f, s) {
8769 this.execCommands[n] = {func : f, scope : s || this};
8770 },
8771
8772 addQueryStateHandler : function(n, f, s) {
8773 this.queryStateCommands[n] = {func : f, scope : s || this};
8774 },
8775
8776 addQueryValueHandler : function(n, f, s) {
8777 this.queryValueCommands[n] = {func : f, scope : s || this};
8778 },
8779
8780 addShortcut : function(pa, desc, cmd_func, sc) {
8781 var t = this, c;
8782
8783 if (!t.settings.custom_shortcuts)
8784 return false;
8785
8786 t.shortcuts = t.shortcuts || {};
8787
8788 if (is(cmd_func, 'string')) {
8789 c = cmd_func;
8790
8791 cmd_func = function() {
8792 t.execCommand(c, false, null);
8793 };
8794 }
8795
8796 if (is(cmd_func, 'object')) {
8797 c = cmd_func;
8798
8799 cmd_func = function() {
8800 t.execCommand(c[0], c[1], c[2]);
8801 };
8802 }
8803
8804 each(explode(pa), function(pa) {
8805 var o = {
8806 func : cmd_func,
8807 scope : sc || this,
8808 desc : desc,
8809 alt : false,
8810 ctrl : false,
8811 shift : false
8812 };
8813
8814 each(explode(pa, '+'), function(v) {
8815 switch (v) {
8816 case 'alt':
8817 case 'ctrl':
8818 case 'shift':
8819 o[v] = true;
8820 break;
8821
8822 default:
8823 o.charCode = v.charCodeAt(0);
8824 o.keyCode = v.toUpperCase().charCodeAt(0);
8825 }
8826 });
8827
8828 t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;
8829 });
8830
8831 return true;
8832 },
8833
8834 execCommand : function(cmd, ui, val, a) {
8835 var t = this, s = 0, o, st;
8836
8837 if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))
8838 t.focus();
8839
8840 o = {};
8841 t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);
8842 if (o.terminate)
8843 return false;
8844
8845 // Command callback
8846 if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {
8847 t.onExecCommand.dispatch(t, cmd, ui, val, a);
8848 return true;
8849 }
8850
8851 // Registred commands
8852 if (o = t.execCommands[cmd]) {
8853 st = o.func.call(o.scope, ui, val);
8854
8855 // Fall through on true
8856 if (st !== true) {
8857 t.onExecCommand.dispatch(t, cmd, ui, val, a);
8858 return st;
8859 }
8860 }
8861
8862 // Plugin commands
8863 each(t.plugins, function(p) {
8864 if (p.execCommand && p.execCommand(cmd, ui, val)) {
8865 t.onExecCommand.dispatch(t, cmd, ui, val, a);
8866 s = 1;
8867 return false;
8868 }
8869 });
8870
8871 if (s)
8872 return true;
8873
8874 // Theme commands
8875 if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {
8876 t.onExecCommand.dispatch(t, cmd, ui, val, a);
8877 return true;
8878 }
8879
8880 // Execute global commands
8881 if (tinymce.GlobalCommands.execCommand(t, cmd, ui, val)) {
8882 t.onExecCommand.dispatch(t, cmd, ui, val, a);
8883 return true;
8884 }
8885
8886 // Editor commands
8887 if (t.editorCommands.execCommand(cmd, ui, val)) {
8888 t.onExecCommand.dispatch(t, cmd, ui, val, a);
8889 return true;
8890 }
8891
8892 // Browser commands
8893 t.getDoc().execCommand(cmd, ui, val);
8894 t.onExecCommand.dispatch(t, cmd, ui, val, a);
8895 },
8896
8897 queryCommandState : function(c) {
8898 var t = this, o, s;
8899
8900 // Is hidden then return undefined
8901 if (t._isHidden())
8902 return;
8903
8904 // Registred commands
8905 if (o = t.queryStateCommands[c]) {
8906 s = o.func.call(o.scope);
8907
8908 // Fall though on true
8909 if (s !== true)
8910 return s;
8911 }
8912
8913 // Registred commands
8914 o = t.editorCommands.queryCommandState(c);
8915 if (o !== -1)
8916 return o;
8917
8918 // Browser commands
8919 try {
8920 return this.getDoc().queryCommandState(c);
8921 } catch (ex) {
8922 // Fails sometimes see bug: 1896577
8923 }
8924 },
8925
8926 queryCommandValue : function(c) {
8927 var t = this, o, s;
8928
8929 // Is hidden then return undefined
8930 if (t._isHidden())
8931 return;
8932
8933 // Registred commands
8934 if (o = t.queryValueCommands[c]) {
8935 s = o.func.call(o.scope);
8936
8937 // Fall though on true
8938 if (s !== true)
8939 return s;
8940 }
8941
8942 // Registred commands
8943 o = t.editorCommands.queryCommandValue(c);
8944 if (is(o))
8945 return o;
8946
8947 // Browser commands
8948 try {
8949 return this.getDoc().queryCommandValue(c);
8950 } catch (ex) {
8951 // Fails sometimes see bug: 1896577
8952 }
8953 },
8954
8955 show : function() {
8956 var t = this;
8957
8958 DOM.show(t.getContainer());
8959 DOM.hide(t.id);
8960 t.load();
8961 },
8962
8963 hide : function() {
8964 var t = this, d = t.getDoc();
8965
8966 // Fixed bug where IE has a blinking cursor left from the editor
8967 if (isIE && d)
8968 d.execCommand('SelectAll');
8969
8970 // We must save before we hide so Safari doesn't crash
8971 t.save();
8972 DOM.hide(t.getContainer());
8973 DOM.setStyle(t.id, 'display', t.orgDisplay);
8974 },
8975
8976 isHidden : function() {
8977 return !DOM.isHidden(this.id);
8978 },
8979
8980 setProgressState : function(b, ti, o) {
8981 this.onSetProgressState.dispatch(this, b, ti, o);
8982
8983 return b;
8984 },
8985
8986 load : function(o) {
8987 var t = this, e = t.getElement(), h;
8988
8989 if (e) {
8990 o = o || {};
8991 o.load = true;
8992
8993 // Double encode existing entities in the value
8994 h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);
8995 o.element = e;
8996
8997 if (!o.no_events)
8998 t.onLoadContent.dispatch(t, o);
8999
9000 o.element = e = null;
9001
9002 return h;
9003 }
9004 },
9005
9006 save : function(o) {
9007 var t = this, e = t.getElement(), h, f;
9008
9009 if (!e || !t.initialized)
9010 return;
9011
9012 o = o || {};
9013 o.save = true;
9014
9015 // Add undo level will trigger onchange event
9016 if (!o.no_events) {
9017 t.undoManager.typing = 0;
9018 t.undoManager.add();
9019 }
9020
9021 o.element = e;
9022 h = o.content = t.getContent(o);
9023
9024 if (!o.no_events)
9025 t.onSaveContent.dispatch(t, o);
9026
9027 h = o.content;
9028
9029 if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {
9030 e.innerHTML = h;
9031
9032 // Update hidden form element
9033 if (f = DOM.getParent(t.id, 'form')) {
9034 each(f.elements, function(e) {
9035 if (e.name == t.id) {
9036 e.value = h;
9037 return false;
9038 }
9039 });
9040 }
9041 } else
9042 e.value = h;
9043
9044 o.element = e = null;
9045
9046 return h;
9047 },
9048
9049 setContent : function(h, o) {
9050 var t = this;
9051
9052 o = o || {};
9053 o.format = o.format || 'html';
9054 o.set = true;
9055 o.content = h;
9056
9057 if (!o.no_events)
9058 t.onBeforeSetContent.dispatch(t, o);
9059
9060 // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
9061 // It will also be impossible to place the caret in the editor unless there is a BR element present
9062 if (!tinymce.isIE && (h.length === 0 || /^\s+$/.test(h))) {
9063 o.content = t.dom.setHTML(t.getBody(), '<br mce_bogus="1" />');
9064 o.format = 'raw';
9065 }
9066
9067 o.content = t.dom.setHTML(t.getBody(), tinymce.trim(o.content));
9068
9069 if (o.format != 'raw' && t.settings.cleanup) {
9070 o.getInner = true;
9071 o.content = t.dom.setHTML(t.getBody(), t.serializer.serialize(t.getBody(), o));
9072 }
9073
9074 if (!o.no_events)
9075 t.onSetContent.dispatch(t, o);
9076
9077 return o.content;
9078 },
9079
9080 getContent : function(o) {
9081 var t = this, h;
9082
9083 o = o || {};
9084 o.format = o.format || 'html';
9085 o.get = true;
9086
9087 if (!o.no_events)
9088 t.onBeforeGetContent.dispatch(t, o);
9089
9090 if (o.format != 'raw' && t.settings.cleanup) {
9091 o.getInner = true;
9092 h = t.serializer.serialize(t.getBody(), o);
9093 } else
9094 h = t.getBody().innerHTML;
9095
9096 h = h.replace(/^\s*|\s*$/g, '');
9097 o.content = h;
9098
9099 if (!o.no_events)
9100 t.onGetContent.dispatch(t, o);
9101
9102 return o.content;
9103 },
9104
9105 isDirty : function() {
9106 var t = this;
9107
9108 return tinymce.trim(t.startContent) != tinymce.trim(t.getContent({format : 'raw', no_events : 1})) && !t.isNotDirty;
9109 },
9110
9111 getContainer : function() {
9112 var t = this;
9113
9114 if (!t.container)
9115 t.container = DOM.get(t.editorContainer || t.id + '_parent');
9116
9117 return t.container;
9118 },
9119
9120 getContentAreaContainer : function() {
9121 return this.contentAreaContainer;
9122 },
9123
9124 getElement : function() {
9125 return DOM.get(this.settings.content_element || this.id);
9126 },
9127
9128 getWin : function() {
9129 var t = this, e;
9130
9131 if (!t.contentWindow) {
9132 e = DOM.get(t.id + "_ifr");
9133
9134 if (e)
9135 t.contentWindow = e.contentWindow;
9136 }
9137
9138 return t.contentWindow;
9139 },
9140
9141 getDoc : function() {
9142 var t = this, w;
9143
9144 if (!t.contentDocument) {
9145 w = t.getWin();
9146
9147 if (w)
9148 t.contentDocument = w.document;
9149 }
9150
9151 return t.contentDocument;
9152 },
9153
9154 getBody : function() {
9155 return this.bodyElement || this.getDoc().body;
9156 },
9157
9158 convertURL : function(u, n, e) {
9159 var t = this, s = t.settings;
9160
9161 // Use callback instead
9162 if (s.urlconverter_callback)
9163 return t.execCallback('urlconverter_callback', u, e, true, n);
9164
9165 // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs
9166 if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)
9167 return u;
9168
9169 // Convert to relative
9170 if (s.relative_urls)
9171 return t.documentBaseURI.toRelative(u);
9172
9173 // Convert to absolute
9174 u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);
9175
9176 return u;
9177 },
9178
9179 addVisual : function(e) {
9180 var t = this, s = t.settings;
9181
9182 e = e || t.getBody();
9183
9184 if (!is(t.hasVisual))
9185 t.hasVisual = s.visual;
9186
9187 each(t.dom.select('table,a', e), function(e) {
9188 var v;
9189
9190 switch (e.nodeName) {
9191 case 'TABLE':
9192 v = t.dom.getAttrib(e, 'border');
9193
9194 if (!v || v == '0') {
9195 if (t.hasVisual)
9196 t.dom.addClass(e, s.visual_table_class);
9197 else
9198 t.dom.removeClass(e, s.visual_table_class);
9199 }
9200
9201 return;
9202
9203 case 'A':
9204 v = t.dom.getAttrib(e, 'name');
9205
9206 if (v) {
9207 if (t.hasVisual)
9208 t.dom.addClass(e, 'mceItemAnchor');
9209 else
9210 t.dom.removeClass(e, 'mceItemAnchor');
9211 }
9212
9213 return;
9214 }
9215 });
9216
9217 t.onVisualAid.dispatch(t, e, t.hasVisual);
9218 },
9219
9220 remove : function() {
9221 var t = this, e = t.getContainer();
9222
9223 t.removed = 1; // Cancels post remove event execution
9224 t.hide();
9225
9226 t.execCallback('remove_instance_callback', t);
9227 t.onRemove.dispatch(t);
9228
9229 // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command
9230 t.onExecCommand.listeners = [];
9231
9232 EditorManager.remove(t);
9233 DOM.remove(e);
9234 },
9235
9236 destroy : function(s) {
9237 var t = this;
9238
9239 // One time is enough
9240 if (t.destroyed)
9241 return;
9242
9243 if (!s) {
9244 tinymce.removeUnload(t.destroy);
9245 tinyMCE.onBeforeUnload.remove(t._beforeUnload);
9246
9247 // Manual destroy
9248 if (t.theme && t.theme.destroy)
9249 t.theme.destroy();
9250
9251 // Destroy controls, selection and dom
9252 t.controlManager.destroy();
9253 t.selection.destroy();
9254 t.dom.destroy();
9255
9256 // Remove all events
9257
9258 // Don't clear the window or document if content editable
9259 // is enabled since other instances might still be present
9260 if (!t.settings.content_editable) {
9261 Event.clear(t.getWin());
9262 Event.clear(t.getDoc());
9263 }
9264
9265 Event.clear(t.getBody());
9266 Event.clear(t.formElement);
9267 }
9268
9269 if (t.formElement) {
9270 t.formElement.submit = t.formElement._mceOldSubmit;
9271 t.formElement._mceOldSubmit = null;
9272 }
9273
9274 t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;
9275
9276 if (t.selection)
9277 t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;
9278
9279 t.destroyed = 1;
9280 },
9281
9282 // Internal functions
9283
9284 _addEvents : function() {
9285 // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset
9286 var t = this, i, s = t.settings, lo = {
9287 mouseup : 'onMouseUp',
9288 mousedown : 'onMouseDown',
9289 click : 'onClick',
9290 keyup : 'onKeyUp',
9291 keydown : 'onKeyDown',
9292 keypress : 'onKeyPress',
9293 submit : 'onSubmit',
9294 reset : 'onReset',
9295 contextmenu : 'onContextMenu',
9296 dblclick : 'onDblClick',
9297 paste : 'onPaste' // Doesn't work in all browsers yet
9298 };
9299
9300 function eventHandler(e, o) {
9301 var ty = e.type;
9302
9303 // Don't fire events when it's removed
9304 if (t.removed)
9305 return;
9306
9307 // Generic event handler
9308 if (t.onEvent.dispatch(t, e, o) !== false) {
9309 // Specific event handler
9310 t[lo[e.fakeType || e.type]].dispatch(t, e, o);
9311 }
9312 };
9313
9314 // Add DOM events
9315 each(lo, function(v, k) {
9316 switch (k) {
9317 case 'contextmenu':
9318 if (tinymce.isOpera) {
9319 // Fake contextmenu on Opera
9320 t.dom.bind(t.getBody(), 'mousedown', function(e) {
9321 if (e.ctrlKey) {
9322 e.fakeType = 'contextmenu';
9323 eventHandler(e);
9324 }
9325 });
9326 } else
9327 t.dom.bind(t.getBody(), k, eventHandler);
9328 break;
9329
9330 case 'paste':
9331 t.dom.bind(t.getBody(), k, function(e) {
9332 eventHandler(e);
9333 });
9334 break;
9335
9336 case 'submit':
9337 case 'reset':
9338 t.dom.bind(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);
9339 break;
9340
9341 default:
9342 t.dom.bind(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler);
9343 }
9344 });
9345
9346 t.dom.bind(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) {
9347 t.focus(true);
9348 });
9349
9350
9351 // Fixes bug where a specified document_base_uri could result in broken images
9352 // This will also fix drag drop of images in Gecko
9353 if (tinymce.isGecko) {
9354 // Convert all images to absolute URLs
9355/* t.onSetContent.add(function(ed, o) {
9356 each(ed.dom.select('img'), function(e) {
9357 var v;
9358
9359 if (v = e.getAttribute('mce_src'))
9360 e.src = t.documentBaseURI.toAbsolute(v);
9361 })
9362 });*/
9363
9364 t.dom.bind(t.getDoc(), 'DOMNodeInserted', function(e) {
9365 var v;
9366
9367 e = e.target;
9368
9369 if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('mce_src')))
9370 e.src = t.documentBaseURI.toAbsolute(v);
9371 });
9372 }
9373
9374 // Set various midas options in Gecko
9375 if (isGecko) {
9376 function setOpts() {
9377 var t = this, d = t.getDoc(), s = t.settings;
9378
9379 if (isGecko && !s.readonly) {
9380 if (t._isHidden()) {
9381 try {
9382 if (!s.content_editable)
9383 d.designMode = 'On';
9384 } catch (ex) {
9385 // Fails if it's hidden
9386 }
9387 }
9388
9389 try {
9390 // Try new Gecko method
9391 d.execCommand("styleWithCSS", 0, false);
9392 } catch (ex) {
9393 // Use old method
9394 if (!t._isHidden())
9395 try {d.execCommand("useCSS", 0, true);} catch (ex) {}
9396 }
9397
9398 if (!s.table_inline_editing)
9399 try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {}
9400
9401 if (!s.object_resizing)
9402 try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {}
9403 }
9404 };
9405
9406 t.onBeforeExecCommand.add(setOpts);
9407 t.onMouseDown.add(setOpts);
9408 }
9409
9410 // Add node change handlers
9411 t.onMouseUp.add(t.nodeChanged);
9412 t.onClick.add(t.nodeChanged);
9413 t.onKeyUp.add(function(ed, e) {
9414 var c = e.keyCode;
9415
9416 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)
9417 t.nodeChanged();
9418 });
9419
9420 // Add reset handler
9421 t.onReset.add(function() {
9422 t.setContent(t.startContent, {format : 'raw'});
9423 });
9424
9425 // Add shortcuts
9426 if (s.custom_shortcuts) {
9427 if (s.custom_undo_redo_keyboard_shortcuts) {
9428 t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo');
9429 t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo');
9430 }
9431
9432 // Add default shortcuts for gecko
9433 if (isGecko) {
9434 t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');
9435 t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');
9436 t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');
9437 }
9438
9439 // BlockFormat shortcuts keys
9440 for (i=1; i<=6; i++)
9441 t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, '<h' + i + '>']);
9442
9443 t.addShortcut('ctrl+7', '', ['FormatBlock', false, '<p>']);
9444 t.addShortcut('ctrl+8', '', ['FormatBlock', false, '<div>']);
9445 t.addShortcut('ctrl+9', '', ['FormatBlock', false, '<address>']);
9446
9447 function find(e) {
9448 var v = null;
9449
9450 if (!e.altKey && !e.ctrlKey && !e.metaKey)
9451 return v;
9452
9453 each(t.shortcuts, function(o) {
9454 if (tinymce.isMac && o.ctrl != e.metaKey)
9455 return;
9456 else if (!tinymce.isMac && o.ctrl != e.ctrlKey)
9457 return;
9458
9459 if (o.alt != e.altKey)
9460 return;
9461
9462 if (o.shift != e.shiftKey)
9463 return;
9464
9465 if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {
9466 v = o;
9467 return false;
9468 }
9469 });
9470
9471 return v;
9472 };
9473
9474 t.onKeyUp.add(function(ed, e) {
9475 var o = find(e);
9476
9477 if (o)
9478 return Event.cancel(e);
9479 });
9480
9481 t.onKeyPress.add(function(ed, e) {
9482 var o = find(e);
9483
9484 if (o)
9485 return Event.cancel(e);
9486 });
9487
9488 t.onKeyDown.add(function(ed, e) {
9489 var o = find(e);
9490
9491 if (o) {
9492 o.func.call(o.scope);
9493 return Event.cancel(e);
9494 }
9495 });
9496 }
9497
9498 if (tinymce.isIE) {
9499 // Fix so resize will only update the width and height attributes not the styles of an image
9500 // It will also block mceItemNoResize items
9501 t.dom.bind(t.getDoc(), 'controlselect', function(e) {
9502 var re = t.resizeInfo, cb;
9503
9504 e = e.target;
9505
9506 // Don't do this action for non image elements
9507 if (e.nodeName !== 'IMG')
9508 return;
9509
9510 if (re)
9511 t.dom.unbind(re.node, re.ev, re.cb);
9512
9513 if (!t.dom.hasClass(e, 'mceItemNoResize')) {
9514 ev = 'resizeend';
9515 cb = t.dom.bind(e, ev, function(e) {
9516 var v;
9517
9518 e = e.target;
9519
9520 if (v = t.dom.getStyle(e, 'width')) {
9521 t.dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, ''));
9522 t.dom.setStyle(e, 'width', '');
9523 }
9524
9525 if (v = t.dom.getStyle(e, 'height')) {
9526 t.dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, ''));
9527 t.dom.setStyle(e, 'height', '');
9528 }
9529 });
9530 } else {
9531 ev = 'resizestart';
9532 cb = t.dom.bind(e, 'resizestart', Event.cancel, Event);
9533 }
9534
9535 re = t.resizeInfo = {
9536 node : e,
9537 ev : ev,
9538 cb : cb
9539 };
9540 });
9541
9542 t.onKeyDown.add(function(ed, e) {
9543 switch (e.keyCode) {
9544 case 8:
9545 // Fix IE control + backspace browser bug
9546 if (t.selection.getRng().item) {
9547 t.selection.getRng().item(0).removeNode();
9548 return Event.cancel(e);
9549 }
9550 }
9551 });
9552
9553 /*if (t.dom.boxModel) {
9554 t.getBody().style.height = '100%';
9555
9556 Event.add(t.getWin(), 'resize', function(e) {
9557 var docElm = t.getDoc().documentElement;
9558
9559 docElm.style.height = (docElm.offsetHeight - 10) + 'px';
9560 });
9561 }*/
9562 }
9563
9564 if (tinymce.isOpera) {
9565 t.onClick.add(function(ed, e) {
9566 Event.prevent(e);
9567 });
9568 }
9569
9570 // Add custom undo/redo handlers
9571 if (s.custom_undo_redo) {
9572 function addUndo() {
9573 t.undoManager.typing = 0;
9574 t.undoManager.add();
9575 };
9576
9577 // Add undo level on editor blur
9578 if (tinymce.isIE) {
9579 t.dom.bind(t.getWin(), 'blur', function(e) {
9580 var n;
9581
9582 // Check added for fullscreen bug
9583 if (t.selection) {
9584 n = t.selection.getNode();
9585
9586 // Add undo level is selection was lost to another document
9587 if (!t.removed && n.ownerDocument && n.ownerDocument != t.getDoc())
9588 addUndo();
9589 }
9590 });
9591 } else {
9592 t.dom.bind(t.getDoc(), 'blur', function() {
9593 if (t.selection && !t.removed)
9594 addUndo();
9595 });
9596 }
9597
9598 t.onMouseDown.add(addUndo);
9599
9600 t.onKeyUp.add(function(ed, e) {
9601 if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45 || e.ctrlKey) {
9602 t.undoManager.typing = 0;
9603 t.undoManager.add();
9604 }
9605 });
9606
9607 t.onKeyDown.add(function(ed, e) {
9608 // Is caracter positon keys
9609 if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) {
9610 if (t.undoManager.typing) {
9611 t.undoManager.add();
9612 t.undoManager.typing = 0;
9613 }
9614
9615 return;
9616 }
9617
9618 if (!t.undoManager.typing) {
9619 t.undoManager.add();
9620 t.undoManager.typing = 1;
9621 }
9622 });
9623 }
9624 },
9625
9626 _convertInlineElements : function() {
9627 var t = this, s = t.settings, dom = t.dom, v, e, na, st, sp;
9628
9629 function convert(ed, o) {
9630 if (!s.inline_styles)
9631 return;
9632
9633 if (o.get) {
9634 each(t.dom.select('table,u,strike', o.node), function(n) {
9635 switch (n.nodeName) {
9636 case 'TABLE':
9637 if (v = dom.getAttrib(n, 'height')) {
9638 dom.setStyle(n, 'height', v);
9639 dom.setAttrib(n, 'height', '');
9640 }
9641 break;
9642
9643 case 'U':
9644 case 'STRIKE':
9645 //sp = dom.create('span', {style : dom.getAttrib(n, 'style')});
9646 n.style.textDecoration = n.nodeName == 'U' ? 'underline' : 'line-through';
9647 dom.setAttrib(n, 'mce_style', '');
9648 dom.setAttrib(n, 'mce_name', 'span');
9649 break;
9650 }
9651 });
9652 } else if (o.set) {
9653 each(t.dom.select('table,span', o.node).reverse(), function(n) {
9654 if (n.nodeName == 'TABLE') {
9655 if (v = dom.getStyle(n, 'height'))
9656 dom.setAttrib(n, 'height', v.replace(/[^0-9%]+/g, ''));
9657 } else {
9658 // Convert spans to elements
9659 if (n.style.textDecoration == 'underline')
9660 na = 'u';
9661 else if (n.style.textDecoration == 'line-through')
9662 na = 'strike';
9663 else
9664 na = '';
9665
9666 if (na) {
9667 n.style.textDecoration = '';
9668 dom.setAttrib(n, 'mce_style', '');
9669
9670 e = dom.create(na, {
9671 style : dom.getAttrib(n, 'style')
9672 });
9673
9674 dom.replace(e, n, 1);
9675 }
9676 }
9677 });
9678 }
9679 };
9680
9681 t.onPreProcess.add(convert);
9682
9683 if (!s.cleanup_on_startup) {
9684 t.onSetContent.add(function(ed, o) {
9685 if (o.initial)
9686 convert(t, {node : t.getBody(), set : 1});
9687 });
9688 }
9689 },
9690
9691 _convertFonts : function() {
9692 var t = this, s = t.settings, dom = t.dom, fz, fzn, sl, cl;
9693
9694 // No need
9695 if (!s.inline_styles)
9696 return;
9697
9698 // Font pt values and font size names
9699 fz = [8, 10, 12, 14, 18, 24, 36];
9700 fzn = ['xx-small', 'x-small','small','medium','large','x-large', 'xx-large'];
9701
9702 if (sl = s.font_size_style_values)
9703 sl = explode(sl);
9704
9705 if (cl = s.font_size_classes)
9706 cl = explode(cl);
9707
9708 function process(no) {
9709 var n, sp, nl, x;
9710
9711 // Keep unit tests happy
9712 if (!s.inline_styles)
9713 return;
9714
9715 nl = t.dom.select('font', no);
9716 for (x = nl.length - 1; x >= 0; x--) {
9717 n = nl[x];
9718
9719 sp = dom.create('span', {
9720 style : dom.getAttrib(n, 'style'),
9721 'class' : dom.getAttrib(n, 'class')
9722 });
9723
9724 dom.setStyles(sp, {
9725 fontFamily : dom.getAttrib(n, 'face'),
9726 color : dom.getAttrib(n, 'color'),
9727 backgroundColor : n.style.backgroundColor
9728 });
9729
9730 if (n.size) {
9731 if (sl)
9732 dom.setStyle(sp, 'fontSize', sl[parseInt(n.size) - 1]);
9733 else
9734 dom.setAttrib(sp, 'class', cl[parseInt(n.size) - 1]);
9735 }
9736
9737 dom.setAttrib(sp, 'mce_style', '');
9738 dom.replace(sp, n, 1);
9739 }
9740 };
9741
9742 // Run on cleanup
9743 t.onPreProcess.add(function(ed, o) {
9744 if (o.get)
9745 process(o.node);
9746 });
9747
9748 t.onSetContent.add(function(ed, o) {
9749 if (o.initial)
9750 process(o.node);
9751 });
9752 },
9753
9754 _isHidden : function() {
9755 var s;
9756
9757 if (!isGecko)
9758 return 0;
9759
9760 // Weird, wheres that cursor selection?
9761 s = this.selection.getSel();
9762 return (!s || !s.rangeCount || s.rangeCount == 0);
9763 },
9764
9765 // Fix for bug #1867292
9766 _fixNesting : function(s) {
9767 var d = [], i;
9768
9769 s = s.replace(/<(\/)?([^\s>]+)[^>]*?>/g, function(a, b, c) {
9770 var e;
9771
9772 // Handle end element
9773 if (b === '/') {
9774 if (!d.length)
9775 return '';
9776
9777 if (c !== d[d.length - 1].tag) {
9778 for (i=d.length - 1; i>=0; i--) {
9779 if (d[i].tag === c) {
9780 d[i].close = 1;
9781 break;
9782 }
9783 }
9784
9785 return '';
9786 } else {
9787 d.pop();
9788
9789 if (d.length && d[d.length - 1].close) {
9790 a = a + '</' + d[d.length - 1].tag + '>';
9791 d.pop();
9792 }
9793 }
9794 } else {
9795 // Ignore these
9796 if (/^(br|hr|input|meta|img|link|param)$/i.test(c))
9797 return a;
9798
9799 // Ignore closed ones
9800 if (/\/>$/.test(a))
9801 return a;
9802
9803 d.push({tag : c}); // Push start element
9804 }
9805
9806 return a;
9807 });
9808
9809 // End all open tags
9810 for (i=d.length - 1; i>=0; i--)
9811 s += '</' + d[i].tag + '>';
9812
9813 return s;
9814 }
9815 });
9816})(tinymce);
9817(function(tinymce) {
9818 var each = tinymce.each, isIE = tinymce.isIE, isGecko = tinymce.isGecko, isOpera = tinymce.isOpera, isWebKit = tinymce.isWebKit;
9819
9820 tinymce.create('tinymce.EditorCommands', {
9821 EditorCommands : function(ed) {
9822 this.editor = ed;
9823 },
9824
9825 execCommand : function(cmd, ui, val) {
9826 var t = this, ed = t.editor, f;
9827
9828 switch (cmd) {
9829 // Ignore these
9830 case 'mceResetDesignMode':
9831 case 'mceBeginUndoLevel':
9832 return true;
9833
9834 // Ignore these
9835 case 'unlink':
9836 t.UnLink();
9837 return true;
9838
9839 // Bundle these together
9840 case 'JustifyLeft':
9841 case 'JustifyCenter':
9842 case 'JustifyRight':
9843 case 'JustifyFull':
9844 t.mceJustify(cmd, cmd.substring(7).toLowerCase());
9845 return true;
9846
9847 default:
9848 f = this[cmd];
9849
9850 if (f) {
9851 f.call(this, ui, val);
9852 return true;
9853 }
9854 }
9855
9856 return false;
9857 },
9858
9859 Indent : function() {
9860 var ed = this.editor, d = ed.dom, s = ed.selection, e, iv, iu;
9861
9862 // Setup indent level
9863 iv = ed.settings.indentation;
9864 iu = /[a-z%]+$/i.exec(iv);
9865 iv = parseInt(iv);
9866
9867 if (ed.settings.inline_styles && (!this.queryStateInsertUnorderedList() && !this.queryStateInsertOrderedList())) {
9868 each(s.getSelectedBlocks(), function(e) {
9869 d.setStyle(e, 'paddingLeft', (parseInt(e.style.paddingLeft || 0) + iv) + iu);
9870 });
9871
9872 return;
9873 }
9874
9875 ed.getDoc().execCommand('Indent', false, null);
9876
9877 if (isIE) {
9878 d.getParent(s.getNode(), function(n) {
9879 if (n.nodeName == 'BLOCKQUOTE') {
9880 n.dir = n.style.cssText = '';
9881 }
9882 });
9883 }
9884 },
9885
9886 Outdent : function() {
9887 var ed = this.editor, d = ed.dom, s = ed.selection, e, v, iv, iu;
9888
9889 // Setup indent level
9890 iv = ed.settings.indentation;
9891 iu = /[a-z%]+$/i.exec(iv);
9892 iv = parseInt(iv);
9893
9894 if (ed.settings.inline_styles && (!this.queryStateInsertUnorderedList() && !this.queryStateInsertOrderedList())) {
9895 each(s.getSelectedBlocks(), function(e) {
9896 v = Math.max(0, parseInt(e.style.paddingLeft || 0) - iv);
9897 d.setStyle(e, 'paddingLeft', v ? v + iu : '');
9898 });
9899
9900 return;
9901 }
9902
9903 ed.getDoc().execCommand('Outdent', false, null);
9904 },
9905
9906/*
9907 mceSetAttribute : function(u, v) {
9908 var ed = this.editor, d = ed.dom, e;
9909
9910 if (e = d.getParent(ed.selection.getNode(), d.isBlock))
9911 d.setAttrib(e, v.name, v.value);
9912 },
9913*/
9914 mceSetContent : function(u, v) {
9915 this.editor.setContent(v);
9916 },
9917
9918 mceToggleVisualAid : function() {
9919 var ed = this.editor;
9920
9921 ed.hasVisual = !ed.hasVisual;
9922 ed.addVisual();
9923 },
9924
9925 mceReplaceContent : function(u, v) {
9926 var s = this.editor.selection;
9927
9928 s.setContent(v.replace(/\{\$selection\}/g, s.getContent({format : 'text'})));
9929 },
9930
9931 mceInsertLink : function(u, v) {
9932 var ed = this.editor, s = ed.selection, e = ed.dom.getParent(s.getNode(), 'a');
9933
9934 if (tinymce.is(v, 'string'))
9935 v = {href : v};
9936
9937 function set(e) {
9938 each(v, function(v, k) {
9939 ed.dom.setAttrib(e, k, v);
9940 });
9941 };
9942
9943 if (!e) {
9944 ed.execCommand('CreateLink', false, 'javascript:mctmp(0);');
9945 each(ed.dom.select('a[href=javascript:mctmp(0);]'), function(e) {
9946 set(e);
9947 });
9948 } else {
9949 if (v.href)
9950 set(e);
9951 else
9952 ed.dom.remove(e, 1);
9953 }
9954 },
9955
9956 UnLink : function() {
9957 var ed = this.editor, s = ed.selection;
9958
9959 if (s.isCollapsed())
9960 s.select(s.getNode());
9961
9962 ed.getDoc().execCommand('unlink', false, null);
9963 s.collapse(0);
9964 },
9965
9966 FontName : function(u, v) {
9967 var t = this, ed = t.editor, s = ed.selection, e;
9968
9969 if (!v) {
9970 if (s.isCollapsed())
9971 s.select(s.getNode());
9972 } else {
9973 if (ed.settings.convert_fonts_to_spans)
9974 t._applyInlineStyle('span', {style : {fontFamily : v}});
9975 else
9976 ed.getDoc().execCommand('FontName', false, v);
9977 }
9978 },
9979
9980 FontSize : function(u, v) {
9981 var ed = this.editor, s = ed.settings, fc, fs;
9982
9983 // Use style options instead
9984 if (s.convert_fonts_to_spans && v >= 1 && v <= 7) {
9985 fs = tinymce.explode(s.font_size_style_values);
9986 fc = tinymce.explode(s.font_size_classes);
9987
9988 if (fc)
9989 v = fc[v - 1] || v;
9990 else
9991 v = fs[v - 1] || v;
9992 }
9993
9994 if (v >= 1 && v <= 7)
9995 ed.getDoc().execCommand('FontSize', false, v);
9996 else
9997 this._applyInlineStyle('span', {style : {fontSize : v}});
9998 },
9999
10000 queryCommandValue : function(c) {
10001 var f = this['queryValue' + c];
10002
10003 if (f)
10004 return f.call(this, c);
10005
10006 return false;
10007 },
10008
10009 queryCommandState : function(cmd) {
10010 var f;
10011
10012 switch (cmd) {
10013 // Bundle these together
10014 case 'JustifyLeft':
10015 case 'JustifyCenter':
10016 case 'JustifyRight':
10017 case 'JustifyFull':
10018 return this.queryStateJustify(cmd, cmd.substring(7).toLowerCase());
10019
10020 default:
10021 if (f = this['queryState' + cmd])
10022 return f.call(this, cmd);
10023 }
10024
10025 return -1;
10026 },
10027
10028 _queryState : function(c) {
10029 try {
10030 return this.editor.getDoc().queryCommandState(c);
10031 } catch (ex) {
10032 // Ignore exception
10033 }
10034 },
10035
10036 _queryVal : function(c) {
10037 try {
10038 return this.editor.getDoc().queryCommandValue(c);
10039 } catch (ex) {
10040 // Ignore exception
10041 }
10042 },
10043
10044 queryValueFontSize : function() {
10045 var ed = this.editor, v = 0, p;
10046
10047 if (p = ed.dom.getParent(ed.selection.getNode(), 'span'))
10048 v = p.style.fontSize;
10049
10050 if (!v && (isOpera || isWebKit)) {
10051 if (p = ed.dom.getParent(ed.selection.getNode(), 'font'))
10052 v = p.size;
10053
10054 return v;
10055 }
10056
10057 return v || this._queryVal('FontSize');
10058 },
10059
10060 queryValueFontName : function() {
10061 var ed = this.editor, v = 0, p;
10062
10063 if (p = ed.dom.getParent(ed.selection.getNode(), 'font'))
10064 v = p.face;
10065
10066 if (p = ed.dom.getParent(ed.selection.getNode(), 'span'))
10067 v = p.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();
10068
10069 if (!v)
10070 v = this._queryVal('FontName');
10071
10072 return v;
10073 },
10074
10075 mceJustify : function(c, v) {
10076 var ed = this.editor, se = ed.selection, n = se.getNode(), nn = n.nodeName, bl, nb, dom = ed.dom, rm;
10077
10078 if (ed.settings.inline_styles && this.queryStateJustify(c, v))
10079 rm = 1;
10080
10081 bl = dom.getParent(n, ed.dom.isBlock);
10082
10083 if (nn == 'IMG') {
10084 if (v == 'full')
10085 return;
10086
10087 if (rm) {
10088 if (v == 'center')
10089 dom.setStyle(bl || n.parentNode, 'textAlign', '');
10090
10091 dom.setStyle(n, 'float', '');
10092 this.mceRepaint();
10093 return;
10094 }
10095
10096 if (v == 'center') {
10097 // Do not change table elements
10098 if (bl && /^(TD|TH)$/.test(bl.nodeName))
10099 bl = 0;
10100
10101 if (!bl || bl.childNodes.length > 1) {
10102 nb = dom.create('p');
10103 nb.appendChild(n.cloneNode(false));
10104
10105 if (bl)
10106 dom.insertAfter(nb, bl);
10107 else
10108 dom.insertAfter(nb, n);
10109
10110 dom.remove(n);
10111 n = nb.firstChild;
10112 bl = nb;
10113 }
10114
10115 dom.setStyle(bl, 'textAlign', v);
10116 dom.setStyle(n, 'float', '');
10117 } else {
10118 dom.setStyle(n, 'float', v);
10119 dom.setStyle(bl || n.parentNode, 'textAlign', '');
10120 }
10121
10122 this.mceRepaint();
10123 return;
10124 }
10125
10126 // Handle the alignment outselfs, less quirks in all browsers
10127 if (ed.settings.inline_styles && ed.settings.forced_root_block) {
10128 if (rm)
10129 v = '';
10130
10131 each(se.getSelectedBlocks(dom.getParent(se.getStart(), dom.isBlock), dom.getParent(se.getEnd(), dom.isBlock)), function(e) {
10132 dom.setAttrib(e, 'align', '');
10133 dom.setStyle(e, 'textAlign', v == 'full' ? 'justify' : v);
10134 });
10135
10136 return;
10137 } else if (!rm)
10138 ed.getDoc().execCommand(c, false, null);
10139
10140 if (ed.settings.inline_styles) {
10141 if (rm) {
10142 dom.getParent(ed.selection.getNode(), function(n) {
10143 if (n.style && n.style.textAlign)
10144 dom.setStyle(n, 'textAlign', '');
10145 });
10146
10147 return;
10148 }
10149
10150 each(dom.select('*'), function(n) {
10151 var v = n.align;
10152
10153 if (v) {
10154 if (v == 'full')
10155 v = 'justify';
10156
10157 dom.setStyle(n, 'textAlign', v);
10158 dom.setAttrib(n, 'align', '');
10159 }
10160 });
10161 }
10162 },
10163
10164 mceSetCSSClass : function(u, v) {
10165 this.mceSetStyleInfo(0, {command : 'setattrib', name : 'class', value : v});
10166 },
10167
10168 getSelectedElement : function() {
10169 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;
10170
10171 if (se.isCollapsed() || r.item)
10172 return se.getNode();
10173
10174 // Setup regexp
10175 re = ed.settings.merge_styles_invalid_parents;
10176 if (tinymce.is(re, 'string'))
10177 re = new RegExp(re, 'i');
10178
10179 if (isIE) {
10180 r1 = r.duplicate();
10181 r1.collapse(true);
10182 sc = r1.parentElement();
10183
10184 r2 = r.duplicate();
10185 r2.collapse(false);
10186 ec = r2.parentElement();
10187
10188 if (sc != ec) {
10189 r1.move('character', 1);
10190 sc = r1.parentElement();
10191 }
10192
10193 if (sc == ec) {
10194 r1 = r.duplicate();
10195 r1.moveToElementText(sc);
10196
10197 if (r1.compareEndPoints('StartToStart', r) == 0 && r1.compareEndPoints('EndToEnd', r) == 0)
10198 return re && re.test(sc.nodeName) ? null : sc;
10199 }
10200 } else {
10201 function getParent(n) {
10202 return dom.getParent(n, '*');
10203 };
10204
10205 sc = r.startContainer;
10206 ec = r.endContainer;
10207 so = r.startOffset;
10208 eo = r.endOffset;
10209
10210 if (!r.collapsed) {
10211 if (sc == ec) {
10212 if (so - eo < 2) {
10213 if (sc.hasChildNodes()) {
10214 sp = sc.childNodes[so];
10215 return re && re.test(sp.nodeName) ? null : sp;
10216 }
10217 }
10218 }
10219 }
10220
10221 if (sc.nodeType != 3 || ec.nodeType != 3)
10222 return null;
10223
10224 if (so == 0) {
10225 sp = getParent(sc);
10226
10227 if (sp && sp.firstChild != sc)
10228 sp = null;
10229 }
10230
10231 if (so == sc.nodeValue.length) {
10232 e = sc.nextSibling;
10233
10234 if (e && e.nodeType == 1)
10235 sp = sc.nextSibling;
10236 }
10237
10238 if (eo == 0) {
10239 e = ec.previousSibling;
10240
10241 if (e && e.nodeType == 1)
10242 ep = e;
10243 }
10244
10245 if (eo == ec.nodeValue.length) {
10246 ep = getParent(ec);
10247
10248 if (ep && ep.lastChild != ec)
10249 ep = null;
10250 }
10251
10252 // Same element
10253 if (sp == ep)
10254 return re && sp && re.test(sp.nodeName) ? null : sp;
10255 }
10256
10257 return null;
10258 },
10259
10260 mceSetStyleInfo : function(u, v) {
10261 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;
10262
10263 function set(n, e) {
10264 if (n.nodeType == 1) {
10265 switch (v.command) {
10266 case 'setattrib':
10267 return dom.setAttrib(n, v.name, v.value);
10268
10269 case 'setstyle':
10270 return dom.setStyle(n, v.name, v.value);
10271
10272 case 'removeformat':
10273 return dom.setAttrib(n, 'class', '');
10274 }
10275 }
10276 };
10277
10278 // Setup regexp
10279 re = ed.settings.merge_styles_invalid_parents;
10280 if (tinymce.is(re, 'string'))
10281 re = new RegExp(re, 'i');
10282
10283 // Set style info on selected element
10284 if ((e = t.getSelectedElement()) && !ed.settings.force_span_wrappers)
10285 set(e, 1);
10286 else {
10287 // Generate wrappers and set styles on them
10288 d.execCommand('FontName', false, '__');
10289 each(dom.select('span,font'), function(n) {
10290 var sp, e;
10291
10292 if (dom.getAttrib(n, 'face') == '__' || n.style.fontFamily === '__') {
10293 sp = dom.create(nn, {mce_new : '1'});
10294
10295 set(sp);
10296
10297 each (n.childNodes, function(n) {
10298 sp.appendChild(n.cloneNode(true));
10299 });
10300
10301 dom.replace(sp, n);
10302 }
10303 });
10304 }
10305
10306 // Remove wrappers inside new ones
10307 each(dom.select(nn).reverse(), function(n) {
10308 var p = n.parentNode;
10309
10310 // Check if it's an old span in a new wrapper
10311 if (!dom.getAttrib(n, 'mce_new')) {
10312 // Find new wrapper
10313 p = dom.getParent(n, '*[mce_new]');
10314
10315 if (p)
10316 dom.remove(n, 1);
10317 }
10318 });
10319
10320 // Merge wrappers with parent wrappers
10321 each(dom.select(nn).reverse(), function(n) {
10322 var p = n.parentNode;
10323
10324 if (!p || !dom.getAttrib(n, 'mce_new'))
10325 return;
10326
10327 if (ed.settings.force_span_wrappers && p.nodeName != 'SPAN')
10328 return;
10329
10330 // Has parent of the same type and only child
10331 if (p.nodeName == nn.toUpperCase() && p.childNodes.length == 1)
10332 return dom.remove(p, 1);
10333
10334 // Has parent that is more suitable to have the class and only child
10335 if (n.nodeType == 1 && (!re || !re.test(p.nodeName)) && p.childNodes.length == 1) {
10336 set(p); // Set style info on parent instead
10337 dom.setAttrib(n, 'class', '');
10338 }
10339 });
10340
10341 // Remove empty wrappers
10342 each(dom.select(nn).reverse(), function(n) {
10343 if (dom.getAttrib(n, 'mce_new') || (dom.getAttribs(n).length <= 1 && n.className === '')) {
10344 if (!dom.getAttrib(n, 'class') && !dom.getAttrib(n, 'style'))
10345 return dom.remove(n, 1);
10346
10347 dom.setAttrib(n, 'mce_new', ''); // Remove mce_new marker
10348 }
10349 });
10350
10351 s.moveToBookmark(b);
10352 },
10353
10354 queryStateJustify : function(c, v) {
10355 var ed = this.editor, n = ed.selection.getNode(), dom = ed.dom;
10356
10357 if (n && n.nodeName == 'IMG') {
10358 if (dom.getStyle(n, 'float') == v)
10359 return 1;
10360
10361 return n.parentNode.style.textAlign == v;
10362 }
10363
10364 n = dom.getParent(ed.selection.getStart(), function(n) {
10365 return n.nodeType == 1 && n.style.textAlign;
10366 });
10367
10368 if (v == 'full')
10369 v = 'justify';
10370
10371 if (ed.settings.inline_styles)
10372 return (n && n.style.textAlign == v);
10373
10374 return this._queryState(c);
10375 },
10376
10377 ForeColor : function(ui, v) {
10378 var ed = this.editor;
10379
10380 if (ed.settings.convert_fonts_to_spans) {
10381 this._applyInlineStyle('span', {style : {color : v}});
10382 return;
10383 } else
10384 ed.getDoc().execCommand('ForeColor', false, v);
10385 },
10386
10387 HiliteColor : function(ui, val) {
10388 var t = this, ed = t.editor, d = ed.getDoc();
10389
10390 if (ed.settings.convert_fonts_to_spans) {
10391 this._applyInlineStyle('span', {style : {backgroundColor : val}});
10392 return;
10393 }
10394
10395 function set(s) {
10396 if (!isGecko)
10397 return;
10398
10399 try {
10400 // Try new Gecko method
10401 d.execCommand("styleWithCSS", 0, s);
10402 } catch (ex) {
10403 // Use old
10404 d.execCommand("useCSS", 0, !s);
10405 }
10406 };
10407
10408 if (isGecko || isOpera) {
10409 set(true);
10410 d.execCommand('hilitecolor', false, val);
10411 set(false);
10412 } else
10413 d.execCommand('BackColor', false, val);
10414 },
10415
10416 FormatBlock : function(ui, val) {
10417 var t = this, ed = t.editor, s = ed.selection, dom = ed.dom, bl, nb, b;
10418
10419 function isBlock(n) {
10420 return /^(P|DIV|H[1-6]|ADDRESS|BLOCKQUOTE|PRE)$/.test(n.nodeName);
10421 };
10422
10423 bl = dom.getParent(s.getNode(), function(n) {
10424 return isBlock(n);
10425 });
10426
10427 // IE has an issue where it removes the parent div if you change format on the paragrah in <div><p>Content</p></div>
10428 // FF and Opera doesn't change parent DIV elements if you switch format
10429 if (bl) {
10430 if ((isIE && isBlock(bl.parentNode)) || bl.nodeName == 'DIV') {
10431 // Rename block element
10432 nb = ed.dom.create(val);
10433
10434 each(dom.getAttribs(bl), function(v) {
10435 dom.setAttrib(nb, v.nodeName, dom.getAttrib(bl, v.nodeName));
10436 });
10437
10438 b = s.getBookmark();
10439 dom.replace(nb, bl, 1);
10440 s.moveToBookmark(b);
10441 ed.nodeChanged();
10442 return;
10443 }
10444 }
10445
10446 val = ed.settings.forced_root_block ? (val || '<p>') : val;
10447
10448 if (val.indexOf('<') == -1)
10449 val = '<' + val + '>';
10450
10451 if (tinymce.isGecko)
10452 val = val.replace(/<(div|blockquote|code|dt|dd|dl|samp)>/gi, '$1');
10453
10454 ed.getDoc().execCommand('FormatBlock', false, val);
10455 },
10456
10457 mceCleanup : function() {
10458 var ed = this.editor, s = ed.selection, b = s.getBookmark();
10459 ed.setContent(ed.getContent());
10460 s.moveToBookmark(b);
10461 },
10462
10463 mceRemoveNode : function(ui, val) {
10464 var ed = this.editor, s = ed.selection, b, n = val || s.getNode();
10465
10466 // Make sure that the body node isn't removed
10467 if (n == ed.getBody())
10468 return;
10469
10470 b = s.getBookmark();
10471 ed.dom.remove(n, 1);
10472 s.moveToBookmark(b);
10473 ed.nodeChanged();
10474 },
10475
10476 mceSelectNodeDepth : function(ui, val) {
10477 var ed = this.editor, s = ed.selection, c = 0;
10478
10479 ed.dom.getParent(s.getNode(), function(n) {
10480 if (n.nodeType == 1 && c++ == val) {
10481 s.select(n);
10482 ed.nodeChanged();
10483 return false;
10484 }
10485 }, ed.getBody());
10486 },
10487
10488 mceSelectNode : function(u, v) {
10489 this.editor.selection.select(v);
10490 },
10491
10492 mceInsertContent : function(ui, val) {
10493 this.editor.selection.setContent(val);
10494 },
10495
10496 mceInsertRawHTML : function(ui, val) {
10497 var ed = this.editor;
10498
10499 ed.selection.setContent('tiny_mce_marker');
10500 ed.setContent(ed.getContent().replace(/tiny_mce_marker/g, val));
10501 },
10502
10503 mceRepaint : function() {
10504 var s, b, e = this.editor;
10505
10506 if (tinymce.isGecko) {
10507 try {
10508 s = e.selection;
10509 b = s.getBookmark(true);
10510
10511 if (s.getSel())
10512 s.getSel().selectAllChildren(e.getBody());
10513
10514 s.collapse(true);
10515 s.moveToBookmark(b);
10516 } catch (ex) {
10517 // Ignore
10518 }
10519 }
10520 },
10521
10522 queryStateUnderline : function() {
10523 var ed = this.editor, n = ed.selection.getNode();
10524
10525 if (n && n.nodeName == 'A')
10526 return false;
10527
10528 return this._queryState('Underline');
10529 },
10530
10531 queryStateOutdent : function() {
10532 var ed = this.editor, n;
10533
10534 if (ed.settings.inline_styles) {
10535 if ((n = ed.dom.getParent(ed.selection.getStart(), ed.dom.isBlock)) && parseInt(n.style.paddingLeft) > 0)
10536 return true;
10537
10538 if ((n = ed.dom.getParent(ed.selection.getEnd(), ed.dom.isBlock)) && parseInt(n.style.paddingLeft) > 0)
10539 return true;
10540 }
10541
10542 return this.queryStateInsertUnorderedList() || this.queryStateInsertOrderedList() || (!ed.settings.inline_styles && !!ed.dom.getParent(ed.selection.getNode(), 'BLOCKQUOTE'));
10543 },
10544
10545 queryStateInsertUnorderedList : function() {
10546 return this.editor.dom.getParent(this.editor.selection.getNode(), 'UL');
10547 },
10548
10549 queryStateInsertOrderedList : function() {
10550 return this.editor.dom.getParent(this.editor.selection.getNode(), 'OL');
10551 },
10552
10553 queryStatemceBlockQuote : function() {
10554 return !!this.editor.dom.getParent(this.editor.selection.getStart(), function(n) {return n.nodeName === 'BLOCKQUOTE';});
10555 },
10556
10557 _applyInlineStyle : function(na, at, op) {
10558 var t = this, ed = t.editor, dom = ed.dom, bm, lo = {}, kh, found;
10559
10560 na = na.toUpperCase();
10561
10562 if (op && op.check_classes && at['class'])
10563 op.check_classes.push(at['class']);
10564
10565 function removeEmpty() {
10566 each(dom.select(na).reverse(), function(n) {
10567 var c = 0;
10568
10569 // Check if there is any attributes
10570 each(dom.getAttribs(n), function(an) {
10571 if (an.nodeName.substring(0, 1) != '_' && dom.getAttrib(n, an.nodeName) != '') {
10572 //console.log(dom.getOuterHTML(n), dom.getAttrib(n, an.nodeName));
10573 c++;
10574 }
10575 });
10576
10577 // No attributes then remove the element and keep the children
10578 if (c == 0)
10579 dom.remove(n, 1);
10580 });
10581 };
10582
10583 function replaceFonts() {
10584 var bm;
10585
10586 each(dom.select('span,font'), function(n) {
10587 if (n.style.fontFamily == 'mceinline' || n.face == 'mceinline') {
10588 if (!bm)
10589 bm = ed.selection.getBookmark();
10590
10591 at._mce_new = '1';
10592 dom.replace(dom.create(na, at), n, 1);
10593 }
10594 });
10595
10596 // Remove redundant elements
10597 each(dom.select(na + '[_mce_new]'), function(n) {
10598 function removeStyle(n) {
10599 if (n.nodeType == 1) {
10600 each(at.style, function(v, k) {
10601 dom.setStyle(n, k, '');
10602 });
10603
10604 // Remove spans with the same class or marked classes
10605 if (at['class'] && n.className && op) {
10606 each(op.check_classes, function(c) {
10607 if (dom.hasClass(n, c))
10608 dom.removeClass(n, c);
10609 });
10610 }
10611 }
10612 };
10613
10614 // Remove specified style information from child elements
10615 each(dom.select(na, n), removeStyle);
10616
10617 // Remove the specified style information on parent if current node is only child (IE)
10618 if (n.parentNode && n.parentNode.nodeType == 1 && n.parentNode.childNodes.length == 1)
10619 removeStyle(n.parentNode);
10620
10621 // Remove the child elements style info if a parent already has it
10622 dom.getParent(n.parentNode, function(pn) {
10623 if (pn.nodeType == 1) {
10624 if (at.style) {
10625 each(at.style, function(v, k) {
10626 var sv;
10627
10628 if (!lo[k] && (sv = dom.getStyle(pn, k))) {
10629 if (sv === v)
10630 dom.setStyle(n, k, '');
10631
10632 lo[k] = 1;
10633 }
10634 });
10635 }
10636
10637 // Remove spans with the same class or marked classes
10638 if (at['class'] && pn.className && op) {
10639 each(op.check_classes, function(c) {
10640 if (dom.hasClass(pn, c))
10641 dom.removeClass(n, c);
10642 });
10643 }
10644 }
10645
10646 return false;
10647 });
10648
10649 n.removeAttribute('_mce_new');
10650 });
10651
10652 removeEmpty();
10653 ed.selection.moveToBookmark(bm);
10654
10655 return !!bm;
10656 };
10657
10658 // Create inline elements
10659 ed.focus();
10660 ed.getDoc().execCommand('FontName', false, 'mceinline');
10661 replaceFonts();
10662
10663 if (kh = t._applyInlineStyle.keyhandler) {
10664 ed.onKeyUp.remove(kh);
10665 ed.onKeyPress.remove(kh);
10666 ed.onKeyDown.remove(kh);
10667 ed.onSetContent.remove(t._applyInlineStyle.chandler);
10668 }
10669
10670 if (ed.selection.isCollapsed()) {
10671 // IE will format the current word so this code can't be executed on that browser
10672 if (!isIE) {
10673 each(dom.getParents(ed.selection.getNode(), 'span'), function(n) {
10674 each(at.style, function(v, k) {
10675 var kv;
10676
10677 if (kv = dom.getStyle(n, k)) {
10678 if (kv == v) {
10679 dom.setStyle(n, k, '');
10680 found = 2;
10681 return false;
10682 }
10683
10684 found = 1;
10685 return false;
10686 }
10687 });
10688
10689 if (found)
10690 return false;
10691 });
10692
10693 if (found == 2) {
10694 bm = ed.selection.getBookmark();
10695
10696 removeEmpty();
10697
10698 ed.selection.moveToBookmark(bm);
10699
10700 // Node change needs to be detached since the onselect event
10701 // for the select box will run the onclick handler after onselect call. Todo: Add a nicer fix!
10702 window.setTimeout(function() {
10703 ed.nodeChanged();
10704 }, 1);
10705
10706 return;
10707 }
10708 }
10709
10710 // Start collecting styles
10711 t._pendingStyles = tinymce.extend(t._pendingStyles || {}, at.style);
10712
10713 t._applyInlineStyle.chandler = ed.onSetContent.add(function() {
10714 delete t._pendingStyles;
10715 });
10716
10717 t._applyInlineStyle.keyhandler = kh = function(e) {
10718 // Use pending styles
10719 if (t._pendingStyles) {
10720 at.style = t._pendingStyles;
10721 delete t._pendingStyles;
10722 }
10723
10724 if (replaceFonts()) {
10725 ed.onKeyDown.remove(t._applyInlineStyle.keyhandler);
10726 ed.onKeyPress.remove(t._applyInlineStyle.keyhandler);
10727 }
10728
10729 if (e.type == 'keyup')
10730 ed.onKeyUp.remove(t._applyInlineStyle.keyhandler);
10731 };
10732
10733 ed.onKeyDown.add(kh);
10734 ed.onKeyPress.add(kh);
10735 ed.onKeyUp.add(kh);
10736 } else
10737 t._pendingStyles = 0;
10738 }
10739 });
10740})(tinymce);(function(tinymce) {
10741 tinymce.create('tinymce.UndoManager', {
10742 index : 0,
10743 data : null,
10744 typing : 0,
10745
10746 UndoManager : function(ed) {
10747 var t = this, Dispatcher = tinymce.util.Dispatcher;
10748
10749 t.editor = ed;
10750 t.data = [];
10751 t.onAdd = new Dispatcher(this);
10752 t.onUndo = new Dispatcher(this);
10753 t.onRedo = new Dispatcher(this);
10754 },
10755
10756 add : function(l) {
10757 var t = this, i, ed = t.editor, b, s = ed.settings, la;
10758
10759 l = l || {};
10760 l.content = l.content || ed.getContent({format : 'raw', no_events : 1});
10761
10762 // Add undo level if needed
10763 l.content = l.content.replace(/^\s*|\s*$/g, '');
10764 la = t.data[t.index > 0 && (t.index == 0 || t.index == t.data.length) ? t.index - 1 : t.index];
10765 if (!l.initial && la && l.content == la.content)
10766 return null;
10767
10768 // Time to compress
10769 if (s.custom_undo_redo_levels) {
10770 if (t.data.length > s.custom_undo_redo_levels) {
10771 for (i = 0; i < t.data.length - 1; i++)
10772 t.data[i] = t.data[i + 1];
10773
10774 t.data.length--;
10775 t.index = t.data.length;
10776 }
10777 }
10778
10779 if (s.custom_undo_redo_restore_selection && !l.initial)
10780 l.bookmark = b = l.bookmark || ed.selection.getBookmark();
10781
10782 if (t.index < t.data.length)
10783 t.index++;
10784
10785 // Only initial marked undo levels should be allowed as first item
10786 // This to workaround a bug with Firefox and the blur event
10787 if (t.data.length === 0 && !l.initial)
10788 return null;
10789
10790 // Add level
10791 t.data.length = t.index + 1;
10792 t.data[t.index++] = l;
10793
10794 if (l.initial)
10795 t.index = 0;
10796
10797 // Set initial bookmark use first real undo level
10798 if (t.data.length == 2 && t.data[0].initial)
10799 t.data[0].bookmark = b;
10800
10801 t.onAdd.dispatch(t, l);
10802 ed.isNotDirty = 0;
10803
10804 //console.dir(t.data);
10805
10806 return l;
10807 },
10808
10809 undo : function() {
10810 var t = this, ed = t.editor, l = l, i;
10811
10812 if (t.typing) {
10813 t.add();
10814 t.typing = 0;
10815 }
10816
10817 if (t.index > 0) {
10818 // If undo on last index then take snapshot
10819 if (t.index == t.data.length && t.index > 1) {
10820 i = t.index;
10821 t.typing = 0;
10822
10823 if (!t.add())
10824 t.index = i;
10825
10826 --t.index;
10827 }
10828
10829 l = t.data[--t.index];
10830 ed.setContent(l.content, {format : 'raw'});
10831 ed.selection.moveToBookmark(l.bookmark);
10832
10833 t.onUndo.dispatch(t, l);
10834 }
10835
10836 return l;
10837 },
10838
10839 redo : function() {
10840 var t = this, ed = t.editor, l = null;
10841
10842 if (t.index < t.data.length - 1) {
10843 l = t.data[++t.index];
10844 ed.setContent(l.content, {format : 'raw'});
10845 ed.selection.moveToBookmark(l.bookmark);
10846
10847 t.onRedo.dispatch(t, l);
10848 }
10849
10850 return l;
10851 },
10852
10853 clear : function() {
10854 var t = this;
10855
10856 t.data = [];
10857 t.index = 0;
10858 t.typing = 0;
10859 t.add({initial : true});
10860 },
10861
10862 hasUndo : function() {
10863 return this.index != 0 || this.typing;
10864 },
10865
10866 hasRedo : function() {
10867 return this.index < this.data.length - 1;
10868 }
10869 });
10870})(tinymce);
10871(function(tinymce) {
10872 // Shorten names
10873 var Event, isIE, isGecko, isOpera, each, extend;
10874
10875 Event = tinymce.dom.Event;
10876 isIE = tinymce.isIE;
10877 isGecko = tinymce.isGecko;
10878 isOpera = tinymce.isOpera;
10879 each = tinymce.each;
10880 extend = tinymce.extend;
10881
10882 // Checks if the selection/caret is at the end of the specified block element
10883 function isAtEnd(rng, par) {
10884 var rng2 = par.ownerDocument.createRange();
10885
10886 rng2.setStart(rng.endContainer, rng.endOffset);
10887 rng2.setEndAfter(par);
10888
10889 // Get number of characters to the right of the cursor if it's zero then we are at the end and need to merge the next block element
10890 return rng2.cloneContents().textContent.length == 0;
10891 };
10892
10893 function isEmpty(n) {
10894 n = n.innerHTML;
10895
10896 n = n.replace(/<(img|hr|table|input|select|textarea)[ \>]/gi, '-'); // Keep these convert them to - chars
10897 n = n.replace(/<[^>]+>/g, ''); // Remove all tags
10898
10899 return n.replace(/[ \t\r\n]+/g, '') == '';
10900 };
10901
10902 tinymce.create('tinymce.ForceBlocks', {
10903 ForceBlocks : function(ed) {
10904 var t = this, s = ed.settings, elm;
10905
10906 t.editor = ed;
10907 t.dom = ed.dom;
10908 elm = (s.forced_root_block || 'p').toLowerCase();
10909 s.element = elm.toUpperCase();
10910
10911 ed.onPreInit.add(t.setup, t);
10912
10913 t.reOpera = new RegExp('(\\u00a0|&#160;|&nbsp;)<\/' + elm + '>', 'gi');
10914 t.rePadd = new RegExp('<p( )([^>]+)><\\\/p>|<p( )([^>]+)\\\/>|<p( )([^>]+)>\\s+<\\\/p>|<p><\\\/p>|<p\\\/>|<p>\\s+<\\\/p>'.replace(/p/g, elm), 'gi');
10915 t.reNbsp2BR1 = new RegExp('<p( )([^>]+)>[\\s\\u00a0]+<\\\/p>|<p>[\\s\\u00a0]+<\\\/p>'.replace(/p/g, elm), 'gi');
10916 t.reNbsp2BR2 = new RegExp('<%p()([^>]+)>(&nbsp;|&#160;)<\\\/%p>|<%p>(&nbsp;|&#160;)<\\\/%p>'.replace(/%p/g, elm), 'gi');
10917 t.reBR2Nbsp = new RegExp('<p( )([^>]+)>\\s*<br \\\/>\\s*<\\\/p>|<p>\\s*<br \\\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi');
10918
10919 function padd(ed, o) {
10920 if (isOpera)
10921 o.content = o.content.replace(t.reOpera, '</' + elm + '>');
10922
10923 o.content = o.content.replace(t.rePadd, '<' + elm + '$1$2$3$4$5$6>\u00a0</' + elm + '>');
10924
10925 if (!isIE && !isOpera && o.set) {
10926 // Use &nbsp; instead of BR in padded paragraphs
10927 o.content = o.content.replace(t.reNbsp2BR1, '<' + elm + '$1$2><br /></' + elm + '>');
10928 o.content = o.content.replace(t.reNbsp2BR2, '<' + elm + '$1$2><br /></' + elm + '>');
10929 } else
10930 o.content = o.content.replace(t.reBR2Nbsp, '<' + elm + '$1$2>\u00a0</' + elm + '>');
10931 };
10932
10933 ed.onBeforeSetContent.add(padd);
10934 ed.onPostProcess.add(padd);
10935
10936 if (s.forced_root_block) {
10937 ed.onInit.add(t.forceRoots, t);
10938 ed.onSetContent.add(t.forceRoots, t);
10939 ed.onBeforeGetContent.add(t.forceRoots, t);
10940 }
10941 },
10942
10943 setup : function() {
10944 var t = this, ed = t.editor, s = ed.settings;
10945
10946 // Force root blocks when typing and when getting output
10947 if (s.forced_root_block) {
10948 ed.onKeyUp.add(t.forceRoots, t);
10949 ed.onPreProcess.add(t.forceRoots, t);
10950 }
10951
10952 if (s.force_br_newlines) {
10953 // Force IE to produce BRs on enter
10954 if (isIE) {
10955 ed.onKeyPress.add(function(ed, e) {
10956 var n, s = ed.selection;
10957
10958 if (e.keyCode == 13 && s.getNode().nodeName != 'LI') {
10959 s.setContent('<br id="__" /> ', {format : 'raw'});
10960 n = ed.dom.get('__');
10961 n.removeAttribute('id');
10962 s.select(n);
10963 s.collapse();
10964 return Event.cancel(e);
10965 }
10966 });
10967 }
10968
10969 return;
10970 }
10971
10972 if (!isIE && s.force_p_newlines) {
10973/* ed.onPreProcess.add(function(ed, o) {
10974 each(ed.dom.select('br', o.node), function(n) {
10975 var p = n.parentNode;
10976
10977 // Replace <p><br /></p> with <p>&nbsp;</p>
10978 if (p && p.nodeName == 'p' && (p.childNodes.length == 1 || p.lastChild == n)) {
10979 p.replaceChild(ed.getDoc().createTextNode('\u00a0'), n);
10980 }
10981 });
10982 });*/
10983
10984 ed.onKeyPress.add(function(ed, e) {
10985 if (e.keyCode == 13 && !e.shiftKey) {
10986 if (!t.insertPara(e))
10987 Event.cancel(e);
10988 }
10989 });
10990
10991 if (isGecko) {
10992 ed.onKeyDown.add(function(ed, e) {
10993 if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey)
10994 t.backspaceDelete(e, e.keyCode == 8);
10995 });
10996 }
10997 }
10998
10999 function ren(rn, na) {
11000 var ne = ed.dom.create(na);
11001
11002 each(rn.attributes, function(a) {
11003 if (a.specified && a.nodeValue)
11004 ne.setAttribute(a.nodeName.toLowerCase(), a.nodeValue);
11005 });
11006
11007 each(rn.childNodes, function(n) {
11008 ne.appendChild(n.cloneNode(true));
11009 });
11010
11011 rn.parentNode.replaceChild(ne, rn);
11012
11013 return ne;
11014 };
11015
11016 // Padd empty inline elements within block elements
11017 // For example: <p><strong><em></em></strong></p> becomes <p><strong><em>&nbsp;</em></strong></p>
11018 ed.onPreProcess.add(function(ed, o) {
11019 each(ed.dom.select('p,h1,h2,h3,h4,h5,h6,div', o.node), function(p) {
11020 if (isEmpty(p)) {
11021 each(ed.dom.select('span,em,strong,b,i', o.node), function(n) {
11022 if (!n.hasChildNodes()) {
11023 n.appendChild(ed.getDoc().createTextNode('\u00a0'));
11024 return false; // Break the loop one padding is enough
11025 }
11026 });
11027 }
11028 });
11029 });
11030
11031 // IE specific fixes
11032 if (isIE) {
11033 // Replaces IE:s auto generated paragraphs with the specified element name
11034 if (s.element != 'P') {
11035 ed.onKeyPress.add(function(ed, e) {
11036 t.lastElm = ed.selection.getNode().nodeName;
11037 });
11038
11039 ed.onKeyUp.add(function(ed, e) {
11040 var bl, sel = ed.selection, n = sel.getNode(), b = ed.getBody();
11041
11042 if (b.childNodes.length === 1 && n.nodeName == 'P') {
11043 n = ren(n, s.element);
11044 sel.select(n);
11045 sel.collapse();
11046 ed.nodeChanged();
11047 } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') {
11048 bl = ed.dom.getParent(n, 'p');
11049
11050 if (bl) {
11051 ren(bl, s.element);
11052 ed.nodeChanged();
11053 }
11054 }
11055 });
11056 }
11057 }
11058 },
11059
11060 find : function(n, t, s) {
11061 var ed = this.editor, w = ed.getDoc().createTreeWalker(n, 4, null, false), c = -1;
11062
11063 while (n = w.nextNode()) {
11064 c++;
11065
11066 // Index by node
11067 if (t == 0 && n == s)
11068 return c;
11069
11070 // Node by index
11071 if (t == 1 && c == s)
11072 return n;
11073 }
11074
11075 return -1;
11076 },
11077
11078 forceRoots : function(ed, e) {
11079 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;
11080 var nx, bl, bp, sp, le, nl = b.childNodes, i, n, eid;
11081
11082 // Fix for bug #1863847
11083 //if (e && e.keyCode == 13)
11084 // return true;
11085
11086 // Wrap non blocks into blocks
11087 for (i = nl.length - 1; i >= 0; i--) {
11088 nx = nl[i];
11089
11090 // Is text or non block element
11091 if (nx.nodeType === 3 || (!t.dom.isBlock(nx) && nx.nodeType !== 8 && !/^(script|mce:script|style|mce:style)$/i.test(nx.nodeName))) {
11092 if (!bl) {
11093 // Create new block but ignore whitespace
11094 if (nx.nodeType != 3 || /[^\s]/g.test(nx.nodeValue)) {
11095 // Store selection
11096 if (si == -2 && r) {
11097 if (!isIE) {
11098 // If selection is element then mark it
11099 if (r.startContainer.nodeType == 1 && (n = r.startContainer.childNodes[r.startOffset]) && n.nodeType == 1) {
11100 // Save the id of the selected element
11101 eid = n.getAttribute("id");
11102 n.setAttribute("id", "__mce");
11103 } else {
11104 // If element is inside body, might not be the case in contentEdiable mode
11105 if (ed.dom.getParent(r.startContainer, function(e) {return e === b;})) {
11106 so = r.startOffset;
11107 eo = r.endOffset;
11108 si = t.find(b, 0, r.startContainer);
11109 ei = t.find(b, 0, r.endContainer);
11110 }
11111 }
11112 } else {
11113 tr = d.body.createTextRange();
11114 tr.moveToElementText(b);
11115 tr.collapse(1);
11116 bp = tr.move('character', c) * -1;
11117
11118 tr = r.duplicate();
11119 tr.collapse(1);
11120 sp = tr.move('character', c) * -1;
11121
11122 tr = r.duplicate();
11123 tr.collapse(0);
11124 le = (tr.move('character', c) * -1) - sp;
11125
11126 si = sp - bp;
11127 ei = le;
11128 }
11129 }
11130
11131 // Uses replaceChild instead of cloneNode since it removes selected attribute from option elements on IE
11132 // See: http://support.microsoft.com/kb/829907
11133 bl = ed.dom.create(ed.settings.forced_root_block);
11134 nx.parentNode.replaceChild(bl, nx);
11135 bl.appendChild(nx);
11136 }
11137 } else {
11138 if (bl.hasChildNodes())
11139 bl.insertBefore(nx, bl.firstChild);
11140 else
11141 bl.appendChild(nx);
11142 }
11143 } else
11144 bl = null; // Time to create new block
11145 }
11146
11147 // Restore selection
11148 if (si != -2) {
11149 if (!isIE) {
11150 bl = b.getElementsByTagName(ed.settings.element)[0];
11151 r = d.createRange();
11152
11153 // Select last location or generated block
11154 if (si != -1)
11155 r.setStart(t.find(b, 1, si), so);
11156 else
11157 r.setStart(bl, 0);
11158
11159 // Select last location or generated block
11160 if (ei != -1)
11161 r.setEnd(t.find(b, 1, ei), eo);
11162 else
11163 r.setEnd(bl, 0);
11164
11165 if (s) {
11166 s.removeAllRanges();
11167 s.addRange(r);
11168 }
11169 } else {
11170 try {
11171 r = s.createRange();
11172 r.moveToElementText(b);
11173 r.collapse(1);
11174 r.moveStart('character', si);
11175 r.moveEnd('character', ei);
11176 r.select();
11177 } catch (ex) {
11178 // Ignore
11179 }
11180 }
11181 } else if (!isIE && (n = ed.dom.get('__mce'))) {
11182 // Restore the id of the selected element
11183 if (eid)
11184 n.setAttribute('id', eid);
11185 else
11186 n.removeAttribute('id');
11187
11188 // Move caret before selected element
11189 r = d.createRange();
11190 r.setStartBefore(n);
11191 r.setEndBefore(n);
11192 se.setRng(r);
11193 }
11194 },
11195
11196 getParentBlock : function(n) {
11197 var d = this.dom;
11198
11199 return d.getParent(n, d.isBlock);
11200 },
11201
11202 insertPara : function(e) {
11203 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;
11204 var rb, ra, dir, sn, so, en, eo, sb, eb, bn, bef, aft, sc, ec, n, vp = dom.getViewPort(ed.getWin()), y, ch, car;
11205
11206 // If root blocks are forced then use Operas default behavior since it's really good
11207// Removed due to bug: #1853816
11208// if (se.forced_root_block && isOpera)
11209// return true;
11210
11211 // Setup before range
11212 rb = d.createRange();
11213
11214 // If is before the first block element and in body, then move it into first block element
11215 rb.setStart(s.anchorNode, s.anchorOffset);
11216 rb.collapse(true);
11217
11218 // Setup after range
11219 ra = d.createRange();
11220
11221 // If is before the first block element and in body, then move it into first block element
11222 ra.setStart(s.focusNode, s.focusOffset);
11223 ra.collapse(true);
11224
11225 // Setup start/end points
11226 dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0;
11227 sn = dir ? s.anchorNode : s.focusNode;
11228 so = dir ? s.anchorOffset : s.focusOffset;
11229 en = dir ? s.focusNode : s.anchorNode;
11230 eo = dir ? s.focusOffset : s.anchorOffset;
11231
11232 // If selection is in empty table cell
11233 if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) {
11234 if (sn.firstChild.nodeName == 'BR')
11235 dom.remove(sn.firstChild); // Remove BR
11236
11237 // Create two new block elements
11238 if (sn.childNodes.length == 0) {
11239 ed.dom.add(sn, se.element, null, '<br />');
11240 aft = ed.dom.add(sn, se.element, null, '<br />');
11241 } else {
11242 n = sn.innerHTML;
11243 sn.innerHTML = '';
11244 ed.dom.add(sn, se.element, null, n);
11245 aft = ed.dom.add(sn, se.element, null, '<br />');
11246 }
11247
11248 // Move caret into the last one
11249 r = d.createRange();
11250 r.selectNodeContents(aft);
11251 r.collapse(1);
11252 ed.selection.setRng(r);
11253
11254 return false;
11255 }
11256
11257 // If the caret is in an invalid location in FF we need to move it into the first block
11258 if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) {
11259 sn = en = sn.firstChild;
11260 so = eo = 0;
11261 rb = d.createRange();
11262 rb.setStart(sn, 0);
11263 ra = d.createRange();
11264 ra.setStart(en, 0);
11265 }
11266
11267 // Never use body as start or end node
11268 sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes
11269 sn = sn.nodeName == "BODY" ? sn.firstChild : sn;
11270 en = en.nodeName == "HTML" ? d.body : en; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes
11271 en = en.nodeName == "BODY" ? en.firstChild : en;
11272
11273 // Get start and end blocks
11274 sb = t.getParentBlock(sn);
11275 eb = t.getParentBlock(en);
11276 bn = sb ? sb.nodeName : se.element; // Get block name to create
11277
11278 // Return inside list use default browser behavior
11279 if (t.dom.getParent(sb, 'ol,ul,pre'))
11280 return true;
11281
11282 // If caption or absolute layers then always generate new blocks within
11283 if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {
11284 bn = se.element;
11285 sb = null;
11286 }
11287
11288 // If caption or absolute layers then always generate new blocks within
11289 if (eb && (eb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {
11290 bn = se.element;
11291 eb = null;
11292 }
11293
11294 // Use P instead
11295 if (/(TD|TABLE|TH|CAPTION)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(dom.getStyle(sb, 'float', 1)))) {
11296 bn = se.element;
11297 sb = eb = null;
11298 }
11299
11300 // Setup new before and after blocks
11301 bef = (sb && sb.nodeName == bn) ? sb.cloneNode(0) : ed.dom.create(bn);
11302 aft = (eb && eb.nodeName == bn) ? eb.cloneNode(0) : ed.dom.create(bn);
11303
11304 // Remove id from after clone
11305 aft.removeAttribute('id');
11306
11307 // Is header and cursor is at the end, then force paragraph under
11308 if (/^(H[1-6])$/.test(bn) && isAtEnd(r, sb))
11309 aft = ed.dom.create(se.element);
11310
11311 // Find start chop node
11312 n = sc = sn;
11313 do {
11314 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))
11315 break;
11316
11317 sc = n;
11318 } while ((n = n.previousSibling ? n.previousSibling : n.parentNode));
11319
11320 // Find end chop node
11321 n = ec = en;
11322 do {
11323 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))
11324 break;
11325
11326 ec = n;
11327 } while ((n = n.nextSibling ? n.nextSibling : n.parentNode));
11328
11329 // Place first chop part into before block element
11330 if (sc.nodeName == bn)
11331 rb.setStart(sc, 0);
11332 else
11333 rb.setStartBefore(sc);
11334
11335 rb.setEnd(sn, so);
11336 bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari
11337
11338 // Place secnd chop part within new block element
11339 try {
11340 ra.setEndAfter(ec);
11341 } catch(ex) {
11342 //console.debug(s.focusNode, s.focusOffset);
11343 }
11344
11345 ra.setStart(en, eo);
11346 aft.appendChild(ra.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari
11347
11348 // Create range around everything
11349 r = d.createRange();
11350 if (!sc.previousSibling && sc.parentNode.nodeName == bn) {
11351 r.setStartBefore(sc.parentNode);
11352 } else {
11353 if (rb.startContainer.nodeName == bn && rb.startOffset == 0)
11354 r.setStartBefore(rb.startContainer);
11355 else
11356 r.setStart(rb.startContainer, rb.startOffset);
11357 }
11358
11359 if (!ec.nextSibling && ec.parentNode.nodeName == bn)
11360 r.setEndAfter(ec.parentNode);
11361 else
11362 r.setEnd(ra.endContainer, ra.endOffset);
11363
11364 // Delete and replace it with new block elements
11365 r.deleteContents();
11366
11367 if (isOpera)
11368 ed.getWin().scrollTo(0, vp.y);
11369
11370 // Never wrap blocks in blocks
11371 if (bef.firstChild && bef.firstChild.nodeName == bn)
11372 bef.innerHTML = bef.firstChild.innerHTML;
11373
11374 if (aft.firstChild && aft.firstChild.nodeName == bn)
11375 aft.innerHTML = aft.firstChild.innerHTML;
11376
11377 // Padd empty blocks
11378 if (isEmpty(bef))
11379 bef.innerHTML = '<br />';
11380
11381 function appendStyles(e, en) {
11382 var nl = [], nn, n, i;
11383
11384 e.innerHTML = '';
11385
11386 // Make clones of style elements
11387 if (se.keep_styles) {
11388 n = en;
11389 do {
11390 // We only want style specific elements
11391 if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) {
11392 nn = n.cloneNode(false);
11393 dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique
11394 nl.push(nn);
11395 }
11396 } while (n = n.parentNode);
11397 }
11398
11399 // Append style elements to aft
11400 if (nl.length > 0) {
11401 for (i = nl.length - 1, nn = e; i >= 0; i--)
11402 nn = nn.appendChild(nl[i]);
11403
11404 // Padd most inner style element
11405 nl[0].innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there
11406 return nl[0]; // Move caret to most inner element
11407 } else
11408 e.innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there
11409 };
11410
11411 // Fill empty afterblook with current style
11412 if (isEmpty(aft))
11413 car = appendStyles(aft, en);
11414
11415 // Opera needs this one backwards for older versions
11416 if (isOpera && parseFloat(opera.version()) < 9.5) {
11417 r.insertNode(bef);
11418 r.insertNode(aft);
11419 } else {
11420 r.insertNode(aft);
11421 r.insertNode(bef);
11422 }
11423
11424 // Normalize
11425 aft.normalize();
11426 bef.normalize();
11427
11428 function first(n) {
11429 return d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false).nextNode() || n;
11430 };
11431
11432 // Move cursor and scroll into view
11433 r = d.createRange();
11434 r.selectNodeContents(isGecko ? first(car || aft) : car || aft);
11435 r.collapse(1);
11436 s.removeAllRanges();
11437 s.addRange(r);
11438
11439 // 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
11440 y = ed.dom.getPos(aft).y;
11441 ch = aft.clientHeight;
11442
11443 // Is element within viewport
11444 if (y < vp.y || y + ch > vp.y + vp.h) {
11445 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
11446 //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));
11447 }
11448
11449 return false;
11450 },
11451
11452 backspaceDelete : function(e, bs) {
11453 var t = this, ed = t.editor, b = ed.getBody(), dom = ed.dom, n, se = ed.selection, r = se.getRng(), sc = r.startContainer, n, w, tn;
11454
11455 /*
11456 var par, rng, nextBlock;
11457
11458 // Delete key will not merge paragraphs on Gecko so we need to do this manually
11459 // Hitting the delete key at the following caret position doesn't merge the elements <p>A|</p><p>B</p>
11460 // This logic will merge them into this: <p>A|B</p>
11461 if (e.keyCode == 46) {
11462 if (r.collapsed) {
11463 par = dom.getParent(sc, 'p,h1,h2,h3,h4,h5,h6,div');
11464
11465 if (par) {
11466 rng = dom.createRng();
11467
11468 rng.setStart(sc, r.startOffset);
11469 rng.setEndAfter(par);
11470
11471 // Get number of characters to the right of the cursor if it's zero then we are at the end and need to merge the next block element
11472 if (dom.getOuterHTML(rng.cloneContents()).replace(/<[^>]+>/g, '').length == 0) {
11473 nextBlock = dom.getNext(par, 'p,h1,h2,h3,h4,h5,h6,div');
11474
11475 // Copy all children from next sibling block and remove it
11476 if (nextBlock) {
11477 each(nextBlock.childNodes, function(node) {
11478 par.appendChild(node.cloneNode(true));
11479 });
11480
11481 dom.remove(nextBlock);
11482 }
11483
11484 // Block the default even since the Gecko team might eventually fix this
11485 // We will remove this logic once they do we can't feature detect this one
11486 e.preventDefault();
11487 return;
11488 }
11489 }
11490 }
11491 }
11492 */
11493
11494 // The caret sometimes gets stuck in Gecko if you delete empty paragraphs
11495 // This workaround removes the element by hand and moves the caret to the previous element
11496 if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) {
11497 if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) {
11498 // Find previous block element
11499 n = sc;
11500 while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ;
11501
11502 if (n) {
11503 if (sc != b.firstChild) {
11504 // Find last text node
11505 w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
11506 while (tn = w.nextNode())
11507 n = tn;
11508
11509 // Place caret at the end of last text node
11510 r = ed.getDoc().createRange();
11511 r.setStart(n, n.nodeValue ? n.nodeValue.length : 0);
11512 r.setEnd(n, n.nodeValue ? n.nodeValue.length : 0);
11513 se.setRng(r);
11514
11515 // Remove the target container
11516 ed.dom.remove(sc);
11517 }
11518
11519 return Event.cancel(e);
11520 }
11521 }
11522 }
11523
11524 // Gecko generates BR elements here and there, we don't like those so lets remove them
11525 function handler(e) {
11526 var pr;
11527
11528 e = e.target;
11529
11530 // A new BR was created in a block element, remove it
11531 if (e && e.parentNode && e.nodeName == 'BR' && (n = t.getParentBlock(e))) {
11532 pr = e.previousSibling;
11533
11534 Event.remove(b, 'DOMNodeInserted', handler);
11535
11536 // Is there whitespace at the end of the node before then we might need the pesky BR
11537 // to place the caret at a correct location see bug: #2013943
11538 if (pr && pr.nodeType == 3 && /\s+$/.test(pr.nodeValue))
11539 return;
11540
11541 // Only remove BR elements that got inserted in the middle of the text
11542 if (e.previousSibling || e.nextSibling)
11543 ed.dom.remove(e);
11544 }
11545 };
11546
11547 // Listen for new nodes
11548 Event._add(b, 'DOMNodeInserted', handler);
11549
11550 // Remove listener
11551 window.setTimeout(function() {
11552 Event._remove(b, 'DOMNodeInserted', handler);
11553 }, 1);
11554 }
11555 });
11556})(tinymce);
11557(function(tinymce) {
11558 // Shorten names
11559 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;
11560
11561 tinymce.create('tinymce.ControlManager', {
11562 ControlManager : function(ed, s) {
11563 var t = this, i;
11564
11565 s = s || {};
11566 t.editor = ed;
11567 t.controls = {};
11568 t.onAdd = new tinymce.util.Dispatcher(t);
11569 t.onPostRender = new tinymce.util.Dispatcher(t);
11570 t.prefix = s.prefix || ed.id + '_';
11571 t._cls = {};
11572
11573 t.onPostRender.add(function() {
11574 each(t.controls, function(c) {
11575 c.postRender();
11576 });
11577 });
11578 },
11579
11580 get : function(id) {
11581 return this.controls[this.prefix + id] || this.controls[id];
11582 },
11583
11584 setActive : function(id, s) {
11585 var c = null;
11586
11587 if (c = this.get(id))
11588 c.setActive(s);
11589
11590 return c;
11591 },
11592
11593 setDisabled : function(id, s) {
11594 var c = null;
11595
11596 if (c = this.get(id))
11597 c.setDisabled(s);
11598
11599 return c;
11600 },
11601
11602 add : function(c) {
11603 var t = this;
11604
11605 if (c) {
11606 t.controls[c.id] = c;
11607 t.onAdd.dispatch(c, t);
11608 }
11609
11610 return c;
11611 },
11612
11613 createControl : function(n) {
11614 var c, t = this, ed = t.editor;
11615
11616 each(ed.plugins, function(p) {
11617 if (p.createControl) {
11618 c = p.createControl(n, t);
11619
11620 if (c)
11621 return false;
11622 }
11623 });
11624
11625 switch (n) {
11626 case "|":
11627 case "separator":
11628 return t.createSeparator();
11629 }
11630
11631 if (!c && ed.buttons && (c = ed.buttons[n]))
11632 return t.createButton(n, c);
11633
11634 return t.add(c);
11635 },
11636
11637 createDropMenu : function(id, s, cc) {
11638 var t = this, ed = t.editor, c, bm, v, cls;
11639
11640 s = extend({
11641 'class' : 'mceDropDown',
11642 constrain : ed.settings.constrain_menus
11643 }, s);
11644
11645 s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin';
11646 if (v = ed.getParam('skin_variant'))
11647 s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);
11648
11649 id = t.prefix + id;
11650 cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;
11651 c = t.controls[id] = new cls(id, s);
11652 c.onAddItem.add(function(c, o) {
11653 var s = o.settings;
11654
11655 s.title = ed.getLang(s.title, s.title);
11656
11657 if (!s.onclick) {
11658 s.onclick = function(v) {
11659 ed.execCommand(s.cmd, s.ui || false, s.value);
11660 };
11661 }
11662 });
11663
11664 ed.onRemove.add(function() {
11665 c.destroy();
11666 });
11667
11668 // Fix for bug #1897785, #1898007
11669 if (tinymce.isIE) {
11670 c.onShowMenu.add(function() {
11671 // IE 8 needs focus in order to store away a range with the current collapsed caret location
11672 ed.focus();
11673
11674 bm = ed.selection.getBookmark(1);
11675 });
11676
11677 c.onHideMenu.add(function() {
11678 if (bm) {
11679 ed.selection.moveToBookmark(bm);
11680 bm = 0;
11681 }
11682 });
11683 }
11684
11685 return t.add(c);
11686 },
11687
11688 createListBox : function(id, s, cc) {
11689 var t = this, ed = t.editor, cmd, c, cls;
11690
11691 if (t.get(id))
11692 return null;
11693
11694 s.title = ed.translate(s.title);
11695 s.scope = s.scope || ed;
11696
11697 if (!s.onselect) {
11698 s.onselect = function(v) {
11699 ed.execCommand(s.cmd, s.ui || false, v || s.value);
11700 };
11701 }
11702
11703 s = extend({
11704 title : s.title,
11705 'class' : 'mce_' + id,
11706 scope : s.scope,
11707 control_manager : t
11708 }, s);
11709
11710 id = t.prefix + id;
11711
11712 if (ed.settings.use_native_selects)
11713 c = new tinymce.ui.NativeListBox(id, s);
11714 else {
11715 cls = cc || t._cls.listbox || tinymce.ui.ListBox;
11716 c = new cls(id, s);
11717 }
11718
11719 t.controls[id] = c;
11720
11721 // Fix focus problem in Safari
11722 if (tinymce.isWebKit) {
11723 c.onPostRender.add(function(c, n) {
11724 // Store bookmark on mousedown
11725 Event.add(n, 'mousedown', function() {
11726 ed.bookmark = ed.selection.getBookmark(1);
11727 });
11728
11729 // Restore on focus, since it might be lost
11730 Event.add(n, 'focus', function() {
11731 ed.selection.moveToBookmark(ed.bookmark);
11732 ed.bookmark = null;
11733 });
11734 });
11735 }
11736
11737 if (c.hideMenu)
11738 ed.onMouseDown.add(c.hideMenu, c);
11739
11740 return t.add(c);
11741 },
11742
11743 createButton : function(id, s, cc) {
11744 var t = this, ed = t.editor, o, c, cls;
11745
11746 if (t.get(id))
11747 return null;
11748
11749 s.title = ed.translate(s.title);
11750 s.label = ed.translate(s.label);
11751 s.scope = s.scope || ed;
11752
11753 if (!s.onclick && !s.menu_button) {
11754 s.onclick = function() {
11755 ed.execCommand(s.cmd, s.ui || false, s.value);
11756 };
11757 }
11758
11759 s = extend({
11760 title : s.title,
11761 'class' : 'mce_' + id,
11762 unavailable_prefix : ed.getLang('unavailable', ''),
11763 scope : s.scope,
11764 control_manager : t
11765 }, s);
11766
11767 id = t.prefix + id;
11768
11769 if (s.menu_button) {
11770 cls = cc || t._cls.menubutton || tinymce.ui.MenuButton;
11771 c = new cls(id, s);
11772 ed.onMouseDown.add(c.hideMenu, c);
11773 } else {
11774 cls = t._cls.button || tinymce.ui.Button;
11775 c = new cls(id, s);
11776 }
11777
11778 return t.add(c);
11779 },
11780
11781 createMenuButton : function(id, s, cc) {
11782 s = s || {};
11783 s.menu_button = 1;
11784
11785 return this.createButton(id, s, cc);
11786 },
11787
11788 createSplitButton : function(id, s, cc) {
11789 var t = this, ed = t.editor, cmd, c, cls;
11790
11791 if (t.get(id))
11792 return null;
11793
11794 s.title = ed.translate(s.title);
11795 s.scope = s.scope || ed;
11796
11797 if (!s.onclick) {
11798 s.onclick = function(v) {
11799 ed.execCommand(s.cmd, s.ui || false, v || s.value);
11800 };
11801 }
11802
11803 if (!s.onselect) {
11804 s.onselect = function(v) {
11805 ed.execCommand(s.cmd, s.ui || false, v || s.value);
11806 };
11807 }
11808
11809 s = extend({
11810 title : s.title,
11811 'class' : 'mce_' + id,
11812 scope : s.scope,
11813 control_manager : t
11814 }, s);
11815
11816 id = t.prefix + id;
11817 cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton;
11818 c = t.add(new cls(id, s));
11819 ed.onMouseDown.add(c.hideMenu, c);
11820
11821 return c;
11822 },
11823
11824 createColorSplitButton : function(id, s, cc) {
11825 var t = this, ed = t.editor, cmd, c, cls, bm;
11826
11827 if (t.get(id))
11828 return null;
11829
11830 s.title = ed.translate(s.title);
11831 s.scope = s.scope || ed;
11832
11833 if (!s.onclick) {
11834 s.onclick = function(v) {
11835 if (tinymce.isIE)
11836 bm = ed.selection.getBookmark(1);
11837
11838 ed.execCommand(s.cmd, s.ui || false, v || s.value);
11839 };
11840 }
11841
11842 if (!s.onselect) {
11843 s.onselect = function(v) {
11844 ed.execCommand(s.cmd, s.ui || false, v || s.value);
11845 };
11846 }
11847
11848 s = extend({
11849 title : s.title,
11850 'class' : 'mce_' + id,
11851 'menu_class' : ed.getParam('skin') + 'Skin',
11852 scope : s.scope,
11853 more_colors_title : ed.getLang('more_colors')
11854 }, s);
11855
11856 id = t.prefix + id;
11857 cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton;
11858 c = new cls(id, s);
11859 ed.onMouseDown.add(c.hideMenu, c);
11860
11861 // Remove the menu element when the editor is removed
11862 ed.onRemove.add(function() {
11863 c.destroy();
11864 });
11865
11866 // Fix for bug #1897785, #1898007
11867 if (tinymce.isIE) {
11868 c.onShowMenu.add(function() {
11869 // IE 8 needs focus in order to store away a range with the current collapsed caret location
11870 ed.focus();
11871 bm = ed.selection.getBookmark(1);
11872 });
11873
11874 c.onHideMenu.add(function() {
11875 if (bm) {
11876 ed.selection.moveToBookmark(bm);
11877 bm = 0;
11878 }
11879 });
11880 }
11881
11882 return t.add(c);
11883 },
11884
11885 createToolbar : function(id, s, cc) {
11886 var c, t = this, cls;
11887
11888 id = t.prefix + id;
11889 cls = cc || t._cls.toolbar || tinymce.ui.Toolbar;
11890 c = new cls(id, s);
11891
11892 if (t.get(id))
11893 return null;
11894
11895 return t.add(c);
11896 },
11897
11898 createSeparator : function(cc) {
11899 var cls = cc || this._cls.separator || tinymce.ui.Separator;
11900
11901 return new cls();
11902 },
11903
11904 setControlType : function(n, c) {
11905 return this._cls[n.toLowerCase()] = c;
11906 },
11907
11908 destroy : function() {
11909 each(this.controls, function(c) {
11910 c.destroy();
11911 });
11912
11913 this.controls = null;
11914 }
11915 });
11916})(tinymce);
11917(function(tinymce) {
11918 var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;
11919
11920 tinymce.create('tinymce.WindowManager', {
11921 WindowManager : function(ed) {
11922 var t = this;
11923
11924 t.editor = ed;
11925 t.onOpen = new Dispatcher(t);
11926 t.onClose = new Dispatcher(t);
11927 t.params = {};
11928 t.features = {};
11929 },
11930
11931 open : function(s, p) {
11932 var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u;
11933
11934 // Default some options
11935 s = s || {};
11936 p = p || {};
11937 sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window
11938 sh = isOpera ? vp.h : screen.height;
11939 s.name = s.name || 'mc_' + new Date().getTime();
11940 s.width = parseInt(s.width || 320);
11941 s.height = parseInt(s.height || 240);
11942 s.resizable = true;
11943 s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0);
11944 s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0);
11945 p.inline = false;
11946 p.mce_width = s.width;
11947 p.mce_height = s.height;
11948 p.mce_auto_focus = s.auto_focus;
11949
11950 if (mo) {
11951 if (isIE) {
11952 s.center = true;
11953 s.help = false;
11954 s.dialogWidth = s.width + 'px';
11955 s.dialogHeight = s.height + 'px';
11956 s.scroll = s.scrollbars || false;
11957 }
11958 }
11959
11960 // Build features string
11961 each(s, function(v, k) {
11962 if (tinymce.is(v, 'boolean'))
11963 v = v ? 'yes' : 'no';
11964
11965 if (!/^(name|url)$/.test(k)) {
11966 if (isIE && mo)
11967 f += (f ? ';' : '') + k + ':' + v;
11968 else
11969 f += (f ? ',' : '') + k + '=' + v;
11970 }
11971 });
11972
11973 t.features = s;
11974 t.params = p;
11975 t.onOpen.dispatch(t, s, p);
11976
11977 u = s.url || s.file;
11978 u = tinymce._addVer(u);
11979
11980 try {
11981 if (isIE && mo) {
11982 w = 1;
11983 window.showModalDialog(u, window, f);
11984 } else
11985 w = window.open(u, s.name, f);
11986 } catch (ex) {
11987 // Ignore
11988 }
11989
11990 if (!w)
11991 alert(t.editor.getLang('popup_blocked'));
11992 },
11993
11994 close : function(w) {
11995 w.close();
11996 this.onClose.dispatch(this);
11997 },
11998
11999 createInstance : function(cl, a, b, c, d, e) {
12000 var f = tinymce.resolve(cl);
12001
12002 return new f(a, b, c, d, e);
12003 },
12004
12005 confirm : function(t, cb, s, w) {
12006 w = w || window;
12007
12008 cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t))));
12009 },
12010
12011 alert : function(tx, cb, s, w) {
12012 var t = this;
12013
12014 w = w || window;
12015 w.alert(t._decode(t.editor.getLang(tx, tx)));
12016
12017 if (cb)
12018 cb.call(s || t);
12019 },
12020
12021 // Internal functions
12022
12023 _decode : function(s) {
12024 return tinymce.DOM.decode(s).replace(/\\n/g, '\n');
12025 }
12026 });
12027}(tinymce));(function(tinymce) {
12028 tinymce.CommandManager = function() {
12029 var execCommands = {}, queryStateCommands = {}, queryValueCommands = {};
12030
12031 function add(collection, cmd, func, scope) {
12032 if (typeof(cmd) == 'string')
12033 cmd = [cmd];
12034
12035 tinymce.each(cmd, function(cmd) {
12036 collection[cmd.toLowerCase()] = {func : func, scope : scope};
12037 });
12038 };
12039
12040 tinymce.extend(this, {
12041 add : function(cmd, func, scope) {
12042 add(execCommands, cmd, func, scope);
12043 },
12044
12045 addQueryStateHandler : function(cmd, func, scope) {
12046 add(queryStateCommands, cmd, func, scope);
12047 },
12048
12049 addQueryValueHandler : function(cmd, func, scope) {
12050 add(queryValueCommands, cmd, func, scope);
12051 },
12052
12053 execCommand : function(scope, cmd, ui, value, args) {
12054 if (cmd = execCommands[cmd.toLowerCase()]) {
12055 if (cmd.func.call(scope || cmd.scope, ui, value, args) !== false)
12056 return true;
12057 }
12058 },
12059
12060 queryCommandValue : function() {
12061 if (cmd = queryValueCommands[cmd.toLowerCase()])
12062 return cmd.func.call(scope || cmd.scope, ui, value, args);
12063 },
12064
12065 queryCommandState : function() {
12066 if (cmd = queryStateCommands[cmd.toLowerCase()])
12067 return cmd.func.call(scope || cmd.scope, ui, value, args);
12068 }
12069 });
12070 };
12071
12072 tinymce.GlobalCommands = new tinymce.CommandManager();
12073})(tinymce);(function(tinymce) {
12074 function processRange(dom, start, end, callback) {
12075 var ancestor, n, startPoint, endPoint, sib;
12076
12077 function findEndPoint(n, c) {
12078 do {
12079 if (n.parentNode == c)
12080 return n;
12081
12082 n = n.parentNode;
12083 } while(n);
12084 };
12085
12086 function process(n) {
12087 callback(n);
12088 tinymce.walk(n, callback, 'childNodes');
12089 };
12090
12091 // Find common ancestor and end points
12092 ancestor = dom.findCommonAncestor(start, end);
12093 startPoint = findEndPoint(start, ancestor) || start;
12094 endPoint = findEndPoint(end, ancestor) || end;
12095
12096 // Process left leaf
12097 for (n = start; n && n != startPoint; n = n.parentNode) {
12098 for (sib = n.nextSibling; sib; sib = sib.nextSibling)
12099 process(sib);
12100 }
12101
12102 // Process middle from start to end point
12103 if (startPoint != endPoint) {
12104 for (n = startPoint.nextSibling; n && n != endPoint; n = n.nextSibling)
12105 process(n);
12106 } else
12107 process(startPoint);
12108
12109 // Process right leaf
12110 for (n = end; n && n != endPoint; n = n.parentNode) {
12111 for (sib = n.previousSibling; sib; sib = sib.previousSibling)
12112 process(sib);
12113 }
12114 };
12115
12116 tinymce.GlobalCommands.add('RemoveFormat', function() {
12117 var ed = this, dom = ed.dom, s = ed.selection, r = s.getRng(1), nodes = [], bm, start, end, sc, so, ec, eo, n;
12118
12119 function findFormatRoot(n) {
12120 var sp;
12121
12122 dom.getParent(n, function(n) {
12123 if (dom.is(n, ed.getParam('removeformat_selector')))
12124 sp = n;
12125
12126 return dom.isBlock(n);
12127 }, ed.getBody());
12128
12129 return sp;
12130 };
12131
12132 function collect(n) {
12133 if (dom.is(n, ed.getParam('removeformat_selector')))
12134 nodes.push(n);
12135 };
12136
12137 function walk(n) {
12138 collect(n);
12139 tinymce.walk(n, collect, 'childNodes');
12140 };
12141
12142 bm = s.getBookmark();
12143 sc = r.startContainer;
12144 ec = r.endContainer;
12145 so = r.startOffset;
12146 eo = r.endOffset;
12147 sc = sc.nodeType == 1 ? sc.childNodes[Math.min(so, sc.childNodes.length - 1)] : sc;
12148 ec = ec.nodeType == 1 ? ec.childNodes[Math.min(so == eo ? eo : eo - 1, ec.childNodes.length - 1)] : ec;
12149
12150 // Same container
12151 if (sc == ec) { // TEXT_NODE
12152 start = findFormatRoot(sc);
12153
12154 // Handle single text node
12155 if (sc.nodeType == 3) {
12156 if (start && start.nodeType == 1) { // ELEMENT
12157 n = sc.splitText(so);
12158 n.splitText(eo - so);
12159 dom.split(start, n);
12160
12161 s.moveToBookmark(bm);
12162 }
12163
12164 return;
12165 }
12166
12167 // Handle single element
12168 walk(dom.split(start, sc) || sc);
12169 } else {
12170 // Find start/end format root
12171 start = findFormatRoot(sc);
12172 end = findFormatRoot(ec);
12173
12174 // Split start text node
12175 if (start) {
12176 if (sc.nodeType == 3) { // TEXT
12177 // Since IE doesn't support white space nodes in the DOM we need to
12178 // add this invisible character so that the splitText function can split the contents
12179 if (so == sc.nodeValue.length)
12180 sc.nodeValue += '\uFEFF'; // Yet another pesky IE fix
12181
12182 sc = sc.splitText(so);
12183 }
12184 }
12185
12186 // Split end text node
12187 if (end) {
12188 if (ec.nodeType == 3) // TEXT
12189 ec.splitText(eo);
12190 }
12191
12192 // If the start and end format root is the same then we need to wrap
12193 // the end node in a span since the split calls might change the reference
12194 // Example: <p><b><em>x[yz<span>---</span>12]3</em></b></p>
12195 if (start && start == end)
12196 dom.replace(dom.create('span', {id : '__end'}, ec.cloneNode(true)), ec);
12197
12198 // Split all start containers down to the format root
12199 if (start)
12200 start = dom.split(start, sc);
12201 else
12202 start = sc;
12203
12204 // If there is a span wrapper use that one instead
12205 if (n = dom.get('__end')) {
12206 ec = n;
12207 end = findFormatRoot(ec);
12208 }
12209
12210 // Split all end containers down to the format root
12211 if (end)
12212 end = dom.split(end, ec);
12213 else
12214 end = ec;
12215
12216 // Collect nodes in between
12217 processRange(dom, start, end, collect);
12218
12219 // Remove invisible character for IE workaround if we find it
12220 if (sc.nodeValue == '\uFEFF')
12221 sc.nodeValue = '';
12222
12223 // Process start/end container elements
12224 walk(ec);
12225 walk(sc);
12226 }
12227
12228 // Remove all collected nodes
12229 tinymce.each(nodes, function(n) {
12230 dom.remove(n, 1);
12231 });
12232
12233 // Remove leftover wrapper
12234 dom.remove('__end', 1);
12235
12236 s.moveToBookmark(bm);
12237 });
12238})(tinymce);
12239(function(tinymce) {
12240 tinymce.GlobalCommands.add('mceBlockQuote', function() {
12241 var ed = this, s = ed.selection, dom = ed.dom, sb, eb, n, bm, bq, r, bq2, i, nl;
12242
12243 function getBQ(e) {
12244 return dom.getParent(e, function(n) {return n.nodeName === 'BLOCKQUOTE';});
12245 };
12246
12247 // Get start/end block
12248 sb = dom.getParent(s.getStart(), dom.isBlock);
12249 eb = dom.getParent(s.getEnd(), dom.isBlock);
12250
12251 // Remove blockquote(s)
12252 if (bq = getBQ(sb)) {
12253 if (sb != eb || sb.childNodes.length > 1 || (sb.childNodes.length == 1 && sb.firstChild.nodeName != 'BR'))
12254 bm = s.getBookmark();
12255
12256 // Move all elements after the end block into new bq
12257 if (getBQ(eb)) {
12258 bq2 = bq.cloneNode(false);
12259
12260 while (n = eb.nextSibling)
12261 bq2.appendChild(n.parentNode.removeChild(n));
12262 }
12263
12264 // Add new bq after
12265 if (bq2)
12266 dom.insertAfter(bq2, bq);
12267
12268 // Move all selected blocks after the current bq
12269 nl = s.getSelectedBlocks(sb, eb);
12270 for (i = nl.length - 1; i >= 0; i--) {
12271 dom.insertAfter(nl[i], bq);
12272 }
12273
12274 // Empty bq, then remove it
12275 if (/^\s*$/.test(bq.innerHTML))
12276 dom.remove(bq, 1); // Keep children so boomark restoration works correctly
12277
12278 // Empty bq, then remote it
12279 if (bq2 && /^\s*$/.test(bq2.innerHTML))
12280 dom.remove(bq2, 1); // Keep children so boomark restoration works correctly
12281
12282 if (!bm) {
12283 // Move caret inside empty block element
12284 if (!tinymce.isIE) {
12285 r = ed.getDoc().createRange();
12286 r.setStart(sb, 0);
12287 r.setEnd(sb, 0);
12288 s.setRng(r);
12289 } else {
12290 s.select(sb);
12291 s.collapse(0);
12292
12293 // IE misses the empty block some times element so we must move back the caret
12294 if (dom.getParent(s.getStart(), dom.isBlock) != sb) {
12295 r = s.getRng();
12296 r.move('character', -1);
12297 r.select();
12298 }
12299 }
12300 } else
12301 ed.selection.moveToBookmark(bm);
12302
12303 return;
12304 }
12305
12306 // Since IE can start with a totally empty document we need to add the first bq and paragraph
12307 if (tinymce.isIE && !sb && !eb) {
12308 ed.getDoc().execCommand('Indent');
12309 n = getBQ(s.getNode());
12310 n.style.margin = n.dir = ''; // IE adds margin and dir to bq
12311 return;
12312 }
12313
12314 if (!sb || !eb)
12315 return;
12316
12317 // If empty paragraph node then do not use bookmark
12318 if (sb != eb || sb.childNodes.length > 1 || (sb.childNodes.length == 1 && sb.firstChild.nodeName != 'BR'))
12319 bm = s.getBookmark();
12320
12321 // Move selected block elements into a bq
12322 tinymce.each(s.getSelectedBlocks(getBQ(s.getStart()), getBQ(s.getEnd())), function(e) {
12323 // Found existing BQ add to this one
12324 if (e.nodeName == 'BLOCKQUOTE' && !bq) {
12325 bq = e;
12326 return;
12327 }
12328
12329 // No BQ found, create one
12330 if (!bq) {
12331 bq = dom.create('blockquote');
12332 e.parentNode.insertBefore(bq, e);
12333 }
12334
12335 // Add children from existing BQ
12336 if (e.nodeName == 'BLOCKQUOTE' && bq) {
12337 n = e.firstChild;
12338
12339 while (n) {
12340 bq.appendChild(n.cloneNode(true));
12341 n = n.nextSibling;
12342 }
12343
12344 dom.remove(e);
12345 return;
12346 }
12347
12348 // Add non BQ element to BQ
12349 bq.appendChild(dom.remove(e));
12350 });
12351
12352 if (!bm) {
12353 // Move caret inside empty block element
12354 if (!tinymce.isIE) {
12355 r = ed.getDoc().createRange();
12356 r.setStart(sb, 0);
12357 r.setEnd(sb, 0);
12358 s.setRng(r);
12359 } else {
12360 s.select(sb);
12361 s.collapse(1);
12362 }
12363 } else
12364 s.moveToBookmark(bm);
12365 });
12366})(tinymce);
12367(function(tinymce) {
12368 tinymce.each(['Cut', 'Copy', 'Paste'], function(cmd) {
12369 tinymce.GlobalCommands.add(cmd, function() {
12370 var ed = this, doc = ed.getDoc();
12371
12372 try {
12373 doc.execCommand(cmd, false, null);
12374
12375 // On WebKit the command will just be ignored if it's not enabled
12376 if (!doc.queryCommandEnabled(cmd))
12377 throw 'Error';
12378 } catch (ex) {
12379 if (tinymce.isGecko) {
12380 ed.windowManager.confirm(ed.getLang('clipboard_msg'), function(s) {
12381 if (s)
12382 open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', '_blank');
12383 });
12384 } else
12385 ed.windowManager.alert(ed.getLang('clipboard_no_support'));
12386 }
12387 });
12388 });
12389})(tinymce);
12390(function(tinymce) {
12391 tinymce.GlobalCommands.add('InsertHorizontalRule', function() {
12392 if (tinymce.isOpera)
12393 return this.getDoc().execCommand('InsertHorizontalRule', false, '');
12394
12395 this.selection.setContent('<hr />');
12396 });
12397})(tinymce);
12398(function() {
12399 var cmds = tinymce.GlobalCommands;
12400
12401 cmds.add(['mceEndUndoLevel', 'mceAddUndoLevel'], function() {
12402 this.undoManager.add();
12403 });
12404
12405 cmds.add('Undo', function() {
12406 var ed = this;
12407
12408 if (ed.settings.custom_undo_redo) {
12409 ed.undoManager.undo();
12410 ed.nodeChanged();
12411 return true;
12412 }
12413
12414 return false; // Run browser command
12415 });
12416
12417 cmds.add('Redo', function() {
12418 var ed = this;
12419
12420 if (ed.settings.custom_undo_redo) {
12421 ed.undoManager.redo();
12422 ed.nodeChanged();
12423 return true;
12424 }
12425
12426 return false; // Run browser command
12427 });
12428})();