MDL-27890 tinymce - allow it to be smaller
[moodle.git] / lib / editor / tinymce / tiny_mce / 3.4.2 / tiny_mce_src.js
1 (function(win) {
2         var whiteSpaceRe = /^\s*|\s*$/g,
3                 undefined, isRegExpBroken = 'B'.replace(/A(.)|B/, '$1') === '$1';
5         var tinymce = {
6                 majorVersion : '@@tinymce_major_version@@',
8                 minorVersion : '@@tinymce_minor_version@@',
10                 releaseDate : '@@tinymce_release_date@@',
12                 _init : function() {
13                         var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
15                         t.isOpera = win.opera && opera.buildNumber;
17                         t.isWebKit = /WebKit/.test(ua);
19                         t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName);
21                         t.isIE6 = t.isIE && /MSIE [56]/.test(ua);
23                         t.isGecko = !t.isWebKit && /Gecko/.test(ua);
25                         t.isMac = ua.indexOf('Mac') != -1;
27                         t.isAir = /adobeair/i.test(ua);
29                         t.isIDevice = /(iPad|iPhone)/.test(ua);
31                         // TinyMCE .NET webcontrol might be setting the values for TinyMCE
32                         if (win.tinyMCEPreInit) {
33                                 t.suffix = tinyMCEPreInit.suffix;
34                                 t.baseURL = tinyMCEPreInit.base;
35                                 t.query = tinyMCEPreInit.query;
36                                 return;
37                         }
39                         // Get suffix and base
40                         t.suffix = '';
42                         // If base element found, add that infront of baseURL
43                         nl = d.getElementsByTagName('base');
44                         for (i=0; i<nl.length; i++) {
45                                 if (v = nl[i].href) {
46                                         // Host only value like http://site.com or http://site.com:8008
47                                         if (/^https?:\/\/[^\/]+$/.test(v))
48                                                 v += '/';
50                                         base = v ? v.match(/.*\//)[0] : ''; // Get only directory
51                                 }
52                         }
54                         function getBase(n) {
55                                 if (n.src && /tiny_mce(|_gzip|_jquery|_prototype|_full)(_dev|_src)?.js/.test(n.src)) {
56                                         if (/_(src|dev)\.js/g.test(n.src))
57                                                 t.suffix = '_src';
59                                         if ((p = n.src.indexOf('?')) != -1)
60                                                 t.query = n.src.substring(p + 1);
62                                         t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));
64                                         // If path to script is relative and a base href was found add that one infront
65                                         // the src property will always be an absolute one on non IE browsers and IE 8
66                                         // so this logic will basically only be executed on older IE versions
67                                         if (base && t.baseURL.indexOf('://') == -1 && t.baseURL.indexOf('/') !== 0)
68                                                 t.baseURL = base + t.baseURL;
70                                         return t.baseURL;
71                                 }
73                                 return null;
74                         };
76                         // Check document
77                         nl = d.getElementsByTagName('script');
78                         for (i=0; i<nl.length; i++) {
79                                 if (getBase(nl[i]))
80                                         return;
81                         }
83                         // Check head
84                         n = d.getElementsByTagName('head')[0];
85                         if (n) {
86                                 nl = n.getElementsByTagName('script');
87                                 for (i=0; i<nl.length; i++) {
88                                         if (getBase(nl[i]))
89                                                 return;
90                                 }
91                         }
93                         return;
94                 },
96                 is : function(o, t) {
97                         if (!t)
98                                 return o !== undefined;
100                         if (t == 'array' && (o.hasOwnProperty && o instanceof Array))
101                                 return true;
103                         return typeof(o) == t;
104                 },
106                 makeMap : function(items, delim, map) {
107                         var i;
109                         items = items || [];
110                         delim = delim || ',';
112                         if (typeof(items) == "string")
113                                 items = items.split(delim);
115                         map = map || {};
117                         i = items.length;
118                         while (i--)
119                                 map[items[i]] = {};
121                         return map;
122                 },
124                 each : function(o, cb, s) {
125                         var n, l;
127                         if (!o)
128                                 return 0;
130                         s = s || o;
132                         if (o.length !== undefined) {
133                                 // Indexed arrays, needed for Safari
134                                 for (n=0, l = o.length; n < l; n++) {
135                                         if (cb.call(s, o[n], n, o) === false)
136                                                 return 0;
137                                 }
138                         } else {
139                                 // Hashtables
140                                 for (n in o) {
141                                         if (o.hasOwnProperty(n)) {
142                                                 if (cb.call(s, o[n], n, o) === false)
143                                                         return 0;
144                                         }
145                                 }
146                         }
148                         return 1;
149                 },
152                 map : function(a, f) {
153                         var o = [];
155                         tinymce.each(a, function(v) {
156                                 o.push(f(v));
157                         });
159                         return o;
160                 },
162                 grep : function(a, f) {
163                         var o = [];
165                         tinymce.each(a, function(v) {
166                                 if (!f || f(v))
167                                         o.push(v);
168                         });
170                         return o;
171                 },
173                 inArray : function(a, v) {
174                         var i, l;
176                         if (a) {
177                                 for (i = 0, l = a.length; i < l; i++) {
178                                         if (a[i] === v)
179                                                 return i;
180                                 }
181                         }
183                         return -1;
184                 },
186                 extend : function(o, e) {
187                         var i, l, a = arguments;
189                         for (i = 1, l = a.length; i < l; i++) {
190                                 e = a[i];
192                                 tinymce.each(e, function(v, n) {
193                                         if (v !== undefined)
194                                                 o[n] = v;
195                                 });
196                         }
198                         return o;
199                 },
202                 trim : function(s) {
203                         return (s ? '' + s : '').replace(whiteSpaceRe, '');
204                 },
206                 create : function(s, p, root) {
207                         var t = this, sp, ns, cn, scn, c, de = 0;
209                         // Parse : <prefix> <class>:<super class>
210                         s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);
211                         cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name
213                         // Create namespace for new class
214                         ns = t.createNS(s[3].replace(/\.\w+$/, ''), root);
216                         // Class already exists
217                         if (ns[cn])
218                                 return;
220                         // Make pure static class
221                         if (s[2] == 'static') {
222                                 ns[cn] = p;
224                                 if (this.onCreate)
225                                         this.onCreate(s[2], s[3], ns[cn]);
227                                 return;
228                         }
230                         // Create default constructor
231                         if (!p[cn]) {
232                                 p[cn] = function() {};
233                                 de = 1;
234                         }
236                         // Add constructor and methods
237                         ns[cn] = p[cn];
238                         t.extend(ns[cn].prototype, p);
240                         // Extend
241                         if (s[5]) {
242                                 sp = t.resolve(s[5]).prototype;
243                                 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name
245                                 // Extend constructor
246                                 c = ns[cn];
247                                 if (de) {
248                                         // Add passthrough constructor
249                                         ns[cn] = function() {
250                                                 return sp[scn].apply(this, arguments);
251                                         };
252                                 } else {
253                                         // Add inherit constructor
254                                         ns[cn] = function() {
255                                                 this.parent = sp[scn];
256                                                 return c.apply(this, arguments);
257                                         };
258                                 }
259                                 ns[cn].prototype[cn] = ns[cn];
261                                 // Add super methods
262                                 t.each(sp, function(f, n) {
263                                         ns[cn].prototype[n] = sp[n];
264                                 });
266                                 // Add overridden methods
267                                 t.each(p, function(f, n) {
268                                         // Extend methods if needed
269                                         if (sp[n]) {
270                                                 ns[cn].prototype[n] = function() {
271                                                         this.parent = sp[n];
272                                                         return f.apply(this, arguments);
273                                                 };
274                                         } else {
275                                                 if (n != cn)
276                                                         ns[cn].prototype[n] = f;
277                                         }
278                                 });
279                         }
281                         // Add static methods
282                         t.each(p['static'], function(f, n) {
283                                 ns[cn][n] = f;
284                         });
286                         if (this.onCreate)
287                                 this.onCreate(s[2], s[3], ns[cn].prototype);
288                 },
290                 walk : function(o, f, n, s) {
291                         s = s || this;
293                         if (o) {
294                                 if (n)
295                                         o = o[n];
297                                 tinymce.each(o, function(o, i) {
298                                         if (f.call(s, o, i, n) === false)
299                                                 return false;
301                                         tinymce.walk(o, f, n, s);
302                                 });
303                         }
304                 },
306                 createNS : function(n, o) {
307                         var i, v;
309                         o = o || win;
311                         n = n.split('.');
312                         for (i=0; i<n.length; i++) {
313                                 v = n[i];
315                                 if (!o[v])
316                                         o[v] = {};
318                                 o = o[v];
319                         }
321                         return o;
322                 },
324                 resolve : function(n, o) {
325                         var i, l;
327                         o = o || win;
329                         n = n.split('.');
330                         for (i = 0, l = n.length; i < l; i++) {
331                                 o = o[n[i]];
333                                 if (!o)
334                                         break;
335                         }
337                         return o;
338                 },
340                 addUnload : function(f, s) {
341                         var t = this;
343                         f = {func : f, scope : s || this};
345                         if (!t.unloads) {
346                                 function unload() {
347                                         var li = t.unloads, o, n;
349                                         if (li) {
350                                                 // Call unload handlers
351                                                 for (n in li) {
352                                                         o = li[n];
354                                                         if (o && o.func)
355                                                                 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy
356                                                 }
358                                                 // Detach unload function
359                                                 if (win.detachEvent) {
360                                                         win.detachEvent('onbeforeunload', fakeUnload);
361                                                         win.detachEvent('onunload', unload);
362                                                 } else if (win.removeEventListener)
363                                                         win.removeEventListener('unload', unload, false);
365                                                 // Destroy references
366                                                 t.unloads = o = li = w = unload = 0;
368                                                 // Run garbarge collector on IE
369                                                 if (win.CollectGarbage)
370                                                         CollectGarbage();
371                                         }
372                                 };
374                                 function fakeUnload() {
375                                         var d = document;
377                                         // Is there things still loading, then do some magic
378                                         if (d.readyState == 'interactive') {
379                                                 function stop() {
380                                                         // Prevent memory leak
381                                                         d.detachEvent('onstop', stop);
383                                                         // Call unload handler
384                                                         if (unload)
385                                                                 unload();
387                                                         d = 0;
388                                                 };
390                                                 // Fire unload when the currently loading page is stopped
391                                                 if (d)
392                                                         d.attachEvent('onstop', stop);
394                                                 // Remove onstop listener after a while to prevent the unload function
395                                                 // to execute if the user presses cancel in an onbeforeunload
396                                                 // confirm dialog and then presses the browser stop button
397                                                 win.setTimeout(function() {
398                                                         if (d)
399                                                                 d.detachEvent('onstop', stop);
400                                                 }, 0);
401                                         }
402                                 };
404                                 // Attach unload handler
405                                 if (win.attachEvent) {
406                                         win.attachEvent('onunload', unload);
407                                         win.attachEvent('onbeforeunload', fakeUnload);
408                                 } else if (win.addEventListener)
409                                         win.addEventListener('unload', unload, false);
411                                 // Setup initial unload handler array
412                                 t.unloads = [f];
413                         } else
414                                 t.unloads.push(f);
416                         return f;
417                 },
419                 removeUnload : function(f) {
420                         var u = this.unloads, r = null;
422                         tinymce.each(u, function(o, i) {
423                                 if (o && o.func == f) {
424                                         u.splice(i, 1);
425                                         r = f;
426                                         return false;
427                                 }
428                         });
430                         return r;
431                 },
433                 explode : function(s, d) {
434                         return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;
435                 },
437                 _addVer : function(u) {
438                         var v;
440                         if (!this.query)
441                                 return u;
443                         v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;
445                         if (u.indexOf('#') == -1)
446                                 return u + v;
448                         return u.replace('#', v + '#');
449                 },
451                 // Fix function for IE 9 where regexps isn't working correctly
452                 // Todo: remove me once MS fixes the bug
453                 _replace : function(find, replace, str) {
454                         // On IE9 we have to fake $x replacement
455                         if (isRegExpBroken) {
456                                 return str.replace(find, function() {
457                                         var val = replace, args = arguments, i;
459                                         for (i = 0; i < args.length - 2; i++) {
460                                                 if (args[i] === undefined) {
461                                                         val = val.replace(new RegExp('\\$' + i, 'g'), '');
462                                                 } else {
463                                                         val = val.replace(new RegExp('\\$' + i, 'g'), args[i]);
464                                                 }
465                                         }
467                                         return val;
468                                 });
469                         }
471                         return str.replace(find, replace);
472                 }
474                 };
476         // Initialize the API
477         tinymce._init();
479         // Expose tinymce namespace to the global namespace (window)
480         win.tinymce = win.tinyMCE = tinymce;
482         // Describe the different namespaces
484         })(window);
485 tinymce.create('tinymce.util.Dispatcher', {
486         scope : null,
487         listeners : null,
489         Dispatcher : function(s) {
490                 this.scope = s || this;
491                 this.listeners = [];
492         },
494         add : function(cb, s) {
495                 this.listeners.push({cb : cb, scope : s || this.scope});
497                 return cb;
498         },
500         addToTop : function(cb, s) {
501                 this.listeners.unshift({cb : cb, scope : s || this.scope});
503                 return cb;
504         },
506         remove : function(cb) {
507                 var l = this.listeners, o = null;
509                 tinymce.each(l, function(c, i) {
510                         if (cb == c.cb) {
511                                 o = cb;
512                                 l.splice(i, 1);
513                                 return false;
514                         }
515                 });
517                 return o;
518         },
520         dispatch : function() {
521                 var s, a = arguments, i, li = this.listeners, c;
523                 // Needs to be a real loop since the listener count might change while looping
524                 // And this is also more efficient
525                 for (i = 0; i<li.length; i++) {
526                         c = li[i];
527                         s = c.cb.apply(c.scope, a);
529                         if (s === false)
530                                 break;
531                 }
533                 return s;
534         }
536         });
537 (function() {
538         var each = tinymce.each;
540         tinymce.create('tinymce.util.URI', {
541                 URI : function(u, s) {
542                         var t = this, o, a, b;
544                         // Trim whitespace
545                         u = tinymce.trim(u);
547                         // Default settings
548                         s = t.settings = s || {};
550                         // Strange app protocol or local anchor
551                         if (/^(mailto|tel|news|javascript|about|data):/i.test(u) || /^\s*#/.test(u)) {
552                                 t.source = u;
553                                 return;
554                         }
556                         // Absolute path with no host, fake host and protocol
557                         if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)
558                                 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;
560                         // Relative path http:// or protocol relative //path
561                         if (!/^\w*:?\/\//.test(u))
562                                 u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u);
564                         // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
565                         u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
566                         u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);
567                         each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {
568                                 var s = u[i];
570                                 // Zope 3 workaround, they use @@something
571                                 if (s)
572                                         s = s.replace(/\(mce_at\)/g, '@@');
574                                 t[v] = s;
575                         });
577                         if (b = s.base_uri) {
578                                 if (!t.protocol)
579                                         t.protocol = b.protocol;
581                                 if (!t.userInfo)
582                                         t.userInfo = b.userInfo;
584                                 if (!t.port && t.host == 'mce_host')
585                                         t.port = b.port;
587                                 if (!t.host || t.host == 'mce_host')
588                                         t.host = b.host;
590                                 t.source = '';
591                         }
593                         //t.path = t.path || '/';
594                 },
596                 setPath : function(p) {
597                         var t = this;
599                         p = /^(.*?)\/?(\w+)?$/.exec(p);
601                         // Update path parts
602                         t.path = p[0];
603                         t.directory = p[1];
604                         t.file = p[2];
606                         // Rebuild source
607                         t.source = '';
608                         t.getURI();
609                 },
611                 toRelative : function(u) {
612                         var t = this, o;
614                         if (u === "./")
615                                 return u;
617                         u = new tinymce.util.URI(u, {base_uri : t});
619                         // Not on same domain/port or protocol
620                         if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)
621                                 return u.getURI();
623                         o = t.toRelPath(t.path, u.path);
625                         // Add query
626                         if (u.query)
627                                 o += '?' + u.query;
629                         // Add anchor
630                         if (u.anchor)
631                                 o += '#' + u.anchor;
633                         return o;
634                 },
635         
636                 toAbsolute : function(u, nh) {
637                         var u = new tinymce.util.URI(u, {base_uri : this});
639                         return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0);
640                 },
642                 toRelPath : function(base, path) {
643                         var items, bp = 0, out = '', i, l;
645                         // Split the paths
646                         base = base.substring(0, base.lastIndexOf('/'));
647                         base = base.split('/');
648                         items = path.split('/');
650                         if (base.length >= items.length) {
651                                 for (i = 0, l = base.length; i < l; i++) {
652                                         if (i >= items.length || base[i] != items[i]) {
653                                                 bp = i + 1;
654                                                 break;
655                                         }
656                                 }
657                         }
659                         if (base.length < items.length) {
660                                 for (i = 0, l = items.length; i < l; i++) {
661                                         if (i >= base.length || base[i] != items[i]) {
662                                                 bp = i + 1;
663                                                 break;
664                                         }
665                                 }
666                         }
668                         if (bp == 1)
669                                 return path;
671                         for (i = 0, l = base.length - (bp - 1); i < l; i++)
672                                 out += "../";
674                         for (i = bp - 1, l = items.length; i < l; i++) {
675                                 if (i != bp - 1)
676                                         out += "/" + items[i];
677                                 else
678                                         out += items[i];
679                         }
681                         return out;
682                 },
684                 toAbsPath : function(base, path) {
685                         var i, nb = 0, o = [], tr, outPath;
687                         // Split paths
688                         tr = /\/$/.test(path) ? '/' : '';
689                         base = base.split('/');
690                         path = path.split('/');
692                         // Remove empty chunks
693                         each(base, function(k) {
694                                 if (k)
695                                         o.push(k);
696                         });
698                         base = o;
700                         // Merge relURLParts chunks
701                         for (i = path.length - 1, o = []; i >= 0; i--) {
702                                 // Ignore empty or .
703                                 if (path[i].length == 0 || path[i] == ".")
704                                         continue;
706                                 // Is parent
707                                 if (path[i] == '..') {
708                                         nb++;
709                                         continue;
710                                 }
712                                 // Move up
713                                 if (nb > 0) {
714                                         nb--;
715                                         continue;
716                                 }
718                                 o.push(path[i]);
719                         }
721                         i = base.length - nb;
723                         // If /a/b/c or /
724                         if (i <= 0)
725                                 outPath = o.reverse().join('/');
726                         else
727                                 outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');
729                         // Add front / if it's needed
730                         if (outPath.indexOf('/') !== 0)
731                                 outPath = '/' + outPath;
733                         // Add traling / if it's needed
734                         if (tr && outPath.lastIndexOf('/') !== outPath.length - 1)
735                                 outPath += tr;
737                         return outPath;
738                 },
740                 getURI : function(nh) {
741                         var s, t = this;
743                         // Rebuild source
744                         if (!t.source || nh) {
745                                 s = '';
747                                 if (!nh) {
748                                         if (t.protocol)
749                                                 s += t.protocol + '://';
751                                         if (t.userInfo)
752                                                 s += t.userInfo + '@';
754                                         if (t.host)
755                                                 s += t.host;
757                                         if (t.port)
758                                                 s += ':' + t.port;
759                                 }
761                                 if (t.path)
762                                         s += t.path;
764                                 if (t.query)
765                                         s += '?' + t.query;
767                                 if (t.anchor)
768                                         s += '#' + t.anchor;
770                                 t.source = s;
771                         }
773                         return t.source;
774                 }
775         });
776 })();
777 (function() {
778         var each = tinymce.each;
780         tinymce.create('static tinymce.util.Cookie', {
781                 getHash : function(n) {
782                         var v = this.get(n), h;
784                         if (v) {
785                                 each(v.split('&'), function(v) {
786                                         v = v.split('=');
787                                         h = h || {};
788                                         h[unescape(v[0])] = unescape(v[1]);
789                                 });
790                         }
792                         return h;
793                 },
795                 setHash : function(n, v, e, p, d, s) {
796                         var o = '';
798                         each(v, function(v, k) {
799                                 o += (!o ? '' : '&') + escape(k) + '=' + escape(v);
800                         });
802                         this.set(n, o, e, p, d, s);
803                 },
805                 get : function(n) {
806                         var c = document.cookie, e, p = n + "=", b;
808                         // Strict mode
809                         if (!c)
810                                 return;
812                         b = c.indexOf("; " + p);
814                         if (b == -1) {
815                                 b = c.indexOf(p);
817                                 if (b != 0)
818                                         return null;
819                         } else
820                                 b += 2;
822                         e = c.indexOf(";", b);
824                         if (e == -1)
825                                 e = c.length;
827                         return unescape(c.substring(b + p.length, e));
828                 },
830                 set : function(n, v, e, p, d, s) {
831                         document.cookie = n + "=" + escape(v) +
832                                 ((e) ? "; expires=" + e.toGMTString() : "") +
833                                 ((p) ? "; path=" + escape(p) : "") +
834                                 ((d) ? "; domain=" + d : "") +
835                                 ((s) ? "; secure" : "");
836                 },
838                 remove : function(n, p) {
839                         var d = new Date();
841                         d.setTime(d.getTime() - 1000);
843                         this.set(n, '', d, p, d);
844                 }
845         });
846 })();
847 (function() {
848         function serialize(o, quote) {
849                 var i, v, t;
851                 quote = quote || '"';
853                 if (o == null)
854                         return 'null';
856                 t = typeof o;
858                 if (t == 'string') {
859                         v = '\bb\tt\nn\ff\rr\""\'\'\\\\';
861                         return quote + o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g, function(a, b) {
862                                 // Make sure single quotes never get encoded inside double quotes for JSON compatibility
863                                 if (quote === '"' && a === "'")
864                                         return a;
866                                 i = v.indexOf(b);
868                                 if (i + 1)
869                                         return '\\' + v.charAt(i + 1);
871                                 a = b.charCodeAt().toString(16);
873                                 return '\\u' + '0000'.substring(a.length) + a;
874                         }) + quote;
875                 }
877                 if (t == 'object') {
878                         if (o.hasOwnProperty && o instanceof Array) {
879                                         for (i=0, v = '['; i<o.length; i++)
880                                                 v += (i > 0 ? ',' : '') + serialize(o[i], quote);
882                                         return v + ']';
883                                 }
885                                 v = '{';
887                                 for (i in o)
888                                         v += typeof o[i] != 'function' ? (v.length > 1 ? ',' + quote : quote) + i + quote +':' + serialize(o[i], quote) : '';
890                                 return v + '}';
891                 }
893                 return '' + o;
894         };
896         tinymce.util.JSON = {
897                 serialize: serialize,
899                 parse: function(s) {
900                         try {
901                                 return eval('(' + s + ')');
902                         } catch (ex) {
903                                 // Ignore
904                         }
905                 }
907                 };
908 })();
909 tinymce.create('static tinymce.util.XHR', {
910         send : function(o) {
911                 var x, t, w = window, c = 0;
913                 // Default settings
914                 o.scope = o.scope || this;
915                 o.success_scope = o.success_scope || o.scope;
916                 o.error_scope = o.error_scope || o.scope;
917                 o.async = o.async === false ? false : true;
918                 o.data = o.data || '';
920                 function get(s) {
921                         x = 0;
923                         try {
924                                 x = new ActiveXObject(s);
925                         } catch (ex) {
926                         }
928                         return x;
929                 };
931                 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');
933                 if (x) {
934                         if (x.overrideMimeType)
935                                 x.overrideMimeType(o.content_type);
937                         x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);
939                         if (o.content_type)
940                                 x.setRequestHeader('Content-Type', o.content_type);
942                         x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
944                         x.send(o.data);
946                         function ready() {
947                                 if (!o.async || x.readyState == 4 || c++ > 10000) {
948                                         if (o.success && c < 10000 && x.status == 200)
949                                                 o.success.call(o.success_scope, '' + x.responseText, x, o);
950                                         else if (o.error)
951                                                 o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);
953                                         x = null;
954                                 } else
955                                         w.setTimeout(ready, 10);
956                         };
958                         // Syncronous request
959                         if (!o.async)
960                                 return ready();
962                         // Wait for response, onReadyStateChange can not be used since it leaks memory in IE
963                         t = w.setTimeout(ready, 10);
964                 }
965         }
966 });
967 (function() {
968         var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;
970         tinymce.create('tinymce.util.JSONRequest', {
971                 JSONRequest : function(s) {
972                         this.settings = extend({
973                         }, s);
974                         this.count = 0;
975                 },
977                 send : function(o) {
978                         var ecb = o.error, scb = o.success;
980                         o = extend(this.settings, o);
982                         o.success = function(c, x) {
983                                 c = JSON.parse(c);
985                                 if (typeof(c) == 'undefined') {
986                                         c = {
987                                                 error : 'JSON Parse error.'
988                                         };
989                                 }
991                                 if (c.error)
992                                         ecb.call(o.error_scope || o.scope, c.error, x);
993                                 else
994                                         scb.call(o.success_scope || o.scope, c.result);
995                         };
997                         o.error = function(ty, x) {
998                                 if (ecb)
999                                         ecb.call(o.error_scope || o.scope, ty, x);
1000                         };
1002                         o.data = JSON.serialize({
1003                                 id : o.id || 'c' + (this.count++),
1004                                 method : o.method,
1005                                 params : o.params
1006                         });
1008                         // JSON content type for Ruby on rails. Bug: #1883287
1009                         o.content_type = 'application/json';
1011                         XHR.send(o);
1012                 },
1014                 'static' : {
1015                         sendRPC : function(o) {
1016                                 return new tinymce.util.JSONRequest().send(o);
1017                         }
1018                 }
1019         });
1020 }());
1021 (function(tinymce) {
1022         var namedEntities, baseEntities, reverseEntities,
1023                 attrsCharsRegExp = /[&\"\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
1024                 textCharsRegExp = /[<>&\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
1025                 rawCharsRegExp = /[<>&\"\']/g,
1026                 entityRegExp = /&(#)?([\w]+);/g,
1027                 asciiMap = {
1028                                 128 : "\u20AC", 130 : "\u201A", 131 : "\u0192", 132 : "\u201E", 133 : "\u2026", 134 : "\u2020",
1029                                 135 : "\u2021", 136 : "\u02C6", 137 : "\u2030", 138 : "\u0160", 139 : "\u2039", 140 : "\u0152",
1030                                 142 : "\u017D", 145 : "\u2018", 146 : "\u2019", 147 : "\u201C", 148 : "\u201D", 149 : "\u2022",
1031                                 150 : "\u2013", 151 : "\u2014", 152 : "\u02DC", 153 : "\u2122", 154 : "\u0161", 155 : "\u203A",
1032                                 156 : "\u0153", 158 : "\u017E", 159 : "\u0178"
1033                 };
1035         // Raw entities
1036         baseEntities = {
1037                 '"' : '&quot;',
1038                 "'" : '&#39;',
1039                 '<' : '&lt;',
1040                 '>' : '&gt;',
1041                 '&' : '&amp;'
1042         };
1044         // Reverse lookup table for raw entities
1045         reverseEntities = {
1046                 '&lt;' : '<',
1047                 '&gt;' : '>',
1048                 '&amp;' : '&',
1049                 '&quot;' : '"',
1050                 '&apos;' : "'"
1051         };
1053         // Decodes text by using the browser
1054         function nativeDecode(text) {
1055                 var elm;
1057                 elm = document.createElement("div");
1058                 elm.innerHTML = text;
1060                 return elm.textContent || elm.innerText || text;
1061         };
1063         // Build a two way lookup table for the entities
1064         function buildEntitiesLookup(items, radix) {
1065                 var i, chr, entity, lookup = {};
1067                 if (items) {
1068                         items = items.split(',');
1069                         radix = radix || 10;
1071                         // Build entities lookup table
1072                         for (i = 0; i < items.length; i += 2) {
1073                                 chr = String.fromCharCode(parseInt(items[i], radix));
1075                                 // Only add non base entities
1076                                 if (!baseEntities[chr]) {
1077                                         entity = '&' + items[i + 1] + ';';
1078                                         lookup[chr] = entity;
1079                                         lookup[entity] = chr;
1080                                 }
1081                         }
1083                         return lookup;
1084                 }
1085         };
1087         // Unpack entities lookup where the numbers are in radix 32 to reduce the size
1088         namedEntities = buildEntitiesLookup(
1089                 '50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' +
1090                 '5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' +
1091                 '5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' +
1092                 '5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' +
1093                 '68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' +
1094                 '6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' +
1095                 '6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' +
1096                 '75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' +
1097                 '7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' +
1098                 '7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' +
1099                 'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' +
1100                 'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' +
1101                 't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' +
1102                 'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' +
1103                 'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' +
1104                 '81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' +
1105                 '8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' +
1106                 '8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' +
1107                 '8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' +
1108                 '8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' +
1109                 'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' +
1110                 'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' +
1111                 'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' +
1112                 '80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' +
1113                 '811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro'
1114         , 32);
1116         tinymce.html = tinymce.html || {};
1118         tinymce.html.Entities = {
1119                 encodeRaw : function(text, attr) {
1120                         return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
1121                                 return baseEntities[chr] || chr;
1122                         });
1123                 },
1125                 encodeAllRaw : function(text) {
1126                         return ('' + text).replace(rawCharsRegExp, function(chr) {
1127                                 return baseEntities[chr] || chr;
1128                         });
1129                 },
1131                 encodeNumeric : function(text, attr) {
1132                         return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
1133                                 // Multi byte sequence convert it to a single entity
1134                                 if (chr.length > 1)
1135                                         return '&#' + (((chr.charCodeAt(0) - 0xD800) * 0x400) + (chr.charCodeAt(1) - 0xDC00) + 0x10000) + ';';
1137                                 return baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
1138                         });
1139                 },
1141                 encodeNamed : function(text, attr, entities) {
1142                         entities = entities || namedEntities;
1144                         return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
1145                                 return baseEntities[chr] || entities[chr] || chr;
1146                         });
1147                 },
1149                 getEncodeFunc : function(name, entities) {
1150                         var Entities = tinymce.html.Entities;
1152                         entities = buildEntitiesLookup(entities) || namedEntities;
1154                         function encodeNamedAndNumeric(text, attr) {
1155                                 return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
1156                                         return baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
1157                                 });
1158                         };
1160                         function encodeCustomNamed(text, attr) {
1161                                 return Entities.encodeNamed(text, attr, entities);
1162                         };
1164                         // Replace + with , to be compatible with previous TinyMCE versions
1165                         name = tinymce.makeMap(name.replace(/\+/g, ','));
1167                         // Named and numeric encoder
1168                         if (name.named && name.numeric)
1169                                 return encodeNamedAndNumeric;
1171                         // Named encoder
1172                         if (name.named) {
1173                                 // Custom names
1174                                 if (entities)
1175                                         return encodeCustomNamed;
1177                                 return Entities.encodeNamed;
1178                         }
1180                         // Numeric
1181                         if (name.numeric)
1182                                 return Entities.encodeNumeric;
1184                         // Raw encoder
1185                         return Entities.encodeRaw;
1186                 },
1188                 decode : function(text) {
1189                         return text.replace(entityRegExp, function(all, numeric, value) {
1190                                 if (numeric) {
1191                                         value = parseInt(value);
1193                                         // Support upper UTF
1194                                         if (value > 0xFFFF) {
1195                                                 value -= 0x10000;
1197                                                 return String.fromCharCode(0xD800 + (value >> 10), 0xDC00 + (value & 0x3FF));
1198                                         } else
1199                                                 return asciiMap[value] || String.fromCharCode(value);
1200                                 }
1202                                 return reverseEntities[all] || namedEntities[all] || nativeDecode(all);
1203                         });
1204                 }
1205         };
1206 })(tinymce);
1207 tinymce.html.Styles = function(settings, schema) {
1208         var rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,
1209                 urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,
1210                 styleRegExp = /\s*([^:]+):\s*([^;]+);?/g,
1211                 trimRightRegExp = /\s+$/,
1212                 urlColorRegExp = /rgb/,
1213                 undef, i, encodingLookup = {}, encodingItems;
1215         settings = settings || {};
1217         encodingItems = '\\" \\\' \\; \\: ; : _'.split(' ');
1218         for (i = 0; i < encodingItems.length; i++) {
1219                 encodingLookup[encodingItems[i]] = '_' + i;
1220                 encodingLookup['_' + i] = encodingItems[i];
1221         }
1223         function toHex(match, r, g, b) {
1224                 function hex(val) {
1225                         val = parseInt(val).toString(16);
1227                         return val.length > 1 ? val : '0' + val; // 0 -> 00
1228                 };
1230                 return '#' + hex(r) + hex(g) + hex(b);
1231         };
1233         return {
1234                 toHex : function(color) {
1235                         return color.replace(rgbRegExp, toHex);
1236                 },
1238                 parse : function(css) {
1239                         var styles = {}, matches, name, value, isEncoded, urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope || this;
1241                         function compress(prefix, suffix) {
1242                                 var top, right, bottom, left;
1244                                 // Get values and check it it needs compressing
1245                                 top = styles[prefix + '-top' + suffix];
1246                                 if (!top)
1247                                         return;
1249                                 right = styles[prefix + '-right' + suffix];
1250                                 if (top != right)
1251                                         return;
1253                                 bottom = styles[prefix + '-bottom' + suffix];
1254                                 if (right != bottom)
1255                                         return;
1257                                 left = styles[prefix + '-left' + suffix];
1258                                 if (bottom != left)
1259                                         return;
1261                                 // Compress
1262                                 styles[prefix + suffix] = left;
1263                                 delete styles[prefix + '-top' + suffix];
1264                                 delete styles[prefix + '-right' + suffix];
1265                                 delete styles[prefix + '-bottom' + suffix];
1266                                 delete styles[prefix + '-left' + suffix];
1267                         };
1269                         function canCompress(key) {
1270                                 var value = styles[key], i;
1272                                 if (!value || value.indexOf(' ') < 0)
1273                                         return;
1275                                 value = value.split(' ');
1276                                 i = value.length;
1277                                 while (i--) {
1278                                         if (value[i] !== value[0])
1279                                                 return false;
1280                                 }
1282                                 styles[key] = value[0];
1284                                 return true;
1285                         };
1287                         function compress2(target, a, b, c) {
1288                                 if (!canCompress(a))
1289                                         return;
1291                                 if (!canCompress(b))
1292                                         return;
1294                                 if (!canCompress(c))
1295                                         return;
1297                                 // Compress
1298                                 styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c];
1299                                 delete styles[a];
1300                                 delete styles[b];
1301                                 delete styles[c];
1302                         };
1304                         // Encodes the specified string by replacing all \" \' ; : with _<num>
1305                         function encode(str) {
1306                                 isEncoded = true;
1308                                 return encodingLookup[str];
1309                         };
1311                         // Decodes the specified string by replacing all _<num> with it's original value \" \' etc
1312                         // It will also decode the \" \' if keep_slashes is set to fale or omitted
1313                         function decode(str, keep_slashes) {
1314                                 if (isEncoded) {
1315                                         str = str.replace(/_[0-9]/g, function(str) {
1316                                                 return encodingLookup[str];
1317                                         });
1318                                 }
1320                                 if (!keep_slashes)
1321                                         str = str.replace(/\\([\'\";:])/g, "$1");
1323                                 return str;
1324                         }
1326                         if (css) {
1327                                 // Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing
1328                                 css = css.replace(/\\[\"\';:_]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, function(str) {
1329                                         return str.replace(/[;:]/g, encode);
1330                                 });
1332                                 // Parse styles
1333                                 while (matches = styleRegExp.exec(css)) {
1334                                         name = matches[1].replace(trimRightRegExp, '').toLowerCase();
1335                                         value = matches[2].replace(trimRightRegExp, '');
1337                                         if (name && value.length > 0) {
1338                                                 // Opera will produce 700 instead of bold in their style values
1339                                                 if (name === 'font-weight' && value === '700')
1340                                                         value = 'bold';
1341                                                 else if (name === 'color' || name === 'background-color') // Lowercase colors like RED
1342                                                         value = value.toLowerCase();            
1344                                                 // Convert RGB colors to HEX
1345                                                 value = value.replace(rgbRegExp, toHex);
1347                                                 // Convert URLs and force them into url('value') format
1348                                                 value = value.replace(urlOrStrRegExp, function(match, url, url2, url3, str, str2) {
1349                                                         str = str || str2;
1351                                                         if (str) {
1352                                                                 str = decode(str);
1354                                                                 // Force strings into single quote format
1355                                                                 return "'" + str.replace(/\'/g, "\\'") + "'";
1356                                                         }
1358                                                         url = decode(url || url2 || url3);
1360                                                         // Convert the URL to relative/absolute depending on config
1361                                                         if (urlConverter)
1362                                                                 url = urlConverter.call(urlConverterScope, url, 'style');
1364                                                         // Output new URL format
1365                                                         return "url('" + url.replace(/\'/g, "\\'") + "')";
1366                                                 });
1368                                                 styles[name] = isEncoded ? decode(value, true) : value;
1369                                         }
1371                                         styleRegExp.lastIndex = matches.index + matches[0].length;
1372                                 }
1374                                 // Compress the styles to reduce it's size for example IE will expand styles
1375                                 compress("border", "");
1376                                 compress("border", "-width");
1377                                 compress("border", "-color");
1378                                 compress("border", "-style");
1379                                 compress("padding", "");
1380                                 compress("margin", "");
1381                                 compress2('border', 'border-width', 'border-style', 'border-color');
1383                                 // Remove pointless border, IE produces these
1384                                 if (styles.border === 'medium none')
1385                                         delete styles.border;
1386                         }
1388                         return styles;
1389                 },
1391                 serialize : function(styles, element_name) {
1392                         var css = '', name, value;
1394                         function serializeStyles(name) {
1395                                 var styleList, i, l, name, value;
1397                                 styleList = schema.styles[name];
1398                                 if (styleList) {
1399                                         for (i = 0, l = styleList.length; i < l; i++) {
1400                                                 name = styleList[i];
1401                                                 value = styles[name];
1403                                                 if (value !== undef && value.length > 0)
1404                                                         css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
1405                                         }
1406                                 }
1407                         };
1409                         // Serialize styles according to schema
1410                         if (element_name && schema && schema.styles) {
1411                                 // Serialize global styles and element specific styles
1412                                 serializeStyles('*');
1413                                 serializeStyles(name);
1414                         } else {
1415                                 // Output the styles in the order they are inside the object
1416                                 for (name in styles) {
1417                                         value = styles[name];
1419                                         if (value !== undef && value.length > 0)
1420                                                 css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
1421                                 }
1422                         }
1424                         return css;
1425                 }
1426         };
1427 };
1428 (function(tinymce) {
1429         var transitional = {}, boolAttrMap, blockElementsMap, shortEndedElementsMap, nonEmptyElementsMap,
1430                 whiteSpaceElementsMap, selfClosingElementsMap, makeMap = tinymce.makeMap, each = tinymce.each;
1432         function split(str, delim) {
1433                 return str.split(delim || ',');
1434         };
1436         function unpack(lookup, data) {
1437                 var key, elements = {};
1439                 function replace(value) {
1440                         return value.replace(/[A-Z]+/g, function(key) {
1441                                 return replace(lookup[key]);
1442                         });
1443                 };
1445                 // Unpack lookup
1446                 for (key in lookup) {
1447                         if (lookup.hasOwnProperty(key))
1448                                 lookup[key] = replace(lookup[key]);
1449                 }
1451                 // Unpack and parse data into object map
1452                 replace(data).replace(/#/g, '#text').replace(/(\w+)\[([^\]]+)\]\[([^\]]*)\]/g, function(str, name, attributes, children) {
1453                         attributes = split(attributes, '|');
1455                         elements[name] = {
1456                                 attributes : makeMap(attributes),
1457                                 attributesOrder : attributes,
1458                                 children : makeMap(children, '|', {'#comment' : {}})
1459                         }
1460                 });
1462                 return elements;
1463         };
1465         // Build a lookup table for block elements both lowercase and uppercase
1466         blockElementsMap = 'h1,h2,h3,h4,h5,h6,hr,p,div,address,pre,form,table,tbody,thead,tfoot,' + 
1467                                                 'th,tr,td,li,ol,ul,caption,blockquote,center,dl,dt,dd,dir,fieldset,' + 
1468                                                 'noscript,menu,isindex,samp,header,footer,article,section,hgroup';
1469         blockElementsMap = makeMap(blockElementsMap, ',', makeMap(blockElementsMap.toUpperCase()));
1471         // This is the XHTML 1.0 transitional elements with it's attributes and children packed to reduce it's size
1472         transitional = unpack({
1473                 Z : 'H|K|N|O|P',
1474                 Y : 'X|form|R|Q',
1475                 ZG : 'E|span|width|align|char|charoff|valign',
1476                 X : 'p|T|div|U|W|isindex|fieldset|table',
1477                 ZF : 'E|align|char|charoff|valign',
1478                 W : 'pre|hr|blockquote|address|center|noframes',
1479                 ZE : 'abbr|axis|headers|scope|rowspan|colspan|align|char|charoff|valign|nowrap|bgcolor|width|height',
1480                 ZD : '[E][S]',
1481                 U : 'ul|ol|dl|menu|dir',
1482                 ZC : 'p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q',
1483                 T : 'h1|h2|h3|h4|h5|h6',
1484                 ZB : 'X|S|Q',
1485                 S : 'R|P',
1486                 ZA : 'a|G|J|M|O|P',
1487                 R : 'a|H|K|N|O',
1488                 Q : 'noscript|P',
1489                 P : 'ins|del|script',
1490                 O : 'input|select|textarea|label|button',
1491                 N : 'M|L',
1492                 M : 'em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym',
1493                 L : 'sub|sup',
1494                 K : 'J|I',
1495                 J : 'tt|i|b|u|s|strike',
1496                 I : 'big|small|font|basefont',
1497                 H : 'G|F',
1498                 G : 'br|span|bdo',
1499                 F : 'object|applet|img|map|iframe',
1500                 E : 'A|B|C',
1501                 D : 'accesskey|tabindex|onfocus|onblur',
1502                 C : 'onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup',
1503                 B : 'lang|xml:lang|dir',
1504                 A : 'id|class|style|title'
1505         }, 'script[id|charset|type|language|src|defer|xml:space][]' + 
1506                 'style[B|id|type|media|title|xml:space][]' + 
1507                 'object[E|declare|classid|codebase|data|type|codetype|archive|standby|width|height|usemap|name|tabindex|align|border|hspace|vspace][#|param|Y]' + 
1508                 'param[id|name|value|valuetype|type][]' + 
1509                 'p[E|align][#|S]' + 
1510                 'a[E|D|charset|type|name|href|hreflang|rel|rev|shape|coords|target][#|Z]' + 
1511                 'br[A|clear][]' + 
1512                 'span[E][#|S]' + 
1513                 'bdo[A|C|B][#|S]' + 
1514                 'applet[A|codebase|archive|code|object|alt|name|width|height|align|hspace|vspace][#|param|Y]' + 
1515                 'h1[E|align][#|S]' + 
1516                 'img[E|src|alt|name|longdesc|width|height|usemap|ismap|align|border|hspace|vspace][]' + 
1517                 'map[B|C|A|name][X|form|Q|area]' + 
1518                 'h2[E|align][#|S]' + 
1519                 'iframe[A|longdesc|name|src|frameborder|marginwidth|marginheight|scrolling|align|width|height][#|Y]' + 
1520                 'h3[E|align][#|S]' + 
1521                 'tt[E][#|S]' + 
1522                 'i[E][#|S]' + 
1523                 'b[E][#|S]' + 
1524                 'u[E][#|S]' + 
1525                 's[E][#|S]' + 
1526                 'strike[E][#|S]' + 
1527                 'big[E][#|S]' + 
1528                 'small[E][#|S]' + 
1529                 'font[A|B|size|color|face][#|S]' + 
1530                 'basefont[id|size|color|face][]' + 
1531                 'em[E][#|S]' + 
1532                 'strong[E][#|S]' + 
1533                 'dfn[E][#|S]' + 
1534                 'code[E][#|S]' + 
1535                 'q[E|cite][#|S]' + 
1536                 'samp[E][#|S]' + 
1537                 'kbd[E][#|S]' + 
1538                 'var[E][#|S]' + 
1539                 'cite[E][#|S]' + 
1540                 'abbr[E][#|S]' + 
1541                 'acronym[E][#|S]' + 
1542                 'sub[E][#|S]' + 
1543                 'sup[E][#|S]' + 
1544                 'input[E|D|type|name|value|checked|disabled|readonly|size|maxlength|src|alt|usemap|onselect|onchange|accept|align][]' + 
1545                 'select[E|name|size|multiple|disabled|tabindex|onfocus|onblur|onchange][optgroup|option]' + 
1546                 'optgroup[E|disabled|label][option]' + 
1547                 'option[E|selected|disabled|label|value][]' + 
1548                 'textarea[E|D|name|rows|cols|disabled|readonly|onselect|onchange][]' + 
1549                 'label[E|for|accesskey|onfocus|onblur][#|S]' + 
1550                 'button[E|D|name|value|type|disabled][#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]' + 
1551                 'h4[E|align][#|S]' + 
1552                 'ins[E|cite|datetime][#|Y]' + 
1553                 'h5[E|align][#|S]' + 
1554                 'del[E|cite|datetime][#|Y]' + 
1555                 'h6[E|align][#|S]' + 
1556                 'div[E|align][#|Y]' + 
1557                 'ul[E|type|compact][li]' + 
1558                 'li[E|type|value][#|Y]' + 
1559                 'ol[E|type|compact|start][li]' + 
1560                 'dl[E|compact][dt|dd]' + 
1561                 'dt[E][#|S]' + 
1562                 'dd[E][#|Y]' + 
1563                 'menu[E|compact][li]' + 
1564                 'dir[E|compact][li]' + 
1565                 'pre[E|width|xml:space][#|ZA]' + 
1566                 'hr[E|align|noshade|size|width][]' + 
1567                 'blockquote[E|cite][#|Y]' + 
1568                 'address[E][#|S|p]' + 
1569                 'center[E][#|Y]' + 
1570                 'noframes[E][#|Y]' + 
1571                 'isindex[A|B|prompt][]' + 
1572                 'fieldset[E][#|legend|Y]' + 
1573                 'legend[E|accesskey|align][#|S]' + 
1574                 'table[E|summary|width|border|frame|rules|cellspacing|cellpadding|align|bgcolor][caption|col|colgroup|thead|tfoot|tbody|tr]' + 
1575                 'caption[E|align][#|S]' + 
1576                 'col[ZG][]' + 
1577                 'colgroup[ZG][col]' + 
1578                 'thead[ZF][tr]' + 
1579                 'tr[ZF|bgcolor][th|td]' + 
1580                 'th[E|ZE][#|Y]' + 
1581                 'form[E|action|method|name|enctype|onsubmit|onreset|accept|accept-charset|target][#|X|R|Q]' + 
1582                 'noscript[E][#|Y]' + 
1583                 'td[E|ZE][#|Y]' + 
1584                 'tfoot[ZF][tr]' + 
1585                 'tbody[ZF][tr]' + 
1586                 'area[E|D|shape|coords|href|nohref|alt|target][]' + 
1587                 'base[id|href|target][]' + 
1588                 'body[E|onload|onunload|background|bgcolor|text|link|vlink|alink][#|Y]'
1589         );
1591         boolAttrMap = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected,preload,autoplay,loop,controls');
1592         shortEndedElementsMap = makeMap('area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,source');
1593         nonEmptyElementsMap = tinymce.extend(makeMap('td,th,iframe,video,object'), shortEndedElementsMap);
1594         whiteSpaceElementsMap = makeMap('pre,script,style');
1595         selfClosingElementsMap = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr');
1597         tinymce.html.Schema = function(settings) {
1598                 var self = this, elements = {}, children = {}, patternElements = [], validStyles;
1600                 settings = settings || {};
1602                 // Allow all elements and attributes if verify_html is set to false
1603                 if (settings.verify_html === false)
1604                         settings.valid_elements = '*[*]';
1606                 // Build styles list
1607                 if (settings.valid_styles) {
1608                         validStyles = {};
1610                         // Convert styles into a rule list
1611                         each(settings.valid_styles, function(value, key) {
1612                                 validStyles[key] = tinymce.explode(value);
1613                         });
1614                 }
1616                 // Converts a wildcard expression string to a regexp for example *a will become /.*a/.
1617                 function patternToRegExp(str) {
1618                         return new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$');
1619                 };
1621                 // Parses the specified valid_elements string and adds to the current rules
1622                 // This function is a bit hard to read since it's heavily optimized for speed
1623                 function addValidElements(valid_elements) {
1624                         var ei, el, ai, al, yl, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder,
1625                                 prefix, outputName, globalAttributes, globalAttributesOrder, transElement, key, childKey, value,
1626                                 elementRuleRegExp = /^([#+-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,
1627                                 attrRuleRegExp = /^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,
1628                                 hasPatternsRegExp = /[*?+]/;
1630                         if (valid_elements) {
1631                                 // Split valid elements into an array with rules
1632                                 valid_elements = split(valid_elements);
1634                                 if (elements['@']) {
1635                                         globalAttributes = elements['@'].attributes;
1636                                         globalAttributesOrder = elements['@'].attributesOrder;
1637                                 }
1639                                 // Loop all rules
1640                                 for (ei = 0, el = valid_elements.length; ei < el; ei++) {
1641                                         // Parse element rule
1642                                         matches = elementRuleRegExp.exec(valid_elements[ei]);
1643                                         if (matches) {
1644                                                 // Setup local names for matches
1645                                                 prefix = matches[1];
1646                                                 elementName = matches[2];
1647                                                 outputName = matches[3];
1648                                                 attrData = matches[4];
1650                                                 // Create new attributes and attributesOrder
1651                                                 attributes = {};
1652                                                 attributesOrder = [];
1654                                                 // Create the new element
1655                                                 element = {
1656                                                         attributes : attributes,
1657                                                         attributesOrder : attributesOrder
1658                                                 };
1660                                                 // Padd empty elements prefix
1661                                                 if (prefix === '#')
1662                                                         element.paddEmpty = true;
1664                                                 // Remove empty elements prefix
1665                                                 if (prefix === '-')
1666                                                         element.removeEmpty = true;
1668                                                 // Copy attributes from global rule into current rule
1669                                                 if (globalAttributes) {
1670                                                         for (key in globalAttributes)
1671                                                                 attributes[key] = globalAttributes[key];
1673                                                         attributesOrder.push.apply(attributesOrder, globalAttributesOrder);
1674                                                 }
1676                                                 // Attributes defined
1677                                                 if (attrData) {
1678                                                         attrData = split(attrData, '|');
1679                                                         for (ai = 0, al = attrData.length; ai < al; ai++) {
1680                                                                 matches = attrRuleRegExp.exec(attrData[ai]);
1681                                                                 if (matches) {
1682                                                                         attr = {};
1683                                                                         attrType = matches[1];
1684                                                                         attrName = matches[2].replace(/::/g, ':');
1685                                                                         prefix = matches[3];
1686                                                                         value = matches[4];
1688                                                                         // Required
1689                                                                         if (attrType === '!') {
1690                                                                                 element.attributesRequired = element.attributesRequired || [];
1691                                                                                 element.attributesRequired.push(attrName);
1692                                                                                 attr.required = true;
1693                                                                         }
1695                                                                         // Denied from global
1696                                                                         if (attrType === '-') {
1697                                                                                 delete attributes[attrName];
1698                                                                                 attributesOrder.splice(tinymce.inArray(attributesOrder, attrName), 1);
1699                                                                                 continue;
1700                                                                         }
1702                                                                         // Default value
1703                                                                         if (prefix) {
1704                                                                                 // Default value
1705                                                                                 if (prefix === '=') {
1706                                                                                         element.attributesDefault = element.attributesDefault || [];
1707                                                                                         element.attributesDefault.push({name: attrName, value: value});
1708                                                                                         attr.defaultValue = value;
1709                                                                                 }
1711                                                                                 // Forced value
1712                                                                                 if (prefix === ':') {
1713                                                                                         element.attributesForced = element.attributesForced || [];
1714                                                                                         element.attributesForced.push({name: attrName, value: value});
1715                                                                                         attr.forcedValue = value;
1716                                                                                 }
1718                                                                                 // Required values
1719                                                                                 if (prefix === '<')
1720                                                                                         attr.validValues = makeMap(value, '?');
1721                                                                         }
1723                                                                         // Check for attribute patterns
1724                                                                         if (hasPatternsRegExp.test(attrName)) {
1725                                                                                 element.attributePatterns = element.attributePatterns || [];
1726                                                                                 attr.pattern = patternToRegExp(attrName);
1727                                                                                 element.attributePatterns.push(attr);
1728                                                                         } else {
1729                                                                                 // Add attribute to order list if it doesn't already exist
1730                                                                                 if (!attributes[attrName])
1731                                                                                         attributesOrder.push(attrName);
1733                                                                                 attributes[attrName] = attr;
1734                                                                         }
1735                                                                 }
1736                                                         }
1737                                                 }
1739                                                 // Global rule, store away these for later usage
1740                                                 if (!globalAttributes && elementName == '@') {
1741                                                         globalAttributes = attributes;
1742                                                         globalAttributesOrder = attributesOrder;
1743                                                 }
1745                                                 // Handle substitute elements such as b/strong
1746                                                 if (outputName) {
1747                                                         element.outputName = elementName;
1748                                                         elements[outputName] = element;
1749                                                 }
1751                                                 // Add pattern or exact element
1752                                                 if (hasPatternsRegExp.test(elementName)) {
1753                                                         element.pattern = patternToRegExp(elementName);
1754                                                         patternElements.push(element);
1755                                                 } else
1756                                                         elements[elementName] = element;
1757                                         }
1758                                 }
1759                         }
1760                 };
1762                 function setValidElements(valid_elements) {
1763                         elements = {};
1764                         patternElements = [];
1766                         addValidElements(valid_elements);
1768                         each(transitional, function(element, name) {
1769                                 children[name] = element.children;
1770                         });
1771                 };
1773                 // Adds custom non HTML elements to the schema
1774                 function addCustomElements(custom_elements) {
1775                         var customElementRegExp = /^(~)?(.+)$/;
1777                         if (custom_elements) {
1778                                 each(split(custom_elements), function(rule) {
1779                                         var matches = customElementRegExp.exec(rule),
1780                                                 cloneName = matches[1] === '~' ? 'span' : 'div',
1781                                                 name = matches[2];
1783                                         children[name] = children[cloneName];
1785                                         // Add custom elements at span/div positions
1786                                         each(children, function(element, child) {
1787                                                 if (element[cloneName])
1788                                                         element[name] = element[cloneName];
1789                                         });
1790                                 });
1791                         }
1792                 };
1794                 // Adds valid children to the schema object
1795                 function addValidChildren(valid_children) {
1796                         var childRuleRegExp = /^([+\-]?)(\w+)\[([^\]]+)\]$/;
1798                         if (valid_children) {
1799                                 each(split(valid_children), function(rule) {
1800                                         var matches = childRuleRegExp.exec(rule), parent, prefix;
1802                                         if (matches) {
1803                                                 prefix = matches[1];
1805                                                 // Add/remove items from default
1806                                                 if (prefix)
1807                                                         parent = children[matches[2]];
1808                                                 else
1809                                                         parent = children[matches[2]] = {'#comment' : {}};
1811                                                 parent = children[matches[2]];
1813                                                 each(split(matches[3], '|'), function(child) {
1814                                                         if (prefix === '-')
1815                                                                 delete parent[child];
1816                                                         else
1817                                                                 parent[child] = {};
1818                                                 });
1819                                         }
1820                                 });
1821                         }
1822                 }
1824                 if (!settings.valid_elements) {
1825                         // No valid elements defined then clone the elements from the transitional spec
1826                         each(transitional, function(element, name) {
1827                                 elements[name] = {
1828                                         attributes : element.attributes,
1829                                         attributesOrder : element.attributesOrder
1830                                 };
1832                                 children[name] = element.children;
1833                         });
1835                         // Switch these
1836                         each(split('strong/b,em/i'), function(item) {
1837                                 item = split(item, '/');
1838                                 elements[item[1]].outputName = item[0];
1839                         });
1841                         // Add default alt attribute for images
1842                         elements.img.attributesDefault = [{name: 'alt', value: ''}];
1844                         // Remove these if they are empty by default
1845                         each(split('ol,ul,li,sub,sup,blockquote,tr,div,span,font,a,table,tbody'), function(name) {
1846                                 elements[name].removeEmpty = true;
1847                         });
1849                         // Padd these by default
1850                         each(split('p,h1,h2,h3,h4,h5,h6,th,td,pre,div,address,caption'), function(name) {
1851                                 elements[name].paddEmpty = true;
1852                         });
1853                 } else
1854                         setValidElements(settings.valid_elements);
1856                 addCustomElements(settings.custom_elements);
1857                 addValidChildren(settings.valid_children);
1858                 addValidElements(settings.extended_valid_elements);
1860                 // Todo: Remove this when we fix list handling to be valid
1861                 addValidChildren('+ol[ul|ol],+ul[ul|ol]');
1863                 // Delete invalid elements
1864                 if (settings.invalid_elements) {
1865                         tinymce.each(tinymce.explode(settings.invalid_elements), function(item) {
1866                                 if (elements[item])
1867                                         delete elements[item];
1868                         });
1869                 }
1871                 self.children = children;
1873                 self.styles = validStyles;
1875                 self.getBoolAttrs = function() {
1876                         return boolAttrMap;
1877                 };
1879                 self.getBlockElements = function() {
1880                         return blockElementsMap;
1881                 };
1883                 self.getShortEndedElements = function() {
1884                         return shortEndedElementsMap;
1885                 };
1887                 self.getSelfClosingElements = function() {
1888                         return selfClosingElementsMap;
1889                 };
1891                 self.getNonEmptyElements = function() {
1892                         return nonEmptyElementsMap;
1893                 };
1895                 self.getWhiteSpaceElements = function() {
1896                         return whiteSpaceElementsMap;
1897                 };
1899                 self.isValidChild = function(name, child) {
1900                         var parent = children[name];
1902                         return !!(parent && parent[child]);
1903                 };
1905                 self.getElementRule = function(name) {
1906                         var element = elements[name], i;
1908                         // Exact match found
1909                         if (element)
1910                                 return element;
1912                         // No exact match then try the patterns
1913                         i = patternElements.length;
1914                         while (i--) {
1915                                 element = patternElements[i];
1917                                 if (element.pattern.test(name))
1918                                         return element;
1919                         }
1920                 };
1922                 self.addValidElements = addValidElements;
1924                 self.setValidElements = setValidElements;
1926                 self.addCustomElements = addCustomElements;
1928                 self.addValidChildren = addValidChildren;
1929         };
1931         // Expose boolMap and blockElementMap as static properties for usage in DOMUtils
1932         tinymce.html.Schema.boolAttrMap = boolAttrMap;
1933         tinymce.html.Schema.blockElementsMap = blockElementsMap;
1934 })(tinymce);
1935 (function(tinymce) {
1936         tinymce.html.SaxParser = function(settings, schema) {
1937                 var self = this, noop = function() {};
1939                 settings = settings || {};
1940                 self.schema = schema = schema || new tinymce.html.Schema();
1942                 if (settings.fix_self_closing !== false)
1943                         settings.fix_self_closing = true;
1945                 // Add handler functions from settings and setup default handlers
1946                 tinymce.each('comment cdata text start end pi doctype'.split(' '), function(name) {
1947                         if (name)
1948                                 self[name] = settings[name] || noop;
1949                 });
1951                 self.parse = function(html) {
1952                         var self = this, matches, index = 0, value, endRegExp, stack = [], attrList, i, text, name,
1953                                 shortEndedElements, fillAttrsMap, isShortEnded, validate, elementRule, isValidElement, attr, attribsValue,
1954                                 validAttributesMap, validAttributePatterns, attributesRequired, attributesDefault, attributesForced, selfClosing,
1955                                 tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0, decode = tinymce.html.Entities.decode, fixSelfClosing;
1957                         function processEndTag(name) {
1958                                 var pos, i;
1960                                 // Find position of parent of the same type
1961                                 pos = stack.length;
1962                                 while (pos--) {
1963                                         if (stack[pos].name === name)
1964                                                 break;                                          
1965                                 }
1967                                 // Found parent
1968                                 if (pos >= 0) {
1969                                         // Close all the open elements
1970                                         for (i = stack.length - 1; i >= pos; i--) {
1971                                                 name = stack[i];
1973                                                 if (name.valid)
1974                                                         self.end(name.name);
1975                                         }
1977                                         // Remove the open elements from the stack
1978                                         stack.length = pos;
1979                                 }
1980                         };
1982                         // Precompile RegExps and map objects
1983                         tokenRegExp = new RegExp('<(?:' +
1984                                 '(?:!--([\\w\\W]*?)-->)|' + // Comment
1985                                 '(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|' + // CDATA
1986                                 '(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE
1987                                 '(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI
1988                                 '(?:\\/([^>]+)>)|' + // End element
1989                                 '(?:([^\\s\\/<>]+)\\s*((?:[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*)>)' + // Start element
1990                         ')', 'g');
1992                         attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:\\.|[^\"])*)\")|(?:\'((?:\\.|[^\'])*)\')|([^>\s]+)))?/g;
1993                         specialElements = {
1994                                 'script' : /<\/script[^>]*>/gi,
1995                                 'style' : /<\/style[^>]*>/gi,
1996                                 'noscript' : /<\/noscript[^>]*>/gi
1997                         };
1999                         // Setup lookup tables for empty elements and boolean attributes
2000                         shortEndedElements = schema.getShortEndedElements();
2001                         selfClosing = schema.getSelfClosingElements();
2002                         fillAttrsMap = schema.getBoolAttrs();
2003                         validate = settings.validate;
2004                         fixSelfClosing = settings.fix_self_closing;
2006                         while (matches = tokenRegExp.exec(html)) {
2007                                 // Text
2008                                 if (index < matches.index)
2009                                         self.text(decode(html.substr(index, matches.index - index)));
2011                                 if (value = matches[6]) { // End element
2012                                         processEndTag(value.toLowerCase());
2013                                 } else if (value = matches[7]) { // Start element
2014                                         value = value.toLowerCase();
2015                                         isShortEnded = value in shortEndedElements;
2017                                         // Is self closing tag for example an <li> after an open <li>
2018                                         if (fixSelfClosing && selfClosing[value] && stack.length > 0 && stack[stack.length - 1].name === value)
2019                                                 processEndTag(value);
2021                                         // Validate element
2022                                         if (!validate || (elementRule = schema.getElementRule(value))) {
2023                                                 isValidElement = true;
2025                                                 // Grab attributes map and patters when validation is enabled
2026                                                 if (validate) {
2027                                                         validAttributesMap = elementRule.attributes;
2028                                                         validAttributePatterns = elementRule.attributePatterns;
2029                                                 }
2031                                                 // Parse attributes
2032                                                 if (attribsValue = matches[8]) {
2033                                                         attrList = [];
2034                                                         attrList.map = {};
2036                                                         attribsValue.replace(attrRegExp, function(match, name, value, val2, val3) {
2037                                                                 var attrRule, i;
2039                                                                 name = name.toLowerCase();
2040                                                                 value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute
2042                                                                 // Validate name and value
2043                                                                 if (validate && name.indexOf('data-') !== 0) {
2044                                                                         attrRule = validAttributesMap[name];
2046                                                                         // Find rule by pattern matching
2047                                                                         if (!attrRule && validAttributePatterns) {
2048                                                                                 i = validAttributePatterns.length;
2049                                                                                 while (i--) {
2050                                                                                         attrRule = validAttributePatterns[i];
2051                                                                                         if (attrRule.pattern.test(name))
2052                                                                                                 break;
2053                                                                                 }
2055                                                                                 // No rule matched
2056                                                                                 if (i === -1)
2057                                                                                         attrRule = null;
2058                                                                         }
2060                                                                         // No attribute rule found
2061                                                                         if (!attrRule)
2062                                                                                 return;
2064                                                                         // Validate value
2065                                                                         if (attrRule.validValues && !(value in attrRule.validValues))
2066                                                                                 return;
2067                                                                 }
2069                                                                 // Add attribute to list and map
2070                                                                 attrList.map[name] = value;
2071                                                                 attrList.push({
2072                                                                         name: name,
2073                                                                         value: value
2074                                                                 });
2075                                                         });
2076                                                 } else {
2077                                                         attrList = [];
2078                                                         attrList.map = {};
2079                                                 }
2081                                                 // Process attributes if validation is enabled
2082                                                 if (validate) {
2083                                                         attributesRequired = elementRule.attributesRequired;
2084                                                         attributesDefault = elementRule.attributesDefault;
2085                                                         attributesForced = elementRule.attributesForced;
2087                                                         // Handle forced attributes
2088                                                         if (attributesForced) {
2089                                                                 i = attributesForced.length;
2090                                                                 while (i--) {
2091                                                                         attr = attributesForced[i];
2092                                                                         name = attr.name;
2093                                                                         attrValue = attr.value;
2095                                                                         if (attrValue === '{$uid}')
2096                                                                                 attrValue = 'mce_' + idCount++;
2098                                                                         attrList.map[name] = attrValue;
2099                                                                         attrList.push({name: name, value: attrValue});
2100                                                                 }
2101                                                         }
2103                                                         // Handle default attributes
2104                                                         if (attributesDefault) {
2105                                                                 i = attributesDefault.length;
2106                                                                 while (i--) {
2107                                                                         attr = attributesDefault[i];
2108                                                                         name = attr.name;
2110                                                                         if (!(name in attrList.map)) {
2111                                                                                 attrValue = attr.value;
2113                                                                                 if (attrValue === '{$uid}')
2114                                                                                         attrValue = 'mce_' + idCount++;
2116                                                                                 attrList.map[name] = attrValue;
2117                                                                                 attrList.push({name: name, value: attrValue});
2118                                                                         }
2119                                                                 }
2120                                                         }
2122                                                         // Handle required attributes
2123                                                         if (attributesRequired) {
2124                                                                 i = attributesRequired.length;
2125                                                                 while (i--) {
2126                                                                         if (attributesRequired[i] in attrList.map)
2127                                                                                 break;
2128                                                                 }
2130                                                                 // None of the required attributes where found
2131                                                                 if (i === -1)
2132                                                                         isValidElement = false;
2133                                                         }
2135                                                         // Invalidate element if it's marked as bogus
2136                                                         if (attrList.map['data-mce-bogus'])
2137                                                                 isValidElement = false;
2138                                                 }
2140                                                 if (isValidElement)
2141                                                         self.start(value, attrList, isShortEnded);
2142                                         } else
2143                                                 isValidElement = false;
2145                                         // Treat script, noscript and style a bit different since they may include code that looks like elements
2146                                         if (endRegExp = specialElements[value]) {
2147                                                 endRegExp.lastIndex = index = matches.index + matches[0].length;
2149                                                 if (matches = endRegExp.exec(html)) {
2150                                                         if (isValidElement)
2151                                                                 text = html.substr(index, matches.index - index);
2153                                                         index = matches.index + matches[0].length;
2154                                                 } else {
2155                                                         text = html.substr(index);
2156                                                         index = html.length;
2157                                                 }
2159                                                 if (isValidElement && text.length > 0)
2160                                                         self.text(text, true);
2162                                                 if (isValidElement)
2163                                                         self.end(value);
2165                                                 tokenRegExp.lastIndex = index;
2166                                                 continue;
2167                                         }
2169                                         // Push value on to stack
2170                                         if (!isShortEnded) {
2171                                                 if (!attribsValue || attribsValue.indexOf('/') != attribsValue.length - 1)
2172                                                         stack.push({name: value, valid: isValidElement});
2173                                                 else if (isValidElement)
2174                                                         self.end(value);
2175                                         }
2176                                 } else if (value = matches[1]) { // Comment
2177                                         self.comment(value);
2178                                 } else if (value = matches[2]) { // CDATA
2179                                         self.cdata(value);
2180                                 } else if (value = matches[3]) { // DOCTYPE
2181                                         self.doctype(value);
2182                                 } else if (value = matches[4]) { // PI
2183                                         self.pi(value, matches[5]);
2184                                 }
2186                                 index = matches.index + matches[0].length;
2187                         }
2189                         // Text
2190                         if (index < html.length)
2191                                 self.text(decode(html.substr(index)));
2193                         // Close any open elements
2194                         for (i = stack.length - 1; i >= 0; i--) {
2195                                 value = stack[i];
2197                                 if (value.valid)
2198                                         self.end(value.name);
2199                         }
2200                 };
2201         }
2202 })(tinymce);
2203 (function(tinymce) {
2204         var whiteSpaceRegExp = /^[ \t\r\n]*$/, typeLookup = {
2205                 '#text' : 3,
2206                 '#comment' : 8,
2207                 '#cdata' : 4,
2208                 '#pi' : 7,
2209                 '#doctype' : 10,
2210                 '#document-fragment' : 11
2211         };
2213         // Walks the tree left/right
2214         function walk(node, root_node, prev) {
2215                 var sibling, parent, startName = prev ? 'lastChild' : 'firstChild', siblingName = prev ? 'prev' : 'next';
2217                 // Walk into nodes if it has a start
2218                 if (node[startName])
2219                         return node[startName];
2221                 // Return the sibling if it has one
2222                 if (node !== root_node) {
2223                         sibling = node[siblingName];
2225                         if (sibling)
2226                                 return sibling;
2228                         // Walk up the parents to look for siblings
2229                         for (parent = node.parent; parent && parent !== root_node; parent = parent.parent) {
2230                                 sibling = parent[siblingName];
2232                                 if (sibling)
2233                                         return sibling;
2234                         }
2235                 }
2236         };
2238         function Node(name, type) {
2239                 this.name = name;
2240                 this.type = type;
2242                 if (type === 1) {
2243                         this.attributes = [];
2244                         this.attributes.map = {};
2245                 }
2246         }
2248         tinymce.extend(Node.prototype, {
2249                 replace : function(node) {
2250                         var self = this;
2252                         if (node.parent)
2253                                 node.remove();
2255                         self.insert(node, self);
2256                         self.remove();
2258                         return self;
2259                 },
2261                 attr : function(name, value) {
2262                         var self = this, attrs, i, undef;
2264                         if (typeof name !== "string") {
2265                                 for (i in name)
2266                                         self.attr(i, name[i]);
2268                                 return self;
2269                         }
2271                         if (attrs = self.attributes) {
2272                                 if (value !== undef) {
2273                                         // Remove attribute
2274                                         if (value === null) {
2275                                                 if (name in attrs.map) {
2276                                                         delete attrs.map[name];
2278                                                         i = attrs.length;
2279                                                         while (i--) {
2280                                                                 if (attrs[i].name === name) {
2281                                                                         attrs = attrs.splice(i, 1);
2282                                                                         return self;
2283                                                                 }
2284                                                         }
2285                                                 }
2287                                                 return self;
2288                                         }
2290                                         // Set attribute
2291                                         if (name in attrs.map) {
2292                                                 // Set attribute
2293                                                 i = attrs.length;
2294                                                 while (i--) {
2295                                                         if (attrs[i].name === name) {
2296                                                                 attrs[i].value = value;
2297                                                                 break;
2298                                                         }
2299                                                 }
2300                                         } else
2301                                                 attrs.push({name: name, value: value});
2303                                         attrs.map[name] = value;
2305                                         return self;
2306                                 } else {
2307                                         return attrs.map[name];
2308                                 }
2309                         }
2310                 },
2312                 clone : function() {
2313                         var self = this, clone = new Node(self.name, self.type), i, l, selfAttrs, selfAttr, cloneAttrs;
2315                         // Clone element attributes
2316                         if (selfAttrs = self.attributes) {
2317                                 cloneAttrs = [];
2318                                 cloneAttrs.map = {};
2320                                 for (i = 0, l = selfAttrs.length; i < l; i++) {
2321                                         selfAttr = selfAttrs[i];
2323                                         // Clone everything except id
2324                                         if (selfAttr.name !== 'id') {
2325                                                 cloneAttrs[cloneAttrs.length] = {name: selfAttr.name, value: selfAttr.value};
2326                                                 cloneAttrs.map[selfAttr.name] = selfAttr.value;
2327                                         }
2328                                 }
2330                                 clone.attributes = cloneAttrs;
2331                         }
2333                         clone.value = self.value;
2334                         clone.shortEnded = self.shortEnded;
2336                         return clone;
2337                 },
2339                 wrap : function(wrapper) {
2340                         var self = this;
2342                         self.parent.insert(wrapper, self);
2343                         wrapper.append(self);
2345                         return self;
2346                 },
2348                 unwrap : function() {
2349                         var self = this, node, next;
2351                         for (node = self.firstChild; node; ) {
2352                                 next = node.next;
2353                                 self.insert(node, self, true);
2354                                 node = next;
2355                         }
2357                         self.remove();
2358                 },
2360                 remove : function() {
2361                         var self = this, parent = self.parent, next = self.next, prev = self.prev;
2363                         if (parent) {
2364                                 if (parent.firstChild === self) {
2365                                         parent.firstChild = next;
2367                                         if (next)
2368                                                 next.prev = null;
2369                                 } else {
2370                                         prev.next = next;
2371                                 }
2373                                 if (parent.lastChild === self) {
2374                                         parent.lastChild = prev;
2376                                         if (prev)
2377                                                 prev.next = null;
2378                                 } else {
2379                                         next.prev = prev;
2380                                 }
2382                                 self.parent = self.next = self.prev = null;
2383                         }
2385                         return self;
2386                 },
2388                 append : function(node) {
2389                         var self = this, last;
2391                         if (node.parent)
2392                                 node.remove();
2394                         last = self.lastChild;
2395                         if (last) {
2396                                 last.next = node;
2397                                 node.prev = last;
2398                                 self.lastChild = node;
2399                         } else
2400                                 self.lastChild = self.firstChild = node;
2402                         node.parent = self;
2404                         return node;
2405                 },
2407                 insert : function(node, ref_node, before) {
2408                         var parent;
2410                         if (node.parent)
2411                                 node.remove();
2413                         parent = ref_node.parent || this;
2415                         if (before) {
2416                                 if (ref_node === parent.firstChild)
2417                                         parent.firstChild = node;
2418                                 else
2419                                         ref_node.prev.next = node;
2421                                 node.prev = ref_node.prev;
2422                                 node.next = ref_node;
2423                                 ref_node.prev = node;
2424                         } else {
2425                                 if (ref_node === parent.lastChild)
2426                                         parent.lastChild = node;
2427                                 else
2428                                         ref_node.next.prev = node;
2430                                 node.next = ref_node.next;
2431                                 node.prev = ref_node;
2432                                 ref_node.next = node;
2433                         }
2435                         node.parent = parent;
2437                         return node;
2438                 },
2440                 getAll : function(name) {
2441                         var self = this, node, collection = [];
2443                         for (node = self.firstChild; node; node = walk(node, self)) {
2444                                 if (node.name === name)
2445                                         collection.push(node);
2446                         }
2448                         return collection;
2449                 },
2451                 empty : function() {
2452                         var self = this, nodes, i, node;
2454                         // Remove all children
2455                         if (self.firstChild) {
2456                                 nodes = [];
2458                                 // Collect the children
2459                                 for (node = self.firstChild; node; node = walk(node, self))
2460                                         nodes.push(node);
2462                                 // Remove the children
2463                                 i = nodes.length;
2464                                 while (i--) {
2465                                         node = nodes[i];
2466                                         node.parent = node.firstChild = node.lastChild = node.next = node.prev = null;
2467                                 }
2468                         }
2470                         self.firstChild = self.lastChild = null;
2472                         return self;
2473                 },
2475                 isEmpty : function(elements) {
2476                         var self = this, node = self.firstChild, i, name;
2478                         if (node) {
2479                                 do {
2480                                         if (node.type === 1) {
2481                                                 // Ignore bogus elements
2482                                                 if (node.attributes.map['data-mce-bogus'])
2483                                                         continue;
2485                                                 // Keep empty elements like <img />
2486                                                 if (elements[node.name])
2487                                                         return false;
2489                                                 // Keep elements with data attributes or name attribute like <a name="1"></a>
2490                                                 i = node.attributes.length;