06a4803ea8ecc46352c2340575e57ab60258095a
[moodle.git] / lib / editor / tinymce / tiny_mce / 3.4.2 / tiny_mce_jquery_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                 trim : function(s) {
153                         return (s ? '' + s : '').replace(whiteSpaceRe, '');
154                 },
156                 create : function(s, p, root) {
157                         var t = this, sp, ns, cn, scn, c, de = 0;
159                         // Parse : <prefix> <class>:<super class>
160                         s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);
161                         cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name
163                         // Create namespace for new class
164                         ns = t.createNS(s[3].replace(/\.\w+$/, ''), root);
166                         // Class already exists
167                         if (ns[cn])
168                                 return;
170                         // Make pure static class
171                         if (s[2] == 'static') {
172                                 ns[cn] = p;
174                                 if (this.onCreate)
175                                         this.onCreate(s[2], s[3], ns[cn]);
177                                 return;
178                         }
180                         // Create default constructor
181                         if (!p[cn]) {
182                                 p[cn] = function() {};
183                                 de = 1;
184                         }
186                         // Add constructor and methods
187                         ns[cn] = p[cn];
188                         t.extend(ns[cn].prototype, p);
190                         // Extend
191                         if (s[5]) {
192                                 sp = t.resolve(s[5]).prototype;
193                                 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name
195                                 // Extend constructor
196                                 c = ns[cn];
197                                 if (de) {
198                                         // Add passthrough constructor
199                                         ns[cn] = function() {
200                                                 return sp[scn].apply(this, arguments);
201                                         };
202                                 } else {
203                                         // Add inherit constructor
204                                         ns[cn] = function() {
205                                                 this.parent = sp[scn];
206                                                 return c.apply(this, arguments);
207                                         };
208                                 }
209                                 ns[cn].prototype[cn] = ns[cn];
211                                 // Add super methods
212                                 t.each(sp, function(f, n) {
213                                         ns[cn].prototype[n] = sp[n];
214                                 });
216                                 // Add overridden methods
217                                 t.each(p, function(f, n) {
218                                         // Extend methods if needed
219                                         if (sp[n]) {
220                                                 ns[cn].prototype[n] = function() {
221                                                         this.parent = sp[n];
222                                                         return f.apply(this, arguments);
223                                                 };
224                                         } else {
225                                                 if (n != cn)
226                                                         ns[cn].prototype[n] = f;
227                                         }
228                                 });
229                         }
231                         // Add static methods
232                         t.each(p['static'], function(f, n) {
233                                 ns[cn][n] = f;
234                         });
236                         if (this.onCreate)
237                                 this.onCreate(s[2], s[3], ns[cn].prototype);
238                 },
240                 walk : function(o, f, n, s) {
241                         s = s || this;
243                         if (o) {
244                                 if (n)
245                                         o = o[n];
247                                 tinymce.each(o, function(o, i) {
248                                         if (f.call(s, o, i, n) === false)
249                                                 return false;
251                                         tinymce.walk(o, f, n, s);
252                                 });
253                         }
254                 },
256                 createNS : function(n, o) {
257                         var i, v;
259                         o = o || win;
261                         n = n.split('.');
262                         for (i=0; i<n.length; i++) {
263                                 v = n[i];
265                                 if (!o[v])
266                                         o[v] = {};
268                                 o = o[v];
269                         }
271                         return o;
272                 },
274                 resolve : function(n, o) {
275                         var i, l;
277                         o = o || win;
279                         n = n.split('.');
280                         for (i = 0, l = n.length; i < l; i++) {
281                                 o = o[n[i]];
283                                 if (!o)
284                                         break;
285                         }
287                         return o;
288                 },
290                 addUnload : function(f, s) {
291                         var t = this;
293                         f = {func : f, scope : s || this};
295                         if (!t.unloads) {
296                                 function unload() {
297                                         var li = t.unloads, o, n;
299                                         if (li) {
300                                                 // Call unload handlers
301                                                 for (n in li) {
302                                                         o = li[n];
304                                                         if (o && o.func)
305                                                                 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy
306                                                 }
308                                                 // Detach unload function
309                                                 if (win.detachEvent) {
310                                                         win.detachEvent('onbeforeunload', fakeUnload);
311                                                         win.detachEvent('onunload', unload);
312                                                 } else if (win.removeEventListener)
313                                                         win.removeEventListener('unload', unload, false);
315                                                 // Destroy references
316                                                 t.unloads = o = li = w = unload = 0;
318                                                 // Run garbarge collector on IE
319                                                 if (win.CollectGarbage)
320                                                         CollectGarbage();
321                                         }
322                                 };
324                                 function fakeUnload() {
325                                         var d = document;
327                                         // Is there things still loading, then do some magic
328                                         if (d.readyState == 'interactive') {
329                                                 function stop() {
330                                                         // Prevent memory leak
331                                                         d.detachEvent('onstop', stop);
333                                                         // Call unload handler
334                                                         if (unload)
335                                                                 unload();
337                                                         d = 0;
338                                                 };
340                                                 // Fire unload when the currently loading page is stopped
341                                                 if (d)
342                                                         d.attachEvent('onstop', stop);
344                                                 // Remove onstop listener after a while to prevent the unload function
345                                                 // to execute if the user presses cancel in an onbeforeunload
346                                                 // confirm dialog and then presses the browser stop button
347                                                 win.setTimeout(function() {
348                                                         if (d)
349                                                                 d.detachEvent('onstop', stop);
350                                                 }, 0);
351                                         }
352                                 };
354                                 // Attach unload handler
355                                 if (win.attachEvent) {
356                                         win.attachEvent('onunload', unload);
357                                         win.attachEvent('onbeforeunload', fakeUnload);
358                                 } else if (win.addEventListener)
359                                         win.addEventListener('unload', unload, false);
361                                 // Setup initial unload handler array
362                                 t.unloads = [f];
363                         } else
364                                 t.unloads.push(f);
366                         return f;
367                 },
369                 removeUnload : function(f) {
370                         var u = this.unloads, r = null;
372                         tinymce.each(u, function(o, i) {
373                                 if (o && o.func == f) {
374                                         u.splice(i, 1);
375                                         r = f;
376                                         return false;
377                                 }
378                         });
380                         return r;
381                 },
383                 explode : function(s, d) {
384                         return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;
385                 },
387                 _addVer : function(u) {
388                         var v;
390                         if (!this.query)
391                                 return u;
393                         v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;
395                         if (u.indexOf('#') == -1)
396                                 return u + v;
398                         return u.replace('#', v + '#');
399                 },
401                 // Fix function for IE 9 where regexps isn't working correctly
402                 // Todo: remove me once MS fixes the bug
403                 _replace : function(find, replace, str) {
404                         // On IE9 we have to fake $x replacement
405                         if (isRegExpBroken) {
406                                 return str.replace(find, function() {
407                                         var val = replace, args = arguments, i;
409                                         for (i = 0; i < args.length - 2; i++) {
410                                                 if (args[i] === undefined) {
411                                                         val = val.replace(new RegExp('\\$' + i, 'g'), '');
412                                                 } else {
413                                                         val = val.replace(new RegExp('\\$' + i, 'g'), args[i]);
414                                                 }
415                                         }
417                                         return val;
418                                 });
419                         }
421                         return str.replace(find, replace);
422                 }
424                 };
426         // Initialize the API
427         tinymce._init();
429         // Expose tinymce namespace to the global namespace (window)
430         win.tinymce = win.tinyMCE = tinymce;
432         // Describe the different namespaces
434         })(window);
436 (function($, tinymce) {
437         var is = tinymce.is, attrRegExp = /^(href|src|style)$/i, undefined;
439         // jQuery is undefined
440         if (!$)
441                 return alert("Load jQuery first!");
443         // Stick jQuery into the tinymce namespace
444         tinymce.$ = $;
446         // Setup adapter
447         tinymce.adapter = {
448                 patchEditor : function(editor) {
449                         var fn = $.fn;
451                         // Adapt the css function to make sure that the data-mce-style
452                         // attribute gets updated with the new style information
453                         function css(name, value) {
454                                 var self = this;
456                                 // Remove data-mce-style when set operation occurs
457                                 if (value)
458                                         self.removeAttr('data-mce-style');
460                                 return fn.css.apply(self, arguments);
461                         };
463                         // Apapt the attr function to make sure that it uses the data-mce- prefixed variants
464                         function attr(name, value) {
465                                 var self = this;
467                                 // Update/retrive data-mce- attribute variants
468                                 if (attrRegExp.test(name)) {
469                                         if (value !== undefined) {
470                                                 // Use TinyMCE behavior when setting the specifc attributes
471                                                 self.each(function(i, node) {
472                                                         editor.dom.setAttrib(node, name, value);
473                                                 });
475                                                 return self;
476                                         } else
477                                                 return self.attr('data-mce-' + name);
478                                 }
480                                 // Default behavior
481                                 return fn.attr.apply(self, arguments);
482                         };
484                         function htmlPatchFunc(func) {
485                                 // Returns a modified function that processes
486                                 // the HTML before executing the action this makes sure
487                                 // that href/src etc gets moved into the data-mce- variants
488                                 return function(content) {
489                                         if (content)
490                                                 content = editor.dom.processHTML(content);
492                                         return func.call(this, content);
493                                 };
494                         };
496                         // Patch various jQuery functions to handle tinymce specific attribute and content behavior
497                         // we don't patch the jQuery.fn directly since it will most likely break compatibility
498                         // with other jQuery logic on the page. Only instances created by TinyMCE should be patched.
499                         function patch(jq) {
500                                 // Patch some functions, only patch the object once
501                                 if (jq.css !== css) {
502                                         // Patch css/attr to use the data-mce- prefixed attribute variants
503                                         jq.css = css;
504                                         jq.attr = attr;
506                                         // Patch HTML functions to use the DOMUtils.processHTML filter logic
507                                         jq.html = htmlPatchFunc(fn.html);
508                                         jq.append = htmlPatchFunc(fn.append);
509                                         jq.prepend = htmlPatchFunc(fn.prepend);
510                                         jq.after = htmlPatchFunc(fn.after);
511                                         jq.before = htmlPatchFunc(fn.before);
512                                         jq.replaceWith = htmlPatchFunc(fn.replaceWith);
513                                         jq.tinymce = editor;
515                                         // Each pushed jQuery instance needs to be patched
516                                         // as well for example when traversing the DOM
517                                         jq.pushStack = function() {
518                                                 return patch(fn.pushStack.apply(this, arguments));
519                                         };
520                                 }
522                                 return jq;
523                         };
525                         // Add a $ function on each editor instance this one is scoped for the editor document object
526                         // this way you can do chaining like this tinymce.get(0).$('p').append('text').css('color', 'red');
527                         editor.$ = function(selector, scope) {
528                                 var doc = editor.getDoc();
530                                 return patch($(selector || doc, doc || scope));
531                         };
532                 }
533         };
535         // Patch in core NS functions
536         tinymce.extend = $.extend;
537         tinymce.extend(tinymce, {
538                 map : $.map,
539                 grep : function(a, f) {return $.grep(a, f || function(){return 1;});},
540                 inArray : function(a, v) {return $.inArray(v, a || []);}
542                 /* Didn't iterate stylesheets
543                 each : function(o, cb, s) {
544                         if (!o)
545                                 return 0;
547                         var r = 1;
549                         $.each(o, function(nr, el){
550                                 if (cb.call(s, el, nr, o) === false) {
551                                         r = 0;
552                                         return false;
553                                 }
554                         });
556                         return r;
557                 }*/
558         });
560         // Patch in functions in various clases
561         // Add a "#ifndefjquery" statement around each core API function you add below
562         var patches = {
563                 'tinymce.dom.DOMUtils' : {
564                         /*
565                         addClass : function(e, c) {
566                                 if (is(e, 'array') && is(e[0], 'string'))
567                                         e = e.join(',#');
568                                 return (e && $(is(e, 'string') ? '#' + e : e)
569                                         .addClass(c)
570                                         .attr('class')) || false;
571                         },
573                         hasClass : function(n, c) {
574                                 return $(is(n, 'string') ? '#' + n : n).hasClass(c);
575                         },
577                         removeClass : function(e, c) {
578                                 if (!e)
579                                         return false;
581                                 var r = [];
583                                 $(is(e, 'string') ? '#' + e : e)
584                                         .removeClass(c)
585                                         .each(function(){
586                                                 r.push(this.className);
587                                         });
589                                 return r.length == 1 ? r[0] : r;
590                         },
591                         */
593                         select : function(pattern, scope) {
594                                 var t = this;
596                                 return $.find(pattern, t.get(scope) || t.get(t.settings.root_element) || t.doc, []);
597                         },
599                         is : function(n, patt) {
600                                 return $(this.get(n)).is(patt);
601                         }
603                         /*
604                         show : function(e) {
605                                 if (is(e, 'array') && is(e[0], 'string'))
606                                         e = e.join(',#');
608                                 $(is(e, 'string') ? '#' + e : e).css('display', 'block');
609                         },
611                         hide : function(e) {
612                                 if (is(e, 'array') && is(e[0], 'string'))
613                                         e = e.join(',#');
615                                 $(is(e, 'string') ? '#' + e : e).css('display', 'none');
616                         },
618                         isHidden : function(e) {
619                                 return $(is(e, 'string') ? '#' + e : e).is(':hidden');
620                         },
622                         insertAfter : function(n, e) {
623                                 return $(is(e, 'string') ? '#' + e : e).after(n);
624                         },
626                         replace : function(o, n, k) {
627                                 n = $(is(n, 'string') ? '#' + n : n);
629                                 if (k)
630                                         n.children().appendTo(o);
632                                 n.replaceWith(o);
633                         },
635                         setStyle : function(n, na, v) {
636                                 if (is(n, 'array') && is(n[0], 'string'))
637                                         n = n.join(',#');
639                                 $(is(n, 'string') ? '#' + n : n).css(na, v);
640                         },
642                         getStyle : function(n, na, c) {
643                                 return $(is(n, 'string') ? '#' + n : n).css(na);
644                         },
646                         setStyles : function(e, o) {
647                                 if (is(e, 'array') && is(e[0], 'string'))
648                                         e = e.join(',#');
649                                 $(is(e, 'string') ? '#' + e : e).css(o);
650                         },
652                         setAttrib : function(e, n, v) {
653                                 var t = this, s = t.settings;
655                                 if (is(e, 'array') && is(e[0], 'string'))
656                                         e = e.join(',#');
658                                 e = $(is(e, 'string') ? '#' + e : e);
660                                 switch (n) {
661                                         case "style":
662                                                 e.each(function(i, v){
663                                                         if (s.keep_values)
664                                                                 $(v).attr('data-mce-style', v);
666                                                         v.style.cssText = v;
667                                                 });
668                                                 break;
670                                         case "class":
671                                                 e.each(function(){
672                                                         this.className = v;
673                                                 });
674                                                 break;
676                                         case "src":
677                                         case "href":
678                                                 e.each(function(i, v){
679                                                         if (s.keep_values) {
680                                                                 if (s.url_converter)
681                                                                         v = s.url_converter.call(s.url_converter_scope || t, v, n, v);
683                                                                 t.setAttrib(v, 'data-mce-' + n, v);
684                                                         }
685                                                 });
687                                                 break;
688                                 }
690                                 if (v !== null && v.length !== 0)
691                                         e.attr(n, '' + v);
692                                 else
693                                         e.removeAttr(n);
694                         },
696                         setAttribs : function(e, o) {
697                                 var t = this;
699                                 $.each(o, function(n, v){
700                                         t.setAttrib(e,n,v);
701                                 });
702                         }
703                         */
704                 }
706 /*
707                 'tinymce.dom.Event' : {
708                         add : function (o, n, f, s) {
709                                 var lo, cb;
711                                 cb = function(e) {
712                                         e.target = e.target || this;
713                                         f.call(s || this, e);
714                                 };
716                                 if (is(o, 'array') && is(o[0], 'string'))
717                                         o = o.join(',#');
718                                 o = $(is(o, 'string') ? '#' + o : o);
719                                 if (n == 'init') {
720                                         o.ready(cb, s);
721                                 } else {
722                                         if (s) {
723                                                 o.bind(n, s, cb);
724                                         } else {
725                                                 o.bind(n, cb);
726                                         }
727                                 }
729                                 lo = this._jqLookup || (this._jqLookup = []);
730                                 lo.push({func : f, cfunc : cb});
732                                 return cb;
733                         },
735                         remove : function(o, n, f) {
736                                 // Find cfunc
737                                 $(this._jqLookup).each(function() {
738                                         if (this.func === f)
739                                                 f = this.cfunc;
740                                 });
742                                 if (is(o, 'array') && is(o[0], 'string'))
743                                         o = o.join(',#');
745                                 $(is(o, 'string') ? '#' + o : o).unbind(n,f);
747                                 return true;
748                         }
749                 }
750 */
751         };
753         // Patch functions after a class is created
754         tinymce.onCreate = function(ty, c, p) {
755                 tinymce.extend(p, patches[c]);
756         };
757 })(window.jQuery, tinymce);
759 tinymce.create('tinymce.util.Dispatcher', {
760         scope : null,
761         listeners : null,
763         Dispatcher : function(s) {
764                 this.scope = s || this;
765                 this.listeners = [];
766         },
768         add : function(cb, s) {
769                 this.listeners.push({cb : cb, scope : s || this.scope});
771                 return cb;
772         },
774         addToTop : function(cb, s) {
775                 this.listeners.unshift({cb : cb, scope : s || this.scope});
777                 return cb;
778         },
780         remove : function(cb) {
781                 var l = this.listeners, o = null;
783                 tinymce.each(l, function(c, i) {
784                         if (cb == c.cb) {
785                                 o = cb;
786                                 l.splice(i, 1);
787                                 return false;
788                         }
789                 });
791                 return o;
792         },
794         dispatch : function() {
795                 var s, a = arguments, i, li = this.listeners, c;
797                 // Needs to be a real loop since the listener count might change while looping
798                 // And this is also more efficient
799                 for (i = 0; i<li.length; i++) {
800                         c = li[i];
801                         s = c.cb.apply(c.scope, a);
803                         if (s === false)
804                                 break;
805                 }
807                 return s;
808         }
810         });
811 (function() {
812         var each = tinymce.each;
814         tinymce.create('tinymce.util.URI', {
815                 URI : function(u, s) {
816                         var t = this, o, a, b;
818                         // Trim whitespace
819                         u = tinymce.trim(u);
821                         // Default settings
822                         s = t.settings = s || {};
824                         // Strange app protocol or local anchor
825                         if (/^(mailto|tel|news|javascript|about|data):/i.test(u) || /^\s*#/.test(u)) {
826                                 t.source = u;
827                                 return;
828                         }
830                         // Absolute path with no host, fake host and protocol
831                         if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)
832                                 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;
834                         // Relative path http:// or protocol relative //path
835                         if (!/^\w*:?\/\//.test(u))
836                                 u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u);
838                         // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
839                         u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
840                         u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);
841                         each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {
842                                 var s = u[i];
844                                 // Zope 3 workaround, they use @@something
845                                 if (s)
846                                         s = s.replace(/\(mce_at\)/g, '@@');
848                                 t[v] = s;
849                         });
851                         if (b = s.base_uri) {
852                                 if (!t.protocol)
853                                         t.protocol = b.protocol;
855                                 if (!t.userInfo)
856                                         t.userInfo = b.userInfo;
858                                 if (!t.port && t.host == 'mce_host')
859                                         t.port = b.port;
861                                 if (!t.host || t.host == 'mce_host')
862                                         t.host = b.host;
864                                 t.source = '';
865                         }
867                         //t.path = t.path || '/';
868                 },
870                 setPath : function(p) {
871                         var t = this;
873                         p = /^(.*?)\/?(\w+)?$/.exec(p);
875                         // Update path parts
876                         t.path = p[0];
877                         t.directory = p[1];
878                         t.file = p[2];
880                         // Rebuild source
881                         t.source = '';
882                         t.getURI();
883                 },
885                 toRelative : function(u) {
886                         var t = this, o;
888                         if (u === "./")
889                                 return u;
891                         u = new tinymce.util.URI(u, {base_uri : t});
893                         // Not on same domain/port or protocol
894                         if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)
895                                 return u.getURI();
897                         o = t.toRelPath(t.path, u.path);
899                         // Add query
900                         if (u.query)
901                                 o += '?' + u.query;
903                         // Add anchor
904                         if (u.anchor)
905                                 o += '#' + u.anchor;
907                         return o;
908                 },
909         
910                 toAbsolute : function(u, nh) {
911                         var u = new tinymce.util.URI(u, {base_uri : this});
913                         return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0);
914                 },
916                 toRelPath : function(base, path) {
917                         var items, bp = 0, out = '', i, l;
919                         // Split the paths
920                         base = base.substring(0, base.lastIndexOf('/'));
921                         base = base.split('/');
922                         items = path.split('/');
924                         if (base.length >= items.length) {
925                                 for (i = 0, l = base.length; i < l; i++) {
926                                         if (i >= items.length || base[i] != items[i]) {
927                                                 bp = i + 1;
928                                                 break;
929                                         }
930                                 }
931                         }
933                         if (base.length < items.length) {
934                                 for (i = 0, l = items.length; i < l; i++) {
935                                         if (i >= base.length || base[i] != items[i]) {
936                                                 bp = i + 1;
937                                                 break;
938                                         }
939                                 }
940                         }
942                         if (bp == 1)
943                                 return path;
945                         for (i = 0, l = base.length - (bp - 1); i < l; i++)
946                                 out += "../";
948                         for (i = bp - 1, l = items.length; i < l; i++) {
949                                 if (i != bp - 1)
950                                         out += "/" + items[i];
951                                 else
952                                         out += items[i];
953                         }
955                         return out;
956                 },
958                 toAbsPath : function(base, path) {
959                         var i, nb = 0, o = [], tr, outPath;
961                         // Split paths
962                         tr = /\/$/.test(path) ? '/' : '';
963                         base = base.split('/');
964                         path = path.split('/');
966                         // Remove empty chunks
967                         each(base, function(k) {
968                                 if (k)
969                                         o.push(k);
970                         });
972                         base = o;
974                         // Merge relURLParts chunks
975                         for (i = path.length - 1, o = []; i >= 0; i--) {
976                                 // Ignore empty or .
977                                 if (path[i].length == 0 || path[i] == ".")
978                                         continue;
980                                 // Is parent
981                                 if (path[i] == '..') {
982                                         nb++;
983                                         continue;
984                                 }
986                                 // Move up
987                                 if (nb > 0) {
988                                         nb--;
989                                         continue;
990                                 }
992                                 o.push(path[i]);
993                         }
995                         i = base.length - nb;
997                         // If /a/b/c or /
998                         if (i <= 0)
999                                 outPath = o.reverse().join('/');
1000                         else
1001                                 outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');
1003                         // Add front / if it's needed
1004                         if (outPath.indexOf('/') !== 0)
1005                                 outPath = '/' + outPath;
1007                         // Add traling / if it's needed
1008                         if (tr && outPath.lastIndexOf('/') !== outPath.length - 1)
1009                                 outPath += tr;
1011                         return outPath;
1012                 },
1014                 getURI : function(nh) {
1015                         var s, t = this;
1017                         // Rebuild source
1018                         if (!t.source || nh) {
1019                                 s = '';
1021                                 if (!nh) {
1022                                         if (t.protocol)
1023                                                 s += t.protocol + '://';
1025                                         if (t.userInfo)
1026                                                 s += t.userInfo + '@';
1028                                         if (t.host)
1029                                                 s += t.host;
1031                                         if (t.port)
1032                                                 s += ':' + t.port;
1033                                 }
1035                                 if (t.path)
1036                                         s += t.path;
1038                                 if (t.query)
1039                                         s += '?' + t.query;
1041                                 if (t.anchor)
1042                                         s += '#' + t.anchor;
1044                                 t.source = s;
1045                         }
1047                         return t.source;
1048                 }
1049         });
1050 })();
1051 (function() {
1052         var each = tinymce.each;
1054         tinymce.create('static tinymce.util.Cookie', {
1055                 getHash : function(n) {
1056                         var v = this.get(n), h;
1058                         if (v) {
1059                                 each(v.split('&'), function(v) {
1060                                         v = v.split('=');
1061                                         h = h || {};
1062                                         h[unescape(v[0])] = unescape(v[1]);
1063                                 });
1064                         }
1066                         return h;
1067                 },
1069                 setHash : function(n, v, e, p, d, s) {
1070                         var o = '';
1072                         each(v, function(v, k) {
1073                                 o += (!o ? '' : '&') + escape(k) + '=' + escape(v);
1074                         });
1076                         this.set(n, o, e, p, d, s);
1077                 },
1079                 get : function(n) {
1080                         var c = document.cookie, e, p = n + "=", b;
1082                         // Strict mode
1083                         if (!c)
1084                                 return;
1086                         b = c.indexOf("; " + p);
1088                         if (b == -1) {
1089                                 b = c.indexOf(p);
1091                                 if (b != 0)
1092                                         return null;
1093                         } else
1094                                 b += 2;
1096                         e = c.indexOf(";", b);
1098                         if (e == -1)
1099                                 e = c.length;
1101                         return unescape(c.substring(b + p.length, e));
1102                 },
1104                 set : function(n, v, e, p, d, s) {
1105                         document.cookie = n + "=" + escape(v) +
1106                                 ((e) ? "; expires=" + e.toGMTString() : "") +
1107                                 ((p) ? "; path=" + escape(p) : "") +
1108                                 ((d) ? "; domain=" + d : "") +
1109                                 ((s) ? "; secure" : "");
1110                 },
1112                 remove : function(n, p) {
1113                         var d = new Date();
1115                         d.setTime(d.getTime() - 1000);
1117                         this.set(n, '', d, p, d);
1118                 }
1119         });
1120 })();
1121 (function() {
1122         function serialize(o, quote) {
1123                 var i, v, t;
1125                 quote = quote || '"';
1127                 if (o == null)
1128                         return 'null';
1130                 t = typeof o;
1132                 if (t == 'string') {
1133                         v = '\bb\tt\nn\ff\rr\""\'\'\\\\';
1135                         return quote + o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g, function(a, b) {
1136                                 // Make sure single quotes never get encoded inside double quotes for JSON compatibility
1137                                 if (quote === '"' && a === "'")
1138                                         return a;
1140                                 i = v.indexOf(b);
1142                                 if (i + 1)
1143                                         return '\\' + v.charAt(i + 1);
1145                                 a = b.charCodeAt().toString(16);
1147                                 return '\\u' + '0000'.substring(a.length) + a;
1148                         }) + quote;
1149                 }
1151                 if (t == 'object') {
1152                         if (o.hasOwnProperty && o instanceof Array) {
1153                                         for (i=0, v = '['; i<o.length; i++)
1154                                                 v += (i > 0 ? ',' : '') + serialize(o[i], quote);
1156                                         return v + ']';
1157                                 }
1159                                 v = '{';
1161                                 for (i in o)
1162                                         v += typeof o[i] != 'function' ? (v.length > 1 ? ',' + quote : quote) + i + quote +':' + serialize(o[i], quote) : '';
1164                                 return v + '}';
1165                 }
1167                 return '' + o;
1168         };
1170         tinymce.util.JSON = {
1171                 serialize: serialize,
1173                 parse: function(s) {
1174                         try {
1175                                 return eval('(' + s + ')');
1176                         } catch (ex) {
1177                                 // Ignore
1178                         }
1179                 }
1181                 };
1182 })();
1183 tinymce.create('static tinymce.util.XHR', {
1184         send : function(o) {
1185                 var x, t, w = window, c = 0;
1187                 // Default settings
1188                 o.scope = o.scope || this;
1189                 o.success_scope = o.success_scope || o.scope;
1190                 o.error_scope = o.error_scope || o.scope;
1191                 o.async = o.async === false ? false : true;
1192                 o.data = o.data || '';
1194                 function get(s) {
1195                         x = 0;
1197                         try {
1198                                 x = new ActiveXObject(s);
1199                         } catch (ex) {
1200                         }
1202                         return x;
1203                 };
1205                 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');
1207                 if (x) {
1208                         if (x.overrideMimeType)
1209                                 x.overrideMimeType(o.content_type);
1211                         x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);
1213                         if (o.content_type)
1214                                 x.setRequestHeader('Content-Type', o.content_type);
1216                         x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
1218                         x.send(o.data);
1220                         function ready() {
1221                                 if (!o.async || x.readyState == 4 || c++ > 10000) {
1222                                         if (o.success && c < 10000 && x.status == 200)
1223                                                 o.success.call(o.success_scope, '' + x.responseText, x, o);
1224                                         else if (o.error)
1225                                                 o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);
1227                                         x = null;
1228                                 } else
1229                                         w.setTimeout(ready, 10);
1230                         };
1232                         // Syncronous request
1233                         if (!o.async)
1234                                 return ready();
1236                         // Wait for response, onReadyStateChange can not be used since it leaks memory in IE
1237                         t = w.setTimeout(ready, 10);
1238                 }
1239         }
1240 });
1241 (function() {
1242         var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;
1244         tinymce.create('tinymce.util.JSONRequest', {
1245                 JSONRequest : function(s) {
1246                         this.settings = extend({
1247                         }, s);
1248                         this.count = 0;
1249                 },
1251                 send : function(o) {
1252                         var ecb = o.error, scb = o.success;
1254                         o = extend(this.settings, o);
1256                         o.success = function(c, x) {
1257                                 c = JSON.parse(c);
1259                                 if (typeof(c) == 'undefined') {
1260                                         c = {
1261                                                 error : 'JSON Parse error.'
1262                                         };
1263                                 }
1265                                 if (c.error)
1266                                         ecb.call(o.error_scope || o.scope, c.error, x);
1267                                 else
1268                                         scb.call(o.success_scope || o.scope, c.result);
1269                         };
1271                         o.error = function(ty, x) {
1272                                 if (ecb)
1273                                         ecb.call(o.error_scope || o.scope, ty, x);
1274                         };
1276                         o.data = JSON.serialize({
1277                                 id : o.id || 'c' + (this.count++),
1278                                 method : o.method,
1279                                 params : o.params
1280                         });
1282                         // JSON content type for Ruby on rails. Bug: #1883287
1283                         o.content_type = 'application/json';
1285                         XHR.send(o);
1286                 },
1288                 'static' : {
1289                         sendRPC : function(o) {
1290                                 return new tinymce.util.JSONRequest().send(o);
1291                         }
1292                 }
1293         });
1294 }());
1295 (function(tinymce) {
1296         var namedEntities, baseEntities, reverseEntities,
1297                 attrsCharsRegExp = /[&\"\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
1298                 textCharsRegExp = /[<>&\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
1299                 rawCharsRegExp = /[<>&\"\']/g,
1300                 entityRegExp = /&(#)?([\w]+);/g,
1301                 asciiMap = {
1302                                 128 : "\u20AC", 130 : "\u201A", 131 : "\u0192", 132 : "\u201E", 133 : "\u2026", 134 : "\u2020",
1303                                 135 : "\u2021", 136 : "\u02C6", 137 : "\u2030", 138 : "\u0160", 139 : "\u2039", 140 : "\u0152",
1304                                 142 : "\u017D", 145 : "\u2018", 146 : "\u2019", 147 : "\u201C", 148 : "\u201D", 149 : "\u2022",
1305                                 150 : "\u2013", 151 : "\u2014", 152 : "\u02DC", 153 : "\u2122", 154 : "\u0161", 155 : "\u203A",
1306                                 156 : "\u0153", 158 : "\u017E", 159 : "\u0178"
1307                 };
1309         // Raw entities
1310         baseEntities = {
1311                 '"' : '&quot;',
1312                 "'" : '&#39;',
1313                 '<' : '&lt;',
1314                 '>' : '&gt;',
1315                 '&' : '&amp;'
1316         };
1318         // Reverse lookup table for raw entities
1319         reverseEntities = {
1320                 '&lt;' : '<',
1321                 '&gt;' : '>',
1322                 '&amp;' : '&',
1323                 '&quot;' : '"',
1324                 '&apos;' : "'"
1325         };
1327         // Decodes text by using the browser
1328         function nativeDecode(text) {
1329                 var elm;
1331                 elm = document.createElement("div");
1332                 elm.innerHTML = text;
1334                 return elm.textContent || elm.innerText || text;
1335         };
1337         // Build a two way lookup table for the entities
1338         function buildEntitiesLookup(items, radix) {
1339                 var i, chr, entity, lookup = {};
1341                 if (items) {
1342                         items = items.split(',');
1343                         radix = radix || 10;
1345                         // Build entities lookup table
1346                         for (i = 0; i < items.length; i += 2) {
1347                                 chr = String.fromCharCode(parseInt(items[i], radix));
1349                                 // Only add non base entities
1350                                 if (!baseEntities[chr]) {
1351                                         entity = '&' + items[i + 1] + ';';
1352                                         lookup[chr] = entity;
1353                                         lookup[entity] = chr;
1354                                 }
1355                         }
1357                         return lookup;
1358                 }
1359         };
1361         // Unpack entities lookup where the numbers are in radix 32 to reduce the size
1362         namedEntities = buildEntitiesLookup(
1363                 '50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' +
1364                 '5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' +
1365                 '5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' +
1366                 '5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' +
1367                 '68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' +
1368                 '6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' +
1369                 '6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' +
1370                 '75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' +
1371                 '7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' +
1372                 '7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' +
1373                 'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' +
1374                 'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' +
1375                 't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' +
1376                 'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' +
1377                 'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' +
1378                 '81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' +
1379                 '8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' +
1380                 '8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' +
1381                 '8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' +
1382                 '8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' +
1383                 'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' +
1384                 'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' +
1385                 'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' +
1386                 '80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' +
1387                 '811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro'
1388         , 32);
1390         tinymce.html = tinymce.html || {};
1392         tinymce.html.Entities = {
1393                 encodeRaw : function(text, attr) {
1394                         return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
1395                                 return baseEntities[chr] || chr;
1396                         });
1397                 },
1399                 encodeAllRaw : function(text) {
1400                         return ('' + text).replace(rawCharsRegExp, function(chr) {
1401                                 return baseEntities[chr] || chr;
1402                         });
1403                 },
1405                 encodeNumeric : function(text, attr) {
1406                         return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
1407                                 // Multi byte sequence convert it to a single entity
1408                                 if (chr.length > 1)
1409                                         return '&#' + (((chr.charCodeAt(0) - 0xD800) * 0x400) + (chr.charCodeAt(1) - 0xDC00) + 0x10000) + ';';
1411                                 return baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
1412                         });
1413                 },
1415                 encodeNamed : function(text, attr, entities) {
1416                         entities = entities || namedEntities;
1418                         return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
1419                                 return baseEntities[chr] || entities[chr] || chr;
1420                         });
1421                 },
1423                 getEncodeFunc : function(name, entities) {
1424                         var Entities = tinymce.html.Entities;
1426                         entities = buildEntitiesLookup(entities) || namedEntities;
1428                         function encodeNamedAndNumeric(text, attr) {
1429                                 return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
1430                                         return baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
1431                                 });
1432                         };
1434                         function encodeCustomNamed(text, attr) {
1435                                 return Entities.encodeNamed(text, attr, entities);
1436                         };
1438                         // Replace + with , to be compatible with previous TinyMCE versions
1439                         name = tinymce.makeMap(name.replace(/\+/g, ','));
1441                         // Named and numeric encoder
1442                         if (name.named && name.numeric)
1443                                 return encodeNamedAndNumeric;
1445                         // Named encoder
1446                         if (name.named) {
1447                                 // Custom names
1448                                 if (entities)
1449                                         return encodeCustomNamed;
1451                                 return Entities.encodeNamed;
1452                         }
1454                         // Numeric
1455                         if (name.numeric)
1456                                 return Entities.encodeNumeric;
1458                         // Raw encoder
1459                         return Entities.encodeRaw;
1460                 },
1462                 decode : function(text) {
1463                         return text.replace(entityRegExp, function(all, numeric, value) {
1464                                 if (numeric) {
1465                                         value = parseInt(value);
1467                                         // Support upper UTF
1468                                         if (value > 0xFFFF) {
1469                                                 value -= 0x10000;
1471                                                 return String.fromCharCode(0xD800 + (value >> 10), 0xDC00 + (value & 0x3FF));
1472                                         } else
1473                                                 return asciiMap[value] || String.fromCharCode(value);
1474                                 }
1476                                 return reverseEntities[all] || namedEntities[all] || nativeDecode(all);
1477                         });
1478                 }
1479         };
1480 })(tinymce);
1481 tinymce.html.Styles = function(settings, schema) {
1482         var rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,
1483                 urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,
1484                 styleRegExp = /\s*([^:]+):\s*([^;]+);?/g,
1485                 trimRightRegExp = /\s+$/,
1486                 urlColorRegExp = /rgb/,
1487                 undef, i, encodingLookup = {}, encodingItems;
1489         settings = settings || {};
1491         encodingItems = '\\" \\\' \\; \\: ; : _'.split(' ');
1492         for (i = 0; i < encodingItems.length; i++) {
1493                 encodingLookup[encodingItems[i]] = '_' + i;
1494                 encodingLookup['_' + i] = encodingItems[i];
1495         }
1497         function toHex(match, r, g, b) {
1498                 function hex(val) {
1499                         val = parseInt(val).toString(16);
1501                         return val.length > 1 ? val : '0' + val; // 0 -> 00
1502                 };
1504                 return '#' + hex(r) + hex(g) + hex(b);
1505         };
1507         return {
1508                 toHex : function(color) {
1509                         return color.replace(rgbRegExp, toHex);
1510                 },
1512                 parse : function(css) {
1513                         var styles = {}, matches, name, value, isEncoded, urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope || this;
1515                         function compress(prefix, suffix) {
1516                                 var top, right, bottom, left;
1518                                 // Get values and check it it needs compressing
1519                                 top = styles[prefix + '-top' + suffix];
1520                                 if (!top)
1521                                         return;
1523                                 right = styles[prefix + '-right' + suffix];
1524                                 if (top != right)
1525                                         return;
1527                                 bottom = styles[prefix + '-bottom' + suffix];
1528                                 if (right != bottom)
1529                                         return;
1531                                 left = styles[prefix + '-left' + suffix];
1532                                 if (bottom != left)
1533                                         return;
1535                                 // Compress
1536                                 styles[prefix + suffix] = left;
1537                                 delete styles[prefix + '-top' + suffix];
1538                                 delete styles[prefix + '-right' + suffix];
1539                                 delete styles[prefix + '-bottom' + suffix];
1540                                 delete styles[prefix + '-left' + suffix];
1541                         };
1543                         function canCompress(key) {
1544                                 var value = styles[key], i;
1546                                 if (!value || value.indexOf(' ') < 0)
1547                                         return;
1549                                 value = value.split(' ');
1550                                 i = value.length;
1551                                 while (i--) {
1552                                         if (value[i] !== value[0])
1553                                                 return false;
1554                                 }
1556                                 styles[key] = value[0];
1558                                 return true;
1559                         };
1561                         function compress2(target, a, b, c) {
1562                                 if (!canCompress(a))
1563                                         return;
1565                                 if (!canCompress(b))
1566                                         return;
1568                                 if (!canCompress(c))
1569                                         return;
1571                                 // Compress
1572                                 styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c];
1573                                 delete styles[a];
1574                                 delete styles[b];
1575                                 delete styles[c];
1576                         };
1578                         // Encodes the specified string by replacing all \" \' ; : with _<num>
1579                         function encode(str) {
1580                                 isEncoded = true;
1582                                 return encodingLookup[str];
1583                         };
1585                         // Decodes the specified string by replacing all _<num> with it's original value \" \' etc
1586                         // It will also decode the \" \' if keep_slashes is set to fale or omitted
1587                         function decode(str, keep_slashes) {
1588                                 if (isEncoded) {
1589                                         str = str.replace(/_[0-9]/g, function(str) {
1590                                                 return encodingLookup[str];
1591                                         });
1592                                 }
1594                                 if (!keep_slashes)
1595                                         str = str.replace(/\\([\'\";:])/g, "$1");
1597                                 return str;
1598                         }
1600                         if (css) {
1601                                 // Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing
1602                                 css = css.replace(/\\[\"\';:_]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, function(str) {
1603                                         return str.replace(/[;:]/g, encode);
1604                                 });
1606                                 // Parse styles
1607                                 while (matches = styleRegExp.exec(css)) {
1608                                         name = matches[1].replace(trimRightRegExp, '').toLowerCase();
1609                                         value = matches[2].replace(trimRightRegExp, '');
1611                                         if (name && value.length > 0) {
1612                                                 // Opera will produce 700 instead of bold in their style values
1613                                                 if (name === 'font-weight' && value === '700')
1614                                                         value = 'bold';
1615                                                 else if (name === 'color' || name === 'background-color') // Lowercase colors like RED
1616                                                         value = value.toLowerCase();            
1618                                                 // Convert RGB colors to HEX
1619                                                 value = value.replace(rgbRegExp, toHex);
1621                                                 // Convert URLs and force them into url('value') format
1622                                                 value = value.replace(urlOrStrRegExp, function(match, url, url2, url3, str, str2) {
1623                                                         str = str || str2;
1625                                                         if (str) {
1626                                                                 str = decode(str);
1628                                                                 // Force strings into single quote format
1629                                                                 return "'" + str.replace(/\'/g, "\\'") + "'";
1630                                                         }
1632                                                         url = decode(url || url2 || url3);
1634                                                         // Convert the URL to relative/absolute depending on config
1635                                                         if (urlConverter)
1636                                                                 url = urlConverter.call(urlConverterScope, url, 'style');
1638                                                         // Output new URL format
1639                                                         return "url('" + url.replace(/\'/g, "\\'") + "')";
1640                                                 });
1642                                                 styles[name] = isEncoded ? decode(value, true) : value;
1643                                         }
1645                                         styleRegExp.lastIndex = matches.index + matches[0].length;
1646                                 }
1648                                 // Compress the styles to reduce it's size for example IE will expand styles
1649                                 compress("border", "");
1650                                 compress("border", "-width");
1651                                 compress("border", "-color");
1652                                 compress("border", "-style");
1653                                 compress("padding", "");
1654                                 compress("margin", "");
1655                                 compress2('border', 'border-width', 'border-style', 'border-color');
1657                                 // Remove pointless border, IE produces these
1658                                 if (styles.border === 'medium none')
1659                                         delete styles.border;
1660                         }
1662                         return styles;
1663                 },
1665                 serialize : function(styles, element_name) {
1666                         var css = '', name, value;
1668                         function serializeStyles(name) {
1669                                 var styleList, i, l, name, value;
1671                                 styleList = schema.styles[name];
1672                                 if (styleList) {
1673                                         for (i = 0, l = styleList.length; i < l; i++) {
1674                                                 name = styleList[i];
1675                                                 value = styles[name];
1677                                                 if (value !== undef && value.length > 0)
1678                                                         css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
1679                                         }
1680                                 }
1681                         };
1683                         // Serialize styles according to schema
1684                         if (element_name && schema && schema.styles) {
1685                                 // Serialize global styles and element specific styles
1686                                 serializeStyles('*');
1687                                 serializeStyles(name);
1688                         } else {
1689                                 // Output the styles in the order they are inside the object
1690                                 for (name in styles) {
1691                                         value = styles[name];
1693                                         if (value !== undef && value.length > 0)
1694                                                 css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
1695                                 }
1696                         }
1698                         return css;
1699                 }
1700         };
1701 };
1702 (function(tinymce) {
1703         var transitional = {}, boolAttrMap, blockElementsMap, shortEndedElementsMap, nonEmptyElementsMap,
1704                 whiteSpaceElementsMap, selfClosingElementsMap, makeMap = tinymce.makeMap, each = tinymce.each;
1706         function split(str, delim) {
1707                 return str.split(delim || ',');
1708         };
1710         function unpack(lookup, data) {
1711                 var key, elements = {};
1713                 function replace(value) {
1714                         return value.replace(/[A-Z]+/g, function(key) {
1715                                 return replace(lookup[key]);
1716                         });
1717                 };
1719                 // Unpack lookup
1720                 for (key in lookup) {
1721                         if (lookup.hasOwnProperty(key))
1722                                 lookup[key] = replace(lookup[key]);
1723                 }
1725                 // Unpack and parse data into object map
1726                 replace(data).replace(/#/g, '#text').replace(/(\w+)\[([^\]]+)\]\[([^\]]*)\]/g, function(str, name, attributes, children) {
1727                         attributes = split(attributes, '|');
1729                         elements[name] = {
1730                                 attributes : makeMap(attributes),
1731                                 attributesOrder : attributes,
1732                                 children : makeMap(children, '|', {'#comment' : {}})
1733                         }
1734                 });
1736                 return elements;
1737         };
1739         // Build a lookup table for block elements both lowercase and uppercase
1740         blockElementsMap = 'h1,h2,h3,h4,h5,h6,hr,p,div,address,pre,form,table,tbody,thead,tfoot,' + 
1741                                                 'th,tr,td,li,ol,ul,caption,blockquote,center,dl,dt,dd,dir,fieldset,' + 
1742                                                 'noscript,menu,isindex,samp,header,footer,article,section,hgroup';
1743         blockElementsMap = makeMap(blockElementsMap, ',', makeMap(blockElementsMap.toUpperCase()));
1745         // This is the XHTML 1.0 transitional elements with it's attributes and children packed to reduce it's size
1746         transitional = unpack({
1747                 Z : 'H|K|N|O|P',
1748                 Y : 'X|form|R|Q',
1749                 ZG : 'E|span|width|align|char|charoff|valign',
1750                 X : 'p|T|div|U|W|isindex|fieldset|table',
1751                 ZF : 'E|align|char|charoff|valign',
1752                 W : 'pre|hr|blockquote|address|center|noframes',
1753                 ZE : 'abbr|axis|headers|scope|rowspan|colspan|align|char|charoff|valign|nowrap|bgcolor|width|height',
1754                 ZD : '[E][S]',
1755                 U : 'ul|ol|dl|menu|dir',
1756                 ZC : 'p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q',
1757                 T : 'h1|h2|h3|h4|h5|h6',
1758                 ZB : 'X|S|Q',
1759                 S : 'R|P',
1760                 ZA : 'a|G|J|M|O|P',
1761                 R : 'a|H|K|N|O',
1762                 Q : 'noscript|P',
1763                 P : 'ins|del|script',
1764                 O : 'input|select|textarea|label|button',
1765                 N : 'M|L',
1766                 M : 'em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym',
1767                 L : 'sub|sup',
1768                 K : 'J|I',
1769                 J : 'tt|i|b|u|s|strike',
1770                 I : 'big|small|font|basefont',
1771                 H : 'G|F',
1772                 G : 'br|span|bdo',
1773                 F : 'object|applet|img|map|iframe',
1774                 E : 'A|B|C',
1775                 D : 'accesskey|tabindex|onfocus|onblur',
1776                 C : 'onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup',
1777                 B : 'lang|xml:lang|dir',
1778                 A : 'id|class|style|title'
1779         }, 'script[id|charset|type|language|src|defer|xml:space][]' + 
1780                 'style[B|id|type|media|title|xml:space][]' + 
1781                 'object[E|declare|classid|codebase|data|type|codetype|archive|standby|width|height|usemap|name|tabindex|align|border|hspace|vspace][#|param|Y]' + 
1782                 'param[id|name|value|valuetype|type][]' + 
1783                 'p[E|align][#|S]' + 
1784                 'a[E|D|charset|type|name|href|hreflang|rel|rev|shape|coords|target][#|Z]' + 
1785                 'br[A|clear][]' + 
1786                 'span[E][#|S]' + 
1787                 'bdo[A|C|B][#|S]' + 
1788                 'applet[A|codebase|archive|code|object|alt|name|width|height|align|hspace|vspace][#|param|Y]' + 
1789                 'h1[E|align][#|S]' + 
1790                 'img[E|src|alt|name|longdesc|width|height|usemap|ismap|align|border|hspace|vspace][]' + 
1791                 'map[B|C|A|name][X|form|Q|area]' + 
1792                 'h2[E|align][#|S]' + 
1793                 'iframe[A|longdesc|name|src|frameborder|marginwidth|marginheight|scrolling|align|width|height][#|Y]' + 
1794                 'h3[E|align][#|S]' + 
1795                 'tt[E][#|S]' + 
1796                 'i[E][#|S]' + 
1797                 'b[E][#|S]' + 
1798                 'u[E][#|S]' + 
1799                 's[E][#|S]' + 
1800                 'strike[E][#|S]' + 
1801                 'big[E][#|S]' + 
1802                 'small[E][#|S]' + 
1803                 'font[A|B|size|color|face][#|S]' + 
1804                 'basefont[id|size|color|face][]' + 
1805                 'em[E][#|S]' + 
1806                 'strong[E][#|S]' + 
1807                 'dfn[E][#|S]' + 
1808                 'code[E][#|S]' + 
1809                 'q[E|cite][#|S]' + 
1810                 'samp[E][#|S]' + 
1811                 'kbd[E][#|S]' + 
1812                 'var[E][#|S]' + 
1813                 'cite[E][#|S]' + 
1814                 'abbr[E][#|S]' + 
1815                 'acronym[E][#|S]' + 
1816                 'sub[E][#|S]' + 
1817                 'sup[E][#|S]' + 
1818                 'input[E|D|type|name|value|checked|disabled|readonly|size|maxlength|src|alt|usemap|onselect|onchange|accept|align][]' + 
1819                 'select[E|name|size|multiple|disabled|tabindex|onfocus|onblur|onchange][optgroup|option]' + 
1820                 'optgroup[E|disabled|label][option]' + 
1821                 'option[E|selected|disabled|label|value][]' + 
1822                 'textarea[E|D|name|rows|cols|disabled|readonly|onselect|onchange][]' + 
1823                 'label[E|for|accesskey|onfocus|onblur][#|S]' + 
1824                 'button[E|D|name|value|type|disabled][#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]' + 
1825                 'h4[E|align][#|S]' + 
1826                 'ins[E|cite|datetime][#|Y]' + 
1827                 'h5[E|align][#|S]' + 
1828                 'del[E|cite|datetime][#|Y]' + 
1829                 'h6[E|align][#|S]' + 
1830                 'div[E|align][#|Y]' + 
1831                 'ul[E|type|compact][li]' + 
1832                 'li[E|type|value][#|Y]' + 
1833                 'ol[E|type|compact|start][li]' + 
1834                 'dl[E|compact][dt|dd]' + 
1835                 'dt[E][#|S]' + 
1836                 'dd[E][#|Y]' + 
1837                 'menu[E|compact][li]' + 
1838                 'dir[E|compact][li]' + 
1839                 'pre[E|width|xml:space][#|ZA]' + 
1840                 'hr[E|align|noshade|size|width][]' + 
1841                 'blockquote[E|cite][#|Y]' + 
1842                 'address[E][#|S|p]' + 
1843                 'center[E][#|Y]' + 
1844                 'noframes[E][#|Y]' + 
1845                 'isindex[A|B|prompt][]' + 
1846                 'fieldset[E][#|legend|Y]' + 
1847                 'legend[E|accesskey|align][#|S]' + 
1848                 'table[E|summary|width|border|frame|rules|cellspacing|cellpadding|align|bgcolor][caption|col|colgroup|thead|tfoot|tbody|tr]' + 
1849                 'caption[E|align][#|S]' + 
1850                 'col[ZG][]' + 
1851                 'colgroup[ZG][col]' + 
1852                 'thead[ZF][tr]' + 
1853                 'tr[ZF|bgcolor][th|td]' + 
1854                 'th[E|ZE][#|Y]' + 
1855                 'form[E|action|method|name|enctype|onsubmit|onreset|accept|accept-charset|target][#|X|R|Q]' + 
1856                 'noscript[E][#|Y]' + 
1857                 'td[E|ZE][#|Y]' + 
1858                 'tfoot[ZF][tr]' + 
1859                 'tbody[ZF][tr]' + 
1860                 'area[E|D|shape|coords|href|nohref|alt|target][]' + 
1861                 'base[id|href|target][]' + 
1862                 'body[E|onload|onunload|background|bgcolor|text|link|vlink|alink][#|Y]'
1863         );
1865         boolAttrMap = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected,preload,autoplay,loop,controls');
1866         shortEndedElementsMap = makeMap('area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,source');
1867         nonEmptyElementsMap = tinymce.extend(makeMap('td,th,iframe,video,object'), shortEndedElementsMap);
1868         whiteSpaceElementsMap = makeMap('pre,script,style');
1869         selfClosingElementsMap = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr');
1871         tinymce.html.Schema = function(settings) {
1872                 var self = this, elements = {}, children = {}, patternElements = [], validStyles;
1874                 settings = settings || {};
1876                 // Allow all elements and attributes if verify_html is set to false
1877                 if (settings.verify_html === false)
1878                         settings.valid_elements = '*[*]';
1880                 // Build styles list
1881                 if (settings.valid_styles) {
1882                         validStyles = {};
1884                         // Convert styles into a rule list
1885                         each(settings.valid_styles, function(value, key) {
1886                                 validStyles[key] = tinymce.explode(value);
1887                         });
1888                 }
1890                 // Converts a wildcard expression string to a regexp for example *a will become /.*a/.
1891                 function patternToRegExp(str) {
1892                         return new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$');
1893                 };
1895                 // Parses the specified valid_elements string and adds to the current rules
1896                 // This function is a bit hard to read since it's heavily optimized for speed
1897                 function addValidElements(valid_elements) {
1898                         var ei, el, ai, al, yl, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder,
1899                                 prefix, outputName, globalAttributes, globalAttributesOrder, transElement, key, childKey, value,
1900                                 elementRuleRegExp = /^([#+-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,
1901                                 attrRuleRegExp = /^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,
1902                                 hasPatternsRegExp = /[*?+]/;
1904                         if (valid_elements) {
1905                                 // Split valid elements into an array with rules
1906                                 valid_elements = split(valid_elements);
1908                                 if (elements['@']) {
1909                                         globalAttributes = elements['@'].attributes;
1910                                         globalAttributesOrder = elements['@'].attributesOrder;
1911                                 }
1913                                 // Loop all rules
1914                                 for (ei = 0, el = valid_elements.length; ei < el; ei++) {
1915                                         // Parse element rule
1916                                         matches = elementRuleRegExp.exec(valid_elements[ei]);
1917                                         if (matches) {
1918                                                 // Setup local names for matches
1919                                                 prefix = matches[1];
1920                                                 elementName = matches[2];
1921                                                 outputName = matches[3];
1922                                                 attrData = matches[4];
1924                                                 // Create new attributes and attributesOrder
1925                                                 attributes = {};
1926                                                 attributesOrder = [];
1928                                                 // Create the new element
1929                                                 element = {
1930                                                         attributes : attributes,
1931                                                         attributesOrder : attributesOrder
1932                                                 };
1934                                                 // Padd empty elements prefix
1935                                                 if (prefix === '#')
1936                                                         element.paddEmpty = true;
1938                                                 // Remove empty elements prefix
1939                                                 if (prefix === '-')
1940                                                         element.removeEmpty = true;
1942                                                 // Copy attributes from global rule into current rule
1943                                                 if (globalAttributes) {
1944                                                         for (key in globalAttributes)
1945                                                                 attributes[key] = globalAttributes[key];
1947                                                         attributesOrder.push.apply(attributesOrder, globalAttributesOrder);
1948                                                 }
1950                                                 // Attributes defined
1951                                                 if (attrData) {
1952                                                         attrData = split(attrData, '|');
1953                                                         for (ai = 0, al = attrData.length; ai < al; ai++) {
1954                                                                 matches = attrRuleRegExp.exec(attrData[ai]);
1955                                                                 if (matches) {
1956                                                                         attr = {};
1957                                                                         attrType = matches[1];
1958                                                                         attrName = matches[2].replace(/::/g, ':');
1959                                                                         prefix = matches[3];
1960                                                                         value = matches[4];
1962                                                                         // Required
1963                                                                         if (attrType === '!') {
1964                                                                                 element.attributesRequired = element.attributesRequired || [];
1965                                                                                 element.attributesRequired.push(attrName);
1966                                                                                 attr.required = true;
1967                                                                         }
1969                                                                         // Denied from global
1970                                                                         if (attrType === '-') {
1971                                                                                 delete attributes[attrName];
1972                                                                                 attributesOrder.splice(tinymce.inArray(attributesOrder, attrName), 1);
1973                                                                                 continue;
1974                                                                         }
1976                                                                         // Default value
1977                                                                         if (prefix) {
1978                                                                                 // Default value
1979                                                                                 if (prefix === '=') {
1980                                                                                         element.attributesDefault = element.attributesDefault || [];
1981                                                                                         element.attributesDefault.push({name: attrName, value: value});
1982                                                                                         attr.defaultValue = value;
1983                                                                                 }
1985                                                                                 // Forced value
1986                                                                                 if (prefix === ':') {
1987                                                                                         element.attributesForced = element.attributesForced || [];
1988                                                                                         element.attributesForced.push({name: attrName, value: value});
1989                                                                                         attr.forcedValue = value;
1990                                                                                 }
1992                                                                                 // Required values
1993                                                                                 if (prefix === '<')
1994                                                                                         attr.validValues = makeMap(value, '?');
1995                                                                         }
1997                                                                         // Check for attribute patterns
1998                                                                         if (hasPatternsRegExp.test(attrName)) {
1999                                                                                 element.attributePatterns = element.attributePatterns || [];
2000                                                                                 attr.pattern = patternToRegExp(attrName);
2001                                                                                 element.attributePatterns.push(attr);
2002                                                                         } else {
2003                                                                                 // Add attribute to order list if it doesn't already exist
2004                                                                                 if (!attributes[attrName])
2005                                                                                         attributesOrder.push(attrName);
2007                                                                                 attributes[attrName] = attr;
2008                                                                         }
2009                                                                 }
2010                                                         }
2011                                                 }
2013                                                 // Global rule, store away these for later usage
2014                                                 if (!globalAttributes && elementName == '@') {
2015                                                         globalAttributes = attributes;
2016                                                         globalAttributesOrder = attributesOrder;
2017                                                 }
2019                                                 // Handle substitute elements such as b/strong
2020                                                 if (outputName) {
2021                                                         element.outputName = elementName;
2022                                                         elements[outputName] = element;
2023                                                 }
2025                                                 // Add pattern or exact element
2026                                                 if (hasPatternsRegExp.test(elementName)) {
2027                                                         element.pattern = patternToRegExp(elementName);
2028                                                         patternElements.push(element);
2029                                                 } else
2030                                                         elements[elementName] = element;
2031                                         }
2032                                 }
2033                         }
2034                 };
2036                 function setValidElements(valid_elements) {
2037                         elements = {};
2038                         patternElements = [];
2040                         addValidElements(valid_elements);
2042                         each(transitional, function(element, name) {
2043                                 children[name] = element.children;
2044                         });
2045                 };
2047                 // Adds custom non HTML elements to the schema
2048                 function addCustomElements(custom_elements) {
2049                         var customElementRegExp = /^(~)?(.+)$/;
2051                         if (custom_elements) {
2052                                 each(split(custom_elements), function(rule) {
2053                                         var matches = customElementRegExp.exec(rule),
2054                                                 cloneName = matches[1] === '~' ? 'span' : 'div',
2055                                                 name = matches[2];
2057                                         children[name] = children[cloneName];
2059                                         // Add custom elements at span/div positions
2060                                         each(children, function(element, child) {
2061                                                 if (element[cloneName])
2062                                                         element[name] = element[cloneName];
2063                                         });
2064                                 });
2065                         }
2066                 };
2068                 // Adds valid children to the schema object
2069                 function addValidChildren(valid_children) {
2070                         var childRuleRegExp = /^([+\-]?)(\w+)\[([^\]]+)\]$/;
2072                         if (valid_children) {
2073                                 each(split(valid_children), function(rule) {
2074                                         var matches = childRuleRegExp.exec(rule), parent, prefix;
2076                                         if (matches) {
2077                                                 prefix = matches[1];
2079                                                 // Add/remove items from default
2080                                                 if (prefix)
2081                                                         parent = children[matches[2]];
2082                                                 else
2083                                                         parent = children[matches[2]] = {'#comment' : {}};
2085                                                 parent = children[matches[2]];
2087                                                 each(split(matches[3], '|'), function(child) {
2088                                                         if (prefix === '-')
2089                                                                 delete parent[child];
2090                                                         else
2091                                                                 parent[child] = {};
2092                                                 });
2093                                         }
2094                                 });
2095                         }
2096                 }
2098                 if (!settings.valid_elements) {
2099                         // No valid elements defined then clone the elements from the transitional spec
2100                         each(transitional, function(element, name) {
2101                                 elements[name] = {
2102                                         attributes : element.attributes,
2103                                         attributesOrder : element.attributesOrder
2104                                 };
2106                                 children[name] = element.children;
2107                         });
2109                         // Switch these
2110                         each(split('strong/b,em/i'), function(item) {
2111                                 item = split(item, '/');
2112                                 elements[item[1]].outputName = item[0];
2113                         });
2115                         // Add default alt attribute for images
2116                         elements.img.attributesDefault = [{name: 'alt', value: ''}];
2118                         // Remove these if they are empty by default
2119                         each(split('ol,ul,li,sub,sup,blockquote,tr,div,span,font,a,table,tbody'), function(name) {
2120                                 elements[name].removeEmpty = true;
2121                         });
2123                         // Padd these by default
2124                         each(split('p,h1,h2,h3,h4,h5,h6,th,td,pre,div,address,caption'), function(name) {
2125                                 elements[name].paddEmpty = true;
2126                         });
2127                 } else
2128                         setValidElements(settings.valid_elements);
2130                 addCustomElements(settings.custom_elements);
2131                 addValidChildren(settings.valid_children);
2132                 addValidElements(settings.extended_valid_elements);
2134                 // Todo: Remove this when we fix list handling to be valid
2135                 addValidChildren('+ol[ul|ol],+ul[ul|ol]');
2137                 // Delete invalid elements
2138                 if (settings.invalid_elements) {
2139                         tinymce.each(tinymce.explode(settings.invalid_elements), function(item) {
2140                                 if (elements[item])
2141                                         delete elements[item];
2142                         });
2143                 }
2145                 self.children = children;
2147                 self.styles = validStyles;
2149                 self.getBoolAttrs = function() {
2150                         return boolAttrMap;
2151                 };
2153                 self.getBlockElements = function() {
2154                         return blockElementsMap;
2155                 };
2157                 self.getShortEndedElements = function() {
2158                         return shortEndedElementsMap;
2159                 };
2161                 self.getSelfClosingElements = function() {
2162                         return selfClosingElementsMap;
2163                 };
2165                 self.getNonEmptyElements = function() {
2166                         return nonEmptyElementsMap;
2167                 };
2169                 self.getWhiteSpaceElements = function() {
2170                         return whiteSpaceElementsMap;
2171                 };
2173                 self.isValidChild = function(name, child) {
2174                         var parent = children[name];
2176                         return !!(parent && parent[child]);
2177                 };
2179                 self.getElementRule = function(name) {
2180                         var element = elements[name], i;
2182                         // Exact match found
2183                         if (element)
2184                                 return element;
2186                         // No exact match then try the patterns
2187                         i = patternElements.length;
2188                         while (i--) {
2189                                 element = patternElements[i];
2191                                 if (element.pattern.test(name))
2192                                         return element;
2193                         }
2194                 };
2196                 self.addValidElements = addValidElements;
2198                 self.setValidElements = setValidElements;
2200                 self.addCustomElements = addCustomElements;
2202                 self.addValidChildren = addValidChildren;
2203         };
2205         // Expose boolMap and blockElementMap as static properties for usage in DOMUtils
2206         tinymce.html.Schema.boolAttrMap = boolAttrMap;
2207         tinymce.html.Schema.blockElementsMap = blockElementsMap;
2208 })(tinymce);
2209 (function(tinymce) {
2210         tinymce.html.SaxParser = function(settings, schema) {
2211                 var self = this, noop = function() {};
2213                 settings = settings || {};
2214                 self.schema = schema = schema || new tinymce.html.Schema();
2216                 if (settings.fix_self_closing !== false)
2217                         settings.fix_self_closing = true;
2219                 // Add handler functions from settings and setup default handlers
2220                 tinymce.each('comment cdata text start end pi doctype'.split(' '), function(name) {
2221                         if (name)
2222                                 self[name] = settings[name] || noop;
2223                 });
2225                 self.parse = function(html) {
2226                         var self = this, matches, index = 0, value, endRegExp, stack = [], attrList, i, text, name,
2227                                 shortEndedElements, fillAttrsMap, isShortEnded, validate, elementRule, isValidElement, attr, attribsValue,
2228                                 validAttributesMap, validAttributePatterns, attributesRequired, attributesDefault, attributesForced, selfClosing,
2229                                 tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0, decode = tinymce.html.Entities.decode, fixSelfClosing;
2231                         function processEndTag(name) {
2232                                 var pos, i;
2234                                 // Find position of parent of the same type
2235                                 pos = stack.length;
2236                                 while (pos--) {
2237                                         if (stack[pos].name === name)
2238                                                 break;                                          
2239                                 }
2241                                 // Found parent
2242                                 if (pos >= 0) {
2243                                         // Close all the open elements
2244                                         for (i = stack.length - 1; i >= pos; i--) {
2245                                                 name = stack[i];
2247                                                 if (name.valid)
2248                                                         self.end(name.name);
2249                                         }
2251                                         // Remove the open elements from the stack
2252                                         stack.length = pos;
2253                                 }
2254                         };
2256                         // Precompile RegExps and map objects
2257                         tokenRegExp = new RegExp('<(?:' +
2258                                 '(?:!--([\\w\\W]*?)-->)|' + // Comment
2259                                 '(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|' + // CDATA
2260                                 '(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE
2261                                 '(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI
2262                                 '(?:\\/([^>]+)>)|' + // End element
2263                                 '(?:([^\\s\\/<>]+)\\s*((?:[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*)>)' + // Start element
2264                         ')', 'g');
2266                         attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:\\.|[^\"])*)\")|(?:\'((?:\\.|[^\'])*)\')|([^>\s]+)))?/g;
2267                         specialElements = {
2268                                 'script' : /<\/script[^>]*>/gi,
2269                                 'style' : /<\/style[^>]*>/gi,
2270                                 'noscript' : /<\/noscript[^>]*>/gi
2271                         };
2273                         // Setup lookup tables for empty elements and boolean attributes
2274                         shortEndedElements = schema.getShortEndedElements();
2275                         selfClosing = schema.getSelfClosingElements();
2276                         fillAttrsMap = schema.getBoolAttrs();
2277                         validate = settings.validate;
2278                         fixSelfClosing = settings.fix_self_closing;
2280                         while (matches = tokenRegExp.exec(html)) {
2281                                 // Text
2282                                 if (index < matches.index)
2283                                         self.text(decode(html.substr(index, matches.index - index)));
2285                                 if (value = matches[6]) { // End element
2286                                         processEndTag(value.toLowerCase());
2287                                 } else if (value = matches[7]) { // Start element
2288                                         value = value.toLowerCase();
2289                                         isShortEnded = value in shortEndedElements;
2291                                         // Is self closing tag for example an <li> after an open <li>
2292                                         if (fixSelfClosing && selfClosing[value] && stack.length > 0 && stack[stack.length - 1].name === value)
2293                                                 processEndTag(value);
2295                                         // Validate element
2296                                         if (!validate || (elementRule = schema.getElementRule(value))) {
2297                                                 isValidElement = true;
2299                                                 // Grab attributes map and patters when validation is enabled
2300                                                 if (validate) {
2301                                                         validAttributesMap = elementRule.attributes;
2302                                                         validAttributePatterns = elementRule.attributePatterns;
2303                                                 }
2305                                                 // Parse attributes
2306                                                 if (attribsValue = matches[8]) {
2307                                                         attrList = [];
2308                                                         attrList.map = {};
2310                                                         attribsValue.replace(attrRegExp, function(match, name, value, val2, val3) {
2311                                                                 var attrRule, i;
2313                                                                 name = name.toLowerCase();
2314                                                                 value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute
2316                                                                 // Validate name and value
2317                                                                 if (validate && name.indexOf('data-') !== 0) {
2318                                                                         attrRule = validAttributesMap[name];
2320                                                                         // Find rule by pattern matching
2321                                                                         if (!attrRule && validAttributePatterns) {
2322                                                                                 i = validAttributePatterns.length;
2323                                                                                 while (i--) {
2324                                                                                         attrRule = validAttributePatterns[i];
2325                                                                                         if (attrRule.pattern.test(name))
2326                                                                                                 break;
2327                                                                                 }
2329                                                                                 // No rule matched
2330                                                                                 if (i === -1)
2331                                                                                         attrRule = null;
2332                                                                         }
2334                                                                         // No attribute rule found
2335                                                                         if (!attrRule)
2336                                                                                 return;
2338                                                                         // Validate value
2339                                                                         if (attrRule.validValues && !(value in attrRule.validValues))
2340                                                                                 return;
2341                                                                 }
2343                                                                 // Add attribute to list and map
2344                                                                 attrList.map[name] = value;
2345                                                                 attrList.push({
2346                                                                         name: name,
2347                                                                         value: value
2348                                                                 });
2349                                                         });
2350                                                 } else {
2351                                                         attrList = [];
2352                                                         attrList.map = {};
2353                                                 }
2355                                                 // Process attributes if validation is enabled
2356                                                 if (validate) {
2357                                                         attributesRequired = elementRule.attributesRequired;
2358                                                         attributesDefault = elementRule.attributesDefault;
2359                                                         attributesForced = elementRule.attributesForced;
2361                                                         // Handle forced attributes
2362                                                         if (attributesForced) {
2363                                                                 i = attributesForced.length;
2364                                                                 while (i--) {
2365                                                                         attr = attributesForced[i];
2366                                                                         name = attr.name;
2367                                                                         attrValue = attr.value;
2369                                                                         if (attrValue === '{$uid}')
2370                                                                                 attrValue = 'mce_' + idCount++;
2372                                                                         attrList.map[name] = attrValue;
2373                                                                         attrList.push({name: name, value: attrValue});
2374                                                                 }
2375                                                         }
2377                                                         // Handle default attributes
2378                                                         if (attributesDefault) {
2379                                                                 i = attributesDefault.length;
2380                                                                 while (i--) {
2381                                                                         attr = attributesDefault[i];
2382                                                                         name = attr.name;
2384                                                                         if (!(name in attrList.map)) {
2385                                                                                 attrValue = attr.value;
2387                                                                                 if (attrValue === '{$uid}')
2388                                                                                         attrValue = 'mce_' + idCount++;
2390                                                                                 attrList.map[name] = attrValue;
2391                                                                                 attrList.push({name: name, value: attrValue});
2392                                                                         }
2393                                                                 }
2394                                                         }
2396                                                         // Handle required attributes
2397                                                         if (attributesRequired) {
2398                                                                 i = attributesRequired.length;
2399                                                                 while (i--) {
2400                                                                         if (attributesRequired[i] in attrList.map)
2401                                                                                 break;
2402                                                                 }
2404                                                                 // None of the required attributes where found
2405                                                                 if (i === -1)
2406                                                                         isValidElement = false;
2407                                                         }
2409                                                         // Invalidate element if it's marked as bogus
2410                                                         if (attrList.map['data-mce-bogus'])
2411                                                                 isValidElement = false;
2412                                                 }
2414                                                 if (isValidElement)
2415                                                         self.start(value, attrList, isShortEnded);
2416                                         } else
2417                                                 isValidElement = false;
2419                                         // Treat script, noscript and style a bit different since they may include code that looks like elements
2420                                         if (endRegExp = specialElements[value]) {
2421                                                 endRegExp.lastIndex = index = matches.index + matches[0].length;
2423                                                 if (matches = endRegExp.exec(html)) {
2424                                                         if (isValidElement)
2425                                                                 text = html.substr(index, matches.index - index);
2427                                                         index = matches.index + matches[0].length;
2428                                                 } else {
2429                                                         text = html.substr(index);
2430                                                         index = html.length;
2431                                                 }
2433                                                 if (isValidElement && text.length > 0)
2434                                                         self.text(text, true);
2436                                                 if (isValidElement)
2437                                                         self.end(value);
2439                                                 tokenRegExp.lastIndex = index;
2440                                                 continue;
2441                                         }
2443                                         // Push value on to stack
2444                                         if (!isShortEnded) {
2445                                                 if (!attribsValue || attribsValue.indexOf('/') != attribsValue.length - 1)
2446                                                         stack.push({name: value, valid: isValidElement});
2447                                                 else if (isValidElement)
2448                                                         self.end(value);
2449                                         }
2450                                 } else if (value = matches[1]) { // Comment
2451                                         self.comment(value);
2452                                 } else if (value = matches[2]) { // CDATA
2453                                         self.cdata(value);
2454                                 } else if (value = matches[3]) { // DOCTYPE
2455                                         self.doctype(value);
2456                                 } else if (value = matches[4]) { // PI
2457                                         self.pi(value, matches[5]);
2458                                 }
2460                                 index = matches.index + matches[0].length;
2461                         }
2463                         // Text
2464                         if (index < html.length)
2465                                 self.text(decode(html.substr(index)));
2467                         // Close any open elements
2468                         for (i = stack.length - 1; i >= 0; i--) {
2469                                 value = stack[i];
2471                                 if (value.valid)
2472                                         self.end(value.name);
2473                         }
2474                 };
2475         }
2476 })(tinymce);
2477 (function(tinymce) {
2478         var whiteSpaceRegExp = /^[ \t\r\n]*$/, typeLookup = {
2479                 '#text' : 3,
2480                 '#comment' : 8,
2481                 '#cdata' : 4,
2482                 '#pi' : 7,
2483                 '#doctype' : 10,
2484                 '#document-fragment' : 11
2485         };
2487         // Walks the tree left/right
2488         function walk(node, root_node, prev) {
2489                 var sibling, parent, startName = prev ? 'lastChild' : 'firstChild', siblingName = prev ? 'prev' : 'next';
2491                 // Walk into nodes if it has a start
2492                 if (node[startName])
2493                         return node[startName];
2495                 // Return the sibling if it has one
2496                 if (node !== root_node) {
2497                         sibling = node[siblingName];
2499                         if (sibling)
2500                                 return sibling;
2502                         // Walk up the parents to look for siblings
2503                         for (parent = node.parent; parent && parent !== root_node; parent = parent.parent) {
2504                                 sibling = parent[siblingName];
2506                                 if (sibling)
2507                                         return sibling;
2508                         }
2509                 }
2510         };
2512         function Node(name, type) {
2513                 this.name = name;
2514                 this.type = type;
2516                 if (type === 1) {
2517                         this.attributes = [];
2518                         this.attributes.map = {};
2519                 }
2520         }
2522         tinymce.extend(Node.prototype, {
2523                 replace : function(node) {
2524                         var self = this;
2526                         if (node.parent)
2527                                 node.remove();
2529                         self.insert(node, self);
2530                         self.remove();
2532                         return self;
2533                 },
2535                 attr : function(name, value) {
2536                         var self = this, attrs, i, undef;
2538                         if (typeof name !== "string") {
2539                                 for (i in name)
2540                                         self.attr(i, name[i]);
2542                                 return self;
2543                         }
2545                         if (attrs = self.attributes) {
2546                                 if (value !== undef) {
2547                                         // Remove attribute
2548                                         if (value === null) {
2549                                                 if (name in attrs.map) {
2550                                                         delete attrs.map[name];
2552                                                         i = attrs.length;
2553                                                         while (i--) {
2554                                                                 if (attrs[i].name === name) {
2555                                                                         attrs = attrs.splice(i, 1);
2556                                                                         return self;
2557                                                                 }
2558                                                         }
2559                                                 }
2561                                                 return self;
2562                                         }
2564                                         // Set attribute
2565                                         if (name in attrs.map) {
2566                                                 // Set attribute
2567                                                 i = attrs.length;
2568                                                 while (i--) {
2569                                                         if (attrs[i].name === name) {
2570                                                                 attrs[i].value = value;
2571                                                                 break;
2572                                                         }
2573                                                 }
2574                                         } else
2575                                                 attrs.push({name: name, value: value});
2577                                         attrs.map[name] = value;
2579                                         return self;
2580                                 } else {
2581                                         return attrs.map[name];
2582                                 }
2583                         }
2584                 },
2586                 clone : function() {
2587                         var self = this, clone = new Node(self.name, self.type), i, l, selfAttrs, selfAttr, cloneAttrs;
2589                         // Clone element attributes
2590                         if (selfAttrs = self.attributes) {
2591                                 cloneAttrs = [];
2592                                 cloneAttrs.map = {};
2594                                 for (i = 0, l = selfAttrs.length; i < l; i++) {
2595                                         selfAttr = selfAttrs[i];
2597                                         // Clone everything except id
2598                                         if (selfAttr.name !== 'id') {
2599                                                 cloneAttrs[cloneAttrs.length] = {name: selfAttr.name, value: selfAttr.value};
2600                                                 cloneAttrs.map[selfAttr.name] = selfAttr.value;
2601                                         }
2602                                 }
2604                                 clone.attributes = cloneAttrs;
2605                         }
2607                         clone.value = self.value;
2608                         clone.shortEnded = self.shortEnded;
2610                         return clone;
2611                 },
2613                 wrap : function(wrapper) {
2614                         var self = this;
2616                         self.parent.insert(wrapper, self);
2617                         wrapper.append(self);
2619                         return self;
2620                 },
2622                 unwrap : function() {
2623                         var self = this, node, next;
2625                         for (node = self.firstChild; node; ) {
2626                                 next = node.next;
2627                                 self.insert(node, self, true);
2628                                 node = next;
2629                         }
2631                         self.remove();
2632                 },
2634                 remove : function() {
2635                         var self = this, parent = self.parent, next = self.next, prev = self.prev;
2637                         if (parent) {
2638                                 if (parent.firstChild === self) {
2639                                         parent.firstChild = next;
2641                                         if (next)
2642                                                 next.prev = null;
2643                                 } else {
2644                                         prev.next = next;
2645                                 }
2647                                 if (parent.lastChild === self) {
2648                                         parent.lastChild = prev;
2650                                         if (prev)
2651                                                 prev.next = null;
2652                                 } else {
2653                                         next.prev = prev;
2654                                 }
2656                                 self.parent = self.next = self.prev = null;
2657                         }
2659                         return self;
2660                 },
2662                 append : function(node) {
2663                         var self = this, last;
2665                         if (node.parent)
2666                                 node.remove();
2668                         last = self.lastChild;
2669                         if (last) {
2670                                 last.next = node;
2671                                 node.prev = last;
2672                                 self.lastChild = node;
2673                         } else
2674                                 self.lastChild = self.firstChild = node;
2676                         node.parent = self;
2678                         return node;
2679                 },
2681                 insert : function(node, ref_node, before) {
2682                         var parent;
2684                         if (node.parent)
2685                                 node.remove();
2687                         parent = ref_node.parent || this;
2689                         if (before) {
2690                                 if (ref_node === parent.firstChild)
2691                                         parent.firstChild = node;
2692                                 else
2693                                         ref_node.prev.next = node;
2695                                 node.prev = ref_node.prev;
2696                                 node.next = ref_node;
2697                                 ref_node.prev = node;
2698                         } else {
2699                                 if (ref_node === parent.lastChild)
2700                                         parent.lastChild = node;
2701                                 else
2702                                         ref_node.next.prev = node;
2704                                 node.next = ref_node.next;
2705                                 node.prev = ref_node;
2706                                 ref_node.next = node;
2707                         }
2709                         node.parent = parent;
2711                         return node;
2712                 },
2714                 getAll : function(name) {
2715                         var self = this, node, collection = [];
2717                         for (node = self.firstChild; node; node = walk(node, self)) {
2718                                 if (node.name === name)
2719                                         collection.push(node);
2720                         }
2722                         return collection;
2723                 },
2725                 empty : function() {
2726                         var self = this, nodes, i, node;
2728                         // Remove all children
2729                         if (self.firstChild) {
2730                                 nodes = [];
2732                                 // Collect the children