]> git.donarmstrong.com Git - roundcube.git/blob - program/js/tiny_mce/tiny_mce_src.js
Imported Upstream version 0.5.2+dfsg
[roundcube.git] / program / js / tiny_mce / tiny_mce_src.js
1 (function(win) {\r
2         var whiteSpaceRe = /^\s*|\s*$/g,\r
3                 undefined, isRegExpBroken = 'B'.replace(/A(.)|B/, '$1') === '$1';\r
4 \r
5         var tinymce = {\r
6                 majorVersion : '3',\r
7 \r
8                 minorVersion : '4.2',\r
9 \r
10                 releaseDate : '2011-04-07',\r
11 \r
12                 _init : function() {\r
13                         var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;\r
14 \r
15                         t.isOpera = win.opera && opera.buildNumber;\r
16 \r
17                         t.isWebKit = /WebKit/.test(ua);\r
18 \r
19                         t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName);\r
20 \r
21                         t.isIE6 = t.isIE && /MSIE [56]/.test(ua);\r
22 \r
23                         t.isGecko = !t.isWebKit && /Gecko/.test(ua);\r
24 \r
25                         t.isMac = ua.indexOf('Mac') != -1;\r
26 \r
27                         t.isAir = /adobeair/i.test(ua);\r
28 \r
29                         t.isIDevice = /(iPad|iPhone)/.test(ua);\r
30 \r
31                         // TinyMCE .NET webcontrol might be setting the values for TinyMCE\r
32                         if (win.tinyMCEPreInit) {\r
33                                 t.suffix = tinyMCEPreInit.suffix;\r
34                                 t.baseURL = tinyMCEPreInit.base;\r
35                                 t.query = tinyMCEPreInit.query;\r
36                                 return;\r
37                         }\r
38 \r
39                         // Get suffix and base\r
40                         t.suffix = '';\r
41 \r
42                         // If base element found, add that infront of baseURL\r
43                         nl = d.getElementsByTagName('base');\r
44                         for (i=0; i<nl.length; i++) {\r
45                                 if (v = nl[i].href) {\r
46                                         // Host only value like http://site.com or http://site.com:8008\r
47                                         if (/^https?:\/\/[^\/]+$/.test(v))\r
48                                                 v += '/';\r
49 \r
50                                         base = v ? v.match(/.*\//)[0] : ''; // Get only directory\r
51                                 }\r
52                         }\r
53 \r
54                         function getBase(n) {\r
55                                 if (n.src && /tiny_mce(|_gzip|_jquery|_prototype|_full)(_dev|_src)?.js/.test(n.src)) {\r
56                                         if (/_(src|dev)\.js/g.test(n.src))\r
57                                                 t.suffix = '_src';\r
58 \r
59                                         if ((p = n.src.indexOf('?')) != -1)\r
60                                                 t.query = n.src.substring(p + 1);\r
61 \r
62                                         t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));\r
63 \r
64                                         // If path to script is relative and a base href was found add that one infront\r
65                                         // the src property will always be an absolute one on non IE browsers and IE 8\r
66                                         // so this logic will basically only be executed on older IE versions\r
67                                         if (base && t.baseURL.indexOf('://') == -1 && t.baseURL.indexOf('/') !== 0)\r
68                                                 t.baseURL = base + t.baseURL;\r
69 \r
70                                         return t.baseURL;\r
71                                 }\r
72 \r
73                                 return null;\r
74                         };\r
75 \r
76                         // Check document\r
77                         nl = d.getElementsByTagName('script');\r
78                         for (i=0; i<nl.length; i++) {\r
79                                 if (getBase(nl[i]))\r
80                                         return;\r
81                         }\r
82 \r
83                         // Check head\r
84                         n = d.getElementsByTagName('head')[0];\r
85                         if (n) {\r
86                                 nl = n.getElementsByTagName('script');\r
87                                 for (i=0; i<nl.length; i++) {\r
88                                         if (getBase(nl[i]))\r
89                                                 return;\r
90                                 }\r
91                         }\r
92 \r
93                         return;\r
94                 },\r
95 \r
96                 is : function(o, t) {\r
97                         if (!t)\r
98                                 return o !== undefined;\r
99 \r
100                         if (t == 'array' && (o.hasOwnProperty && o instanceof Array))\r
101                                 return true;\r
102 \r
103                         return typeof(o) == t;\r
104                 },\r
105 \r
106                 makeMap : function(items, delim, map) {\r
107                         var i;\r
108 \r
109                         items = items || [];\r
110                         delim = delim || ',';\r
111 \r
112                         if (typeof(items) == "string")\r
113                                 items = items.split(delim);\r
114 \r
115                         map = map || {};\r
116 \r
117                         i = items.length;\r
118                         while (i--)\r
119                                 map[items[i]] = {};\r
120 \r
121                         return map;\r
122                 },\r
123 \r
124                 each : function(o, cb, s) {\r
125                         var n, l;\r
126 \r
127                         if (!o)\r
128                                 return 0;\r
129 \r
130                         s = s || o;\r
131 \r
132                         if (o.length !== undefined) {\r
133                                 // Indexed arrays, needed for Safari\r
134                                 for (n=0, l = o.length; n < l; n++) {\r
135                                         if (cb.call(s, o[n], n, o) === false)\r
136                                                 return 0;\r
137                                 }\r
138                         } else {\r
139                                 // Hashtables\r
140                                 for (n in o) {\r
141                                         if (o.hasOwnProperty(n)) {\r
142                                                 if (cb.call(s, o[n], n, o) === false)\r
143                                                         return 0;\r
144                                         }\r
145                                 }\r
146                         }\r
147 \r
148                         return 1;\r
149                 },\r
150 \r
151 \r
152                 map : function(a, f) {\r
153                         var o = [];\r
154 \r
155                         tinymce.each(a, function(v) {\r
156                                 o.push(f(v));\r
157                         });\r
158 \r
159                         return o;\r
160                 },\r
161 \r
162                 grep : function(a, f) {\r
163                         var o = [];\r
164 \r
165                         tinymce.each(a, function(v) {\r
166                                 if (!f || f(v))\r
167                                         o.push(v);\r
168                         });\r
169 \r
170                         return o;\r
171                 },\r
172 \r
173                 inArray : function(a, v) {\r
174                         var i, l;\r
175 \r
176                         if (a) {\r
177                                 for (i = 0, l = a.length; i < l; i++) {\r
178                                         if (a[i] === v)\r
179                                                 return i;\r
180                                 }\r
181                         }\r
182 \r
183                         return -1;\r
184                 },\r
185 \r
186                 extend : function(o, e) {\r
187                         var i, l, a = arguments;\r
188 \r
189                         for (i = 1, l = a.length; i < l; i++) {\r
190                                 e = a[i];\r
191 \r
192                                 tinymce.each(e, function(v, n) {\r
193                                         if (v !== undefined)\r
194                                                 o[n] = v;\r
195                                 });\r
196                         }\r
197 \r
198                         return o;\r
199                 },\r
200 \r
201 \r
202                 trim : function(s) {\r
203                         return (s ? '' + s : '').replace(whiteSpaceRe, '');\r
204                 },\r
205 \r
206                 create : function(s, p, root) {\r
207                         var t = this, sp, ns, cn, scn, c, de = 0;\r
208 \r
209                         // Parse : <prefix> <class>:<super class>\r
210                         s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);\r
211                         cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name\r
212 \r
213                         // Create namespace for new class\r
214                         ns = t.createNS(s[3].replace(/\.\w+$/, ''), root);\r
215 \r
216                         // Class already exists\r
217                         if (ns[cn])\r
218                                 return;\r
219 \r
220                         // Make pure static class\r
221                         if (s[2] == 'static') {\r
222                                 ns[cn] = p;\r
223 \r
224                                 if (this.onCreate)\r
225                                         this.onCreate(s[2], s[3], ns[cn]);\r
226 \r
227                                 return;\r
228                         }\r
229 \r
230                         // Create default constructor\r
231                         if (!p[cn]) {\r
232                                 p[cn] = function() {};\r
233                                 de = 1;\r
234                         }\r
235 \r
236                         // Add constructor and methods\r
237                         ns[cn] = p[cn];\r
238                         t.extend(ns[cn].prototype, p);\r
239 \r
240                         // Extend\r
241                         if (s[5]) {\r
242                                 sp = t.resolve(s[5]).prototype;\r
243                                 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name\r
244 \r
245                                 // Extend constructor\r
246                                 c = ns[cn];\r
247                                 if (de) {\r
248                                         // Add passthrough constructor\r
249                                         ns[cn] = function() {\r
250                                                 return sp[scn].apply(this, arguments);\r
251                                         };\r
252                                 } else {\r
253                                         // Add inherit constructor\r
254                                         ns[cn] = function() {\r
255                                                 this.parent = sp[scn];\r
256                                                 return c.apply(this, arguments);\r
257                                         };\r
258                                 }\r
259                                 ns[cn].prototype[cn] = ns[cn];\r
260 \r
261                                 // Add super methods\r
262                                 t.each(sp, function(f, n) {\r
263                                         ns[cn].prototype[n] = sp[n];\r
264                                 });\r
265 \r
266                                 // Add overridden methods\r
267                                 t.each(p, function(f, n) {\r
268                                         // Extend methods if needed\r
269                                         if (sp[n]) {\r
270                                                 ns[cn].prototype[n] = function() {\r
271                                                         this.parent = sp[n];\r
272                                                         return f.apply(this, arguments);\r
273                                                 };\r
274                                         } else {\r
275                                                 if (n != cn)\r
276                                                         ns[cn].prototype[n] = f;\r
277                                         }\r
278                                 });\r
279                         }\r
280 \r
281                         // Add static methods\r
282                         t.each(p['static'], function(f, n) {\r
283                                 ns[cn][n] = f;\r
284                         });\r
285 \r
286                         if (this.onCreate)\r
287                                 this.onCreate(s[2], s[3], ns[cn].prototype);\r
288                 },\r
289 \r
290                 walk : function(o, f, n, s) {\r
291                         s = s || this;\r
292 \r
293                         if (o) {\r
294                                 if (n)\r
295                                         o = o[n];\r
296 \r
297                                 tinymce.each(o, function(o, i) {\r
298                                         if (f.call(s, o, i, n) === false)\r
299                                                 return false;\r
300 \r
301                                         tinymce.walk(o, f, n, s);\r
302                                 });\r
303                         }\r
304                 },\r
305 \r
306                 createNS : function(n, o) {\r
307                         var i, v;\r
308 \r
309                         o = o || win;\r
310 \r
311                         n = n.split('.');\r
312                         for (i=0; i<n.length; i++) {\r
313                                 v = n[i];\r
314 \r
315                                 if (!o[v])\r
316                                         o[v] = {};\r
317 \r
318                                 o = o[v];\r
319                         }\r
320 \r
321                         return o;\r
322                 },\r
323 \r
324                 resolve : function(n, o) {\r
325                         var i, l;\r
326 \r
327                         o = o || win;\r
328 \r
329                         n = n.split('.');\r
330                         for (i = 0, l = n.length; i < l; i++) {\r
331                                 o = o[n[i]];\r
332 \r
333                                 if (!o)\r
334                                         break;\r
335                         }\r
336 \r
337                         return o;\r
338                 },\r
339 \r
340                 addUnload : function(f, s) {\r
341                         var t = this;\r
342 \r
343                         f = {func : f, scope : s || this};\r
344 \r
345                         if (!t.unloads) {\r
346                                 function unload() {\r
347                                         var li = t.unloads, o, n;\r
348 \r
349                                         if (li) {\r
350                                                 // Call unload handlers\r
351                                                 for (n in li) {\r
352                                                         o = li[n];\r
353 \r
354                                                         if (o && o.func)\r
355                                                                 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy\r
356                                                 }\r
357 \r
358                                                 // Detach unload function\r
359                                                 if (win.detachEvent) {\r
360                                                         win.detachEvent('onbeforeunload', fakeUnload);\r
361                                                         win.detachEvent('onunload', unload);\r
362                                                 } else if (win.removeEventListener)\r
363                                                         win.removeEventListener('unload', unload, false);\r
364 \r
365                                                 // Destroy references\r
366                                                 t.unloads = o = li = w = unload = 0;\r
367 \r
368                                                 // Run garbarge collector on IE\r
369                                                 if (win.CollectGarbage)\r
370                                                         CollectGarbage();\r
371                                         }\r
372                                 };\r
373 \r
374                                 function fakeUnload() {\r
375                                         var d = document;\r
376 \r
377                                         // Is there things still loading, then do some magic\r
378                                         if (d.readyState == 'interactive') {\r
379                                                 function stop() {\r
380                                                         // Prevent memory leak\r
381                                                         d.detachEvent('onstop', stop);\r
382 \r
383                                                         // Call unload handler\r
384                                                         if (unload)\r
385                                                                 unload();\r
386 \r
387                                                         d = 0;\r
388                                                 };\r
389 \r
390                                                 // Fire unload when the currently loading page is stopped\r
391                                                 if (d)\r
392                                                         d.attachEvent('onstop', stop);\r
393 \r
394                                                 // Remove onstop listener after a while to prevent the unload function\r
395                                                 // to execute if the user presses cancel in an onbeforeunload\r
396                                                 // confirm dialog and then presses the browser stop button\r
397                                                 win.setTimeout(function() {\r
398                                                         if (d)\r
399                                                                 d.detachEvent('onstop', stop);\r
400                                                 }, 0);\r
401                                         }\r
402                                 };\r
403 \r
404                                 // Attach unload handler\r
405                                 if (win.attachEvent) {\r
406                                         win.attachEvent('onunload', unload);\r
407                                         win.attachEvent('onbeforeunload', fakeUnload);\r
408                                 } else if (win.addEventListener)\r
409                                         win.addEventListener('unload', unload, false);\r
410 \r
411                                 // Setup initial unload handler array\r
412                                 t.unloads = [f];\r
413                         } else\r
414                                 t.unloads.push(f);\r
415 \r
416                         return f;\r
417                 },\r
418 \r
419                 removeUnload : function(f) {\r
420                         var u = this.unloads, r = null;\r
421 \r
422                         tinymce.each(u, function(o, i) {\r
423                                 if (o && o.func == f) {\r
424                                         u.splice(i, 1);\r
425                                         r = f;\r
426                                         return false;\r
427                                 }\r
428                         });\r
429 \r
430                         return r;\r
431                 },\r
432 \r
433                 explode : function(s, d) {\r
434                         return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;\r
435                 },\r
436 \r
437                 _addVer : function(u) {\r
438                         var v;\r
439 \r
440                         if (!this.query)\r
441                                 return u;\r
442 \r
443                         v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;\r
444 \r
445                         if (u.indexOf('#') == -1)\r
446                                 return u + v;\r
447 \r
448                         return u.replace('#', v + '#');\r
449                 },\r
450 \r
451                 // Fix function for IE 9 where regexps isn't working correctly\r
452                 // Todo: remove me once MS fixes the bug\r
453                 _replace : function(find, replace, str) {\r
454                         // On IE9 we have to fake $x replacement\r
455                         if (isRegExpBroken) {\r
456                                 return str.replace(find, function() {\r
457                                         var val = replace, args = arguments, i;\r
458 \r
459                                         for (i = 0; i < args.length - 2; i++) {\r
460                                                 if (args[i] === undefined) {\r
461                                                         val = val.replace(new RegExp('\\$' + i, 'g'), '');\r
462                                                 } else {\r
463                                                         val = val.replace(new RegExp('\\$' + i, 'g'), args[i]);\r
464                                                 }\r
465                                         }\r
466 \r
467                                         return val;\r
468                                 });\r
469                         }\r
470 \r
471                         return str.replace(find, replace);\r
472                 }\r
473 \r
474                 };\r
475 \r
476         // Initialize the API\r
477         tinymce._init();\r
478 \r
479         // Expose tinymce namespace to the global namespace (window)\r
480         win.tinymce = win.tinyMCE = tinymce;\r
481 \r
482         // Describe the different namespaces\r
483 \r
484         })(window);\r
485 \r
486 \r
487 tinymce.create('tinymce.util.Dispatcher', {\r
488         scope : null,\r
489         listeners : null,\r
490 \r
491         Dispatcher : function(s) {\r
492                 this.scope = s || this;\r
493                 this.listeners = [];\r
494         },\r
495 \r
496         add : function(cb, s) {\r
497                 this.listeners.push({cb : cb, scope : s || this.scope});\r
498 \r
499                 return cb;\r
500         },\r
501 \r
502         addToTop : function(cb, s) {\r
503                 this.listeners.unshift({cb : cb, scope : s || this.scope});\r
504 \r
505                 return cb;\r
506         },\r
507 \r
508         remove : function(cb) {\r
509                 var l = this.listeners, o = null;\r
510 \r
511                 tinymce.each(l, function(c, i) {\r
512                         if (cb == c.cb) {\r
513                                 o = cb;\r
514                                 l.splice(i, 1);\r
515                                 return false;\r
516                         }\r
517                 });\r
518 \r
519                 return o;\r
520         },\r
521 \r
522         dispatch : function() {\r
523                 var s, a = arguments, i, li = this.listeners, c;\r
524 \r
525                 // Needs to be a real loop since the listener count might change while looping\r
526                 // And this is also more efficient\r
527                 for (i = 0; i<li.length; i++) {\r
528                         c = li[i];\r
529                         s = c.cb.apply(c.scope, a);\r
530 \r
531                         if (s === false)\r
532                                 break;\r
533                 }\r
534 \r
535                 return s;\r
536         }\r
537 \r
538         });\r
539 \r
540 (function() {\r
541         var each = tinymce.each;\r
542 \r
543         tinymce.create('tinymce.util.URI', {\r
544                 URI : function(u, s) {\r
545                         var t = this, o, a, b;\r
546 \r
547                         // Trim whitespace\r
548                         u = tinymce.trim(u);\r
549 \r
550                         // Default settings\r
551                         s = t.settings = s || {};\r
552 \r
553                         // Strange app protocol or local anchor\r
554                         if (/^(mailto|tel|news|javascript|about|data):/i.test(u) || /^\s*#/.test(u)) {\r
555                                 t.source = u;\r
556                                 return;\r
557                         }\r
558 \r
559                         // Absolute path with no host, fake host and protocol\r
560                         if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)\r
561                                 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;\r
562 \r
563                         // Relative path http:// or protocol relative //path\r
564                         if (!/^\w*:?\/\//.test(u))\r
565                                 u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u);\r
566 \r
567                         // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)\r
568                         u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something\r
569                         u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);\r
570                         each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {\r
571                                 var s = u[i];\r
572 \r
573                                 // Zope 3 workaround, they use @@something\r
574                                 if (s)\r
575                                         s = s.replace(/\(mce_at\)/g, '@@');\r
576 \r
577                                 t[v] = s;\r
578                         });\r
579 \r
580                         if (b = s.base_uri) {\r
581                                 if (!t.protocol)\r
582                                         t.protocol = b.protocol;\r
583 \r
584                                 if (!t.userInfo)\r
585                                         t.userInfo = b.userInfo;\r
586 \r
587                                 if (!t.port && t.host == 'mce_host')\r
588                                         t.port = b.port;\r
589 \r
590                                 if (!t.host || t.host == 'mce_host')\r
591                                         t.host = b.host;\r
592 \r
593                                 t.source = '';\r
594                         }\r
595 \r
596                         //t.path = t.path || '/';\r
597                 },\r
598 \r
599                 setPath : function(p) {\r
600                         var t = this;\r
601 \r
602                         p = /^(.*?)\/?(\w+)?$/.exec(p);\r
603 \r
604                         // Update path parts\r
605                         t.path = p[0];\r
606                         t.directory = p[1];\r
607                         t.file = p[2];\r
608 \r
609                         // Rebuild source\r
610                         t.source = '';\r
611                         t.getURI();\r
612                 },\r
613 \r
614                 toRelative : function(u) {\r
615                         var t = this, o;\r
616 \r
617                         if (u === "./")\r
618                                 return u;\r
619 \r
620                         u = new tinymce.util.URI(u, {base_uri : t});\r
621 \r
622                         // Not on same domain/port or protocol\r
623                         if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)\r
624                                 return u.getURI();\r
625 \r
626                         o = t.toRelPath(t.path, u.path);\r
627 \r
628                         // Add query\r
629                         if (u.query)\r
630                                 o += '?' + u.query;\r
631 \r
632                         // Add anchor\r
633                         if (u.anchor)\r
634                                 o += '#' + u.anchor;\r
635 \r
636                         return o;\r
637                 },\r
638         \r
639                 toAbsolute : function(u, nh) {\r
640                         var u = new tinymce.util.URI(u, {base_uri : this});\r
641 \r
642                         return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0);\r
643                 },\r
644 \r
645                 toRelPath : function(base, path) {\r
646                         var items, bp = 0, out = '', i, l;\r
647 \r
648                         // Split the paths\r
649                         base = base.substring(0, base.lastIndexOf('/'));\r
650                         base = base.split('/');\r
651                         items = path.split('/');\r
652 \r
653                         if (base.length >= items.length) {\r
654                                 for (i = 0, l = base.length; i < l; i++) {\r
655                                         if (i >= items.length || base[i] != items[i]) {\r
656                                                 bp = i + 1;\r
657                                                 break;\r
658                                         }\r
659                                 }\r
660                         }\r
661 \r
662                         if (base.length < items.length) {\r
663                                 for (i = 0, l = items.length; i < l; i++) {\r
664                                         if (i >= base.length || base[i] != items[i]) {\r
665                                                 bp = i + 1;\r
666                                                 break;\r
667                                         }\r
668                                 }\r
669                         }\r
670 \r
671                         if (bp == 1)\r
672                                 return path;\r
673 \r
674                         for (i = 0, l = base.length - (bp - 1); i < l; i++)\r
675                                 out += "../";\r
676 \r
677                         for (i = bp - 1, l = items.length; i < l; i++) {\r
678                                 if (i != bp - 1)\r
679                                         out += "/" + items[i];\r
680                                 else\r
681                                         out += items[i];\r
682                         }\r
683 \r
684                         return out;\r
685                 },\r
686 \r
687                 toAbsPath : function(base, path) {\r
688                         var i, nb = 0, o = [], tr, outPath;\r
689 \r
690                         // Split paths\r
691                         tr = /\/$/.test(path) ? '/' : '';\r
692                         base = base.split('/');\r
693                         path = path.split('/');\r
694 \r
695                         // Remove empty chunks\r
696                         each(base, function(k) {\r
697                                 if (k)\r
698                                         o.push(k);\r
699                         });\r
700 \r
701                         base = o;\r
702 \r
703                         // Merge relURLParts chunks\r
704                         for (i = path.length - 1, o = []; i >= 0; i--) {\r
705                                 // Ignore empty or .\r
706                                 if (path[i].length == 0 || path[i] == ".")\r
707                                         continue;\r
708 \r
709                                 // Is parent\r
710                                 if (path[i] == '..') {\r
711                                         nb++;\r
712                                         continue;\r
713                                 }\r
714 \r
715                                 // Move up\r
716                                 if (nb > 0) {\r
717                                         nb--;\r
718                                         continue;\r
719                                 }\r
720 \r
721                                 o.push(path[i]);\r
722                         }\r
723 \r
724                         i = base.length - nb;\r
725 \r
726                         // If /a/b/c or /\r
727                         if (i <= 0)\r
728                                 outPath = o.reverse().join('/');\r
729                         else\r
730                                 outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');\r
731 \r
732                         // Add front / if it's needed\r
733                         if (outPath.indexOf('/') !== 0)\r
734                                 outPath = '/' + outPath;\r
735 \r
736                         // Add traling / if it's needed\r
737                         if (tr && outPath.lastIndexOf('/') !== outPath.length - 1)\r
738                                 outPath += tr;\r
739 \r
740                         return outPath;\r
741                 },\r
742 \r
743                 getURI : function(nh) {\r
744                         var s, t = this;\r
745 \r
746                         // Rebuild source\r
747                         if (!t.source || nh) {\r
748                                 s = '';\r
749 \r
750                                 if (!nh) {\r
751                                         if (t.protocol)\r
752                                                 s += t.protocol + '://';\r
753 \r
754                                         if (t.userInfo)\r
755                                                 s += t.userInfo + '@';\r
756 \r
757                                         if (t.host)\r
758                                                 s += t.host;\r
759 \r
760                                         if (t.port)\r
761                                                 s += ':' + t.port;\r
762                                 }\r
763 \r
764                                 if (t.path)\r
765                                         s += t.path;\r
766 \r
767                                 if (t.query)\r
768                                         s += '?' + t.query;\r
769 \r
770                                 if (t.anchor)\r
771                                         s += '#' + t.anchor;\r
772 \r
773                                 t.source = s;\r
774                         }\r
775 \r
776                         return t.source;\r
777                 }\r
778         });\r
779 })();\r
780 \r
781 (function() {\r
782         var each = tinymce.each;\r
783 \r
784         tinymce.create('static tinymce.util.Cookie', {\r
785                 getHash : function(n) {\r
786                         var v = this.get(n), h;\r
787 \r
788                         if (v) {\r
789                                 each(v.split('&'), function(v) {\r
790                                         v = v.split('=');\r
791                                         h = h || {};\r
792                                         h[unescape(v[0])] = unescape(v[1]);\r
793                                 });\r
794                         }\r
795 \r
796                         return h;\r
797                 },\r
798 \r
799                 setHash : function(n, v, e, p, d, s) {\r
800                         var o = '';\r
801 \r
802                         each(v, function(v, k) {\r
803                                 o += (!o ? '' : '&') + escape(k) + '=' + escape(v);\r
804                         });\r
805 \r
806                         this.set(n, o, e, p, d, s);\r
807                 },\r
808 \r
809                 get : function(n) {\r
810                         var c = document.cookie, e, p = n + "=", b;\r
811 \r
812                         // Strict mode\r
813                         if (!c)\r
814                                 return;\r
815 \r
816                         b = c.indexOf("; " + p);\r
817 \r
818                         if (b == -1) {\r
819                                 b = c.indexOf(p);\r
820 \r
821                                 if (b != 0)\r
822                                         return null;\r
823                         } else\r
824                                 b += 2;\r
825 \r
826                         e = c.indexOf(";", b);\r
827 \r
828                         if (e == -1)\r
829                                 e = c.length;\r
830 \r
831                         return unescape(c.substring(b + p.length, e));\r
832                 },\r
833 \r
834                 set : function(n, v, e, p, d, s) {\r
835                         document.cookie = n + "=" + escape(v) +\r
836                                 ((e) ? "; expires=" + e.toGMTString() : "") +\r
837                                 ((p) ? "; path=" + escape(p) : "") +\r
838                                 ((d) ? "; domain=" + d : "") +\r
839                                 ((s) ? "; secure" : "");\r
840                 },\r
841 \r
842                 remove : function(n, p) {\r
843                         var d = new Date();\r
844 \r
845                         d.setTime(d.getTime() - 1000);\r
846 \r
847                         this.set(n, '', d, p, d);\r
848                 }\r
849         });\r
850 })();\r
851 \r
852 (function() {\r
853         function serialize(o, quote) {\r
854                 var i, v, t;\r
855 \r
856                 quote = quote || '"';\r
857 \r
858                 if (o == null)\r
859                         return 'null';\r
860 \r
861                 t = typeof o;\r
862 \r
863                 if (t == 'string') {\r
864                         v = '\bb\tt\nn\ff\rr\""\'\'\\\\';\r
865 \r
866                         return quote + o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g, function(a, b) {\r
867                                 // Make sure single quotes never get encoded inside double quotes for JSON compatibility\r
868                                 if (quote === '"' && a === "'")\r
869                                         return a;\r
870 \r
871                                 i = v.indexOf(b);\r
872 \r
873                                 if (i + 1)\r
874                                         return '\\' + v.charAt(i + 1);\r
875 \r
876                                 a = b.charCodeAt().toString(16);\r
877 \r
878                                 return '\\u' + '0000'.substring(a.length) + a;\r
879                         }) + quote;\r
880                 }\r
881 \r
882                 if (t == 'object') {\r
883                         if (o.hasOwnProperty && o instanceof Array) {\r
884                                         for (i=0, v = '['; i<o.length; i++)\r
885                                                 v += (i > 0 ? ',' : '') + serialize(o[i], quote);\r
886 \r
887                                         return v + ']';\r
888                                 }\r
889 \r
890                                 v = '{';\r
891 \r
892                                 for (i in o)\r
893                                         v += typeof o[i] != 'function' ? (v.length > 1 ? ',' + quote : quote) + i + quote +':' + serialize(o[i], quote) : '';\r
894 \r
895                                 return v + '}';\r
896                 }\r
897 \r
898                 return '' + o;\r
899         };\r
900 \r
901         tinymce.util.JSON = {\r
902                 serialize: serialize,\r
903 \r
904                 parse: function(s) {\r
905                         try {\r
906                                 return eval('(' + s + ')');\r
907                         } catch (ex) {\r
908                                 // Ignore\r
909                         }\r
910                 }\r
911 \r
912                 };\r
913 })();\r
914 tinymce.create('static tinymce.util.XHR', {\r
915         send : function(o) {\r
916                 var x, t, w = window, c = 0;\r
917 \r
918                 // Default settings\r
919                 o.scope = o.scope || this;\r
920                 o.success_scope = o.success_scope || o.scope;\r
921                 o.error_scope = o.error_scope || o.scope;\r
922                 o.async = o.async === false ? false : true;\r
923                 o.data = o.data || '';\r
924 \r
925                 function get(s) {\r
926                         x = 0;\r
927 \r
928                         try {\r
929                                 x = new ActiveXObject(s);\r
930                         } catch (ex) {\r
931                         }\r
932 \r
933                         return x;\r
934                 };\r
935 \r
936                 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');\r
937 \r
938                 if (x) {\r
939                         if (x.overrideMimeType)\r
940                                 x.overrideMimeType(o.content_type);\r
941 \r
942                         x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);\r
943 \r
944                         if (o.content_type)\r
945                                 x.setRequestHeader('Content-Type', o.content_type);\r
946 \r
947                         x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\r
948 \r
949                         x.send(o.data);\r
950 \r
951                         function ready() {\r
952                                 if (!o.async || x.readyState == 4 || c++ > 10000) {\r
953                                         if (o.success && c < 10000 && x.status == 200)\r
954                                                 o.success.call(o.success_scope, '' + x.responseText, x, o);\r
955                                         else if (o.error)\r
956                                                 o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);\r
957 \r
958                                         x = null;\r
959                                 } else\r
960                                         w.setTimeout(ready, 10);\r
961                         };\r
962 \r
963                         // Syncronous request\r
964                         if (!o.async)\r
965                                 return ready();\r
966 \r
967                         // Wait for response, onReadyStateChange can not be used since it leaks memory in IE\r
968                         t = w.setTimeout(ready, 10);\r
969                 }\r
970         }\r
971 });\r
972 \r
973 (function() {\r
974         var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;\r
975 \r
976         tinymce.create('tinymce.util.JSONRequest', {\r
977                 JSONRequest : function(s) {\r
978                         this.settings = extend({\r
979                         }, s);\r
980                         this.count = 0;\r
981                 },\r
982 \r
983                 send : function(o) {\r
984                         var ecb = o.error, scb = o.success;\r
985 \r
986                         o = extend(this.settings, o);\r
987 \r
988                         o.success = function(c, x) {\r
989                                 c = JSON.parse(c);\r
990 \r
991                                 if (typeof(c) == 'undefined') {\r
992                                         c = {\r
993                                                 error : 'JSON Parse error.'\r
994                                         };\r
995                                 }\r
996 \r
997                                 if (c.error)\r
998                                         ecb.call(o.error_scope || o.scope, c.error, x);\r
999                                 else\r
1000                                         scb.call(o.success_scope || o.scope, c.result);\r
1001                         };\r
1002 \r
1003                         o.error = function(ty, x) {\r
1004                                 if (ecb)\r
1005                                         ecb.call(o.error_scope || o.scope, ty, x);\r
1006                         };\r
1007 \r
1008                         o.data = JSON.serialize({\r
1009                                 id : o.id || 'c' + (this.count++),\r
1010                                 method : o.method,\r
1011                                 params : o.params\r
1012                         });\r
1013 \r
1014                         // JSON content type for Ruby on rails. Bug: #1883287\r
1015                         o.content_type = 'application/json';\r
1016 \r
1017                         XHR.send(o);\r
1018                 },\r
1019 \r
1020                 'static' : {\r
1021                         sendRPC : function(o) {\r
1022                                 return new tinymce.util.JSONRequest().send(o);\r
1023                         }\r
1024                 }\r
1025         });\r
1026 }());\r
1027 (function(tinymce) {\r
1028         var namedEntities, baseEntities, reverseEntities,\r
1029                 attrsCharsRegExp = /[&\"\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,\r
1030                 textCharsRegExp = /[<>&\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,\r
1031                 rawCharsRegExp = /[<>&\"\']/g,\r
1032                 entityRegExp = /&(#)?([\w]+);/g,\r
1033                 asciiMap = {\r
1034                                 128 : "\u20AC", 130 : "\u201A", 131 : "\u0192", 132 : "\u201E", 133 : "\u2026", 134 : "\u2020",\r
1035                                 135 : "\u2021", 136 : "\u02C6", 137 : "\u2030", 138 : "\u0160", 139 : "\u2039", 140 : "\u0152",\r
1036                                 142 : "\u017D", 145 : "\u2018", 146 : "\u2019", 147 : "\u201C", 148 : "\u201D", 149 : "\u2022",\r
1037                                 150 : "\u2013", 151 : "\u2014", 152 : "\u02DC", 153 : "\u2122", 154 : "\u0161", 155 : "\u203A",\r
1038                                 156 : "\u0153", 158 : "\u017E", 159 : "\u0178"\r
1039                 };\r
1040 \r
1041         // Raw entities\r
1042         baseEntities = {\r
1043                 '"' : '&quot;',\r
1044                 "'" : '&#39;',\r
1045                 '<' : '&lt;',\r
1046                 '>' : '&gt;',\r
1047                 '&' : '&amp;'\r
1048         };\r
1049 \r
1050         // Reverse lookup table for raw entities\r
1051         reverseEntities = {\r
1052                 '&lt;' : '<',\r
1053                 '&gt;' : '>',\r
1054                 '&amp;' : '&',\r
1055                 '&quot;' : '"',\r
1056                 '&apos;' : "'"\r
1057         };\r
1058 \r
1059         // Decodes text by using the browser\r
1060         function nativeDecode(text) {\r
1061                 var elm;\r
1062 \r
1063                 elm = document.createElement("div");\r
1064                 elm.innerHTML = text;\r
1065 \r
1066                 return elm.textContent || elm.innerText || text;\r
1067         };\r
1068 \r
1069         // Build a two way lookup table for the entities\r
1070         function buildEntitiesLookup(items, radix) {\r
1071                 var i, chr, entity, lookup = {};\r
1072 \r
1073                 if (items) {\r
1074                         items = items.split(',');\r
1075                         radix = radix || 10;\r
1076 \r
1077                         // Build entities lookup table\r
1078                         for (i = 0; i < items.length; i += 2) {\r
1079                                 chr = String.fromCharCode(parseInt(items[i], radix));\r
1080 \r
1081                                 // Only add non base entities\r
1082                                 if (!baseEntities[chr]) {\r
1083                                         entity = '&' + items[i + 1] + ';';\r
1084                                         lookup[chr] = entity;\r
1085                                         lookup[entity] = chr;\r
1086                                 }\r
1087                         }\r
1088 \r
1089                         return lookup;\r
1090                 }\r
1091         };\r
1092 \r
1093         // Unpack entities lookup where the numbers are in radix 32 to reduce the size\r
1094         namedEntities = buildEntitiesLookup(\r
1095                 '50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' +\r
1096                 '5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' +\r
1097                 '5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' +\r
1098                 '5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' +\r
1099                 '68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' +\r
1100                 '6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' +\r
1101                 '6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' +\r
1102                 '75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' +\r
1103                 '7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' +\r
1104                 '7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' +\r
1105                 'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' +\r
1106                 'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' +\r
1107                 't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' +\r
1108                 'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' +\r
1109                 'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' +\r
1110                 '81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' +\r
1111                 '8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' +\r
1112                 '8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' +\r
1113                 '8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' +\r
1114                 '8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' +\r
1115                 'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' +\r
1116                 'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' +\r
1117                 'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' +\r
1118                 '80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' +\r
1119                 '811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro'\r
1120         , 32);\r
1121 \r
1122         tinymce.html = tinymce.html || {};\r
1123 \r
1124         tinymce.html.Entities = {\r
1125                 encodeRaw : function(text, attr) {\r
1126                         return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {\r
1127                                 return baseEntities[chr] || chr;\r
1128                         });\r
1129                 },\r
1130 \r
1131                 encodeAllRaw : function(text) {\r
1132                         return ('' + text).replace(rawCharsRegExp, function(chr) {\r
1133                                 return baseEntities[chr] || chr;\r
1134                         });\r
1135                 },\r
1136 \r
1137                 encodeNumeric : function(text, attr) {\r
1138                         return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {\r
1139                                 // Multi byte sequence convert it to a single entity\r
1140                                 if (chr.length > 1)\r
1141                                         return '&#' + (((chr.charCodeAt(0) - 0xD800) * 0x400) + (chr.charCodeAt(1) - 0xDC00) + 0x10000) + ';';\r
1142 \r
1143                                 return baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';\r
1144                         });\r
1145                 },\r
1146 \r
1147                 encodeNamed : function(text, attr, entities) {\r
1148                         entities = entities || namedEntities;\r
1149 \r
1150                         return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {\r
1151                                 return baseEntities[chr] || entities[chr] || chr;\r
1152                         });\r
1153                 },\r
1154 \r
1155                 getEncodeFunc : function(name, entities) {\r
1156                         var Entities = tinymce.html.Entities;\r
1157 \r
1158                         entities = buildEntitiesLookup(entities) || namedEntities;\r
1159 \r
1160                         function encodeNamedAndNumeric(text, attr) {\r
1161                                 return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {\r
1162                                         return baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;\r
1163                                 });\r
1164                         };\r
1165 \r
1166                         function encodeCustomNamed(text, attr) {\r
1167                                 return Entities.encodeNamed(text, attr, entities);\r
1168                         };\r
1169 \r
1170                         // Replace + with , to be compatible with previous TinyMCE versions\r
1171                         name = tinymce.makeMap(name.replace(/\+/g, ','));\r
1172 \r
1173                         // Named and numeric encoder\r
1174                         if (name.named && name.numeric)\r
1175                                 return encodeNamedAndNumeric;\r
1176 \r
1177                         // Named encoder\r
1178                         if (name.named) {\r
1179                                 // Custom names\r
1180                                 if (entities)\r
1181                                         return encodeCustomNamed;\r
1182 \r
1183                                 return Entities.encodeNamed;\r
1184                         }\r
1185 \r
1186                         // Numeric\r
1187                         if (name.numeric)\r
1188                                 return Entities.encodeNumeric;\r
1189 \r
1190                         // Raw encoder\r
1191                         return Entities.encodeRaw;\r
1192                 },\r
1193 \r
1194                 decode : function(text) {\r
1195                         return text.replace(entityRegExp, function(all, numeric, value) {\r
1196                                 if (numeric) {\r
1197                                         value = parseInt(value);\r
1198 \r
1199                                         // Support upper UTF\r
1200                                         if (value > 0xFFFF) {\r
1201                                                 value -= 0x10000;\r
1202 \r
1203                                                 return String.fromCharCode(0xD800 + (value >> 10), 0xDC00 + (value & 0x3FF));\r
1204                                         } else\r
1205                                                 return asciiMap[value] || String.fromCharCode(value);\r
1206                                 }\r
1207 \r
1208                                 return reverseEntities[all] || namedEntities[all] || nativeDecode(all);\r
1209                         });\r
1210                 }\r
1211         };\r
1212 })(tinymce);\r
1213 \r
1214 tinymce.html.Styles = function(settings, schema) {\r
1215         var rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,\r
1216                 urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,\r
1217                 styleRegExp = /\s*([^:]+):\s*([^;]+);?/g,\r
1218                 trimRightRegExp = /\s+$/,\r
1219                 urlColorRegExp = /rgb/,\r
1220                 undef, i, encodingLookup = {}, encodingItems;\r
1221 \r
1222         settings = settings || {};\r
1223 \r
1224         encodingItems = '\\" \\\' \\; \\: ; : _'.split(' ');\r
1225         for (i = 0; i < encodingItems.length; i++) {\r
1226                 encodingLookup[encodingItems[i]] = '_' + i;\r
1227                 encodingLookup['_' + i] = encodingItems[i];\r
1228         }\r
1229 \r
1230         function toHex(match, r, g, b) {\r
1231                 function hex(val) {\r
1232                         val = parseInt(val).toString(16);\r
1233 \r
1234                         return val.length > 1 ? val : '0' + val; // 0 -> 00\r
1235                 };\r
1236 \r
1237                 return '#' + hex(r) + hex(g) + hex(b);\r
1238         };\r
1239 \r
1240         return {\r
1241                 toHex : function(color) {\r
1242                         return color.replace(rgbRegExp, toHex);\r
1243                 },\r
1244 \r
1245                 parse : function(css) {\r
1246                         var styles = {}, matches, name, value, isEncoded, urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope || this;\r
1247 \r
1248                         function compress(prefix, suffix) {\r
1249                                 var top, right, bottom, left;\r
1250 \r
1251                                 // Get values and check it it needs compressing\r
1252                                 top = styles[prefix + '-top' + suffix];\r
1253                                 if (!top)\r
1254                                         return;\r
1255 \r
1256                                 right = styles[prefix + '-right' + suffix];\r
1257                                 if (top != right)\r
1258                                         return;\r
1259 \r
1260                                 bottom = styles[prefix + '-bottom' + suffix];\r
1261                                 if (right != bottom)\r
1262                                         return;\r
1263 \r
1264                                 left = styles[prefix + '-left' + suffix];\r
1265                                 if (bottom != left)\r
1266                                         return;\r
1267 \r
1268                                 // Compress\r
1269                                 styles[prefix + suffix] = left;\r
1270                                 delete styles[prefix + '-top' + suffix];\r
1271                                 delete styles[prefix + '-right' + suffix];\r
1272                                 delete styles[prefix + '-bottom' + suffix];\r
1273                                 delete styles[prefix + '-left' + suffix];\r
1274                         };\r
1275 \r
1276                         function canCompress(key) {\r
1277                                 var value = styles[key], i;\r
1278 \r
1279                                 if (!value || value.indexOf(' ') < 0)\r
1280                                         return;\r
1281 \r
1282                                 value = value.split(' ');\r
1283                                 i = value.length;\r
1284                                 while (i--) {\r
1285                                         if (value[i] !== value[0])\r
1286                                                 return false;\r
1287                                 }\r
1288 \r
1289                                 styles[key] = value[0];\r
1290 \r
1291                                 return true;\r
1292                         };\r
1293 \r
1294                         function compress2(target, a, b, c) {\r
1295                                 if (!canCompress(a))\r
1296                                         return;\r
1297 \r
1298                                 if (!canCompress(b))\r
1299                                         return;\r
1300 \r
1301                                 if (!canCompress(c))\r
1302                                         return;\r
1303 \r
1304                                 // Compress\r
1305                                 styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c];\r
1306                                 delete styles[a];\r
1307                                 delete styles[b];\r
1308                                 delete styles[c];\r
1309                         };\r
1310 \r
1311                         // Encodes the specified string by replacing all \" \' ; : with _<num>\r
1312                         function encode(str) {\r
1313                                 isEncoded = true;\r
1314 \r
1315                                 return encodingLookup[str];\r
1316                         };\r
1317 \r
1318                         // Decodes the specified string by replacing all _<num> with it's original value \" \' etc\r
1319                         // It will also decode the \" \' if keep_slashes is set to fale or omitted\r
1320                         function decode(str, keep_slashes) {\r
1321                                 if (isEncoded) {\r
1322                                         str = str.replace(/_[0-9]/g, function(str) {\r
1323                                                 return encodingLookup[str];\r
1324                                         });\r
1325                                 }\r
1326 \r
1327                                 if (!keep_slashes)\r
1328                                         str = str.replace(/\\([\'\";:])/g, "$1");\r
1329 \r
1330                                 return str;\r
1331                         }\r
1332 \r
1333                         if (css) {\r
1334                                 // Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing\r
1335                                 css = css.replace(/\\[\"\';:_]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, function(str) {\r
1336                                         return str.replace(/[;:]/g, encode);\r
1337                                 });\r
1338 \r
1339                                 // Parse styles\r
1340                                 while (matches = styleRegExp.exec(css)) {\r
1341                                         name = matches[1].replace(trimRightRegExp, '').toLowerCase();\r
1342                                         value = matches[2].replace(trimRightRegExp, '');\r
1343 \r
1344                                         if (name && value.length > 0) {\r
1345                                                 // Opera will produce 700 instead of bold in their style values\r
1346                                                 if (name === 'font-weight' && value === '700')\r
1347                                                         value = 'bold';\r
1348                                                 else if (name === 'color' || name === 'background-color') // Lowercase colors like RED\r
1349                                                         value = value.toLowerCase();            \r
1350 \r
1351                                                 // Convert RGB colors to HEX\r
1352                                                 value = value.replace(rgbRegExp, toHex);\r
1353 \r
1354                                                 // Convert URLs and force them into url('value') format\r
1355                                                 value = value.replace(urlOrStrRegExp, function(match, url, url2, url3, str, str2) {\r
1356                                                         str = str || str2;\r
1357 \r
1358                                                         if (str) {\r
1359                                                                 str = decode(str);\r
1360 \r
1361                                                                 // Force strings into single quote format\r
1362                                                                 return "'" + str.replace(/\'/g, "\\'") + "'";\r
1363                                                         }\r
1364 \r
1365                                                         url = decode(url || url2 || url3);\r
1366 \r
1367                                                         // Convert the URL to relative/absolute depending on config\r
1368                                                         if (urlConverter)\r
1369                                                                 url = urlConverter.call(urlConverterScope, url, 'style');\r
1370 \r
1371                                                         // Output new URL format\r
1372                                                         return "url('" + url.replace(/\'/g, "\\'") + "')";\r
1373                                                 });\r
1374 \r
1375                                                 styles[name] = isEncoded ? decode(value, true) : value;\r
1376                                         }\r
1377 \r
1378                                         styleRegExp.lastIndex = matches.index + matches[0].length;\r
1379                                 }\r
1380 \r
1381                                 // Compress the styles to reduce it's size for example IE will expand styles\r
1382                                 compress("border", "");\r
1383                                 compress("border", "-width");\r
1384                                 compress("border", "-color");\r
1385                                 compress("border", "-style");\r
1386                                 compress("padding", "");\r
1387                                 compress("margin", "");\r
1388                                 compress2('border', 'border-width', 'border-style', 'border-color');\r
1389 \r
1390                                 // Remove pointless border, IE produces these\r
1391                                 if (styles.border === 'medium none')\r
1392                                         delete styles.border;\r
1393                         }\r
1394 \r
1395                         return styles;\r
1396                 },\r
1397 \r
1398                 serialize : function(styles, element_name) {\r
1399                         var css = '', name, value;\r
1400 \r
1401                         function serializeStyles(name) {\r
1402                                 var styleList, i, l, name, value;\r
1403 \r
1404                                 styleList = schema.styles[name];\r
1405                                 if (styleList) {\r
1406                                         for (i = 0, l = styleList.length; i < l; i++) {\r
1407                                                 name = styleList[i];\r
1408                                                 value = styles[name];\r
1409 \r
1410                                                 if (value !== undef && value.length > 0)\r
1411                                                         css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';\r
1412                                         }\r
1413                                 }\r
1414                         };\r
1415 \r
1416                         // Serialize styles according to schema\r
1417                         if (element_name && schema && schema.styles) {\r
1418                                 // Serialize global styles and element specific styles\r
1419                                 serializeStyles('*');\r
1420                                 serializeStyles(name);\r
1421                         } else {\r
1422                                 // Output the styles in the order they are inside the object\r
1423                                 for (name in styles) {\r
1424                                         value = styles[name];\r
1425 \r
1426                                         if (value !== undef && value.length > 0)\r
1427                                                 css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';\r
1428                                 }\r
1429                         }\r
1430 \r
1431                         return css;\r
1432                 }\r
1433         };\r
1434 };\r
1435 \r
1436 (function(tinymce) {\r
1437         var transitional = {}, boolAttrMap, blockElementsMap, shortEndedElementsMap, nonEmptyElementsMap,\r
1438                 whiteSpaceElementsMap, selfClosingElementsMap, makeMap = tinymce.makeMap, each = tinymce.each;\r
1439 \r
1440         function split(str, delim) {\r
1441                 return str.split(delim || ',');\r
1442         };\r
1443 \r
1444         function unpack(lookup, data) {\r
1445                 var key, elements = {};\r
1446 \r
1447                 function replace(value) {\r
1448                         return value.replace(/[A-Z]+/g, function(key) {\r
1449                                 return replace(lookup[key]);\r
1450                         });\r
1451                 };\r
1452 \r
1453                 // Unpack lookup\r
1454                 for (key in lookup) {\r
1455                         if (lookup.hasOwnProperty(key))\r
1456                                 lookup[key] = replace(lookup[key]);\r
1457                 }\r
1458 \r
1459                 // Unpack and parse data into object map\r
1460                 replace(data).replace(/#/g, '#text').replace(/(\w+)\[([^\]]+)\]\[([^\]]*)\]/g, function(str, name, attributes, children) {\r
1461                         attributes = split(attributes, '|');\r
1462 \r
1463                         elements[name] = {\r
1464                                 attributes : makeMap(attributes),\r
1465                                 attributesOrder : attributes,\r
1466                                 children : makeMap(children, '|', {'#comment' : {}})\r
1467                         }\r
1468                 });\r
1469 \r
1470                 return elements;\r
1471         };\r
1472 \r
1473         // Build a lookup table for block elements both lowercase and uppercase\r
1474         blockElementsMap = 'h1,h2,h3,h4,h5,h6,hr,p,div,address,pre,form,table,tbody,thead,tfoot,' + \r
1475                                                 'th,tr,td,li,ol,ul,caption,blockquote,center,dl,dt,dd,dir,fieldset,' + \r
1476                                                 'noscript,menu,isindex,samp,header,footer,article,section,hgroup';\r
1477         blockElementsMap = makeMap(blockElementsMap, ',', makeMap(blockElementsMap.toUpperCase()));\r
1478 \r
1479         // This is the XHTML 1.0 transitional elements with it's attributes and children packed to reduce it's size\r
1480         transitional = unpack({\r
1481                 Z : 'H|K|N|O|P',\r
1482                 Y : 'X|form|R|Q',\r
1483                 ZG : 'E|span|width|align|char|charoff|valign',\r
1484                 X : 'p|T|div|U|W|isindex|fieldset|table',\r
1485                 ZF : 'E|align|char|charoff|valign',\r
1486                 W : 'pre|hr|blockquote|address|center|noframes',\r
1487                 ZE : 'abbr|axis|headers|scope|rowspan|colspan|align|char|charoff|valign|nowrap|bgcolor|width|height',\r
1488                 ZD : '[E][S]',\r
1489                 U : 'ul|ol|dl|menu|dir',\r
1490                 ZC : 'p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q',\r
1491                 T : 'h1|h2|h3|h4|h5|h6',\r
1492                 ZB : 'X|S|Q',\r
1493                 S : 'R|P',\r
1494                 ZA : 'a|G|J|M|O|P',\r
1495                 R : 'a|H|K|N|O',\r
1496                 Q : 'noscript|P',\r
1497                 P : 'ins|del|script',\r
1498                 O : 'input|select|textarea|label|button',\r
1499                 N : 'M|L',\r
1500                 M : 'em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym',\r
1501                 L : 'sub|sup',\r
1502                 K : 'J|I',\r
1503                 J : 'tt|i|b|u|s|strike',\r
1504                 I : 'big|small|font|basefont',\r
1505                 H : 'G|F',\r
1506                 G : 'br|span|bdo',\r
1507                 F : 'object|applet|img|map|iframe',\r
1508                 E : 'A|B|C',\r
1509                 D : 'accesskey|tabindex|onfocus|onblur',\r
1510                 C : 'onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup',\r
1511                 B : 'lang|xml:lang|dir',\r
1512                 A : 'id|class|style|title'\r
1513         }, 'script[id|charset|type|language|src|defer|xml:space][]' + \r
1514                 'style[B|id|type|media|title|xml:space][]' + \r
1515                 'object[E|declare|classid|codebase|data|type|codetype|archive|standby|width|height|usemap|name|tabindex|align|border|hspace|vspace][#|param|Y]' + \r
1516                 'param[id|name|value|valuetype|type][]' + \r
1517                 'p[E|align][#|S]' + \r
1518                 'a[E|D|charset|type|name|href|hreflang|rel|rev|shape|coords|target][#|Z]' + \r
1519                 'br[A|clear][]' + \r
1520                 'span[E][#|S]' + \r
1521                 'bdo[A|C|B][#|S]' + \r
1522                 'applet[A|codebase|archive|code|object|alt|name|width|height|align|hspace|vspace][#|param|Y]' + \r
1523                 'h1[E|align][#|S]' + \r
1524                 'img[E|src|alt|name|longdesc|width|height|usemap|ismap|align|border|hspace|vspace][]' + \r
1525                 'map[B|C|A|name][X|form|Q|area]' + \r
1526                 'h2[E|align][#|S]' + \r
1527                 'iframe[A|longdesc|name|src|frameborder|marginwidth|marginheight|scrolling|align|width|height][#|Y]' + \r
1528                 'h3[E|align][#|S]' + \r
1529                 'tt[E][#|S]' + \r
1530                 'i[E][#|S]' + \r
1531                 'b[E][#|S]' + \r
1532                 'u[E][#|S]' + \r
1533                 's[E][#|S]' + \r
1534                 'strike[E][#|S]' + \r
1535                 'big[E][#|S]' + \r
1536                 'small[E][#|S]' + \r
1537                 'font[A|B|size|color|face][#|S]' + \r
1538                 'basefont[id|size|color|face][]' + \r
1539                 'em[E][#|S]' + \r
1540                 'strong[E][#|S]' + \r
1541                 'dfn[E][#|S]' + \r
1542                 'code[E][#|S]' + \r
1543                 'q[E|cite][#|S]' + \r
1544                 'samp[E][#|S]' + \r
1545                 'kbd[E][#|S]' + \r
1546                 'var[E][#|S]' + \r
1547                 'cite[E][#|S]' + \r
1548                 'abbr[E][#|S]' + \r
1549                 'acronym[E][#|S]' + \r
1550                 'sub[E][#|S]' + \r
1551                 'sup[E][#|S]' + \r
1552                 'input[E|D|type|name|value|checked|disabled|readonly|size|maxlength|src|alt|usemap|onselect|onchange|accept|align][]' + \r
1553                 'select[E|name|size|multiple|disabled|tabindex|onfocus|onblur|onchange][optgroup|option]' + \r
1554                 'optgroup[E|disabled|label][option]' + \r
1555                 'option[E|selected|disabled|label|value][]' + \r
1556                 'textarea[E|D|name|rows|cols|disabled|readonly|onselect|onchange][]' + \r
1557                 'label[E|for|accesskey|onfocus|onblur][#|S]' + \r
1558                 'button[E|D|name|value|type|disabled][#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]' + \r
1559                 'h4[E|align][#|S]' + \r
1560                 'ins[E|cite|datetime][#|Y]' + \r
1561                 'h5[E|align][#|S]' + \r
1562                 'del[E|cite|datetime][#|Y]' + \r
1563                 'h6[E|align][#|S]' + \r
1564                 'div[E|align][#|Y]' + \r
1565                 'ul[E|type|compact][li]' + \r
1566                 'li[E|type|value][#|Y]' + \r
1567                 'ol[E|type|compact|start][li]' + \r
1568                 'dl[E|compact][dt|dd]' + \r
1569                 'dt[E][#|S]' + \r
1570                 'dd[E][#|Y]' + \r
1571                 'menu[E|compact][li]' + \r
1572                 'dir[E|compact][li]' + \r
1573                 'pre[E|width|xml:space][#|ZA]' + \r
1574                 'hr[E|align|noshade|size|width][]' + \r
1575                 'blockquote[E|cite][#|Y]' + \r
1576                 'address[E][#|S|p]' + \r
1577                 'center[E][#|Y]' + \r
1578                 'noframes[E][#|Y]' + \r
1579                 'isindex[A|B|prompt][]' + \r
1580                 'fieldset[E][#|legend|Y]' + \r
1581                 'legend[E|accesskey|align][#|S]' + \r
1582                 'table[E|summary|width|border|frame|rules|cellspacing|cellpadding|align|bgcolor][caption|col|colgroup|thead|tfoot|tbody|tr]' + \r
1583                 'caption[E|align][#|S]' + \r
1584                 'col[ZG][]' + \r
1585                 'colgroup[ZG][col]' + \r
1586                 'thead[ZF][tr]' + \r
1587                 'tr[ZF|bgcolor][th|td]' + \r
1588                 'th[E|ZE][#|Y]' + \r
1589                 'form[E|action|method|name|enctype|onsubmit|onreset|accept|accept-charset|target][#|X|R|Q]' + \r
1590                 'noscript[E][#|Y]' + \r
1591                 'td[E|ZE][#|Y]' + \r
1592                 'tfoot[ZF][tr]' + \r
1593                 'tbody[ZF][tr]' + \r
1594                 'area[E|D|shape|coords|href|nohref|alt|target][]' + \r
1595                 'base[id|href|target][]' + \r
1596                 'body[E|onload|onunload|background|bgcolor|text|link|vlink|alink][#|Y]'\r
1597         );\r
1598 \r
1599         boolAttrMap = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected,preload,autoplay,loop,controls');\r
1600         shortEndedElementsMap = makeMap('area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,source');\r
1601         nonEmptyElementsMap = tinymce.extend(makeMap('td,th,iframe,video,object'), shortEndedElementsMap);\r
1602         whiteSpaceElementsMap = makeMap('pre,script,style');\r
1603         selfClosingElementsMap = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr');\r
1604 \r
1605         tinymce.html.Schema = function(settings) {\r
1606                 var self = this, elements = {}, children = {}, patternElements = [], validStyles;\r
1607 \r
1608                 settings = settings || {};\r
1609 \r
1610                 // Allow all elements and attributes if verify_html is set to false\r
1611                 if (settings.verify_html === false)\r
1612                         settings.valid_elements = '*[*]';\r
1613 \r
1614                 // Build styles list\r
1615                 if (settings.valid_styles) {\r
1616                         validStyles = {};\r
1617 \r
1618                         // Convert styles into a rule list\r
1619                         each(settings.valid_styles, function(value, key) {\r
1620                                 validStyles[key] = tinymce.explode(value);\r
1621                         });\r
1622                 }\r
1623 \r
1624                 // Converts a wildcard expression string to a regexp for example *a will become /.*a/.\r
1625                 function patternToRegExp(str) {\r
1626                         return new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$');\r
1627                 };\r
1628 \r
1629                 // Parses the specified valid_elements string and adds to the current rules\r
1630                 // This function is a bit hard to read since it's heavily optimized for speed\r
1631                 function addValidElements(valid_elements) {\r
1632                         var ei, el, ai, al, yl, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder,\r
1633                                 prefix, outputName, globalAttributes, globalAttributesOrder, transElement, key, childKey, value,\r
1634                                 elementRuleRegExp = /^([#+-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,\r
1635                                 attrRuleRegExp = /^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,\r
1636                                 hasPatternsRegExp = /[*?+]/;\r
1637 \r
1638                         if (valid_elements) {\r
1639                                 // Split valid elements into an array with rules\r
1640                                 valid_elements = split(valid_elements);\r
1641 \r
1642                                 if (elements['@']) {\r
1643                                         globalAttributes = elements['@'].attributes;\r
1644                                         globalAttributesOrder = elements['@'].attributesOrder;\r
1645                                 }\r
1646 \r
1647                                 // Loop all rules\r
1648                                 for (ei = 0, el = valid_elements.length; ei < el; ei++) {\r
1649                                         // Parse element rule\r
1650                                         matches = elementRuleRegExp.exec(valid_elements[ei]);\r
1651                                         if (matches) {\r
1652                                                 // Setup local names for matches\r
1653                                                 prefix = matches[1];\r
1654                                                 elementName = matches[2];\r
1655                                                 outputName = matches[3];\r
1656                                                 attrData = matches[4];\r
1657 \r
1658                                                 // Create new attributes and attributesOrder\r
1659                                                 attributes = {};\r
1660                                                 attributesOrder = [];\r
1661 \r
1662                                                 // Create the new element\r
1663                                                 element = {\r
1664                                                         attributes : attributes,\r
1665                                                         attributesOrder : attributesOrder\r
1666                                                 };\r
1667 \r
1668                                                 // Padd empty elements prefix\r
1669                                                 if (prefix === '#')\r
1670                                                         element.paddEmpty = true;\r
1671 \r
1672                                                 // Remove empty elements prefix\r
1673                                                 if (prefix === '-')\r
1674                                                         element.removeEmpty = true;\r
1675 \r
1676                                                 // Copy attributes from global rule into current rule\r
1677                                                 if (globalAttributes) {\r
1678                                                         for (key in globalAttributes)\r
1679                                                                 attributes[key] = globalAttributes[key];\r
1680 \r
1681                                                         attributesOrder.push.apply(attributesOrder, globalAttributesOrder);\r
1682                                                 }\r
1683 \r
1684                                                 // Attributes defined\r
1685                                                 if (attrData) {\r
1686                                                         attrData = split(attrData, '|');\r
1687                                                         for (ai = 0, al = attrData.length; ai < al; ai++) {\r
1688                                                                 matches = attrRuleRegExp.exec(attrData[ai]);\r
1689                                                                 if (matches) {\r
1690                                                                         attr = {};\r
1691                                                                         attrType = matches[1];\r
1692                                                                         attrName = matches[2].replace(/::/g, ':');\r
1693                                                                         prefix = matches[3];\r
1694                                                                         value = matches[4];\r
1695 \r
1696                                                                         // Required\r
1697                                                                         if (attrType === '!') {\r
1698                                                                                 element.attributesRequired = element.attributesRequired || [];\r
1699                                                                                 element.attributesRequired.push(attrName);\r
1700                                                                                 attr.required = true;\r
1701                                                                         }\r
1702 \r
1703                                                                         // Denied from global\r
1704                                                                         if (attrType === '-') {\r
1705                                                                                 delete attributes[attrName];\r
1706                                                                                 attributesOrder.splice(tinymce.inArray(attributesOrder, attrName), 1);\r
1707                                                                                 continue;\r
1708                                                                         }\r
1709 \r
1710                                                                         // Default value\r
1711                                                                         if (prefix) {\r
1712                                                                                 // Default value\r
1713                                                                                 if (prefix === '=') {\r
1714                                                                                         element.attributesDefault = element.attributesDefault || [];\r
1715                                                                                         element.attributesDefault.push({name: attrName, value: value});\r
1716                                                                                         attr.defaultValue = value;\r
1717                                                                                 }\r
1718 \r
1719                                                                                 // Forced value\r
1720                                                                                 if (prefix === ':') {\r
1721                                                                                         element.attributesForced = element.attributesForced || [];\r
1722                                                                                         element.attributesForced.push({name: attrName, value: value});\r
1723                                                                                         attr.forcedValue = value;\r
1724                                                                                 }\r
1725 \r
1726                                                                                 // Required values\r
1727                                                                                 if (prefix === '<')\r
1728                                                                                         attr.validValues = makeMap(value, '?');\r
1729                                                                         }\r
1730 \r
1731                                                                         // Check for attribute patterns\r
1732                                                                         if (hasPatternsRegExp.test(attrName)) {\r
1733                                                                                 element.attributePatterns = element.attributePatterns || [];\r
1734                                                                                 attr.pattern = patternToRegExp(attrName);\r
1735                                                                                 element.attributePatterns.push(attr);\r
1736                                                                         } else {\r
1737                                                                                 // Add attribute to order list if it doesn't already exist\r
1738                                                                                 if (!attributes[attrName])\r
1739                                                                                         attributesOrder.push(attrName);\r
1740 \r
1741                                                                                 attributes[attrName] = attr;\r
1742                                                                         }\r
1743                                                                 }\r
1744                                                         }\r
1745                                                 }\r
1746 \r
1747                                                 // Global rule, store away these for later usage\r
1748                                                 if (!globalAttributes && elementName == '@') {\r
1749                                                         globalAttributes = attributes;\r
1750                                                         globalAttributesOrder = attributesOrder;\r
1751                                                 }\r
1752 \r
1753                                                 // Handle substitute elements such as b/strong\r
1754                                                 if (outputName) {\r
1755                                                         element.outputName = elementName;\r
1756                                                         elements[outputName] = element;\r
1757                                                 }\r
1758 \r
1759                                                 // Add pattern or exact element\r
1760                                                 if (hasPatternsRegExp.test(elementName)) {\r
1761                                                         element.pattern = patternToRegExp(elementName);\r
1762                                                         patternElements.push(element);\r
1763                                                 } else\r
1764                                                         elements[elementName] = element;\r
1765                                         }\r
1766                                 }\r
1767                         }\r
1768                 };\r
1769 \r
1770                 function setValidElements(valid_elements) {\r
1771                         elements = {};\r
1772                         patternElements = [];\r
1773 \r
1774                         addValidElements(valid_elements);\r
1775 \r
1776                         each(transitional, function(element, name) {\r
1777                                 children[name] = element.children;\r
1778                         });\r
1779                 };\r
1780 \r
1781                 // Adds custom non HTML elements to the schema\r
1782                 function addCustomElements(custom_elements) {\r
1783                         var customElementRegExp = /^(~)?(.+)$/;\r
1784 \r
1785                         if (custom_elements) {\r
1786                                 each(split(custom_elements), function(rule) {\r
1787                                         var matches = customElementRegExp.exec(rule),\r
1788                                                 cloneName = matches[1] === '~' ? 'span' : 'div',\r
1789                                                 name = matches[2];\r
1790 \r
1791                                         children[name] = children[cloneName];\r
1792 \r
1793                                         // Add custom elements at span/div positions\r
1794                                         each(children, function(element, child) {\r
1795                                                 if (element[cloneName])\r
1796                                                         element[name] = element[cloneName];\r
1797                                         });\r
1798                                 });\r
1799                         }\r
1800                 };\r
1801 \r
1802                 // Adds valid children to the schema object\r
1803                 function addValidChildren(valid_children) {\r
1804                         var childRuleRegExp = /^([+\-]?)(\w+)\[([^\]]+)\]$/;\r
1805 \r
1806                         if (valid_children) {\r
1807                                 each(split(valid_children), function(rule) {\r
1808                                         var matches = childRuleRegExp.exec(rule), parent, prefix;\r
1809 \r
1810                                         if (matches) {\r
1811                                                 prefix = matches[1];\r
1812 \r
1813                                                 // Add/remove items from default\r
1814                                                 if (prefix)\r
1815                                                         parent = children[matches[2]];\r
1816                                                 else\r
1817                                                         parent = children[matches[2]] = {'#comment' : {}};\r
1818 \r
1819                                                 parent = children[matches[2]];\r
1820 \r
1821                                                 each(split(matches[3], '|'), function(child) {\r
1822                                                         if (prefix === '-')\r
1823                                                                 delete parent[child];\r
1824                                                         else\r
1825                                                                 parent[child] = {};\r
1826                                                 });\r
1827                                         }\r
1828                                 });\r
1829                         }\r
1830                 }\r
1831 \r
1832                 if (!settings.valid_elements) {\r
1833                         // No valid elements defined then clone the elements from the transitional spec\r
1834                         each(transitional, function(element, name) {\r
1835                                 elements[name] = {\r
1836                                         attributes : element.attributes,\r
1837                                         attributesOrder : element.attributesOrder\r
1838                                 };\r
1839 \r
1840                                 children[name] = element.children;\r
1841                         });\r
1842 \r
1843                         // Switch these\r
1844                         each(split('strong/b,em/i'), function(item) {\r
1845                                 item = split(item, '/');\r
1846                                 elements[item[1]].outputName = item[0];\r
1847                         });\r
1848 \r
1849                         // Add default alt attribute for images\r
1850                         elements.img.attributesDefault = [{name: 'alt', value: ''}];\r
1851 \r
1852                         // Remove these if they are empty by default\r
1853                         each(split('ol,ul,li,sub,sup,blockquote,tr,div,span,font,a,table,tbody'), function(name) {\r
1854                                 elements[name].removeEmpty = true;\r
1855                         });\r
1856 \r
1857                         // Padd these by default\r
1858                         each(split('p,h1,h2,h3,h4,h5,h6,th,td,pre,div,address,caption'), function(name) {\r
1859                                 elements[name].paddEmpty = true;\r
1860                         });\r
1861                 } else\r
1862                         setValidElements(settings.valid_elements);\r
1863 \r
1864                 addCustomElements(settings.custom_elements);\r
1865                 addValidChildren(settings.valid_children);\r
1866                 addValidElements(settings.extended_valid_elements);\r
1867 \r
1868                 // Todo: Remove this when we fix list handling to be valid\r
1869                 addValidChildren('+ol[ul|ol],+ul[ul|ol]');\r
1870 \r
1871                 // Delete invalid elements\r
1872                 if (settings.invalid_elements) {\r
1873                         tinymce.each(tinymce.explode(settings.invalid_elements), function(item) {\r
1874                                 if (elements[item])\r
1875                                         delete elements[item];\r
1876                         });\r
1877                 }\r
1878 \r
1879                 self.children = children;\r
1880 \r
1881                 self.styles = validStyles;\r
1882 \r
1883                 self.getBoolAttrs = function() {\r
1884                         return boolAttrMap;\r
1885                 };\r
1886 \r
1887                 self.getBlockElements = function() {\r
1888                         return blockElementsMap;\r
1889                 };\r
1890 \r
1891                 self.getShortEndedElements = function() {\r
1892                         return shortEndedElementsMap;\r
1893                 };\r
1894 \r
1895                 self.getSelfClosingElements = function() {\r
1896                         return selfClosingElementsMap;\r
1897                 };\r
1898 \r
1899                 self.getNonEmptyElements = function() {\r
1900                         return nonEmptyElementsMap;\r
1901                 };\r
1902 \r
1903                 self.getWhiteSpaceElements = function() {\r
1904                         return whiteSpaceElementsMap;\r
1905                 };\r
1906 \r
1907                 self.isValidChild = function(name, child) {\r
1908                         var parent = children[name];\r
1909 \r
1910                         return !!(parent && parent[child]);\r
1911                 };\r
1912 \r
1913                 self.getElementRule = function(name) {\r
1914                         var element = elements[name], i;\r
1915 \r
1916                         // Exact match found\r
1917                         if (element)\r
1918                                 return element;\r
1919 \r
1920                         // No exact match then try the patterns\r
1921                         i = patternElements.length;\r
1922                         while (i--) {\r
1923                                 element = patternElements[i];\r
1924 \r
1925                                 if (element.pattern.test(name))\r
1926                                         return element;\r
1927                         }\r
1928                 };\r
1929 \r
1930                 self.addValidElements = addValidElements;\r
1931 \r
1932                 self.setValidElements = setValidElements;\r
1933 \r
1934                 self.addCustomElements = addCustomElements;\r
1935 \r
1936                 self.addValidChildren = addValidChildren;\r
1937         };\r
1938 \r
1939         // Expose boolMap and blockElementMap as static properties for usage in DOMUtils\r
1940         tinymce.html.Schema.boolAttrMap = boolAttrMap;\r
1941         tinymce.html.Schema.blockElementsMap = blockElementsMap;\r
1942 })(tinymce);\r
1943 \r
1944 (function(tinymce) {\r
1945         tinymce.html.SaxParser = function(settings, schema) {\r
1946                 var self = this, noop = function() {};\r
1947 \r
1948                 settings = settings || {};\r
1949                 self.schema = schema = schema || new tinymce.html.Schema();\r
1950 \r
1951                 if (settings.fix_self_closing !== false)\r
1952                         settings.fix_self_closing = true;\r
1953 \r
1954                 // Add handler functions from settings and setup default handlers\r
1955                 tinymce.each('comment cdata text start end pi doctype'.split(' '), function(name) {\r
1956                         if (name)\r
1957                                 self[name] = settings[name] || noop;\r
1958                 });\r
1959 \r
1960                 self.parse = function(html) {\r
1961                         var self = this, matches, index = 0, value, endRegExp, stack = [], attrList, i, text, name,\r
1962                                 shortEndedElements, fillAttrsMap, isShortEnded, validate, elementRule, isValidElement, attr, attribsValue,\r
1963                                 validAttributesMap, validAttributePatterns, attributesRequired, attributesDefault, attributesForced, selfClosing,\r
1964                                 tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0, decode = tinymce.html.Entities.decode, fixSelfClosing;\r
1965 \r
1966                         function processEndTag(name) {\r
1967                                 var pos, i;\r
1968 \r
1969                                 // Find position of parent of the same type\r
1970                                 pos = stack.length;\r
1971                                 while (pos--) {\r
1972                                         if (stack[pos].name === name)\r
1973                                                 break;                                          \r
1974                                 }\r
1975 \r
1976                                 // Found parent\r
1977                                 if (pos >= 0) {\r
1978                                         // Close all the open elements\r
1979                                         for (i = stack.length - 1; i >= pos; i--) {\r
1980                                                 name = stack[i];\r
1981 \r
1982                                                 if (name.valid)\r
1983                                                         self.end(name.name);\r
1984                                         }\r
1985 \r
1986                                         // Remove the open elements from the stack\r
1987                                         stack.length = pos;\r
1988                                 }\r
1989                         };\r
1990 \r
1991                         // Precompile RegExps and map objects\r
1992                         tokenRegExp = new RegExp('<(?:' +\r
1993                                 '(?:!--([\\w\\W]*?)-->)|' + // Comment\r
1994                                 '(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|' + // CDATA\r
1995                                 '(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE\r
1996                                 '(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI\r
1997                                 '(?:\\/([^>]+)>)|' + // End element\r
1998                                 '(?:([^\\s\\/<>]+)\\s*((?:[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*)>)' + // Start element\r
1999                         ')', 'g');\r
2000 \r
2001                         attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:\\.|[^\"])*)\")|(?:\'((?:\\.|[^\'])*)\')|([^>\s]+)))?/g;\r
2002                         specialElements = {\r
2003                                 'script' : /<\/script[^>]*>/gi,\r
2004                                 'style' : /<\/style[^>]*>/gi,\r
2005                                 'noscript' : /<\/noscript[^>]*>/gi\r
2006                         };\r
2007 \r
2008                         // Setup lookup tables for empty elements and boolean attributes\r
2009                         shortEndedElements = schema.getShortEndedElements();\r
2010                         selfClosing = schema.getSelfClosingElements();\r
2011                         fillAttrsMap = schema.getBoolAttrs();\r
2012                         validate = settings.validate;\r
2013                         fixSelfClosing = settings.fix_self_closing;\r
2014 \r
2015                         while (matches = tokenRegExp.exec(html)) {\r
2016                                 // Text\r
2017                                 if (index < matches.index)\r
2018                                         self.text(decode(html.substr(index, matches.index - index)));\r
2019 \r
2020                                 if (value = matches[6]) { // End element\r
2021                                         processEndTag(value.toLowerCase());\r
2022                                 } else if (value = matches[7]) { // Start element\r
2023                                         value = value.toLowerCase();\r
2024                                         isShortEnded = value in shortEndedElements;\r
2025 \r
2026                                         // Is self closing tag for example an <li> after an open <li>\r
2027                                         if (fixSelfClosing && selfClosing[value] && stack.length > 0 && stack[stack.length - 1].name === value)\r
2028                                                 processEndTag(value);\r
2029 \r
2030                                         // Validate element\r
2031                                         if (!validate || (elementRule = schema.getElementRule(value))) {\r
2032                                                 isValidElement = true;\r
2033 \r
2034                                                 // Grab attributes map and patters when validation is enabled\r
2035                                                 if (validate) {\r
2036                                                         validAttributesMap = elementRule.attributes;\r
2037                                                         validAttributePatterns = elementRule.attributePatterns;\r
2038                                                 }\r
2039 \r
2040                                                 // Parse attributes\r
2041                                                 if (attribsValue = matches[8]) {\r
2042                                                         attrList = [];\r
2043                                                         attrList.map = {};\r
2044 \r
2045                                                         attribsValue.replace(attrRegExp, function(match, name, value, val2, val3) {\r
2046                                                                 var attrRule, i;\r
2047 \r
2048                                                                 name = name.toLowerCase();\r
2049                                                                 value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute\r
2050 \r
2051                                                                 // Validate name and value\r
2052                                                                 if (validate && name.indexOf('data-') !== 0) {\r
2053                                                                         attrRule = validAttributesMap[name];\r
2054 \r
2055                                                                         // Find rule by pattern matching\r
2056                                                                         if (!attrRule && validAttributePatterns) {\r
2057                                                                                 i = validAttributePatterns.length;\r
2058                                                                                 while (i--) {\r
2059                                                                                         attrRule = validAttributePatterns[i];\r
2060                                                                                         if (attrRule.pattern.test(name))\r
2061                                                                                                 break;\r
2062                                                                                 }\r
2063 \r
2064                                                                                 // No rule matched\r
2065                                                                                 if (i === -1)\r
2066                                                                                         attrRule = null;\r
2067                                                                         }\r
2068 \r
2069                                                                         // No attribute rule found\r
2070                                                                         if (!attrRule)\r
2071                                                                                 return;\r
2072 \r
2073                                                                         // Validate value\r
2074                                                                         if (attrRule.validValues && !(value in attrRule.validValues))\r
2075                                                                                 return;\r
2076                                                                 }\r
2077 \r
2078                                                                 // Add attribute to list and map\r
2079                                                                 attrList.map[name] = value;\r
2080                                                                 attrList.push({\r
2081                                                                         name: name,\r
2082                                                                         value: value\r
2083                                                                 });\r
2084                                                         });\r
2085                                                 } else {\r
2086                                                         attrList = [];\r
2087                                                         attrList.map = {};\r
2088                                                 }\r
2089 \r
2090                                                 // Process attributes if validation is enabled\r
2091                                                 if (validate) {\r
2092                                                         attributesRequired = elementRule.attributesRequired;\r
2093                                                         attributesDefault = elementRule.attributesDefault;\r
2094                                                         attributesForced = elementRule.attributesForced;\r
2095 \r
2096                                                         // Handle forced attributes\r
2097                                                         if (attributesForced) {\r
2098                                                                 i = attributesForced.length;\r
2099                                                                 while (i--) {\r
2100                                                                         attr = attributesForced[i];\r
2101                                                                         name = attr.name;\r
2102                                                                         attrValue = attr.value;\r
2103 \r
2104                                                                         if (attrValue === '{$uid}')\r
2105                                                                                 attrValue = 'mce_' + idCount++;\r
2106 \r
2107                                                                         attrList.map[name] = attrValue;\r
2108                                                                         attrList.push({name: name, value: attrValue});\r
2109                                                                 }\r
2110                                                         }\r
2111 \r
2112                                                         // Handle default attributes\r
2113                                                         if (attributesDefault) {\r
2114                                                                 i = attributesDefault.length;\r
2115                                                                 while (i--) {\r
2116                                                                         attr = attributesDefault[i];\r
2117                                                                         name = attr.name;\r
2118 \r
2119                                                                         if (!(name in attrList.map)) {\r
2120                                                                                 attrValue = attr.value;\r
2121 \r
2122                                                                                 if (attrValue === '{$uid}')\r
2123                                                                                         attrValue = 'mce_' + idCount++;\r
2124 \r
2125                                                                                 attrList.map[name] = attrValue;\r
2126                                                                                 attrList.push({name: name, value: attrValue});\r
2127                                                                         }\r
2128                                                                 }\r
2129                                                         }\r
2130 \r
2131                                                         // Handle required attributes\r
2132                                                         if (attributesRequired) {\r
2133                                                                 i = attributesRequired.length;\r
2134                                                                 while (i--) {\r
2135                                                                         if (attributesRequired[i] in attrList.map)\r
2136                                                                                 break;\r
2137                                                                 }\r
2138 \r
2139                                                                 // None of the required attributes where found\r
2140                                                                 if (i === -1)\r
2141                                                                         isValidElement = false;\r
2142                                                         }\r
2143 \r
2144                                                         // Invalidate element if it's marked as bogus\r
2145                                                         if (attrList.map['data-mce-bogus'])\r
2146                                                                 isValidElement = false;\r
2147                                                 }\r
2148 \r
2149                                                 if (isValidElement)\r
2150                                                         self.start(value, attrList, isShortEnded);\r
2151                                         } else\r
2152                                                 isValidElement = false;\r
2153 \r
2154                                         // Treat script, noscript and style a bit different since they may include code that looks like elements\r
2155                                         if (endRegExp = specialElements[value]) {\r
2156                                                 endRegExp.lastIndex = index = matches.index + matches[0].length;\r
2157 \r
2158                                                 if (matches = endRegExp.exec(html)) {\r
2159                                                         if (isValidElement)\r
2160                                                                 text = html.substr(index, matches.index - index);\r
2161 \r
2162                                                         index = matches.index + matches[0].length;\r
2163                                                 } else {\r
2164                                                         text = html.substr(index);\r
2165                                                         index = html.length;\r
2166                                                 }\r
2167 \r
2168                                                 if (isValidElement && text.length > 0)\r
2169                                                         self.text(text, true);\r
2170 \r
2171                                                 if (isValidElement)\r
2172                                                         self.end(value);\r
2173 \r
2174                                                 tokenRegExp.lastIndex = index;\r
2175                                                 continue;\r
2176                                         }\r
2177 \r
2178                                         // Push value on to stack\r
2179                                         if (!isShortEnded) {\r
2180                                                 if (!attribsValue || attribsValue.indexOf('/') != attribsValue.length - 1)\r
2181                                                         stack.push({name: value, valid: isValidElement});\r
2182                                                 else if (isValidElement)\r
2183                                                         self.end(value);\r
2184                                         }\r
2185                                 } else if (value = matches[1]) { // Comment\r
2186                                         self.comment(value);\r
2187                                 } else if (value = matches[2]) { // CDATA\r
2188                                         self.cdata(value);\r
2189                                 } else if (value = matches[3]) { // DOCTYPE\r
2190                                         self.doctype(value);\r
2191                                 } else if (value = matches[4]) { // PI\r
2192                                         self.pi(value, matches[5]);\r
2193                                 }\r
2194 \r
2195                                 index = matches.index + matches[0].length;\r
2196                         }\r
2197 \r
2198                         // Text\r
2199                         if (index < html.length)\r
2200                                 self.text(decode(html.substr(index)));\r
2201 \r
2202                         // Close any open elements\r
2203                         for (i = stack.length - 1; i >= 0; i--) {\r
2204                                 value = stack[i];\r
2205 \r
2206                                 if (value.valid)\r
2207                                         self.end(value.name);\r
2208                         }\r
2209                 };\r
2210         }\r
2211 })(tinymce);\r
2212 \r
2213 (function(tinymce) {\r
2214         var whiteSpaceRegExp = /^[ \t\r\n]*$/, typeLookup = {\r
2215                 '#text' : 3,\r
2216                 '#comment' : 8,\r
2217                 '#cdata' : 4,\r
2218                 '#pi' : 7,\r
2219                 '#doctype' : 10,\r
2220                 '#document-fragment' : 11\r
2221         };\r
2222 \r
2223         // Walks the tree left/right\r
2224         function walk(node, root_node, prev) {\r
2225                 var sibling, parent, startName = prev ? 'lastChild' : 'firstChild', siblingName = prev ? 'prev' : 'next';\r
2226 \r
2227                 // Walk into nodes if it has a start\r
2228                 if (node[startName])\r
2229                         return node[startName];\r
2230 \r
2231                 // Return the sibling if it has one\r
2232                 if (node !== root_node) {\r
2233                         sibling = node[siblingName];\r
2234 \r
2235                         if (sibling)\r
2236                                 return sibling;\r
2237 \r
2238                         // Walk up the parents to look for siblings\r
2239                         for (parent = node.parent; parent && parent !== root_node; parent = parent.parent) {\r
2240                                 sibling = parent[siblingName];\r
2241 \r
2242                                 if (sibling)\r
2243                                         return sibling;\r
2244                         }\r
2245                 }\r
2246         };\r
2247 \r
2248         function Node(name, type) {\r
2249                 this.name = name;\r
2250                 this.type = type;\r
2251 \r
2252                 if (type === 1) {\r
2253                         this.attributes = [];\r
2254                         this.attributes.map = {};\r
2255                 }\r
2256         }\r
2257 \r
2258         tinymce.extend(Node.prototype, {\r
2259                 replace : function(node) {\r
2260                         var self = this;\r
2261 \r
2262                         if (node.parent)\r
2263                                 node.remove();\r
2264 \r
2265                         self.insert(node, self);\r
2266                         self.remove();\r
2267 \r
2268                         return self;\r
2269                 },\r
2270 \r
2271                 attr : function(name, value) {\r
2272                         var self = this, attrs, i, undef;\r
2273 \r
2274                         if (typeof name !== "string") {\r
2275                                 for (i in name)\r
2276                                         self.attr(i, name[i]);\r
2277 \r
2278                                 return self;\r
2279                         }\r
2280 \r
2281                         if (attrs = self.attributes) {\r
2282                                 if (value !== undef) {\r
2283                                         // Remove attribute\r
2284                                         if (value === null) {\r
2285                                                 if (name in attrs.map) {\r
2286                                                         delete attrs.map[name];\r
2287 \r
2288                                                         i = attrs.length;\r
2289                                                         while (i--) {\r
2290                                                                 if (attrs[i].name === name) {\r
2291                                                                         attrs = attrs.splice(i, 1);\r
2292                                                                         return self;\r
2293                                                                 }\r
2294                                                         }\r
2295                                                 }\r
2296 \r
2297                                                 return self;\r
2298                                         }\r
2299 \r
2300                                         // Set attribute\r
2301                                         if (name in attrs.map) {\r
2302                                                 // Set attribute\r
2303                                                 i = attrs.length;\r
2304                                                 while (i--) {\r
2305                                                         if (attrs[i].name === name) {\r
2306                                                                 attrs[i].value = value;\r
2307                                                                 break;\r
2308                                                         }\r
2309                                                 }\r
2310                                         } else\r
2311                                                 attrs.push({name: name, value: value});\r
2312 \r
2313                                         attrs.map[name] = value;\r
2314 \r
2315                                         return self;\r
2316                                 } else {\r
2317                                         return attrs.map[name];\r
2318                                 }\r
2319                         }\r
2320                 },\r
2321 \r
2322                 clone : function() {\r
2323                         var self = this, clone = new Node(self.name, self.type), i, l, selfAttrs, selfAttr, cloneAttrs;\r
2324 \r
2325                         // Clone element attributes\r
2326                         if (selfAttrs = self.attributes) {\r
2327                                 cloneAttrs = [];\r
2328                                 cloneAttrs.map = {};\r
2329 \r
2330                                 for (i = 0, l = selfAttrs.length; i < l; i++) {\r
2331                                         selfAttr = selfAttrs[i];\r
2332 \r
2333                                         // Clone everything except id\r
2334                                         if (selfAttr.name !== 'id') {\r
2335                                                 cloneAttrs[cloneAttrs.length] = {name: selfAttr.name, value: selfAttr.value};\r
2336                                                 cloneAttrs.map[selfAttr.name] = selfAttr.value;\r
2337                                         }\r
2338                                 }\r
2339 \r
2340                                 clone.attributes = cloneAttrs;\r
2341                         }\r
2342 \r
2343                         clone.value = self.value;\r
2344                         clone.shortEnded = self.shortEnded;\r
2345 \r
2346                         return clone;\r
2347                 },\r
2348 \r
2349                 wrap : function(wrapper) {\r
2350                         var self = this;\r
2351 \r
2352                         self.parent.insert(wrapper, self);\r
2353                         wrapper.append(self);\r
2354 \r
2355                         return self;\r
2356                 },\r
2357 \r
2358                 unwrap : function() {\r
2359                         var self = this, node, next;\r
2360 \r
2361                         for (node = self.firstChild; node; ) {\r
2362                                 next = node.next;\r
2363                                 self.insert(node, self, true);\r
2364                                 node = next;\r
2365                         }\r
2366 \r
2367                         self.remove();\r
2368                 },\r
2369 \r
2370                 remove : function() {\r
2371                         var self = this, parent = self.parent, next = self.next, prev = self.prev;\r
2372 \r
2373                         if (parent) {\r
2374                                 if (parent.firstChild === self) {\r
2375                                         parent.firstChild = next;\r
2376 \r
2377                                         if (next)\r
2378                                                 next.prev = null;\r
2379                                 } else {\r
2380                                         prev.next = next;\r
2381                                 }\r
2382 \r
2383                                 if (parent.lastChild === self) {\r
2384                                         parent.lastChild = prev;\r
2385 \r
2386                                         if (prev)\r
2387                                                 prev.next = null;\r
2388                                 } else {\r
2389                                         next.prev = prev;\r
2390                                 }\r
2391 \r
2392                                 self.parent = self.next = self.prev = null;\r
2393                         }\r
2394 \r
2395                         return self;\r
2396                 },\r
2397 \r
2398                 append : function(node) {\r
2399                         var self = this, last;\r
2400 \r
2401                         if (node.parent)\r
2402                                 node.remove();\r
2403 \r
2404                         last = self.lastChild;\r
2405                         if (last) {\r
2406                                 last.next = node;\r
2407                                 node.prev = last;\r
2408                                 self.lastChild = node;\r
2409                         } else\r
2410                                 self.lastChild = self.firstChild = node;\r
2411 \r
2412                         node.parent = self;\r
2413 \r
2414                         return node;\r
2415                 },\r
2416 \r
2417                 insert : function(node, ref_node, before) {\r
2418                         var parent;\r
2419 \r
2420                         if (node.parent)\r
2421                                 node.remove();\r
2422 \r
2423                         parent = ref_node.parent || this;\r
2424 \r
2425                         if (before) {\r
2426                                 if (ref_node === parent.firstChild)\r
2427                                         parent.firstChild = node;\r
2428                                 else\r
2429                                         ref_node.prev.next = node;\r
2430 \r
2431                                 node.prev = ref_node.prev;\r
2432                                 node.next = ref_node;\r
2433                                 ref_node.prev = node;\r
2434                         } else {\r
2435                                 if (ref_node === parent.lastChild)\r
2436                                         parent.lastChild = node;\r
2437                                 else\r
2438                                         ref_node.next.prev = node;\r
2439 \r
2440                                 node.next = ref_node.next;\r
2441                                 node.prev = ref_node;\r
2442                                 ref_node.next = node;\r
2443                         }\r
2444 \r
2445                         node.parent = parent;\r
2446 \r
2447                         return node;\r
2448                 },\r
2449 \r
2450                 getAll : function(name) {\r
2451                         var self = this, node, collection = [];\r
2452 \r
2453                         for (node = self.firstChild; node; node = walk(node, self)) {\r
2454                                 if (node.name === name)\r
2455                                         collection.push(node);\r
2456                         }\r
2457 \r
2458                         return collection;\r
2459                 },\r
2460 \r
2461                 empty : function() {\r
2462                         var self = this, nodes, i, node;\r
2463 \r
2464                         // Remove all children\r
2465                         if (self.firstChild) {\r
2466                                 nodes = [];\r
2467 \r
2468                                 // Collect the children\r
2469                                 for (node = self.firstChild; node; node = walk(node, self))\r
2470                                         nodes.push(node);\r
2471 \r
2472                                 // Remove the children\r
2473                                 i = nodes.length;\r
2474                                 while (i--) {\r
2475                                         node = nodes[i];\r
2476                                         node.parent = node.firstChild = node.lastChild = node.next = node.prev = null;\r
2477                                 }\r
2478                         }\r
2479 \r
2480                         self.firstChild = self.lastChild = null;\r
2481 \r
2482                         return self;\r
2483                 },\r
2484 \r
2485                 isEmpty : function(elements) {\r
2486                         var self = this, node = self.firstChild, i, name;\r
2487 \r
2488                         if (node) {\r
2489                                 do {\r
2490                                         if (node.type === 1) {\r
2491                                                 // Ignore bogus elements\r
2492                                                 if (node.attributes.map['data-mce-bogus'])\r
2493                                                         continue;\r
2494 \r
2495                                                 // Keep empty elements like <img />\r
2496                                                 if (elements[node.name])\r
2497                                                         return false;\r
2498 \r
2499                                                 // Keep elements with data attributes or name attribute like <a name="1"></a>\r
2500                                                 i = node.attributes.length;\r
2501                                                 while (i--) {\r
2502                                                         name = node.attributes[i].name;\r
2503                                                         if (name === "name" || name.indexOf('data-') === 0)\r
2504                                                                 return false;\r
2505                                                 }\r
2506                                         }\r
2507 \r
2508                                         // Keep non whitespace text nodes\r
2509                                         if ((node.type === 3 && !whiteSpaceRegExp.test(node.value)))\r
2510                                                 return false;\r
2511                                 } while (node = walk(node, self));\r
2512                         }\r
2513 \r
2514                         return true;\r
2515                 }\r
2516         });\r
2517 \r
2518         tinymce.extend(Node, {\r
2519                 create : function(name, attrs) {\r
2520                         var node, attrName;\r
2521 \r
2522                         // Create node\r
2523                         node = new Node(name, typeLookup[name] || 1);\r
2524 \r
2525                         // Add attributes if needed\r
2526                         if (attrs) {\r
2527                                 for (attrName in attrs)\r
2528                                         node.attr(attrName, attrs[attrName]);\r
2529                         }\r
2530 \r
2531                         return node;\r
2532                 }\r
2533         });\r
2534 \r
2535         tinymce.html.Node = Node;\r
2536 })(tinymce);\r
2537 \r
2538 (function(tinymce) {\r
2539         var Node = tinymce.html.Node;\r
2540 \r
2541         tinymce.html.DomParser = function(settings, schema) {\r
2542                 var self = this, nodeFilters = {}, attributeFilters = [], matchedNodes = {}, matchedAttributes = {};\r
2543 \r
2544                 settings = settings || {};\r
2545                 settings.validate = "validate" in settings ? settings.validate : true;\r
2546                 settings.root_name = settings.root_name || 'body';\r
2547                 self.schema = schema = schema || new tinymce.html.Schema();\r
2548 \r
2549                 function fixInvalidChildren(nodes) {\r
2550                         var ni, node, parent, parents, newParent, currentNode, tempNode, childNode, i,\r
2551                                 childClone, nonEmptyElements, nonSplitableElements, sibling, nextNode;\r
2552 \r
2553                         nonSplitableElements = tinymce.makeMap('tr,td,th,tbody,thead,tfoot,table');\r
2554                         nonEmptyElements = schema.getNonEmptyElements();\r
2555 \r
2556                         for (ni = 0; ni < nodes.length; ni++) {\r
2557                                 node = nodes[ni];\r
2558 \r
2559                                 // Already removed\r
2560                                 if (!node.parent)\r
2561                                         continue;\r
2562 \r
2563                                 // Get list of all parent nodes until we find a valid parent to stick the child into\r
2564                                 parents = [node];\r
2565                                 for (parent = node.parent; parent && !schema.isValidChild(parent.name, node.name) && !nonSplitableElements[parent.name]; parent = parent.parent)\r
2566                                         parents.push(parent);\r
2567 \r
2568                                 // Found a suitable parent\r
2569                                 if (parent && parents.length > 1) {\r
2570                                         // Reverse the array since it makes looping easier\r
2571                                         parents.reverse();\r
2572 \r
2573                                         // Clone the related parent and insert that after the moved node\r
2574                                         newParent = currentNode = self.filterNode(parents[0].clone());\r
2575 \r
2576                                         // Start cloning and moving children on the left side of the target node\r
2577                                         for (i = 0; i < parents.length - 1; i++) {\r
2578                                                 if (schema.isValidChild(currentNode.name, parents[i].name)) {\r
2579                                                         tempNode = self.filterNode(parents[i].clone());\r
2580                                                         currentNode.append(tempNode);\r
2581                                                 } else\r
2582                                                         tempNode = currentNode;\r
2583 \r
2584                                                 for (childNode = parents[i].firstChild; childNode && childNode != parents[i + 1]; ) {\r
2585                                                         nextNode = childNode.next;\r
2586                                                         tempNode.append(childNode);\r
2587                                                         childNode = nextNode;\r
2588                                                 }\r
2589 \r
2590                                                 currentNode = tempNode;\r
2591                                         }\r
2592 \r
2593                                         if (!newParent.isEmpty(nonEmptyElements)) {\r
2594                                                 parent.insert(newParent, parents[0], true);\r
2595                                                 parent.insert(node, newParent);\r
2596                                         } else {\r
2597                                                 parent.insert(node, parents[0], true);\r
2598                                         }\r
2599 \r
2600                                         // Check if the element is empty by looking through it's contents and special treatment for <p><br /></p>\r
2601                                         parent = parents[0];\r
2602                                         if (parent.isEmpty(nonEmptyElements) || parent.firstChild === parent.lastChild && parent.firstChild.name === 'br') {\r
2603                                                 parent.empty().remove();\r
2604                                         }\r
2605                                 } else if (node.parent) {\r
2606                                         // If it's an LI try to find a UL/OL for it or wrap it\r
2607                                         if (node.name === 'li') {\r
2608                                                 sibling = node.prev;\r
2609                                                 if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {\r
2610                                                         sibling.append(node);\r
2611                                                         continue;\r
2612                                                 }\r
2613 \r
2614                                                 sibling = node.next;\r
2615                                                 if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {\r
2616                                                         sibling.insert(node, sibling.firstChild, true);\r
2617                                                         continue;\r
2618                                                 }\r
2619 \r
2620                                                 node.wrap(self.filterNode(new Node('ul', 1)));\r
2621                                                 continue;\r
2622                                         }\r
2623 \r
2624                                         // Try wrapping the element in a DIV\r
2625                                         if (schema.isValidChild(node.parent.name, 'div') && schema.isValidChild('div', node.name)) {\r
2626                                                 node.wrap(self.filterNode(new Node('div', 1)));\r
2627                                         } else {\r
2628                                                 // We failed wrapping it, then remove or unwrap it\r
2629                                                 if (node.name === 'style' || node.name === 'script')\r
2630                                                         node.empty().remove();\r
2631                                                 else\r
2632                                                         node.unwrap();\r
2633                                         }\r
2634                                 }\r
2635                         }\r
2636                 };\r
2637 \r
2638                 self.filterNode = function(node) {\r
2639                         var i, name, list;\r
2640 \r
2641                         // Run element filters\r
2642                         if (name in nodeFilters) {\r
2643                                 list = matchedNodes[name];\r
2644 \r
2645                                 if (list)\r
2646                                         list.push(node);\r
2647                                 else\r
2648                                         matchedNodes[name] = [node];\r
2649                         }\r
2650 \r
2651                         // Run attribute filters\r
2652                         i = attributeFilters.length;\r
2653                         while (i--) {\r
2654                                 name = attributeFilters[i].name;\r
2655 \r
2656                                 if (name in node.attributes.map) {\r
2657                                         list = matchedAttributes[name];\r
2658 \r
2659                                         if (list)\r
2660                                                 list.push(node);\r
2661                                         else\r
2662                                                 matchedAttributes[name] = [node];\r
2663                                 }\r
2664                         }\r
2665 \r
2666                         return node;\r
2667                 };\r
2668 \r
2669                 self.addNodeFilter = function(name, callback) {\r
2670                         tinymce.each(tinymce.explode(name), function(name) {\r
2671                                 var list = nodeFilters[name];\r
2672 \r
2673                                 if (!list)\r
2674                                         nodeFilters[name] = list = [];\r
2675 \r
2676                                 list.push(callback);\r
2677                         });\r
2678                 };\r
2679 \r
2680                 self.addAttributeFilter = function(name, callback) {\r
2681                         tinymce.each(tinymce.explode(name), function(name) {\r
2682                                 var i;\r
2683 \r
2684                                 for (i = 0; i < attributeFilters.length; i++) {\r
2685                                         if (attributeFilters[i].name === name) {\r
2686                                                 attributeFilters[i].callbacks.push(callback);\r
2687                                                 return;\r
2688                                         }\r
2689                                 }\r
2690 \r
2691                                 attributeFilters.push({name: name, callbacks: [callback]});\r
2692                         });\r
2693                 };\r
2694 \r
2695                 self.parse = function(html, args) {\r
2696                         var parser, rootNode, node, nodes, i, l, fi, fl, list, name, validate,\r
2697                                 blockElements, startWhiteSpaceRegExp, invalidChildren = [],\r
2698                                 endWhiteSpaceRegExp, allWhiteSpaceRegExp, whiteSpaceElements, children, nonEmptyElements;\r
2699 \r
2700                         args = args || {};\r
2701                         matchedNodes = {};\r
2702                         matchedAttributes = {};\r
2703                         blockElements = tinymce.extend(tinymce.makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements());\r
2704                         nonEmptyElements = schema.getNonEmptyElements();\r
2705                         children = schema.children;\r
2706                         validate = settings.validate;\r
2707 \r
2708                         whiteSpaceElements = schema.getWhiteSpaceElements();\r
2709                         startWhiteSpaceRegExp = /^[ \t\r\n]+/;\r
2710                         endWhiteSpaceRegExp = /[ \t\r\n]+$/;\r
2711                         allWhiteSpaceRegExp = /[ \t\r\n]+/g;\r
2712 \r
2713                         function createNode(name, type) {\r
2714                                 var node = new Node(name, type), list;\r
2715 \r
2716                                 if (name in nodeFilters) {\r
2717                                         list = matchedNodes[name];\r
2718 \r
2719                                         if (list)\r
2720                                                 list.push(node);\r
2721                                         else\r
2722                                                 matchedNodes[name] = [node];\r
2723                                 }\r
2724 \r
2725                                 return node;\r
2726                         };\r
2727 \r
2728                         function removeWhitespaceBefore(node) {\r
2729                                 var textNode, textVal, sibling;\r
2730 \r
2731                                 for (textNode = node.prev; textNode && textNode.type === 3; ) {\r
2732                                         textVal = textNode.value.replace(endWhiteSpaceRegExp, '');\r
2733 \r
2734                                         if (textVal.length > 0) {\r
2735                                                 textNode.value = textVal;\r
2736                                                 textNode = textNode.prev;\r
2737                                         } else {\r
2738                                                 sibling = textNode.prev;\r
2739                                                 textNode.remove();\r
2740                                                 textNode = sibling;\r
2741                                         }\r
2742                                 }\r
2743                         };\r
2744 \r
2745                         parser = new tinymce.html.SaxParser({\r
2746                                 validate : validate,\r
2747                                 fix_self_closing : !validate, // Let the DOM parser handle <li> in <li> or <p> in <p> for better results\r
2748 \r
2749                                 cdata: function(text) {\r
2750                                         node.append(createNode('#cdata', 4)).value = text;\r
2751                                 },\r
2752 \r
2753                                 text: function(text, raw) {\r
2754                                         var textNode;\r
2755 \r
2756                                         // Trim all redundant whitespace on non white space elements\r
2757                                         if (!whiteSpaceElements[node.name]) {\r
2758                                                 text = text.replace(allWhiteSpaceRegExp, ' ');\r
2759 \r
2760                                                 if (node.lastChild && blockElements[node.lastChild.name])\r
2761                                                         text = text.replace(startWhiteSpaceRegExp, '');\r
2762                                         }\r
2763 \r
2764                                         // Do we need to create the node\r
2765                                         if (text.length !== 0) {\r
2766                                                 textNode = createNode('#text', 3);\r
2767                                                 textNode.raw = !!raw;\r
2768                                                 node.append(textNode).value = text;\r
2769                                         }\r
2770                                 },\r
2771 \r
2772                                 comment: function(text) {\r
2773                                         node.append(createNode('#comment', 8)).value = text;\r
2774                                 },\r
2775 \r
2776                                 pi: function(name, text) {\r
2777                                         node.append(createNode(name, 7)).value = text;\r
2778                                         removeWhitespaceBefore(node);\r
2779                                 },\r
2780 \r
2781                                 doctype: function(text) {\r
2782                                         var newNode;\r
2783                 \r
2784                                         newNode = node.append(createNode('#doctype', 10));\r
2785                                         newNode.value = text;\r
2786                                         removeWhitespaceBefore(node);\r
2787                                 },\r
2788 \r
2789                                 start: function(name, attrs, empty) {\r
2790                                         var newNode, attrFiltersLen, elementRule, textNode, attrName, text, sibling, parent;\r
2791 \r
2792                                         elementRule = validate ? schema.getElementRule(name) : {};\r
2793                                         if (elementRule) {\r
2794                                                 newNode = createNode(elementRule.outputName || name, 1);\r
2795                                                 newNode.attributes = attrs;\r
2796                                                 newNode.shortEnded = empty;\r
2797 \r
2798                                                 node.append(newNode);\r
2799 \r
2800                                                 // Check if node is valid child of the parent node is the child is\r
2801                                                 // unknown we don't collect it since it's probably a custom element\r
2802                                                 parent = children[node.name];\r
2803                                                 if (parent && children[newNode.name] && !parent[newNode.name])\r
2804                                                         invalidChildren.push(newNode);\r
2805 \r
2806                                                 attrFiltersLen = attributeFilters.length;\r
2807                                                 while (attrFiltersLen--) {\r
2808                                                         attrName = attributeFilters[attrFiltersLen].name;\r
2809 \r
2810                                                         if (attrName in attrs.map) {\r
2811                                                                 list = matchedAttributes[attrName];\r
2812 \r
2813                                                                 if (list)\r
2814                                                                         list.push(newNode);\r
2815                                                                 else\r
2816                                                                         matchedAttributes[attrName] = [newNode];\r
2817                                                         }\r
2818                                                 }\r
2819 \r
2820                                                 // Trim whitespace before block\r
2821                                                 if (blockElements[name])\r
2822                                                         removeWhitespaceBefore(newNode);\r
2823 \r
2824                                                 // Change current node if the element wasn't empty i.e not <br /> or <img />\r
2825                                                 if (!empty)\r
2826                                                         node = newNode;\r
2827                                         }\r
2828                                 },\r
2829 \r
2830                                 end: function(name) {\r
2831                                         var textNode, elementRule, text, sibling, tempNode;\r
2832 \r
2833                                         elementRule = validate ? schema.getElementRule(name) : {};\r
2834                                         if (elementRule) {\r
2835                                                 if (blockElements[name]) {\r
2836                                                         if (!whiteSpaceElements[node.name]) {\r
2837                                                                 // Trim whitespace at beginning of block\r
2838                                                                 for (textNode = node.firstChild; textNode && textNode.type === 3; ) {\r
2839                                                                         text = textNode.value.replace(startWhiteSpaceRegExp, '');\r
2840 \r
2841                                                                         if (text.length > 0) {\r
2842                                                                                 textNode.value = text;\r
2843                                                                                 textNode = textNode.next;\r
2844                                                                         } else {\r
2845                                                                                 sibling = textNode.next;\r
2846                                                                                 textNode.remove();\r
2847                                                                                 textNode = sibling;\r
2848                                                                         }\r
2849                                                                 }\r
2850 \r
2851                                                                 // Trim whitespace at end of block\r
2852                                                                 for (textNode = node.lastChild; textNode && textNode.type === 3; ) {\r
2853                                                                         text = textNode.value.replace(endWhiteSpaceRegExp, '');\r
2854 \r
2855                                                                         if (text.length > 0) {\r
2856                                                                                 textNode.value = text;\r
2857                                                                                 textNode = textNode.prev;\r
2858                                                                         } else {\r
2859                                                                                 sibling = textNode.prev;\r
2860                                                                                 textNode.remove();\r
2861                                                                                 textNode = sibling;\r
2862                                                                         }\r
2863                                                                 }\r
2864                                                         }\r
2865 \r
2866                                                         // Trim start white space\r
2867                                                         textNode = node.prev;\r
2868                                                         if (textNode && textNode.type === 3) {\r
2869                                                                 text = textNode.value.replace(startWhiteSpaceRegExp, '');\r
2870 \r
2871                                                                 if (text.length > 0)\r
2872                                                                         textNode.value = text;\r
2873                                                                 else\r
2874                                                                         textNode.remove();\r
2875                                                         }\r
2876                                                 }\r
2877 \r
2878                                                 // Handle empty nodes\r
2879                                                 if (elementRule.removeEmpty || elementRule.paddEmpty) {\r
2880                                                         if (node.isEmpty(nonEmptyElements)) {\r
2881                                                                 if (elementRule.paddEmpty)\r
2882                                                                         node.empty().append(new Node('#text', '3')).value = '\u00a0';\r
2883                                                                 else {\r
2884                                                                         // Leave nodes that have a name like <a name="name">\r
2885                                                                         if (!node.attributes.map.name) {\r
2886                                                                                 tempNode = node.parent;\r
2887                                                                                 node.empty().remove();\r
2888                                                                                 node = tempNode;\r
2889                                                                                 return;\r
2890                                                                         }\r
2891                                                                 }\r
2892                                                         }\r
2893                                                 }\r
2894 \r
2895                                                 node = node.parent;\r
2896                                         }\r
2897                                 }\r
2898                         }, schema);\r
2899 \r
2900                         rootNode = node = new Node(settings.root_name, 11);\r
2901 \r
2902                         parser.parse(html);\r
2903 \r
2904                         if (validate)\r
2905                                 fixInvalidChildren(invalidChildren);\r
2906 \r
2907                         // Run node filters\r
2908                         for (name in matchedNodes) {\r
2909                                 list = nodeFilters[name];\r
2910                                 nodes = matchedNodes[name];\r
2911 \r
2912                                 // Remove already removed children\r
2913                                 fi = nodes.length;\r
2914                                 while (fi--) {\r
2915                                         if (!nodes[fi].parent)\r
2916                                                 nodes.splice(fi, 1);\r
2917                                 }\r
2918 \r
2919                                 for (i = 0, l = list.length; i < l; i++)\r
2920                                         list[i](nodes, name, args);\r
2921                         }\r
2922 \r
2923                         // Run attribute filters\r
2924                         for (i = 0, l = attributeFilters.length; i < l; i++) {\r
2925                                 list = attributeFilters[i];\r
2926 \r
2927                                 if (list.name in matchedAttributes) {\r
2928                                         nodes = matchedAttributes[list.name];\r
2929 \r
2930                                         // Remove already removed children\r
2931                                         fi = nodes.length;\r
2932                                         while (fi--) {\r
2933                                                 if (!nodes[fi].parent)\r
2934                                                         nodes.splice(fi, 1);\r
2935                                         }\r
2936 \r
2937                                         for (fi = 0, fl = list.callbacks.length; fi < fl; fi++)\r
2938                                                 list.callbacks[fi](nodes, list.name, args);\r
2939                                 }\r
2940                         }\r
2941 \r
2942                         return rootNode;\r
2943                 };\r
2944 \r
2945                 // Remove <br> at end of block elements Gecko and WebKit injects BR elements to\r
2946                 // make it possible to place the caret inside empty blocks. This logic tries to remove\r
2947                 // these elements and keep br elements that where intended to be there intact\r
2948                 if (settings.remove_trailing_brs) {\r
2949                         self.addNodeFilter('br', function(nodes, name) {\r
2950                                 var i, l = nodes.length, node, blockElements = schema.getBlockElements(),\r
2951                                         nonEmptyElements = schema.getNonEmptyElements(), parent, prev, prevName;\r
2952 \r
2953                                 // Must loop forwards since it will otherwise remove all brs in <p>a<br><br><br></p>\r
2954                                 for (i = 0; i < l; i++) {\r
2955                                         node = nodes[i];\r
2956                                         parent = node.parent;\r
2957 \r
2958                                         if (blockElements[node.parent.name] && node === parent.lastChild) {\r
2959                                                 // Loop all nodes to the right of the current node and check for other BR elements\r
2960                                                 // excluding bookmarks since they are invisible\r
2961                                                 prev = node.prev;\r
2962                                                 while (prev) {\r
2963                                                         prevName = prev.name;\r
2964 \r
2965                                                         // Ignore bookmarks\r
2966                                                         if (prevName !== "span" || prev.attr('data-mce-type') !== 'bookmark') {\r
2967                                                                 // Found a non BR element\r
2968                                                                 if (prevName !== "br")\r
2969                                                                         break;\r
2970         \r
2971                                                                 // Found another br it's a <br><br> structure then don't remove anything\r
2972                                                                 if (prevName === 'br') {\r
2973                                                                         node = null;\r
2974                                                                         break;\r
2975                                                                 }\r
2976                                                         }\r
2977 \r
2978                                                         prev = prev.prev;\r
2979                                                 }\r
2980 \r
2981                                                 if (node) {\r
2982                                                         node.remove();\r
2983 \r
2984                                                         // Is the parent to be considered empty after we removed the BR\r
2985                                                         if (parent.isEmpty(nonEmptyElements)) {\r
2986                                                                 elementRule = schema.getElementRule(parent.name);\r
2987 \r
2988                                                                 // Remove or padd the element depending on schema rule\r
2989                                                                 if (elementRule.removeEmpty)\r
2990                                                                         parent.remove();\r
2991                                                                 else if (elementRule.paddEmpty) \r
2992                                                                         parent.empty().append(new tinymce.html.Node('#text', 3)).value = '\u00a0';\r
2993                                                         }\r
2994                                                 }\r
2995                                         }\r
2996                                 }\r
2997                         });\r
2998                 }\r
2999         }\r
3000 })(tinymce);\r
3001 \r
3002 tinymce.html.Writer = function(settings) {\r
3003         var html = [], indent, indentBefore, indentAfter, encode, htmlOutput;\r
3004 \r
3005         settings = settings || {};\r
3006         indent = settings.indent;\r
3007         indentBefore = tinymce.makeMap(settings.indent_before || '');\r
3008         indentAfter = tinymce.makeMap(settings.indent_after || '');\r
3009         encode = tinymce.html.Entities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);\r
3010         htmlOutput = settings.element_format == "html";\r
3011 \r
3012         return {\r
3013                 start: function(name, attrs, empty) {\r
3014                         var i, l, attr, value;\r
3015 \r
3016                         if (indent && indentBefore[name] && html.length > 0) {\r
3017                                 value = html[html.length - 1];\r
3018 \r
3019                                 if (value.length > 0 && value !== '\n')\r
3020                                         html.push('\n');\r
3021                         }\r
3022 \r
3023                         html.push('<', name);\r
3024 \r
3025                         if (attrs) {\r
3026                                 for (i = 0, l = attrs.length; i < l; i++) {\r
3027                                         attr = attrs[i];\r
3028                                         html.push(' ', attr.name, '="', encode(attr.value, true), '"');\r
3029                                 }\r
3030                         }\r
3031 \r
3032                         if (!empty || htmlOutput)\r
3033                                 html[html.length] = '>';\r
3034                         else\r
3035                                 html[html.length] = ' />';\r
3036 \r
3037                         if (empty && indent && indentAfter[name] && html.length > 0) {\r
3038                                 value = html[html.length - 1];\r
3039 \r
3040                                 if (value.length > 0 && value !== '\n')\r
3041                                         html.push('\n');\r
3042                         }\r
3043                 },\r
3044 \r
3045                 end: function(name) {\r
3046                         var value;\r
3047 \r
3048                         /*if (indent && indentBefore[name] && html.length > 0) {\r
3049                                 value = html[html.length - 1];\r
3050 \r
3051                                 if (value.length > 0 && value !== '\n')\r
3052                                         html.push('\n');\r
3053                         }*/\r
3054 \r
3055                         html.push('</', name, '>');\r
3056 \r
3057                         if (indent && indentAfter[name] && html.length > 0) {\r
3058                                 value = html[html.length - 1];\r
3059 \r
3060                                 if (value.length > 0 && value !== '\n')\r
3061                                         html.push('\n');\r
3062                         }\r
3063                 },\r
3064 \r
3065                 text: function(text, raw) {\r
3066                         if (text.length > 0)\r
3067                                 html[html.length] = raw ? text : encode(text);\r
3068                 },\r
3069 \r
3070                 cdata: function(text) {\r
3071                         html.push('<![CDATA[', text, ']]>');\r
3072                 },\r
3073 \r
3074                 comment: function(text) {\r
3075                         html.push('<!--', text, '-->');\r
3076                 },\r
3077 \r
3078                 pi: function(name, text) {\r
3079                         if (text)\r
3080                                 html.push('<?', name, ' ', text, '?>');\r
3081                         else\r
3082                                 html.push('<?', name, '?>');\r
3083 \r
3084                         if (indent)\r
3085                                 html.push('\n');\r
3086                 },\r
3087 \r
3088                 doctype: function(text) {\r
3089                         html.push('<!DOCTYPE', text, '>', indent ? '\n' : '');\r
3090                 },\r
3091 \r
3092                 reset: function() {\r
3093                         html.length = 0;\r
3094                 },\r
3095 \r
3096                 getContent: function() {\r
3097                         return html.join('').replace(/\n$/, '');\r
3098                 }\r
3099         };\r
3100 };\r
3101 \r
3102 (function(tinymce) {\r
3103         tinymce.html.Serializer = function(settings, schema) {\r
3104                 var self = this, writer = new tinymce.html.Writer(settings);\r
3105 \r
3106                 settings = settings || {};\r
3107                 settings.validate = "validate" in settings ? settings.validate : true;\r
3108 \r
3109                 self.schema = schema = schema || new tinymce.html.Schema();\r
3110                 self.writer = writer;\r
3111 \r
3112                 self.serialize = function(node) {\r
3113                         var handlers, validate;\r
3114 \r
3115                         validate = settings.validate;\r
3116 \r
3117                         handlers = {\r
3118                                 // #text\r
3119                                 3: function(node, raw) {\r
3120                                         writer.text(node.value, node.raw);\r
3121                                 },\r
3122 \r
3123                                 // #comment\r
3124                                 8: function(node) {\r
3125                                         writer.comment(node.value);\r
3126                                 },\r
3127 \r
3128                                 // Processing instruction\r
3129                                 7: function(node) {\r
3130                                         writer.pi(node.name, node.value);\r
3131                                 },\r
3132 \r
3133                                 // Doctype\r
3134                                 10: function(node) {\r
3135                                         writer.doctype(node.value);\r
3136                                 },\r
3137 \r
3138                                 // CDATA\r
3139                                 4: function(node) {\r
3140                                         writer.cdata(node.value);\r
3141                                 },\r
3142 \r
3143                                 // Document fragment\r
3144                                 11: function(node) {\r
3145                                         if ((node = node.firstChild)) {\r
3146                                                 do {\r
3147                                                         walk(node);\r
3148                                                 } while (node = node.next);\r
3149                                         }\r
3150                                 }\r
3151                         };\r
3152 \r
3153                         writer.reset();\r
3154 \r
3155                         function walk(node) {\r
3156                                 var handler = handlers[node.type], name, isEmpty, attrs, attrName, attrValue, sortedAttrs, i, l, elementRule;\r
3157 \r
3158                                 if (!handler) {\r
3159                                         name = node.name;\r
3160                                         isEmpty = node.shortEnded;\r
3161                                         attrs = node.attributes;\r
3162 \r
3163                                         // Sort attributes\r
3164                                         if (validate && attrs && attrs.length > 1) {\r
3165                                                 sortedAttrs = [];\r
3166                                                 sortedAttrs.map = {};\r
3167 \r
3168                                                 elementRule = schema.getElementRule(node.name);\r
3169                                                 for (i = 0, l = elementRule.attributesOrder.length; i < l; i++) {\r
3170                                                         attrName = elementRule.attributesOrder[i];\r
3171 \r
3172                                                         if (attrName in attrs.map) {\r
3173                                                                 attrValue = attrs.map[attrName];\r
3174                                                                 sortedAttrs.map[attrName] = attrValue;\r
3175                                                                 sortedAttrs.push({name: attrName, value: attrValue});\r
3176                                                         }\r
3177                                                 }\r
3178 \r
3179                                                 for (i = 0, l = attrs.length; i < l; i++) {\r
3180                                                         attrName = attrs[i].name;\r
3181 \r
3182                                                         if (!(attrName in sortedAttrs.map)) {\r
3183                                                                 attrValue = attrs.map[attrName];\r
3184                                                                 sortedAttrs.map[attrName] = attrValue;\r
3185                                                                 sortedAttrs.push({name: attrName, value: attrValue});\r
3186                                                         }\r
3187                                                 }\r
3188 \r
3189                                                 attrs = sortedAttrs;\r
3190                                         }\r
3191 \r
3192                                         writer.start(node.name, attrs, isEmpty);\r
3193 \r
3194                                         if (!isEmpty) {\r
3195                                                 if ((node = node.firstChild)) {\r
3196                                                         do {\r
3197                                                                 walk(node);\r
3198                                                         } while (node = node.next);\r
3199                                                 }\r
3200 \r
3201                                                 writer.end(name);\r
3202                                         }\r
3203                                 } else\r
3204                                         handler(node);\r
3205                         }\r
3206 \r
3207                         // Serialize element and treat all non elements as fragments\r
3208                         if (node.type == 1 && !settings.inner)\r
3209                                 walk(node);\r
3210                         else\r
3211                                 handlers[11](node);\r
3212 \r
3213                         return writer.getContent();\r
3214                 };\r
3215         }\r
3216 })(tinymce);\r
3217 \r
3218 (function(tinymce) {\r
3219         // Shorten names\r
3220         var each = tinymce.each,\r
3221                 is = tinymce.is,\r
3222                 isWebKit = tinymce.isWebKit,\r
3223                 isIE = tinymce.isIE,\r
3224                 Entities = tinymce.html.Entities,\r
3225                 simpleSelectorRe = /^([a-z0-9],?)+$/i,\r
3226                 blockElementsMap = tinymce.html.Schema.blockElementsMap,\r
3227                 whiteSpaceRegExp = /^[ \t\r\n]*$/;\r
3228 \r
3229         tinymce.create('tinymce.dom.DOMUtils', {\r
3230                 doc : null,\r
3231                 root : null,\r
3232                 files : null,\r
3233                 pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,\r
3234                 props : {\r
3235                         "for" : "htmlFor",\r
3236                         "class" : "className",\r
3237                         className : "className",\r
3238                         checked : "checked",\r
3239                         disabled : "disabled",\r
3240                         maxlength : "maxLength",\r
3241                         readonly : "readOnly",\r
3242                         selected : "selected",\r
3243                         value : "value",\r
3244                         id : "id",\r
3245                         name : "name",\r
3246                         type : "type"\r
3247                 },\r
3248 \r
3249                 DOMUtils : function(d, s) {\r
3250                         var t = this, globalStyle;\r
3251 \r
3252                         t.doc = d;\r
3253                         t.win = window;\r
3254                         t.files = {};\r
3255                         t.cssFlicker = false;\r
3256                         t.counter = 0;\r
3257                         t.stdMode = !tinymce.isIE || d.documentMode >= 8;\r
3258                         t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat" || t.stdMode;\r
3259                         t.hasOuterHTML = "outerHTML" in d.createElement("a");\r
3260 \r
3261                         t.settings = s = tinymce.extend({\r
3262                                 keep_values : false,\r
3263                                 hex_colors : 1\r
3264                         }, s);\r
3265                         \r
3266                         t.schema = s.schema;\r
3267                         t.styles = new tinymce.html.Styles({\r
3268                                 url_converter : s.url_converter,\r
3269                                 url_converter_scope : s.url_converter_scope\r
3270                         }, s.schema);\r
3271 \r
3272                         // Fix IE6SP2 flicker and check it failed for pre SP2\r
3273                         if (tinymce.isIE6) {\r
3274                                 try {\r
3275                                         d.execCommand('BackgroundImageCache', false, true);\r
3276                                 } catch (e) {\r
3277                                         t.cssFlicker = true;\r
3278                                 }\r
3279                         }\r
3280 \r
3281                         if (isIE) {\r
3282                                 // Add missing HTML 4/5 elements to IE\r
3283                                 ('abbr article aside audio canvas ' +\r
3284                                 'details figcaption figure footer ' +\r
3285                                 'header hgroup mark menu meter nav ' +\r
3286                                 'output progress section summary ' +\r
3287                                 'time video').replace(/\w+/g, function(name) {\r
3288                                         d.createElement(name);\r
3289                                 });\r
3290                         }\r
3291 \r
3292                         tinymce.addUnload(t.destroy, t);\r
3293                 },\r
3294 \r
3295                 getRoot : function() {\r
3296                         var t = this, s = t.settings;\r
3297 \r
3298                         return (s && t.get(s.root_element)) || t.doc.body;\r
3299                 },\r
3300 \r
3301                 getViewPort : function(w) {\r
3302                         var d, b;\r
3303 \r
3304                         w = !w ? this.win : w;\r
3305                         d = w.document;\r
3306                         b = this.boxModel ? d.documentElement : d.body;\r
3307 \r
3308                         // Returns viewport size excluding scrollbars\r
3309                         return {\r
3310                                 x : w.pageXOffset || b.scrollLeft,\r
3311                                 y : w.pageYOffset || b.scrollTop,\r
3312                                 w : w.innerWidth || b.clientWidth,\r
3313                                 h : w.innerHeight || b.clientHeight\r
3314                         };\r
3315                 },\r
3316 \r
3317                 getRect : function(e) {\r
3318                         var p, t = this, sr;\r
3319 \r
3320                         e = t.get(e);\r
3321                         p = t.getPos(e);\r
3322                         sr = t.getSize(e);\r
3323 \r
3324                         return {\r
3325                                 x : p.x,\r
3326                                 y : p.y,\r
3327                                 w : sr.w,\r
3328                                 h : sr.h\r
3329                         };\r
3330                 },\r
3331 \r
3332                 getSize : function(e) {\r
3333                         var t = this, w, h;\r
3334 \r
3335                         e = t.get(e);\r
3336                         w = t.getStyle(e, 'width');\r
3337                         h = t.getStyle(e, 'height');\r
3338 \r
3339                         // Non pixel value, then force offset/clientWidth\r
3340                         if (w.indexOf('px') === -1)\r
3341                                 w = 0;\r
3342 \r
3343                         // Non pixel value, then force offset/clientWidth\r
3344                         if (h.indexOf('px') === -1)\r
3345                                 h = 0;\r
3346 \r
3347                         return {\r
3348                                 w : parseInt(w) || e.offsetWidth || e.clientWidth,\r
3349                                 h : parseInt(h) || e.offsetHeight || e.clientHeight\r
3350                         };\r
3351                 },\r
3352 \r
3353                 getParent : function(n, f, r) {\r
3354                         return this.getParents(n, f, r, false);\r
3355                 },\r
3356 \r
3357                 getParents : function(n, f, r, c) {\r
3358                         var t = this, na, se = t.settings, o = [];\r
3359 \r
3360                         n = t.get(n);\r
3361                         c = c === undefined;\r
3362 \r
3363                         if (se.strict_root)\r
3364                                 r = r || t.getRoot();\r
3365 \r
3366                         // Wrap node name as func\r
3367                         if (is(f, 'string')) {\r
3368                                 na = f;\r
3369 \r
3370                                 if (f === '*') {\r
3371                                         f = function(n) {return n.nodeType == 1;};\r
3372                                 } else {\r
3373                                         f = function(n) {\r
3374                                                 return t.is(n, na);\r
3375                                         };\r
3376                                 }\r
3377                         }\r
3378 \r
3379                         while (n) {\r
3380                                 if (n == r || !n.nodeType || n.nodeType === 9)\r
3381                                         break;\r
3382 \r
3383                                 if (!f || f(n)) {\r
3384                                         if (c)\r
3385                                                 o.push(n);\r
3386                                         else\r
3387                                                 return n;\r
3388                                 }\r
3389 \r
3390                                 n = n.parentNode;\r
3391                         }\r
3392 \r
3393                         return c ? o : null;\r
3394                 },\r
3395 \r
3396                 get : function(e) {\r
3397                         var n;\r
3398 \r
3399                         if (e && this.doc && typeof(e) == 'string') {\r
3400                                 n = e;\r
3401                                 e = this.doc.getElementById(e);\r
3402 \r
3403                                 // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick\r
3404                                 if (e && e.id !== n)\r
3405                                         return this.doc.getElementsByName(n)[1];\r
3406                         }\r
3407 \r
3408                         return e;\r
3409                 },\r
3410 \r
3411                 getNext : function(node, selector) {\r
3412                         return this._findSib(node, selector, 'nextSibling');\r
3413                 },\r
3414 \r
3415                 getPrev : function(node, selector) {\r
3416                         return this._findSib(node, selector, 'previousSibling');\r
3417                 },\r
3418 \r
3419 \r
3420                 select : function(pa, s) {\r
3421                         var t = this;\r
3422 \r
3423                         return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []);\r
3424                 },\r
3425 \r
3426                 is : function(n, selector) {\r
3427                         var i;\r
3428 \r
3429                         // If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance\r
3430                         if (n.length === undefined) {\r
3431                                 // Simple all selector\r
3432                                 if (selector === '*')\r
3433                                         return n.nodeType == 1;\r
3434 \r
3435                                 // Simple selector just elements\r
3436                                 if (simpleSelectorRe.test(selector)) {\r
3437                                         selector = selector.toLowerCase().split(/,/);\r
3438                                         n = n.nodeName.toLowerCase();\r
3439 \r
3440                                         for (i = selector.length - 1; i >= 0; i--) {\r
3441                                                 if (selector[i] == n)\r
3442                                                         return true;\r
3443                                         }\r
3444 \r
3445                                         return false;\r
3446                                 }\r
3447                         }\r
3448 \r
3449                         return tinymce.dom.Sizzle.matches(selector, n.nodeType ? [n] : n).length > 0;\r
3450                 },\r
3451 \r
3452 \r
3453                 add : function(p, n, a, h, c) {\r
3454                         var t = this;\r
3455 \r
3456                         return this.run(p, function(p) {\r
3457                                 var e, k;\r
3458 \r
3459                                 e = is(n, 'string') ? t.doc.createElement(n) : n;\r
3460                                 t.setAttribs(e, a);\r
3461 \r
3462                                 if (h) {\r
3463                                         if (h.nodeType)\r
3464                                                 e.appendChild(h);\r
3465                                         else\r
3466                                                 t.setHTML(e, h);\r
3467                                 }\r
3468 \r
3469                                 return !c ? p.appendChild(e) : e;\r
3470                         });\r
3471                 },\r
3472 \r
3473                 create : function(n, a, h) {\r
3474                         return this.add(this.doc.createElement(n), n, a, h, 1);\r
3475                 },\r
3476 \r
3477                 createHTML : function(n, a, h) {\r
3478                         var o = '', t = this, k;\r
3479 \r
3480                         o += '<' + n;\r
3481 \r
3482                         for (k in a) {\r
3483                                 if (a.hasOwnProperty(k))\r
3484                                         o += ' ' + k + '="' + t.encode(a[k]) + '"';\r
3485                         }\r
3486 \r
3487                         // A call to tinymce.is doesn't work for some odd reason on IE9 possible bug inside their JS runtime\r
3488                         if (typeof(h) != "undefined")\r
3489                                 return o + '>' + h + '</' + n + '>';\r
3490 \r
3491                         return o + ' />';\r
3492                 },\r
3493 \r
3494                 remove : function(node, keep_children) {\r
3495                         return this.run(node, function(node) {\r
3496                                 var child, parent = node.parentNode;\r
3497 \r
3498                                 if (!parent)\r
3499                                         return null;\r
3500 \r
3501                                 if (keep_children) {\r
3502                                         while (child = node.firstChild) {\r
3503                                                 // IE 8 will crash if you don't remove completely empty text nodes\r
3504                                                 if (!tinymce.isIE || child.nodeType !== 3 || child.nodeValue)\r
3505                                                         parent.insertBefore(child, node);\r
3506                                                 else\r
3507                                                         node.removeChild(child);\r
3508                                         }\r
3509                                 }\r
3510 \r
3511                                 return parent.removeChild(node);\r
3512                         });\r
3513                 },\r
3514 \r
3515                 setStyle : function(n, na, v) {\r
3516                         var t = this;\r
3517 \r
3518                         return t.run(n, function(e) {\r
3519                                 var s, i;\r
3520 \r
3521                                 s = e.style;\r
3522 \r
3523                                 // Camelcase it, if needed\r
3524                                 na = na.replace(/-(\D)/g, function(a, b){\r
3525                                         return b.toUpperCase();\r
3526                                 });\r
3527 \r
3528                                 // Default px suffix on these\r
3529                                 if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v)))\r
3530                                         v += 'px';\r
3531 \r
3532                                 switch (na) {\r
3533                                         case 'opacity':\r
3534                                                 // IE specific opacity\r
3535                                                 if (isIE) {\r
3536                                                         s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")";\r
3537 \r
3538                                                         if (!n.currentStyle || !n.currentStyle.hasLayout)\r
3539                                                                 s.display = 'inline-block';\r
3540                                                 }\r
3541 \r
3542                                                 // Fix for older browsers\r
3543                                                 s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || '';\r
3544                                                 break;\r
3545 \r
3546                                         case 'float':\r
3547                                                 isIE ? s.styleFloat = v : s.cssFloat = v;\r
3548                                                 break;\r
3549                                         \r
3550                                         default:\r
3551                                                 s[na] = v || '';\r
3552                                 }\r
3553 \r
3554                                 // Force update of the style data\r
3555                                 if (t.settings.update_styles)\r
3556                                         t.setAttrib(e, 'data-mce-style');\r
3557                         });\r
3558                 },\r
3559 \r
3560                 getStyle : function(n, na, c) {\r
3561                         n = this.get(n);\r
3562 \r
3563                         if (!n)\r
3564                                 return;\r
3565 \r
3566                         // Gecko\r
3567                         if (this.doc.defaultView && c) {\r
3568                                 // Remove camelcase\r
3569                                 na = na.replace(/[A-Z]/g, function(a){\r
3570                                         return '-' + a;\r
3571                                 });\r
3572 \r
3573                                 try {\r
3574                                         return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);\r
3575                                 } catch (ex) {\r
3576                                         // Old safari might fail\r
3577                                         return null;\r
3578                                 }\r
3579                         }\r
3580 \r
3581                         // Camelcase it, if needed\r
3582                         na = na.replace(/-(\D)/g, function(a, b){\r
3583                                 return b.toUpperCase();\r
3584                         });\r
3585 \r
3586                         if (na == 'float')\r
3587                                 na = isIE ? 'styleFloat' : 'cssFloat';\r
3588 \r
3589                         // IE & Opera\r
3590                         if (n.currentStyle && c)\r
3591                                 return n.currentStyle[na];\r
3592 \r
3593                         return n.style ? n.style[na] : undefined;\r
3594                 },\r
3595 \r
3596                 setStyles : function(e, o) {\r
3597                         var t = this, s = t.settings, ol;\r
3598 \r
3599                         ol = s.update_styles;\r
3600                         s.update_styles = 0;\r
3601 \r
3602                         each(o, function(v, n) {\r
3603                                 t.setStyle(e, n, v);\r
3604                         });\r
3605 \r
3606                         // Update style info\r
3607                         s.update_styles = ol;\r
3608                         if (s.update_styles)\r
3609                                 t.setAttrib(e, s.cssText);\r
3610                 },\r
3611 \r
3612                 removeAllAttribs: function(e) {\r
3613                         return this.run(e, function(e) {\r
3614                                 var i, attrs = e.attributes;\r
3615                                 for (i = attrs.length - 1; i >= 0; i--) {\r
3616                                         e.removeAttributeNode(attrs.item(i));\r
3617                                 }\r
3618                         });\r
3619                 },\r
3620 \r
3621                 setAttrib : function(e, n, v) {\r
3622                         var t = this;\r
3623 \r
3624                         // Whats the point\r
3625                         if (!e || !n)\r
3626                                 return;\r
3627 \r
3628                         // Strict XML mode\r
3629                         if (t.settings.strict)\r
3630                                 n = n.toLowerCase();\r
3631 \r
3632                         return this.run(e, function(e) {\r
3633                                 var s = t.settings;\r
3634 \r
3635                                 switch (n) {\r
3636                                         case "style":\r
3637                                                 if (!is(v, 'string')) {\r
3638                                                         each(v, function(v, n) {\r
3639                                                                 t.setStyle(e, n, v);\r
3640                                                         });\r
3641 \r
3642                                                         return;\r
3643                                                 }\r
3644 \r
3645                                                 // No mce_style for elements with these since they might get resized by the user\r
3646                                                 if (s.keep_values) {\r
3647                                                         if (v && !t._isRes(v))\r
3648                                                                 e.setAttribute('data-mce-style', v, 2);\r
3649                                                         else\r
3650                                                                 e.removeAttribute('data-mce-style', 2);\r
3651                                                 }\r
3652 \r
3653                                                 e.style.cssText = v;\r
3654                                                 break;\r
3655 \r
3656                                         case "class":\r
3657                                                 e.className = v || ''; // Fix IE null bug\r
3658                                                 break;\r
3659 \r
3660                                         case "src":\r
3661                                         case "href":\r
3662                                                 if (s.keep_values) {\r
3663                                                         if (s.url_converter)\r
3664                                                                 v = s.url_converter.call(s.url_converter_scope || t, v, n, e);\r
3665 \r
3666                                                         t.setAttrib(e, 'data-mce-' + n, v, 2);\r
3667                                                 }\r
3668 \r
3669                                                 break;\r
3670 \r
3671                                         case "shape":\r
3672                                                 e.setAttribute('data-mce-style', v);\r
3673                                                 break;\r
3674                                 }\r
3675 \r
3676                                 if (is(v) && v !== null && v.length !== 0)\r
3677                                         e.setAttribute(n, '' + v, 2);\r
3678                                 else\r
3679                                         e.removeAttribute(n, 2);\r
3680                         });\r
3681                 },\r
3682 \r
3683                 setAttribs : function(e, o) {\r
3684                         var t = this;\r
3685 \r
3686                         return this.run(e, function(e) {\r
3687                                 each(o, function(v, n) {\r
3688                                         t.setAttrib(e, n, v);\r
3689                                 });\r
3690                         });\r
3691                 },\r
3692 \r
3693                 getAttrib : function(e, n, dv) {\r
3694                         var v, t = this;\r
3695 \r
3696                         e = t.get(e);\r
3697 \r
3698                         if (!e || e.nodeType !== 1)\r
3699                                 return false;\r
3700 \r
3701                         if (!is(dv))\r
3702                                 dv = '';\r
3703 \r
3704                         // Try the mce variant for these\r
3705                         if (/^(src|href|style|coords|shape)$/.test(n)) {\r
3706                                 v = e.getAttribute("data-mce-" + n);\r
3707 \r
3708                                 if (v)\r
3709                                         return v;\r
3710                         }\r
3711 \r
3712                         if (isIE && t.props[n]) {\r
3713                                 v = e[t.props[n]];\r
3714                                 v = v && v.nodeValue ? v.nodeValue : v;\r
3715                         }\r
3716 \r
3717                         if (!v)\r
3718                                 v = e.getAttribute(n, 2);\r
3719 \r
3720                         // Check boolean attribs\r
3721                         if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(n)) {\r
3722                                 if (e[t.props[n]] === true && v === '')\r
3723                                         return n;\r
3724 \r
3725                                 return v ? n : '';\r
3726                         }\r
3727 \r
3728                         // Inner input elements will override attributes on form elements\r
3729                         if (e.nodeName === "FORM" && e.getAttributeNode(n))\r
3730                                 return e.getAttributeNode(n).nodeValue;\r
3731 \r
3732                         if (n === 'style') {\r
3733                                 v = v || e.style.cssText;\r
3734 \r
3735                                 if (v) {\r
3736                                         v = t.serializeStyle(t.parseStyle(v), e.nodeName);\r
3737 \r
3738                                         if (t.settings.keep_values && !t._isRes(v))\r
3739                                                 e.setAttribute('data-mce-style', v);\r
3740                                 }\r
3741                         }\r
3742 \r
3743                         // Remove Apple and WebKit stuff\r
3744                         if (isWebKit && n === "class" && v)\r
3745                                 v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, '');\r
3746 \r
3747                         // Handle IE issues\r
3748                         if (isIE) {\r
3749                                 switch (n) {\r
3750                                         case 'rowspan':\r
3751                                         case 'colspan':\r
3752                                                 // IE returns 1 as default value\r
3753                                                 if (v === 1)\r
3754                                                         v = '';\r
3755 \r
3756                                                 break;\r
3757 \r
3758                                         case 'size':\r
3759                                                 // IE returns +0 as default value for size\r
3760                                                 if (v === '+0' || v === 20 || v === 0)\r
3761                                                         v = '';\r
3762 \r
3763                                                 break;\r
3764 \r
3765                                         case 'width':\r
3766                                         case 'height':\r
3767                                         case 'vspace':\r
3768                                         case 'checked':\r
3769                                         case 'disabled':\r
3770                                         case 'readonly':\r
3771                                                 if (v === 0)\r
3772                                                         v = '';\r
3773 \r
3774                                                 break;\r
3775 \r
3776                                         case 'hspace':\r
3777                                                 // IE returns -1 as default value\r
3778                                                 if (v === -1)\r
3779                                                         v = '';\r
3780 \r
3781                                                 break;\r
3782 \r
3783                                         case 'maxlength':\r
3784                                         case 'tabindex':\r
3785                                                 // IE returns default value\r
3786                                                 if (v === 32768 || v === 2147483647 || v === '32768')\r
3787                                                         v = '';\r
3788 \r
3789                                                 break;\r
3790 \r
3791                                         case 'multiple':\r
3792                                         case 'compact':\r
3793                                         case 'noshade':\r
3794                                         case 'nowrap':\r
3795                                                 if (v === 65535)\r
3796                                                         return n;\r
3797 \r
3798                                                 return dv;\r
3799 \r
3800                                         case 'shape':\r
3801                                                 v = v.toLowerCase();\r
3802                                                 break;\r
3803 \r
3804                                         default:\r
3805                                                 // IE has odd anonymous function for event attributes\r
3806                                                 if (n.indexOf('on') === 0 && v)\r
3807                                                         v = tinymce._replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1', '' + v);\r
3808                                 }\r
3809                         }\r
3810 \r
3811                         return (v !== undefined && v !== null && v !== '') ? '' + v : dv;\r
3812                 },\r
3813 \r
3814                 getPos : function(n, ro) {\r
3815                         var t = this, x = 0, y = 0, e, d = t.doc, r;\r
3816 \r
3817                         n = t.get(n);\r
3818                         ro = ro || d.body;\r
3819 \r
3820                         if (n) {\r
3821                                 // Use getBoundingClientRect on IE, Opera has it but it's not perfect\r
3822                                 if (isIE && !t.stdMode) {\r
3823                                         n = n.getBoundingClientRect();\r
3824                                         e = t.boxModel ? d.documentElement : d.body;\r
3825                                         x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border\r
3826                                         x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x;\r
3827 \r
3828                                         return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x};\r
3829                                 }\r
3830 \r
3831                                 r = n;\r
3832                                 while (r && r != ro && r.nodeType) {\r
3833                                         x += r.offsetLeft || 0;\r
3834                                         y += r.offsetTop || 0;\r
3835                                         r = r.offsetParent;\r
3836                                 }\r
3837 \r
3838                                 r = n.parentNode;\r
3839                                 while (r && r != ro && r.nodeType) {\r
3840                                         x -= r.scrollLeft || 0;\r
3841                                         y -= r.scrollTop || 0;\r
3842                                         r = r.parentNode;\r
3843                                 }\r
3844                         }\r
3845 \r
3846                         return {x : x, y : y};\r
3847                 },\r
3848 \r
3849                 parseStyle : function(st) {\r
3850                         return this.styles.parse(st);\r
3851                 },\r
3852 \r
3853                 serializeStyle : function(o, name) {\r
3854                         return this.styles.serialize(o, name);\r
3855                 },\r
3856 \r
3857                 loadCSS : function(u) {\r
3858                         var t = this, d = t.doc, head;\r
3859 \r
3860                         if (!u)\r
3861                                 u = '';\r
3862 \r
3863                         head = t.select('head')[0];\r
3864 \r
3865                         each(u.split(','), function(u) {\r
3866                                 var link;\r
3867 \r
3868                                 if (t.files[u])\r
3869                                         return;\r
3870 \r
3871                                 t.files[u] = true;\r
3872                                 link = t.create('link', {rel : 'stylesheet', href : tinymce._addVer(u)});\r
3873 \r
3874                                 // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug\r
3875                                 // This fix seems to resolve that issue by realcing the document ones a stylesheet finishes loading\r
3876                                 // It's ugly but it seems to work fine.\r
3877                                 if (isIE && d.documentMode && d.recalc) {\r
3878                                         link.onload = function() {\r
3879                                                 if (d.recalc)\r
3880                                                         d.recalc();\r
3881 \r
3882                                                 link.onload = null;\r
3883                                         };\r
3884                                 }\r
3885 \r
3886                                 head.appendChild(link);\r
3887                         });\r
3888                 },\r
3889 \r
3890                 addClass : function(e, c) {\r
3891                         return this.run(e, function(e) {\r
3892                                 var o;\r
3893 \r
3894                                 if (!c)\r
3895                                         return 0;\r
3896 \r
3897                                 if (this.hasClass(e, c))\r
3898                                         return e.className;\r
3899 \r
3900                                 o = this.removeClass(e, c);\r
3901 \r
3902                                 return e.className = (o != '' ? (o + ' ') : '') + c;\r
3903                         });\r
3904                 },\r
3905 \r
3906                 removeClass : function(e, c) {\r
3907                         var t = this, re;\r
3908 \r
3909                         return t.run(e, function(e) {\r
3910                                 var v;\r
3911 \r
3912                                 if (t.hasClass(e, c)) {\r
3913                                         if (!re)\r
3914                                                 re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g");\r
3915 \r
3916                                         v = e.className.replace(re, ' ');\r
3917                                         v = tinymce.trim(v != ' ' ? v : '');\r
3918 \r
3919                                         e.className = v;\r
3920 \r
3921                                         // Empty class attr\r
3922                                         if (!v) {\r
3923                                                 e.removeAttribute('class');\r
3924                                                 e.removeAttribute('className');\r
3925                                         }\r
3926 \r
3927                                         return v;\r
3928                                 }\r
3929 \r
3930                                 return e.className;\r
3931                         });\r
3932                 },\r
3933 \r
3934                 hasClass : function(n, c) {\r
3935                         n = this.get(n);\r
3936 \r
3937                         if (!n || !c)\r
3938                                 return false;\r
3939 \r
3940                         return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1;\r
3941                 },\r
3942 \r
3943                 show : function(e) {\r
3944                         return this.setStyle(e, 'display', 'block');\r
3945                 },\r
3946 \r
3947                 hide : function(e) {\r
3948                         return this.setStyle(e, 'display', 'none');\r
3949                 },\r
3950 \r
3951                 isHidden : function(e) {\r
3952                         e = this.get(e);\r
3953 \r
3954                         return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none';\r
3955                 },\r
3956 \r
3957                 uniqueId : function(p) {\r
3958                         return (!p ? 'mce_' : p) + (this.counter++);\r
3959                 },\r
3960 \r
3961                 setHTML : function(element, html) {\r
3962                         var self = this;\r
3963 \r
3964                         return self.run(element, function(element) {\r
3965                                 if (isIE) {\r
3966                                         // Remove all child nodes, IE keeps empty text nodes in DOM\r
3967                                         while (element.firstChild)\r
3968                                                 element.removeChild(element.firstChild);\r
3969 \r
3970                                         try {\r
3971                                                 // IE will remove comments from the beginning\r
3972                                                 // unless you padd the contents with something\r
3973                                                 element.innerHTML = '<br />' + html;\r
3974                                                 element.removeChild(element.firstChild);\r
3975                                         } catch (ex) {\r
3976                                                 // IE sometimes produces an unknown runtime error on innerHTML if it's an block element within a block element for example a div inside a p\r
3977                                                 // This seems to fix this problem\r
3978 \r
3979                                                 // Create new div with HTML contents and a BR infront to keep comments\r
3980                                                 element = self.create('div');\r
3981                                                 element.innerHTML = '<br />' + html;\r
3982 \r
3983                                                 // Add all children from div to target\r
3984                                                 each (element.childNodes, function(node, i) {\r
3985                                                         // Skip br element\r
3986                                                         if (i)\r
3987                                                                 element.appendChild(node);\r
3988                                                 });\r
3989                                         }\r
3990                                 } else\r
3991                                         element.innerHTML = html;\r
3992 \r
3993                                 return html;\r
3994                         });\r
3995                 },\r
3996 \r
3997                 getOuterHTML : function(elm) {\r
3998                         var doc, self = this;\r
3999 \r
4000                         elm = self.get(elm);\r
4001 \r
4002                         if (!elm)\r
4003                                 return null;\r
4004 \r
4005                         if (elm.nodeType === 1 && self.hasOuterHTML)\r
4006                                 return elm.outerHTML;\r
4007 \r
4008                         doc = (elm.ownerDocument || self.doc).createElement("body");\r
4009                         doc.appendChild(elm.cloneNode(true));\r
4010 \r
4011                         return doc.innerHTML;\r
4012                 },\r
4013 \r
4014                 setOuterHTML : function(e, h, d) {\r
4015                         var t = this;\r
4016 \r
4017                         function setHTML(e, h, d) {\r
4018                                 var n, tp;\r
4019 \r
4020                                 tp = d.createElement("body");\r
4021                                 tp.innerHTML = h;\r
4022 \r
4023                                 n = tp.lastChild;\r
4024                                 while (n) {\r
4025                                         t.insertAfter(n.cloneNode(true), e);\r
4026                                         n = n.previousSibling;\r
4027                                 }\r
4028 \r
4029                                 t.remove(e);\r
4030                         };\r
4031 \r
4032                         return this.run(e, function(e) {\r
4033                                 e = t.get(e);\r
4034 \r
4035                                 // Only set HTML on elements\r
4036                                 if (e.nodeType == 1) {\r
4037                                         d = d || e.ownerDocument || t.doc;\r
4038 \r
4039                                         if (isIE) {\r
4040                                                 try {\r
4041                                                         // Try outerHTML for IE it sometimes produces an unknown runtime error\r
4042                                                         if (isIE && e.nodeType == 1)\r
4043                                                                 e.outerHTML = h;\r
4044                                                         else\r
4045                                                                 setHTML(e, h, d);\r
4046                                                 } catch (ex) {\r
4047                                                         // Fix for unknown runtime error\r
4048                                                         setHTML(e, h, d);\r
4049                                                 }\r
4050                                         } else\r
4051                                                 setHTML(e, h, d);\r
4052                                 }\r
4053                         });\r
4054                 },\r
4055 \r
4056                 decode : Entities.decode,\r
4057 \r
4058                 encode : Entities.encodeAllRaw,\r
4059 \r
4060                 insertAfter : function(node, reference_node) {\r
4061                         reference_node = this.get(reference_node);\r
4062 \r
4063                         return this.run(node, function(node) {\r
4064                                 var parent, nextSibling;\r
4065 \r
4066                                 parent = reference_node.parentNode;\r
4067                                 nextSibling = reference_node.nextSibling;\r
4068 \r
4069                                 if (nextSibling)\r
4070                                         parent.insertBefore(node, nextSibling);\r
4071                                 else\r
4072                                         parent.appendChild(node);\r
4073 \r
4074                                 return node;\r
4075                         });\r
4076                 },\r
4077 \r
4078                 isBlock : function(node) {\r
4079                         var type = node.nodeType;\r
4080 \r
4081                         // If it's a node then check the type and use the nodeName\r
4082                         if (type)\r
4083                                 return !!(type === 1 && blockElementsMap[node.nodeName]);\r
4084 \r
4085                         return !!blockElementsMap[node];\r
4086                 },\r
4087 \r
4088                 replace : function(n, o, k) {\r
4089                         var t = this;\r
4090 \r
4091                         if (is(o, 'array'))\r
4092                                 n = n.cloneNode(true);\r
4093 \r
4094                         return t.run(o, function(o) {\r
4095                                 if (k) {\r
4096                                         each(tinymce.grep(o.childNodes), function(c) {\r
4097                                                 n.appendChild(c);\r
4098                                         });\r
4099                                 }\r
4100 \r
4101                                 return o.parentNode.replaceChild(n, o);\r
4102                         });\r
4103                 },\r
4104 \r
4105                 rename : function(elm, name) {\r
4106                         var t = this, newElm;\r
4107 \r
4108                         if (elm.nodeName != name.toUpperCase()) {\r
4109                                 // Rename block element\r
4110                                 newElm = t.create(name);\r
4111 \r
4112                                 // Copy attribs to new block\r
4113                                 each(t.getAttribs(elm), function(attr_node) {\r
4114                                         t.setAttrib(newElm, attr_node.nodeName, t.getAttrib(elm, attr_node.nodeName));\r
4115                                 });\r
4116 \r
4117                                 // Replace block\r
4118                                 t.replace(newElm, elm, 1);\r
4119                         }\r
4120 \r
4121                         return newElm || elm;\r
4122                 },\r
4123 \r
4124                 findCommonAncestor : function(a, b) {\r
4125                         var ps = a, pe;\r
4126 \r
4127                         while (ps) {\r
4128                                 pe = b;\r
4129 \r
4130                                 while (pe && ps != pe)\r
4131                                         pe = pe.parentNode;\r
4132 \r
4133                                 if (ps == pe)\r
4134                                         break;\r
4135 \r
4136                                 ps = ps.parentNode;\r
4137                         }\r
4138 \r
4139                         if (!ps && a.ownerDocument)\r
4140                                 return a.ownerDocument.documentElement;\r
4141 \r
4142                         return ps;\r
4143                 },\r
4144 \r
4145                 toHex : function(s) {\r
4146                         var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);\r
4147 \r
4148                         function hex(s) {\r
4149                                 s = parseInt(s).toString(16);\r
4150 \r
4151                                 return s.length > 1 ? s : '0' + s; // 0 -> 00\r
4152                         };\r
4153 \r
4154                         if (c) {\r
4155                                 s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);\r
4156 \r
4157                                 return s;\r
4158                         }\r
4159 \r
4160                         return s;\r
4161                 },\r
4162 \r
4163                 getClasses : function() {\r
4164                         var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;\r
4165 \r
4166                         if (t.classes)\r
4167                                 return t.classes;\r
4168 \r
4169                         function addClasses(s) {\r
4170                                 // IE style imports\r
4171                                 each(s.imports, function(r) {\r
4172                                         addClasses(r);\r
4173                                 });\r
4174 \r
4175                                 each(s.cssRules || s.rules, function(r) {\r
4176                                         // Real type or fake it on IE\r
4177                                         switch (r.type || 1) {\r
4178                                                 // Rule\r
4179                                                 case 1:\r
4180                                                         if (r.selectorText) {\r
4181                                                                 each(r.selectorText.split(','), function(v) {\r
4182                                                                         v = v.replace(/^\s*|\s*$|^\s\./g, "");\r
4183 \r
4184                                                                         // Is internal or it doesn't contain a class\r
4185                                                                         if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v))\r
4186                                                                                 return;\r
4187 \r
4188                                                                         // Remove everything but class name\r
4189                                                                         ov = v;\r
4190                                                                         v = tinymce._replace(/.*\.([a-z0-9_\-]+).*/i, '$1', v);\r
4191 \r
4192                                                                         // Filter classes\r
4193                                                                         if (f && !(v = f(v, ov)))\r
4194                                                                                 return;\r
4195 \r
4196                                                                         if (!lo[v]) {\r
4197                                                                                 cl.push({'class' : v});\r
4198                                                                                 lo[v] = 1;\r
4199                                                                         }\r
4200                                                                 });\r
4201                                                         }\r
4202                                                         break;\r
4203 \r
4204                                                 // Import\r
4205                                                 case 3:\r
4206                                                         addClasses(r.styleSheet);\r
4207                                                         break;\r
4208                                         }\r
4209                                 });\r
4210                         };\r
4211 \r
4212                         try {\r
4213                                 each(t.doc.styleSheets, addClasses);\r
4214                         } catch (ex) {\r
4215                                 // Ignore\r
4216                         }\r
4217 \r
4218                         if (cl.length > 0)\r
4219                                 t.classes = cl;\r
4220 \r
4221                         return cl;\r
4222                 },\r
4223 \r
4224                 run : function(e, f, s) {\r
4225                         var t = this, o;\r
4226 \r
4227                         if (t.doc && typeof(e) === 'string')\r
4228                                 e = t.get(e);\r
4229 \r
4230                         if (!e)\r
4231                                 return false;\r
4232 \r
4233                         s = s || this;\r
4234                         if (!e.nodeType && (e.length || e.length === 0)) {\r
4235                                 o = [];\r
4236 \r
4237                                 each(e, function(e, i) {\r
4238                                         if (e) {\r
4239                                                 if (typeof(e) == 'string')\r
4240                                                         e = t.doc.getElementById(e);\r
4241 \r
4242                                                 o.push(f.call(s, e, i));\r
4243                                         }\r
4244                                 });\r
4245 \r
4246                                 return o;\r
4247                         }\r
4248 \r
4249                         return f.call(s, e);\r
4250                 },\r
4251 \r
4252                 getAttribs : function(n) {\r
4253                         var o;\r
4254 \r
4255                         n = this.get(n);\r
4256 \r
4257                         if (!n)\r
4258                                 return [];\r
4259 \r
4260                         if (isIE) {\r
4261                                 o = [];\r
4262 \r
4263                                 // Object will throw exception in IE\r
4264                                 if (n.nodeName == 'OBJECT')\r
4265                                         return n.attributes;\r
4266 \r
4267                                 // IE doesn't keep the selected attribute if you clone option elements\r
4268                                 if (n.nodeName === 'OPTION' && this.getAttrib(n, 'selected'))\r
4269                                         o.push({specified : 1, nodeName : 'selected'});\r
4270 \r
4271                                 // It's crazy that this is faster in IE but it's because it returns all attributes all the time\r
4272                                 n.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi, '').replace(/[\w:\-]+/gi, function(a) {\r
4273                                         o.push({specified : 1, nodeName : a});\r
4274                                 });\r
4275 \r
4276                                 return o;\r
4277                         }\r
4278 \r
4279                         return n.attributes;\r
4280                 },\r
4281 \r
4282                 isEmpty : function(node, elements) {\r
4283                         var self = this, i, attributes, type, walker, name;\r
4284 \r
4285                         node = node.firstChild;\r
4286                         if (node) {\r
4287                                 walker = new tinymce.dom.TreeWalker(node);\r
4288                                 elements = elements || self.schema ? self.schema.getNonEmptyElements() : null;\r
4289 \r
4290                                 do {\r
4291                                         type = node.nodeType;\r
4292 \r
4293                                         if (type === 1) {\r
4294                                                 // Ignore bogus elements\r
4295                                                 if (node.getAttribute('data-mce-bogus'))\r
4296                                                         continue;\r
4297 \r
4298                                                 // Keep empty elements like <img />\r
4299                                                 if (elements && elements[node.nodeName.toLowerCase()])\r
4300                                                         return false;\r
4301 \r
4302                                                 // Keep elements with data attributes or name attribute like <a name="1"></a>\r
4303                                                 attributes = self.getAttribs(node);\r
4304                                                 i = node.attributes.length;\r
4305                                                 while (i--) {\r
4306                                                         name = node.attributes[i].nodeName;\r
4307                                                         if (name === "name" || name.indexOf('data-') === 0)\r
4308                                                                 return false;\r
4309                                                 }\r
4310                                         }\r
4311 \r
4312                                         // Keep non whitespace text nodes\r
4313                                         if ((type === 3 && !whiteSpaceRegExp.test(node.nodeValue)))\r
4314                                                 return false;\r
4315                                 } while (node = walker.next());\r
4316                         }\r
4317 \r
4318                         return true;\r
4319                 },\r
4320 \r
4321                 destroy : function(s) {\r
4322                         var t = this;\r
4323 \r
4324                         if (t.events)\r
4325                                 t.events.destroy();\r
4326 \r
4327                         t.win = t.doc = t.root = t.events = null;\r
4328 \r
4329                         // Manual destroy then remove unload handler\r
4330                         if (!s)\r
4331                                 tinymce.removeUnload(t.destroy);\r
4332                 },\r
4333 \r
4334                 createRng : function() {\r
4335                         var d = this.doc;\r
4336 \r
4337                         return d.createRange ? d.createRange() : new tinymce.dom.Range(this);\r
4338                 },\r
4339 \r
4340                 nodeIndex : function(node, normalized) {\r
4341                         var idx = 0, lastNodeType, lastNode, nodeType, nodeValueExists;\r
4342 \r
4343                         if (node) {\r
4344                                 for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {\r
4345                                         nodeType = node.nodeType;\r
4346 \r
4347                                         // Normalize text nodes\r
4348                                         if (normalized && nodeType == 3) {\r
4349                                                 // ensure that text nodes that have been removed are handled correctly in Internet Explorer.\r
4350                                                 // (the nodeValue attribute will not exist, and will error here).\r
4351                                                 nodeValueExists = false;\r
4352                                                 try {nodeValueExists = node.nodeValue.length} catch (c) {}\r
4353                                                 if (nodeType == lastNodeType || !nodeValueExists)\r
4354                                                         continue;\r
4355                                         }\r
4356                                         idx++;\r
4357                                         lastNodeType = nodeType;\r
4358                                 }\r
4359                         }\r
4360 \r
4361                         return idx;\r
4362                 },\r
4363 \r
4364                 split : function(pe, e, re) {\r
4365                         var t = this, r = t.createRng(), bef, aft, pa;\r
4366 \r
4367                         // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sense\r
4368                         // but we don't want that in our code since it serves no purpose for the end user\r
4369                         // For example if this is chopped:\r
4370                         //   <p>text 1<span><b>CHOP</b></span>text 2</p>\r
4371                         // would produce:\r
4372                         //   <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>\r
4373                         // this function will then trim of empty edges and produce:\r
4374                         //   <p>text 1</p><b>CHOP</b><p>text 2</p>\r
4375                         function trim(node) {\r
4376                                 var i, children = node.childNodes, type = node.nodeType;\r
4377 \r
4378                                 if (type == 1 && node.getAttribute('data-mce-type') == 'bookmark')\r
4379                                         return;\r
4380 \r
4381                                 for (i = children.length - 1; i >= 0; i--)\r
4382                                         trim(children[i]);\r
4383 \r
4384                                 if (type != 9) {\r
4385                                         // Keep non whitespace text nodes\r
4386                                         if (type == 3 && node.nodeValue.length > 0) {\r
4387                                                 // If parent element isn't a block or there isn't any useful contents for example "<p>   </p>"\r
4388                                                 if (!t.isBlock(node.parentNode) || tinymce.trim(node.nodeValue).length > 0)\r
4389                                                         return;\r
4390                                         } else if (type == 1) {\r
4391                                                 // If the only child is a bookmark then move it up\r
4392                                                 children = node.childNodes;\r
4393                                                 if (children.length == 1 && children[0] && children[0].nodeType == 1 && children[0].getAttribute('data-mce-type') == 'bookmark')\r
4394                                                         node.parentNode.insertBefore(children[0], node);\r
4395 \r
4396                                                 // Keep non empty elements or img, hr etc\r
4397                                                 if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName))\r
4398                                                         return;\r
4399                                         }\r
4400 \r
4401                                         t.remove(node);\r
4402                                 }\r
4403 \r
4404                                 return node;\r
4405                         };\r
4406 \r
4407                         if (pe && e) {\r
4408                                 // Get before chunk\r
4409                                 r.setStart(pe.parentNode, t.nodeIndex(pe));\r
4410                                 r.setEnd(e.parentNode, t.nodeIndex(e));\r
4411                                 bef = r.extractContents();\r
4412 \r
4413                                 // Get after chunk\r
4414                                 r = t.createRng();\r
4415                                 r.setStart(e.parentNode, t.nodeIndex(e) + 1);\r
4416                                 r.setEnd(pe.parentNode, t.nodeIndex(pe) + 1);\r
4417                                 aft = r.extractContents();\r
4418 \r
4419                                 // Insert before chunk\r
4420                                 pa = pe.parentNode;\r
4421                                 pa.insertBefore(trim(bef), pe);\r
4422 \r
4423                                 // Insert middle chunk\r
4424                                 if (re)\r
4425                                         pa.replaceChild(re, e);\r
4426                                 else\r
4427                                         pa.insertBefore(e, pe);\r
4428 \r
4429                                 // Insert after chunk\r
4430                                 pa.insertBefore(trim(aft), pe);\r
4431                                 t.remove(pe);\r
4432 \r
4433                                 return re || e;\r
4434                         }\r
4435                 },\r
4436 \r
4437                 bind : function(target, name, func, scope) {\r
4438                         var t = this;\r
4439 \r
4440                         if (!t.events)\r
4441                                 t.events = new tinymce.dom.EventUtils();\r
4442 \r
4443                         return t.events.add(target, name, func, scope || this);\r
4444                 },\r
4445 \r
4446                 unbind : function(target, name, func) {\r
4447                         var t = this;\r
4448 \r
4449                         if (!t.events)\r
4450                                 t.events = new tinymce.dom.EventUtils();\r
4451 \r
4452                         return t.events.remove(target, name, func);\r
4453                 },\r
4454 \r
4455 \r
4456                 _findSib : function(node, selector, name) {\r
4457                         var t = this, f = selector;\r
4458 \r
4459                         if (node) {\r
4460                                 // If expression make a function of it using is\r
4461                                 if (is(f, 'string')) {\r
4462                                         f = function(node) {\r
4463                                                 return t.is(node, selector);\r
4464                                         };\r
4465                                 }\r
4466 \r
4467                                 // Loop all siblings\r
4468                                 for (node = node[name]; node; node = node[name]) {\r
4469                                         if (f(node))\r
4470                                                 return node;\r
4471                                 }\r
4472                         }\r
4473 \r
4474                         return null;\r
4475                 },\r
4476 \r
4477                 _isRes : function(c) {\r
4478                         // Is live resizble element\r
4479                         return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c);\r
4480                 }\r
4481 \r
4482                 /*\r
4483                 walk : function(n, f, s) {\r
4484                         var d = this.doc, w;\r
4485 \r
4486                         if (d.createTreeWalker) {\r
4487                                 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);\r
4488 \r
4489                                 while ((n = w.nextNode()) != null)\r
4490                                         f.call(s || this, n);\r
4491                         } else\r
4492                                 tinymce.walk(n, f, 'childNodes', s);\r
4493                 }\r
4494                 */\r
4495 \r
4496                 /*\r
4497                 toRGB : function(s) {\r
4498                         var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);\r
4499 \r
4500                         if (c) {\r
4501                                 // #FFF -> #FFFFFF\r
4502                                 if (!is(c[3]))\r
4503                                         c[3] = c[2] = c[1];\r
4504 \r
4505                                 return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";\r
4506                         }\r
4507 \r
4508                         return s;\r
4509                 }\r
4510                 */\r
4511         });\r
4512 \r
4513         tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});\r
4514 })(tinymce);\r
4515 \r
4516 (function(ns) {\r
4517         // Range constructor\r
4518         function Range(dom) {\r
4519                 var t = this,\r
4520                         doc = dom.doc,\r
4521                         EXTRACT = 0,\r
4522                         CLONE = 1,\r
4523                         DELETE = 2,\r
4524                         TRUE = true,\r
4525                         FALSE = false,\r
4526                         START_OFFSET = 'startOffset',\r
4527                         START_CONTAINER = 'startContainer',\r
4528                         END_CONTAINER = 'endContainer',\r
4529                         END_OFFSET = 'endOffset',\r
4530                         extend = tinymce.extend,\r
4531                         nodeIndex = dom.nodeIndex;\r
4532 \r
4533                 extend(t, {\r
4534                         // Inital states\r
4535                         startContainer : doc,\r
4536                         startOffset : 0,\r
4537                         endContainer : doc,\r
4538                         endOffset : 0,\r
4539                         collapsed : TRUE,\r
4540                         commonAncestorContainer : doc,\r
4541 \r
4542                         // Range constants\r
4543                         START_TO_START : 0,\r
4544                         START_TO_END : 1,\r
4545                         END_TO_END : 2,\r
4546                         END_TO_START : 3,\r
4547 \r
4548                         // Public methods\r
4549                         setStart : setStart,\r
4550                         setEnd : setEnd,\r
4551                         setStartBefore : setStartBefore,\r
4552                         setStartAfter : setStartAfter,\r
4553                         setEndBefore : setEndBefore,\r
4554                         setEndAfter : setEndAfter,\r
4555                         collapse : collapse,\r
4556                         selectNode : selectNode,\r
4557                         selectNodeContents : selectNodeContents,\r
4558                         compareBoundaryPoints : compareBoundaryPoints,\r
4559                         deleteContents : deleteContents,\r
4560                         extractContents : extractContents,\r
4561                         cloneContents : cloneContents,\r
4562                         insertNode : insertNode,\r
4563                         surroundContents : surroundContents,\r
4564                         cloneRange : cloneRange\r
4565                 });\r
4566 \r
4567                 function setStart(n, o) {\r
4568                         _setEndPoint(TRUE, n, o);\r
4569                 };\r
4570 \r
4571                 function setEnd(n, o) {\r
4572                         _setEndPoint(FALSE, n, o);\r
4573                 };\r
4574 \r
4575                 function setStartBefore(n) {\r
4576                         setStart(n.parentNode, nodeIndex(n));\r
4577                 };\r
4578 \r
4579                 function setStartAfter(n) {\r
4580                         setStart(n.parentNode, nodeIndex(n) + 1);\r
4581                 };\r
4582 \r
4583                 function setEndBefore(n) {\r
4584                         setEnd(n.parentNode, nodeIndex(n));\r
4585                 };\r
4586 \r
4587                 function setEndAfter(n) {\r
4588                         setEnd(n.parentNode, nodeIndex(n) + 1);\r
4589                 };\r
4590 \r
4591                 function collapse(ts) {\r
4592                         if (ts) {\r
4593                                 t[END_CONTAINER] = t[START_CONTAINER];\r
4594                                 t[END_OFFSET] = t[START_OFFSET];\r
4595                         } else {\r
4596                                 t[START_CONTAINER] = t[END_CONTAINER];\r
4597                                 t[START_OFFSET] = t[END_OFFSET];\r
4598                         }\r
4599 \r
4600                         t.collapsed = TRUE;\r
4601                 };\r
4602 \r
4603                 function selectNode(n) {\r
4604                         setStartBefore(n);\r
4605                         setEndAfter(n);\r
4606                 };\r
4607 \r
4608                 function selectNodeContents(n) {\r
4609                         setStart(n, 0);\r
4610                         setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);\r
4611                 };\r
4612 \r
4613                 function compareBoundaryPoints(h, r) {\r
4614                         var sc = t[START_CONTAINER], so = t[START_OFFSET], ec = t[END_CONTAINER], eo = t[END_OFFSET],\r
4615                         rsc = r.startContainer, rso = r.startOffset, rec = r.endContainer, reo = r.endOffset;\r
4616 \r
4617                         // Check START_TO_START\r
4618                         if (h === 0)\r
4619                                 return _compareBoundaryPoints(sc, so, rsc, rso);\r
4620         \r
4621                         // Check START_TO_END\r
4622                         if (h === 1)\r
4623                                 return _compareBoundaryPoints(ec, eo, rsc, rso);\r
4624         \r
4625                         // Check END_TO_END\r
4626                         if (h === 2)\r
4627                                 return _compareBoundaryPoints(ec, eo, rec, reo);\r
4628         \r
4629                         // Check END_TO_START\r
4630                         if (h === 3) \r
4631                                 return _compareBoundaryPoints(sc, so, rec, reo);\r
4632                 };\r
4633 \r
4634                 function deleteContents() {\r
4635                         _traverse(DELETE);\r
4636                 };\r
4637 \r
4638                 function extractContents() {\r
4639                         return _traverse(EXTRACT);\r
4640                 };\r
4641 \r
4642                 function cloneContents() {\r
4643                         return _traverse(CLONE);\r
4644                 };\r
4645 \r
4646                 function insertNode(n) {\r
4647                         var startContainer = this[START_CONTAINER],\r
4648                                 startOffset = this[START_OFFSET], nn, o;\r
4649 \r
4650                         // Node is TEXT_NODE or CDATA\r
4651                         if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {\r
4652                                 if (!startOffset) {\r
4653                                         // At the start of text\r
4654                                         startContainer.parentNode.insertBefore(n, startContainer);\r
4655                                 } else if (startOffset >= startContainer.nodeValue.length) {\r
4656                                         // At the end of text\r
4657                                         dom.insertAfter(n, startContainer);\r
4658                                 } else {\r
4659                                         // Middle, need to split\r
4660                                         nn = startContainer.splitText(startOffset);\r
4661                                         startContainer.parentNode.insertBefore(n, nn);\r
4662                                 }\r
4663                         } else {\r
4664                                 // Insert element node\r
4665                                 if (startContainer.childNodes.length > 0)\r
4666                                         o = startContainer.childNodes[startOffset];\r
4667 \r
4668                                 if (o)\r
4669                                         startContainer.insertBefore(n, o);\r
4670                                 else\r
4671                                         startContainer.appendChild(n);\r
4672                         }\r
4673                 };\r
4674 \r
4675                 function surroundContents(n) {\r
4676                         var f = t.extractContents();\r
4677 \r
4678                         t.insertNode(n);\r
4679                         n.appendChild(f);\r
4680                         t.selectNode(n);\r
4681                 };\r
4682 \r
4683                 function cloneRange() {\r
4684                         return extend(new Range(dom), {\r
4685                                 startContainer : t[START_CONTAINER],\r
4686                                 startOffset : t[START_OFFSET],\r
4687                                 endContainer : t[END_CONTAINER],\r
4688                                 endOffset : t[END_OFFSET],\r
4689                                 collapsed : t.collapsed,\r
4690                                 commonAncestorContainer : t.commonAncestorContainer\r
4691                         });\r
4692                 };\r
4693 \r
4694                 // Private methods\r
4695 \r
4696                 function _getSelectedNode(container, offset) {\r
4697                         var child;\r
4698 \r
4699                         if (container.nodeType == 3 /* TEXT_NODE */)\r
4700                                 return container;\r
4701 \r
4702                         if (offset < 0)\r
4703                                 return container;\r
4704 \r
4705                         child = container.firstChild;\r
4706                         while (child && offset > 0) {\r
4707                                 --offset;\r
4708                                 child = child.nextSibling;\r
4709                         }\r
4710 \r
4711                         if (child)\r
4712                                 return child;\r
4713 \r
4714                         return container;\r
4715                 };\r
4716 \r
4717                 function _isCollapsed() {\r
4718                         return (t[START_CONTAINER] == t[END_CONTAINER] && t[START_OFFSET] == t[END_OFFSET]);\r
4719                 };\r
4720 \r
4721                 function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {\r
4722                         var c, offsetC, n, cmnRoot, childA, childB;\r
4723                         \r
4724                         // In the first case the boundary-points have the same container. A is before B\r
4725                         // if its offset is less than the offset of B, A is equal to B if its offset is\r
4726                         // equal to the offset of B, and A is after B if its offset is greater than the\r
4727                         // offset of B.\r
4728                         if (containerA == containerB) {\r
4729                                 if (offsetA == offsetB)\r
4730                                         return 0; // equal\r
4731 \r
4732                                 if (offsetA < offsetB)\r
4733                                         return -1; // before\r
4734 \r
4735                                 return 1; // after\r
4736                         }\r
4737 \r
4738                         // In the second case a child node C of the container of A is an ancestor\r
4739                         // container of B. In this case, A is before B if the offset of A is less than or\r
4740                         // equal to the index of the child node C and A is after B otherwise.\r
4741                         c = containerB;\r
4742                         while (c && c.parentNode != containerA)\r
4743                                 c = c.parentNode;\r
4744 \r
4745                         if (c) {\r
4746                                 offsetC = 0;\r
4747                                 n = containerA.firstChild;\r
4748 \r
4749                                 while (n != c && offsetC < offsetA) {\r
4750                                         offsetC++;\r
4751                                         n = n.nextSibling;\r
4752                                 }\r
4753 \r
4754                                 if (offsetA <= offsetC)\r
4755                                         return -1; // before\r
4756 \r
4757                                 return 1; // after\r
4758                         }\r
4759 \r
4760                         // In the third case a child node C of the container of B is an ancestor container\r
4761                         // of A. In this case, A is before B if the index of the child node C is less than\r
4762                         // the offset of B and A is after B otherwise.\r
4763                         c = containerA;\r
4764                         while (c && c.parentNode != containerB) {\r
4765                                 c = c.parentNode;\r
4766                         }\r
4767 \r
4768                         if (c) {\r
4769                                 offsetC = 0;\r
4770                                 n = containerB.firstChild;\r
4771 \r
4772                                 while (n != c && offsetC < offsetB) {\r
4773                                         offsetC++;\r
4774                                         n = n.nextSibling;\r
4775                                 }\r
4776 \r
4777                                 if (offsetC < offsetB)\r
4778                                         return -1; // before\r
4779 \r
4780                                 return 1; // after\r
4781                         }\r
4782 \r
4783                         // In the fourth case, none of three other cases hold: the containers of A and B\r
4784                         // are siblings or descendants of sibling nodes. In this case, A is before B if\r
4785                         // the container of A is before the container of B in a pre-order traversal of the\r
4786                         // Ranges' context tree and A is after B otherwise.\r
4787                         cmnRoot = dom.findCommonAncestor(containerA, containerB);\r
4788                         childA = containerA;\r
4789 \r
4790                         while (childA && childA.parentNode != cmnRoot)\r
4791                                 childA = childA.parentNode;\r
4792 \r
4793                         if (!childA)\r
4794                                 childA = cmnRoot;\r
4795 \r
4796                         childB = containerB;\r
4797                         while (childB && childB.parentNode != cmnRoot)\r
4798                                 childB = childB.parentNode;\r
4799 \r
4800                         if (!childB)\r
4801                                 childB = cmnRoot;\r
4802 \r
4803                         if (childA == childB)\r
4804                                 return 0; // equal\r
4805 \r
4806                         n = cmnRoot.firstChild;\r
4807                         while (n) {\r
4808                                 if (n == childA)\r
4809                                         return -1; // before\r
4810 \r
4811                                 if (n == childB)\r
4812                                         return 1; // after\r
4813 \r
4814                                 n = n.nextSibling;\r
4815                         }\r
4816                 };\r
4817 \r
4818                 function _setEndPoint(st, n, o) {\r
4819                         var ec, sc;\r
4820 \r
4821                         if (st) {\r
4822                                 t[START_CONTAINER] = n;\r
4823                                 t[START_OFFSET] = o;\r
4824                         } else {\r
4825                                 t[END_CONTAINER] = n;\r
4826                                 t[END_OFFSET] = o;\r
4827                         }\r
4828 \r
4829                         // If one boundary-point of a Range is set to have a root container\r
4830                         // other than the current one for the Range, the Range is collapsed to\r
4831                         // the new position. This enforces the restriction that both boundary-\r
4832                         // points of a Range must have the same root container.\r
4833                         ec = t[END_CONTAINER];\r
4834                         while (ec.parentNode)\r
4835                                 ec = ec.parentNode;\r
4836 \r
4837                         sc = t[START_CONTAINER];\r
4838                         while (sc.parentNode)\r
4839                                 sc = sc.parentNode;\r
4840 \r
4841                         if (sc == ec) {\r
4842                                 // The start position of a Range is guaranteed to never be after the\r
4843                                 // end position. To enforce this restriction, if the start is set to\r
4844                                 // be at a position after the end, the Range is collapsed to that\r
4845                                 // position.\r
4846                                 if (_compareBoundaryPoints(t[START_CONTAINER], t[START_OFFSET], t[END_CONTAINER], t[END_OFFSET]) > 0)\r
4847                                         t.collapse(st);\r
4848                         } else\r
4849                                 t.collapse(st);\r
4850 \r
4851                         t.collapsed = _isCollapsed();\r
4852                         t.commonAncestorContainer = dom.findCommonAncestor(t[START_CONTAINER], t[END_CONTAINER]);\r
4853                 };\r
4854 \r
4855                 function _traverse(how) {\r
4856                         var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;\r
4857 \r
4858                         if (t[START_CONTAINER] == t[END_CONTAINER])\r
4859                                 return _traverseSameContainer(how);\r
4860 \r
4861                         for (c = t[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
4862                                 if (p == t[START_CONTAINER])\r
4863                                         return _traverseCommonStartContainer(c, how);\r
4864 \r
4865                                 ++endContainerDepth;\r
4866                         }\r
4867 \r
4868                         for (c = t[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
4869                                 if (p == t[END_CONTAINER])\r
4870                                         return _traverseCommonEndContainer(c, how);\r
4871 \r
4872                                 ++startContainerDepth;\r
4873                         }\r
4874 \r
4875                         depthDiff = startContainerDepth - endContainerDepth;\r
4876 \r
4877                         startNode = t[START_CONTAINER];\r
4878                         while (depthDiff > 0) {\r
4879                                 startNode = startNode.parentNode;\r
4880                                 depthDiff--;\r
4881                         }\r
4882 \r
4883                         endNode = t[END_CONTAINER];\r
4884                         while (depthDiff < 0) {\r
4885                                 endNode = endNode.parentNode;\r
4886                                 depthDiff++;\r
4887                         }\r
4888 \r
4889                         // ascend the ancestor hierarchy until we have a common parent.\r
4890                         for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {\r
4891                                 startNode = sp;\r
4892                                 endNode = ep;\r
4893                         }\r
4894 \r
4895                         return _traverseCommonAncestors(startNode, endNode, how);\r
4896                 };\r
4897 \r
4898                  function _traverseSameContainer(how) {\r
4899                         var frag, s, sub, n, cnt, sibling, xferNode;\r
4900 \r
4901                         if (how != DELETE)\r
4902                                 frag = doc.createDocumentFragment();\r
4903 \r
4904                         // If selection is empty, just return the fragment\r
4905                         if (t[START_OFFSET] == t[END_OFFSET])\r
4906                                 return frag;\r
4907 \r
4908                         // Text node needs special case handling\r
4909                         if (t[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {\r
4910                                 // get the substring\r
4911                                 s = t[START_CONTAINER].nodeValue;\r
4912                                 sub = s.substring(t[START_OFFSET], t[END_OFFSET]);\r
4913 \r
4914                                 // set the original text node to its new value\r
4915                                 if (how != CLONE) {\r
4916                                         t[START_CONTAINER].deleteData(t[START_OFFSET], t[END_OFFSET] - t[START_OFFSET]);\r
4917 \r
4918                                         // Nothing is partially selected, so collapse to start point\r
4919                                         t.collapse(TRUE);\r
4920                                 }\r
4921 \r
4922                                 if (how == DELETE)\r
4923                                         return;\r
4924 \r
4925                                 frag.appendChild(doc.createTextNode(sub));\r
4926                                 return frag;\r
4927                         }\r
4928 \r
4929                         // Copy nodes between the start/end offsets.\r
4930                         n = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]);\r
4931                         cnt = t[END_OFFSET] - t[START_OFFSET];\r
4932 \r
4933                         while (cnt > 0) {\r
4934                                 sibling = n.nextSibling;\r
4935                                 xferNode = _traverseFullySelected(n, how);\r
4936 \r
4937                                 if (frag)\r
4938                                         frag.appendChild( xferNode );\r
4939 \r
4940                                 --cnt;\r
4941                                 n = sibling;\r
4942                         }\r
4943 \r
4944                         // Nothing is partially selected, so collapse to start point\r
4945                         if (how != CLONE)\r
4946                                 t.collapse(TRUE);\r
4947 \r
4948                         return frag;\r
4949                 };\r
4950 \r
4951                 function _traverseCommonStartContainer(endAncestor, how) {\r
4952                         var frag, n, endIdx, cnt, sibling, xferNode;\r
4953 \r
4954                         if (how != DELETE)\r
4955                                 frag = doc.createDocumentFragment();\r
4956 \r
4957                         n = _traverseRightBoundary(endAncestor, how);\r
4958 \r
4959                         if (frag)\r
4960                                 frag.appendChild(n);\r
4961 \r
4962                         endIdx = nodeIndex(endAncestor);\r
4963                         cnt = endIdx - t[START_OFFSET];\r
4964 \r
4965                         if (cnt <= 0) {\r
4966                                 // Collapse to just before the endAncestor, which\r
4967                                 // is partially selected.\r
4968                                 if (how != CLONE) {\r
4969                                         t.setEndBefore(endAncestor);\r
4970                                         t.collapse(FALSE);\r
4971                                 }\r
4972 \r
4973                                 return frag;\r
4974                         }\r
4975 \r
4976                         n = endAncestor.previousSibling;\r
4977                         while (cnt > 0) {\r
4978                                 sibling = n.previousSibling;\r
4979                                 xferNode = _traverseFullySelected(n, how);\r
4980 \r
4981                                 if (frag)\r
4982                                         frag.insertBefore(xferNode, frag.firstChild);\r
4983 \r
4984                                 --cnt;\r
4985                                 n = sibling;\r
4986                         }\r
4987 \r
4988                         // Collapse to just before the endAncestor, which\r
4989                         // is partially selected.\r
4990                         if (how != CLONE) {\r
4991                                 t.setEndBefore(endAncestor);\r
4992                                 t.collapse(FALSE);\r
4993                         }\r
4994 \r
4995                         return frag;\r
4996                 };\r
4997 \r
4998                 function _traverseCommonEndContainer(startAncestor, how) {\r
4999                         var frag, startIdx, n, cnt, sibling, xferNode;\r
5000 \r
5001                         if (how != DELETE)\r
5002                                 frag = doc.createDocumentFragment();\r
5003 \r
5004                         n = _traverseLeftBoundary(startAncestor, how);\r
5005                         if (frag)\r
5006                                 frag.appendChild(n);\r
5007 \r
5008                         startIdx = nodeIndex(startAncestor);\r
5009                         ++startIdx; // Because we already traversed it\r
5010 \r
5011                         cnt = t[END_OFFSET] - startIdx;\r
5012                         n = startAncestor.nextSibling;\r
5013                         while (cnt > 0) {\r
5014                                 sibling = n.nextSibling;\r
5015                                 xferNode = _traverseFullySelected(n, how);\r
5016 \r
5017                                 if (frag)\r
5018                                         frag.appendChild(xferNode);\r
5019 \r
5020                                 --cnt;\r
5021                                 n = sibling;\r
5022                         }\r
5023 \r
5024                         if (how != CLONE) {\r
5025                                 t.setStartAfter(startAncestor);\r
5026                                 t.collapse(TRUE);\r
5027                         }\r
5028 \r
5029                         return frag;\r
5030                 };\r
5031 \r
5032                 function _traverseCommonAncestors(startAncestor, endAncestor, how) {\r
5033                         var n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;\r
5034 \r
5035                         if (how != DELETE)\r
5036                                 frag = doc.createDocumentFragment();\r
5037 \r
5038                         n = _traverseLeftBoundary(startAncestor, how);\r
5039                         if (frag)\r
5040                                 frag.appendChild(n);\r
5041 \r
5042                         commonParent = startAncestor.parentNode;\r
5043                         startOffset = nodeIndex(startAncestor);\r
5044                         endOffset = nodeIndex(endAncestor);\r
5045                         ++startOffset;\r
5046 \r
5047                         cnt = endOffset - startOffset;\r
5048                         sibling = startAncestor.nextSibling;\r
5049 \r
5050                         while (cnt > 0) {\r
5051                                 nextSibling = sibling.nextSibling;\r
5052                                 n = _traverseFullySelected(sibling, how);\r
5053 \r
5054                                 if (frag)\r
5055                                         frag.appendChild(n);\r
5056 \r
5057                                 sibling = nextSibling;\r
5058                                 --cnt;\r
5059                         }\r
5060 \r
5061                         n = _traverseRightBoundary(endAncestor, how);\r
5062 \r
5063                         if (frag)\r
5064                                 frag.appendChild(n);\r
5065 \r
5066                         if (how != CLONE) {\r
5067                                 t.setStartAfter(startAncestor);\r
5068                                 t.collapse(TRUE);\r
5069                         }\r
5070 \r
5071                         return frag;\r
5072                 };\r
5073 \r
5074                 function _traverseRightBoundary(root, how) {\r
5075                         var next = _getSelectedNode(t[END_CONTAINER], t[END_OFFSET] - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != t[END_CONTAINER];\r
5076 \r
5077                         if (next == root)\r
5078                                 return _traverseNode(next, isFullySelected, FALSE, how);\r
5079 \r
5080                         parent = next.parentNode;\r
5081                         clonedParent = _traverseNode(parent, FALSE, FALSE, how);\r
5082 \r
5083                         while (parent) {\r
5084                                 while (next) {\r
5085                                         prevSibling = next.previousSibling;\r
5086                                         clonedChild = _traverseNode(next, isFullySelected, FALSE, how);\r
5087 \r
5088                                         if (how != DELETE)\r
5089                                                 clonedParent.insertBefore(clonedChild, clonedParent.firstChild);\r
5090 \r
5091                                         isFullySelected = TRUE;\r
5092                                         next = prevSibling;\r
5093                                 }\r
5094 \r
5095                                 if (parent == root)\r
5096                                         return clonedParent;\r
5097 \r
5098                                 next = parent.previousSibling;\r
5099                                 parent = parent.parentNode;\r
5100 \r
5101                                 clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);\r
5102 \r
5103                                 if (how != DELETE)\r
5104                                         clonedGrandParent.appendChild(clonedParent);\r
5105 \r
5106                                 clonedParent = clonedGrandParent;\r
5107                         }\r
5108                 };\r
5109 \r
5110                 function _traverseLeftBoundary(root, how) {\r
5111                         var next = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]), isFullySelected = next != t[START_CONTAINER], parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;\r
5112 \r
5113                         if (next == root)\r
5114                                 return _traverseNode(next, isFullySelected, TRUE, how);\r
5115 \r
5116                         parent = next.parentNode;\r
5117                         clonedParent = _traverseNode(parent, FALSE, TRUE, how);\r
5118 \r
5119                         while (parent) {\r
5120                                 while (next) {\r
5121                                         nextSibling = next.nextSibling;\r
5122                                         clonedChild = _traverseNode(next, isFullySelected, TRUE, how);\r
5123 \r
5124                                         if (how != DELETE)\r
5125                                                 clonedParent.appendChild(clonedChild);\r
5126 \r
5127                                         isFullySelected = TRUE;\r
5128                                         next = nextSibling;\r
5129                                 }\r
5130 \r
5131                                 if (parent == root)\r
5132                                         return clonedParent;\r
5133 \r
5134                                 next = parent.nextSibling;\r
5135                                 parent = parent.parentNode;\r
5136 \r
5137                                 clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);\r
5138 \r
5139                                 if (how != DELETE)\r
5140                                         clonedGrandParent.appendChild(clonedParent);\r
5141 \r
5142                                 clonedParent = clonedGrandParent;\r
5143                         }\r
5144                 };\r
5145 \r
5146                 function _traverseNode(n, isFullySelected, isLeft, how) {\r
5147                         var txtValue, newNodeValue, oldNodeValue, offset, newNode;\r
5148 \r
5149                         if (isFullySelected)\r
5150                                 return _traverseFullySelected(n, how);\r
5151 \r
5152                         if (n.nodeType == 3 /* TEXT_NODE */) {\r
5153                                 txtValue = n.nodeValue;\r
5154 \r
5155                                 if (isLeft) {\r
5156                                         offset = t[START_OFFSET];\r
5157                                         newNodeValue = txtValue.substring(offset);\r
5158                                         oldNodeValue = txtValue.substring(0, offset);\r
5159                                 } else {\r
5160                                         offset = t[END_OFFSET];\r
5161                                         newNodeValue = txtValue.substring(0, offset);\r
5162                                         oldNodeValue = txtValue.substring(offset);\r
5163                                 }\r
5164 \r
5165                                 if (how != CLONE)\r
5166                                         n.nodeValue = oldNodeValue;\r
5167 \r
5168                                 if (how == DELETE)\r
5169                                         return;\r
5170 \r
5171                                 newNode = n.cloneNode(FALSE);\r
5172                                 newNode.nodeValue = newNodeValue;\r
5173 \r
5174                                 return newNode;\r
5175                         }\r
5176 \r
5177                         if (how == DELETE)\r
5178                                 return;\r
5179 \r
5180                         return n.cloneNode(FALSE);\r
5181                 };\r
5182 \r
5183                 function _traverseFullySelected(n, how) {\r
5184                         if (how != DELETE)\r
5185                                 return how == CLONE ? n.cloneNode(TRUE) : n;\r
5186 \r
5187                         n.parentNode.removeChild(n);\r
5188                 };\r
5189         };\r
5190 \r
5191         ns.Range = Range;\r
5192 })(tinymce.dom);\r
5193 \r
5194 (function() {\r
5195         function Selection(selection) {\r
5196                 var t = this, invisibleChar = '\uFEFF', range, lastIERng, dom = selection.dom, TRUE = true, FALSE = false;\r
5197 \r
5198                 // Returns a W3C DOM compatible range object by using the IE Range API\r
5199                 function getRange() {\r
5200                         var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed;\r
5201 \r
5202                         // If selection is outside the current document just return an empty range\r
5203                         element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();\r
5204                         if (element.ownerDocument != dom.doc)\r
5205                                 return domRange;\r
5206 \r
5207                         collapsed = selection.isCollapsed();\r
5208 \r
5209                         // Handle control selection or text selection of a image\r
5210                         if (ieRange.item || !element.hasChildNodes()) {\r
5211                                 if (collapsed) {\r
5212                                         domRange.setStart(element, 0);\r
5213                                         domRange.setEnd(element, 0);\r
5214                                 } else {\r
5215                                         domRange.setStart(element.parentNode, dom.nodeIndex(element));\r
5216                                         domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);\r
5217                                 }\r
5218 \r
5219                                 return domRange;\r
5220                         }\r
5221 \r
5222                         function findEndPoint(start) {\r
5223                                 var marker, container, offset, nodes, startIndex = 0, endIndex, index, parent, checkRng, position;\r
5224 \r
5225                                 // Setup temp range and collapse it\r
5226                                 checkRng = ieRange.duplicate();\r
5227                                 checkRng.collapse(start);\r
5228 \r
5229                                 // Create marker and insert it at the end of the endpoints parent\r
5230                                 marker = dom.create('a');\r
5231                                 parent = checkRng.parentElement();\r
5232 \r
5233                                 // If parent doesn't have any children then set the container to that parent and the index to 0\r
5234                                 if (!parent.hasChildNodes()) {\r
5235                                         domRange[start ? 'setStart' : 'setEnd'](parent, 0);\r
5236                                         return;\r
5237                                 }\r
5238 \r
5239                                 parent.appendChild(marker);\r
5240                                 checkRng.moveToElementText(marker);\r
5241                                 position = ieRange.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', checkRng);\r
5242                                 if (position > 0) {\r
5243                                         // The position is after the end of the parent element.\r
5244                                         // This is the case where IE puts the caret to the left edge of a table.\r
5245                                         domRange[start ? 'setStartAfter' : 'setEndAfter'](parent);\r
5246                                         dom.remove(marker);\r
5247                                         return;\r
5248                                 }\r
5249 \r
5250                                 // Setup node list and endIndex\r
5251                                 nodes = tinymce.grep(parent.childNodes);\r
5252                                 endIndex = nodes.length - 1;\r
5253                                 // Perform a binary search for the position\r
5254                                 while (startIndex <= endIndex) {\r
5255                                         index = Math.floor((startIndex + endIndex) / 2);\r
5256 \r
5257                                         // Insert marker and check it's position relative to the selection\r
5258                                         parent.insertBefore(marker, nodes[index]);\r
5259                                         checkRng.moveToElementText(marker);\r
5260                                         position = ieRange.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', checkRng);\r
5261                                         if (position > 0) {\r
5262                                                 // Marker is to the right\r
5263                                                 startIndex = index + 1;\r
5264                                         } else if (position < 0) {\r
5265                                                 // Marker is to the left\r
5266                                                 endIndex = index - 1;\r
5267                                         } else {\r
5268                                                 // Maker is where we are\r
5269                                                 found = true;\r
5270                                                 break;\r
5271                                         }\r
5272                                 }\r
5273 \r
5274                                 // Setup container\r
5275                                 container = position > 0 || index == 0 ? marker.nextSibling : marker.previousSibling;\r
5276 \r
5277                                 // Handle element selection\r
5278                                 if (container.nodeType == 1) {\r
5279                                         dom.remove(marker);\r
5280 \r
5281                                         // Find offset and container\r
5282                                         offset = dom.nodeIndex(container);\r
5283                                         container = container.parentNode;\r
5284 \r
5285                                         // Move the offset if we are setting the end or the position is after an element\r
5286                                         if (!start || index > 0)\r
5287                                                 offset++;\r
5288                                 } else {\r
5289                                         // Calculate offset within text node\r
5290                                         if (position > 0 || index == 0) {\r
5291                                                 checkRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', ieRange);\r
5292                                                 offset = checkRng.text.length;\r
5293                                         } else {\r
5294                                                 checkRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', ieRange);\r
5295                                                 offset = container.nodeValue.length - checkRng.text.length;\r
5296                                         }\r
5297 \r
5298                                         dom.remove(marker);\r
5299                                 }\r
5300 \r
5301                                 domRange[start ? 'setStart' : 'setEnd'](container, offset);\r
5302                         };\r
5303 \r
5304                         // Find start point\r
5305                         findEndPoint(true);\r
5306 \r
5307                         // Find end point if needed\r
5308                         if (!collapsed)\r
5309                                 findEndPoint();\r
5310 \r
5311                         return domRange;\r
5312                 };\r
5313 \r
5314                 this.addRange = function(rng) {\r
5315                         var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, doc = selection.dom.doc, body = doc.body;\r
5316 \r
5317                         function setEndPoint(start) {\r
5318                                 var container, offset, marker, tmpRng, nodes;\r
5319 \r
5320                                 marker = dom.create('a');\r
5321                                 container = start ? startContainer : endContainer;\r
5322                                 offset = start ? startOffset : endOffset;\r
5323                                 tmpRng = ieRng.duplicate();\r
5324 \r
5325                                 if (container == doc || container == doc.documentElement) {\r
5326                                         container = body;\r
5327                                         offset = 0;\r
5328                                 }\r
5329 \r
5330                                 if (container.nodeType == 3) {\r
5331                                         container.parentNode.insertBefore(marker, container);\r
5332                                         tmpRng.moveToElementText(marker);\r
5333                                         tmpRng.moveStart('character', offset);\r
5334                                         dom.remove(marker);\r
5335                                         ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);\r
5336                                 } else {\r
5337                                         nodes = container.childNodes;\r
5338 \r
5339                                         if (nodes.length) {\r
5340                                                 if (offset >= nodes.length) {\r
5341                                                         dom.insertAfter(marker, nodes[nodes.length - 1]);\r
5342                                                 } else {\r
5343                                                         container.insertBefore(marker, nodes[offset]);\r
5344                                                 }\r
5345 \r
5346                                                 tmpRng.moveToElementText(marker);\r
5347                                         } else {\r
5348                                                 // Empty node selection for example <div>|</div>\r
5349                                                 marker = doc.createTextNode(invisibleChar);\r
5350                                                 container.appendChild(marker);\r
5351                                                 tmpRng.moveToElementText(marker.parentNode);\r
5352                                                 tmpRng.collapse(TRUE);\r
5353                                         }\r
5354 \r
5355                                         ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);\r
5356                                         dom.remove(marker);\r
5357                                 }\r
5358                         }\r
5359 \r
5360                         // Destroy cached range\r
5361                         this.destroy();\r
5362 \r
5363                         // Setup some shorter versions\r
5364                         startContainer = rng.startContainer;\r
5365                         startOffset = rng.startOffset;\r
5366                         endContainer = rng.endContainer;\r
5367                         endOffset = rng.endOffset;\r
5368                         ieRng = body.createTextRange();\r
5369 \r
5370                         // If single element selection then try making a control selection out of it\r
5371                         if (startContainer == endContainer && startContainer.nodeType == 1 && startOffset == endOffset - 1) {\r
5372                                 if (startOffset == endOffset - 1) {\r
5373                                         try {\r
5374                                                 ctrlRng = body.createControlRange();\r
5375                                                 ctrlRng.addElement(startContainer.childNodes[startOffset]);\r
5376                                                 ctrlRng.select();\r
5377                                                 return;\r
5378                                         } catch (ex) {\r
5379                                                 // Ignore\r
5380                                         }\r
5381                                 }\r
5382                         }\r
5383 \r
5384                         // Set start/end point of selection\r
5385                         setEndPoint(true);\r
5386                         setEndPoint();\r
5387 \r
5388                         // Select the new range and scroll it into view\r
5389                         ieRng.select();\r
5390                 };\r
5391 \r
5392                 this.getRangeAt = function() {\r
5393                         // Setup new range if the cache is empty\r
5394                         if (!range || !tinymce.dom.RangeUtils.compareRanges(lastIERng, selection.getRng())) {\r
5395                                 range = getRange();\r
5396 \r
5397                                 // Store away text range for next call\r
5398                                 lastIERng = selection.getRng();\r
5399                         }\r
5400 \r
5401                         // IE will say that the range is equal then produce an invalid argument exception\r
5402                         // if you perform specific operations in a keyup event. For example Ctrl+Del.\r
5403                         // This hack will invalidate the range cache if the exception occurs\r
5404                         try {\r
5405                                 range.startContainer.nextSibling;\r
5406                         } catch (ex) {\r
5407                                 range = getRange();\r
5408                                 lastIERng = null;\r
5409                         }\r
5410 \r
5411                         // Return cached range\r
5412                         return range;\r
5413                 };\r
5414 \r
5415                 this.destroy = function() {\r
5416                         // Destroy cached range and last IE range to avoid memory leaks\r
5417                         lastIERng = range = null;\r
5418                 };\r
5419         };\r
5420 \r
5421         // Expose the selection object\r
5422         tinymce.dom.TridentSelection = Selection;\r
5423 })();\r
5424 \r
5425 \r
5426 /*\r
5427  * Sizzle CSS Selector Engine - v1.0\r
5428  *  Copyright 2009, The Dojo Foundation\r
5429  *  Released under the MIT, BSD, and GPL Licenses.\r
5430  *  More information: http://sizzlejs.com/\r
5431  */\r
5432 (function(){\r
5433 \r
5434 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,\r
5435         done = 0,\r
5436         toString = Object.prototype.toString,\r
5437         hasDuplicate = false,\r
5438         baseHasDuplicate = true;\r
5439 \r
5440 // Here we check if the JavaScript engine is using some sort of\r
5441 // optimization where it does not always call our comparision\r
5442 // function. If that is the case, discard the hasDuplicate value.\r
5443 //   Thus far that includes Google Chrome.\r
5444 [0, 0].sort(function(){\r
5445         baseHasDuplicate = false;\r
5446         return 0;\r
5447 });\r
5448 \r
5449 var Sizzle = function(selector, context, results, seed) {\r
5450         results = results || [];\r
5451         context = context || document;\r
5452 \r
5453         var origContext = context;\r
5454 \r
5455         if ( context.nodeType !== 1 && context.nodeType !== 9 ) {\r
5456                 return [];\r
5457         }\r
5458         \r
5459         if ( !selector || typeof selector !== "string" ) {\r
5460                 return results;\r
5461         }\r
5462 \r
5463         var parts = [], m, set, checkSet, extra, prune = true, contextXML = Sizzle.isXML(context),\r
5464                 soFar = selector, ret, cur, pop, i;\r
5465         \r
5466         // Reset the position of the chunker regexp (start from head)\r
5467         do {\r
5468                 chunker.exec("");\r
5469                 m = chunker.exec(soFar);\r
5470 \r
5471                 if ( m ) {\r
5472                         soFar = m[3];\r
5473                 \r
5474                         parts.push( m[1] );\r
5475                 \r
5476                         if ( m[2] ) {\r
5477                                 extra = m[3];\r
5478                                 break;\r
5479                         }\r
5480                 }\r
5481         } while ( m );\r
5482 \r
5483         if ( parts.length > 1 && origPOS.exec( selector ) ) {\r
5484                 if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {\r
5485                         set = posProcess( parts[0] + parts[1], context );\r
5486                 } else {\r
5487                         set = Expr.relative[ parts[0] ] ?\r
5488                                 [ context ] :\r
5489                                 Sizzle( parts.shift(), context );\r
5490 \r
5491                         while ( parts.length ) {\r
5492                                 selector = parts.shift();\r
5493 \r
5494                                 if ( Expr.relative[ selector ] ) {\r
5495                                         selector += parts.shift();\r
5496                                 }\r
5497                                 \r
5498                                 set = posProcess( selector, set );\r
5499                         }\r
5500                 }\r
5501         } else {\r
5502                 // Take a shortcut and set the context if the root selector is an ID\r
5503                 // (but not if it'll be faster if the inner selector is an ID)\r
5504                 if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&\r
5505                                 Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {\r
5506                         ret = Sizzle.find( parts.shift(), context, contextXML );\r
5507                         context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];\r
5508                 }\r
5509 \r
5510                 if ( context ) {\r
5511                         ret = seed ?\r
5512                                 { expr: parts.pop(), set: makeArray(seed) } :\r
5513                                 Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );\r
5514                         set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;\r
5515 \r
5516                         if ( parts.length > 0 ) {\r
5517                                 checkSet = makeArray(set);\r
5518                         } else {\r
5519                                 prune = false;\r
5520                         }\r
5521 \r
5522                         while ( parts.length ) {\r
5523                                 cur = parts.pop();\r
5524                                 pop = cur;\r
5525 \r
5526                                 if ( !Expr.relative[ cur ] ) {\r
5527                                         cur = "";\r
5528                                 } else {\r
5529                                         pop = parts.pop();\r
5530                                 }\r
5531 \r
5532                                 if ( pop == null ) {\r
5533                                         pop = context;\r
5534                                 }\r
5535 \r
5536                                 Expr.relative[ cur ]( checkSet, pop, contextXML );\r
5537                         }\r
5538                 } else {\r
5539                         checkSet = parts = [];\r
5540                 }\r
5541         }\r
5542 \r
5543         if ( !checkSet ) {\r
5544                 checkSet = set;\r
5545         }\r
5546 \r
5547         if ( !checkSet ) {\r
5548                 Sizzle.error( cur || selector );\r
5549         }\r
5550 \r
5551         if ( toString.call(checkSet) === "[object Array]" ) {\r
5552                 if ( !prune ) {\r
5553                         results.push.apply( results, checkSet );\r
5554                 } else if ( context && context.nodeType === 1 ) {\r
5555                         for ( i = 0; checkSet[i] != null; i++ ) {\r
5556                                 if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {\r
5557                                         results.push( set[i] );\r
5558                                 }\r
5559                         }\r
5560                 } else {\r
5561                         for ( i = 0; checkSet[i] != null; i++ ) {\r
5562                                 if ( checkSet[i] && checkSet[i].nodeType === 1 ) {\r
5563                                         results.push( set[i] );\r
5564                                 }\r
5565                         }\r
5566                 }\r
5567         } else {\r
5568                 makeArray( checkSet, results );\r
5569         }\r
5570 \r
5571         if ( extra ) {\r
5572                 Sizzle( extra, origContext, results, seed );\r
5573                 Sizzle.uniqueSort( results );\r
5574         }\r
5575 \r
5576         return results;\r
5577 };\r
5578 \r
5579 Sizzle.uniqueSort = function(results){\r
5580         if ( sortOrder ) {\r
5581                 hasDuplicate = baseHasDuplicate;\r
5582                 results.sort(sortOrder);\r
5583 \r
5584                 if ( hasDuplicate ) {\r
5585                         for ( var i = 1; i < results.length; i++ ) {\r
5586                                 if ( results[i] === results[i-1] ) {\r
5587                                         results.splice(i--, 1);\r
5588                                 }\r
5589                         }\r
5590                 }\r
5591         }\r
5592 \r
5593         return results;\r
5594 };\r
5595 \r
5596 Sizzle.matches = function(expr, set){\r
5597         return Sizzle(expr, null, null, set);\r
5598 };\r
5599 \r
5600 Sizzle.find = function(expr, context, isXML){\r
5601         var set;\r
5602 \r
5603         if ( !expr ) {\r
5604                 return [];\r
5605         }\r
5606 \r
5607         for ( var i = 0, l = Expr.order.length; i < l; i++ ) {\r
5608                 var type = Expr.order[i], match;\r
5609                 \r
5610                 if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {\r
5611                         var left = match[1];\r
5612                         match.splice(1,1);\r
5613 \r
5614                         if ( left.substr( left.length - 1 ) !== "\\" ) {\r
5615                                 match[1] = (match[1] || "").replace(/\\/g, "");\r
5616                                 set = Expr.find[ type ]( match, context, isXML );\r
5617                                 if ( set != null ) {\r
5618                                         expr = expr.replace( Expr.match[ type ], "" );\r
5619                                         break;\r
5620                                 }\r
5621                         }\r
5622                 }\r
5623         }\r
5624 \r
5625         if ( !set ) {\r
5626                 set = context.getElementsByTagName("*");\r
5627         }\r
5628 \r
5629         return {set: set, expr: expr};\r
5630 };\r
5631 \r
5632 Sizzle.filter = function(expr, set, inplace, not){\r
5633         var old = expr, result = [], curLoop = set, match, anyFound,\r
5634                 isXMLFilter = set && set[0] && Sizzle.isXML(set[0]);\r
5635 \r
5636         while ( expr && set.length ) {\r
5637                 for ( var type in Expr.filter ) {\r
5638                         if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {\r
5639                                 var filter = Expr.filter[ type ], found, item, left = match[1];\r
5640                                 anyFound = false;\r
5641 \r
5642                                 match.splice(1,1);\r
5643 \r
5644                                 if ( left.substr( left.length - 1 ) === "\\" ) {\r
5645                                         continue;\r
5646                                 }\r
5647 \r
5648                                 if ( curLoop === result ) {\r
5649                                         result = [];\r
5650                                 }\r
5651 \r
5652                                 if ( Expr.preFilter[ type ] ) {\r
5653                                         match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );\r
5654 \r
5655                                         if ( !match ) {\r
5656                                                 anyFound = found = true;\r
5657                                         } else if ( match === true ) {\r
5658                                                 continue;\r
5659                                         }\r
5660                                 }\r
5661 \r
5662                                 if ( match ) {\r
5663                                         for ( var i = 0; (item = curLoop[i]) != null; i++ ) {\r
5664                                                 if ( item ) {\r
5665                                                         found = filter( item, match, i, curLoop );\r
5666                                                         var pass = not ^ !!found;\r
5667 \r
5668                                                         if ( inplace && found != null ) {\r
5669                                                                 if ( pass ) {\r
5670                                                                         anyFound = true;\r
5671                                                                 } else {\r
5672                                                                         curLoop[i] = false;\r
5673                                                                 }\r
5674                                                         } else if ( pass ) {\r
5675                                                                 result.push( item );\r
5676                                                                 anyFound = true;\r
5677                                                         }\r
5678                                                 }\r
5679                                         }\r
5680                                 }\r
5681 \r
5682                                 if ( found !== undefined ) {\r
5683                                         if ( !inplace ) {\r
5684                                                 curLoop = result;\r
5685                                         }\r
5686 \r
5687                                         expr = expr.replace( Expr.match[ type ], "" );\r
5688 \r
5689                                         if ( !anyFound ) {\r
5690                                                 return [];\r
5691                                         }\r
5692 \r
5693                                         break;\r
5694                                 }\r
5695                         }\r
5696                 }\r
5697 \r
5698                 // Improper expression\r
5699                 if ( expr === old ) {\r
5700                         if ( anyFound == null ) {\r
5701                                 Sizzle.error( expr );\r
5702                         } else {\r
5703                                 break;\r
5704                         }\r
5705                 }\r
5706 \r
5707                 old = expr;\r
5708         }\r
5709 \r
5710         return curLoop;\r
5711 };\r
5712 \r
5713 Sizzle.error = function( msg ) {\r
5714         throw "Syntax error, unrecognized expression: " + msg;\r
5715 };\r
5716 \r
5717 var Expr = Sizzle.selectors = {\r
5718         order: [ "ID", "NAME", "TAG" ],\r
5719         match: {\r
5720                 ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,\r
5721                 CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,\r
5722                 NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,\r
5723                 ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,\r
5724                 TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,\r
5725                 CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,\r
5726                 POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,\r
5727                 PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/\r
5728         },\r
5729         leftMatch: {},\r
5730         attrMap: {\r
5731                 "class": "className",\r
5732                 "for": "htmlFor"\r
5733         },\r
5734         attrHandle: {\r
5735                 href: function(elem){\r
5736                         return elem.getAttribute("href");\r
5737                 }\r
5738         },\r
5739         relative: {\r
5740                 "+": function(checkSet, part){\r
5741                         var isPartStr = typeof part === "string",\r
5742                                 isTag = isPartStr && !/\W/.test(part),\r
5743                                 isPartStrNotTag = isPartStr && !isTag;\r
5744 \r
5745                         if ( isTag ) {\r
5746                                 part = part.toLowerCase();\r
5747                         }\r
5748 \r
5749                         for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {\r
5750                                 if ( (elem = checkSet[i]) ) {\r
5751                                         while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}\r
5752 \r
5753                                         checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?\r
5754                                                 elem || false :\r
5755                                                 elem === part;\r
5756                                 }\r
5757                         }\r
5758 \r
5759                         if ( isPartStrNotTag ) {\r
5760                                 Sizzle.filter( part, checkSet, true );\r
5761                         }\r
5762                 },\r
5763                 ">": function(checkSet, part){\r
5764                         var isPartStr = typeof part === "string",\r
5765                                 elem, i = 0, l = checkSet.length;\r
5766 \r
5767                         if ( isPartStr && !/\W/.test(part) ) {\r
5768                                 part = part.toLowerCase();\r
5769 \r
5770                                 for ( ; i < l; i++ ) {\r
5771                                         elem = checkSet[i];\r
5772                                         if ( elem ) {\r
5773                                                 var parent = elem.parentNode;\r
5774                                                 checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;\r
5775                                         }\r
5776                                 }\r
5777                         } else {\r
5778                                 for ( ; i < l; i++ ) {\r
5779                                         elem = checkSet[i];\r
5780                                         if ( elem ) {\r
5781                                                 checkSet[i] = isPartStr ?\r
5782                                                         elem.parentNode :\r
5783                                                         elem.parentNode === part;\r
5784                                         }\r
5785                                 }\r
5786 \r
5787                                 if ( isPartStr ) {\r
5788                                         Sizzle.filter( part, checkSet, true );\r
5789                                 }\r
5790                         }\r
5791                 },\r
5792                 "": function(checkSet, part, isXML){\r
5793                         var doneName = done++, checkFn = dirCheck, nodeCheck;\r
5794 \r
5795                         if ( typeof part === "string" && !/\W/.test(part) ) {\r
5796                                 part = part.toLowerCase();\r
5797                                 nodeCheck = part;\r
5798                                 checkFn = dirNodeCheck;\r
5799                         }\r
5800 \r
5801                         checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);\r
5802                 },\r
5803                 "~": function(checkSet, part, isXML){\r
5804                         var doneName = done++, checkFn = dirCheck, nodeCheck;\r
5805 \r
5806                         if ( typeof part === "string" && !/\W/.test(part) ) {\r
5807                                 part = part.toLowerCase();\r
5808                                 nodeCheck = part;\r
5809                                 checkFn = dirNodeCheck;\r
5810                         }\r
5811 \r
5812                         checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);\r
5813                 }\r
5814         },\r
5815         find: {\r
5816                 ID: function(match, context, isXML){\r
5817                         if ( typeof context.getElementById !== "undefined" && !isXML ) {\r
5818                                 var m = context.getElementById(match[1]);\r
5819                                 return m ? [m] : [];\r
5820                         }\r
5821                 },\r
5822                 NAME: function(match, context){\r
5823                         if ( typeof context.getElementsByName !== "undefined" ) {\r
5824                                 var ret = [], results = context.getElementsByName(match[1]);\r
5825 \r
5826                                 for ( var i = 0, l = results.length; i < l; i++ ) {\r
5827                                         if ( results[i].getAttribute("name") === match[1] ) {\r
5828                                                 ret.push( results[i] );\r
5829                                         }\r
5830                                 }\r
5831 \r
5832                                 return ret.length === 0 ? null : ret;\r
5833                         }\r
5834                 },\r
5835                 TAG: function(match, context){\r
5836                         return context.getElementsByTagName(match[1]);\r
5837                 }\r
5838         },\r
5839         preFilter: {\r
5840                 CLASS: function(match, curLoop, inplace, result, not, isXML){\r
5841                         match = " " + match[1].replace(/\\/g, "") + " ";\r
5842 \r
5843                         if ( isXML ) {\r
5844                                 return match;\r
5845                         }\r
5846 \r
5847                         for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {\r
5848                                 if ( elem ) {\r
5849                                         if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {\r
5850                                                 if ( !inplace ) {\r
5851                                                         result.push( elem );\r
5852                                                 }\r
5853                                         } else if ( inplace ) {\r
5854                                                 curLoop[i] = false;\r
5855                                         }\r
5856                                 }\r
5857                         }\r
5858 \r
5859                         return false;\r
5860                 },\r
5861                 ID: function(match){\r
5862                         return match[1].replace(/\\/g, "");\r
5863                 },\r
5864                 TAG: function(match, curLoop){\r
5865                         return match[1].toLowerCase();\r
5866                 },\r
5867                 CHILD: function(match){\r
5868                         if ( match[1] === "nth" ) {\r
5869                                 // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'\r
5870                                 var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(\r
5871                                         match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||\r
5872                                         !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);\r
5873 \r
5874                                 // calculate the numbers (first)n+(last) including if they are negative\r
5875                                 match[2] = (test[1] + (test[2] || 1)) - 0;\r
5876                                 match[3] = test[3] - 0;\r
5877                         }\r
5878 \r
5879                         // TODO: Move to normal caching system\r
5880                         match[0] = done++;\r
5881 \r
5882                         return match;\r
5883                 },\r
5884                 ATTR: function(match, curLoop, inplace, result, not, isXML){\r
5885                         var name = match[1].replace(/\\/g, "");\r
5886                         \r
5887                         if ( !isXML && Expr.attrMap[name] ) {\r
5888                                 match[1] = Expr.attrMap[name];\r
5889                         }\r
5890 \r
5891                         if ( match[2] === "~=" ) {\r
5892                                 match[4] = " " + match[4] + " ";\r
5893                         }\r
5894 \r
5895                         return match;\r
5896                 },\r
5897                 PSEUDO: function(match, curLoop, inplace, result, not){\r
5898                         if ( match[1] === "not" ) {\r
5899                                 // If we're dealing with a complex expression, or a simple one\r
5900                                 if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {\r
5901                                         match[3] = Sizzle(match[3], null, null, curLoop);\r
5902                                 } else {\r
5903                                         var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);\r
5904                                         if ( !inplace ) {\r
5905                                                 result.push.apply( result, ret );\r
5906                                         }\r
5907                                         return false;\r
5908                                 }\r
5909                         } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {\r
5910                                 return true;\r
5911                         }\r
5912                         \r
5913                         return match;\r
5914                 },\r
5915                 POS: function(match){\r
5916                         match.unshift( true );\r
5917                         return match;\r
5918                 }\r
5919         },\r
5920         filters: {\r
5921                 enabled: function(elem){\r
5922                         return elem.disabled === false && elem.type !== "hidden";\r
5923                 },\r
5924                 disabled: function(elem){\r
5925                         return elem.disabled === true;\r
5926                 },\r
5927                 checked: function(elem){\r
5928                         return elem.checked === true;\r
5929                 },\r
5930                 selected: function(elem){\r
5931                         // Accessing this property makes selected-by-default\r
5932                         // options in Safari work properly\r
5933                         elem.parentNode.selectedIndex;\r
5934                         return elem.selected === true;\r
5935                 },\r
5936                 parent: function(elem){\r
5937                         return !!elem.firstChild;\r
5938                 },\r
5939                 empty: function(elem){\r
5940                         return !elem.firstChild;\r
5941                 },\r
5942                 has: function(elem, i, match){\r
5943                         return !!Sizzle( match[3], elem ).length;\r
5944                 },\r
5945                 header: function(elem){\r
5946                         return (/h\d/i).test( elem.nodeName );\r
5947                 },\r
5948                 text: function(elem){\r
5949                         return "text" === elem.type;\r
5950                 },\r
5951                 radio: function(elem){\r
5952                         return "radio" === elem.type;\r
5953                 },\r
5954                 checkbox: function(elem){\r
5955                         return "checkbox" === elem.type;\r
5956                 },\r
5957                 file: function(elem){\r
5958                         return "file" === elem.type;\r
5959                 },\r
5960                 password: function(elem){\r
5961                         return "password" === elem.type;\r
5962                 },\r
5963                 submit: function(elem){\r
5964                         return "submit" === elem.type;\r
5965                 },\r
5966                 image: function(elem){\r
5967                         return "image" === elem.type;\r
5968                 },\r
5969                 reset: function(elem){\r
5970                         return "reset" === elem.type;\r
5971                 },\r
5972                 button: function(elem){\r
5973                         return "button" === elem.type || elem.nodeName.toLowerCase() === "button";\r
5974                 },\r
5975                 input: function(elem){\r
5976                         return (/input|select|textarea|button/i).test(elem.nodeName);\r
5977                 }\r
5978         },\r
5979         setFilters: {\r
5980                 first: function(elem, i){\r
5981                         return i === 0;\r
5982                 },\r
5983                 last: function(elem, i, match, array){\r
5984                         return i === array.length - 1;\r
5985                 },\r
5986                 even: function(elem, i){\r
5987                         return i % 2 === 0;\r
5988                 },\r
5989                 odd: function(elem, i){\r
5990                         return i % 2 === 1;\r
5991                 },\r
5992                 lt: function(elem, i, match){\r
5993                         return i < match[3] - 0;\r
5994                 },\r
5995                 gt: function(elem, i, match){\r
5996                         return i > match[3] - 0;\r
5997                 },\r
5998                 nth: function(elem, i, match){\r
5999                         return match[3] - 0 === i;\r
6000                 },\r
6001                 eq: function(elem, i, match){\r
6002                         return match[3] - 0 === i;\r
6003                 }\r
6004         },\r
6005         filter: {\r
6006                 PSEUDO: function(elem, match, i, array){\r
6007                         var name = match[1], filter = Expr.filters[ name ];\r
6008 \r
6009                         if ( filter ) {\r
6010                                 return filter( elem, i, match, array );\r
6011                         } else if ( name === "contains" ) {\r
6012                                 return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;\r
6013                         } else if ( name === "not" ) {\r
6014                                 var not = match[3];\r
6015 \r
6016                                 for ( var j = 0, l = not.length; j < l; j++ ) {\r
6017                                         if ( not[j] === elem ) {\r
6018                                                 return false;\r
6019                                         }\r
6020                                 }\r
6021 \r
6022                                 return true;\r
6023                         } else {\r
6024                                 Sizzle.error( "Syntax error, unrecognized expression: " + name );\r
6025                         }\r
6026                 },\r
6027                 CHILD: function(elem, match){\r
6028                         var type = match[1], node = elem;\r
6029                         switch (type) {\r
6030                                 case 'only':\r
6031                                 case 'first':\r
6032                                         while ( (node = node.previousSibling) )  {\r
6033                                                 if ( node.nodeType === 1 ) { \r
6034                                                         return false; \r
6035                                                 }\r
6036                                         }\r
6037                                         if ( type === "first" ) { \r
6038                                                 return true; \r
6039                                         }\r
6040                                         node = elem;\r
6041                                 case 'last':\r
6042                                         while ( (node = node.nextSibling) )      {\r
6043                                                 if ( node.nodeType === 1 ) { \r
6044                                                         return false; \r
6045                                                 }\r
6046                                         }\r
6047                                         return true;\r
6048                                 case 'nth':\r
6049                                         var first = match[2], last = match[3];\r
6050 \r
6051                                         if ( first === 1 && last === 0 ) {\r
6052                                                 return true;\r
6053                                         }\r
6054                                         \r
6055                                         var doneName = match[0],\r
6056                                                 parent = elem.parentNode;\r
6057         \r
6058                                         if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {\r
6059                                                 var count = 0;\r
6060                                                 for ( node = parent.firstChild; node; node = node.nextSibling ) {\r
6061                                                         if ( node.nodeType === 1 ) {\r
6062                                                                 node.nodeIndex = ++count;\r
6063                                                         }\r
6064                                                 } \r
6065                                                 parent.sizcache = doneName;\r
6066                                         }\r
6067                                         \r
6068                                         var diff = elem.nodeIndex - last;\r
6069                                         if ( first === 0 ) {\r
6070                                                 return diff === 0;\r
6071                                         } else {\r
6072                                                 return ( diff % first === 0 && diff / first >= 0 );\r
6073                                         }\r
6074                         }\r
6075                 },\r
6076                 ID: function(elem, match){\r
6077                         return elem.nodeType === 1 && elem.getAttribute("id") === match;\r
6078                 },\r
6079                 TAG: function(elem, match){\r
6080                         return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;\r
6081                 },\r
6082                 CLASS: function(elem, match){\r
6083                         return (" " + (elem.className || elem.getAttribute("class")) + " ")\r
6084                                 .indexOf( match ) > -1;\r
6085                 },\r
6086                 ATTR: function(elem, match){\r
6087                         var name = match[1],\r
6088                                 result = Expr.attrHandle[ name ] ?\r
6089                                         Expr.attrHandle[ name ]( elem ) :\r
6090                                         elem[ name ] != null ?\r
6091                                                 elem[ name ] :\r
6092                                                 elem.getAttribute( name ),\r
6093                                 value = result + "",\r
6094                                 type = match[2],\r
6095                                 check = match[4];\r
6096 \r
6097                         return result == null ?\r
6098                                 type === "!=" :\r
6099                                 type === "=" ?\r
6100                                 value === check :\r
6101                                 type === "*=" ?\r
6102                                 value.indexOf(check) >= 0 :\r
6103                                 type === "~=" ?\r
6104                                 (" " + value + " ").indexOf(check) >= 0 :\r
6105                                 !check ?\r
6106                                 value && result !== false :\r
6107                                 type === "!=" ?\r
6108                                 value !== check :\r
6109                                 type === "^=" ?\r
6110                                 value.indexOf(check) === 0 :\r
6111                                 type === "$=" ?\r
6112                                 value.substr(value.length - check.length) === check :\r
6113                                 type === "|=" ?\r
6114                                 value === check || value.substr(0, check.length + 1) === check + "-" :\r
6115                                 false;\r
6116                 },\r
6117                 POS: function(elem, match, i, array){\r
6118                         var name = match[2], filter = Expr.setFilters[ name ];\r
6119 \r
6120                         if ( filter ) {\r
6121                                 return filter( elem, i, match, array );\r
6122                         }\r
6123                 }\r
6124         }\r
6125 };\r
6126 \r
6127 var origPOS = Expr.match.POS,\r
6128         fescape = function(all, num){\r
6129                 return "\\" + (num - 0 + 1);\r
6130         };\r
6131 \r
6132 for ( var type in Expr.match ) {\r
6133         Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );\r
6134         Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );\r
6135 }\r
6136 \r
6137 var makeArray = function(array, results) {\r
6138         array = Array.prototype.slice.call( array, 0 );\r
6139 \r
6140         if ( results ) {\r
6141                 results.push.apply( results, array );\r
6142                 return results;\r
6143         }\r
6144         \r
6145         return array;\r
6146 };\r
6147 \r
6148 // Perform a simple check to determine if the browser is capable of\r
6149 // converting a NodeList to an array using builtin methods.\r
6150 // Also verifies that the returned array holds DOM nodes\r
6151 // (which is not the case in the Blackberry browser)\r
6152 try {\r
6153         Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;\r
6154 \r
6155 // Provide a fallback method if it does not work\r
6156 } catch(e){\r
6157         makeArray = function(array, results) {\r
6158                 var ret = results || [], i = 0;\r
6159 \r
6160                 if ( toString.call(array) === "[object Array]" ) {\r
6161                         Array.prototype.push.apply( ret, array );\r
6162                 } else {\r
6163                         if ( typeof array.length === "number" ) {\r
6164                                 for ( var l = array.length; i < l; i++ ) {\r
6165                                         ret.push( array[i] );\r
6166                                 }\r
6167                         } else {\r
6168                                 for ( ; array[i]; i++ ) {\r
6169                                         ret.push( array[i] );\r
6170                                 }\r
6171                         }\r
6172                 }\r
6173 \r
6174                 return ret;\r
6175         };\r
6176 }\r
6177 \r
6178 var sortOrder;\r
6179 \r
6180 if ( document.documentElement.compareDocumentPosition ) {\r
6181         sortOrder = function( a, b ) {\r
6182                 if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {\r
6183                         if ( a == b ) {\r
6184                                 hasDuplicate = true;\r
6185                         }\r
6186                         return a.compareDocumentPosition ? -1 : 1;\r
6187                 }\r
6188 \r
6189                 var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;\r
6190                 if ( ret === 0 ) {\r
6191                         hasDuplicate = true;\r
6192                 }\r
6193                 return ret;\r
6194         };\r
6195 } else if ( "sourceIndex" in document.documentElement ) {\r
6196         sortOrder = function( a, b ) {\r
6197                 if ( !a.sourceIndex || !b.sourceIndex ) {\r
6198                         if ( a == b ) {\r
6199                                 hasDuplicate = true;\r
6200                         }\r
6201                         return a.sourceIndex ? -1 : 1;\r
6202                 }\r
6203 \r
6204                 var ret = a.sourceIndex - b.sourceIndex;\r
6205                 if ( ret === 0 ) {\r
6206                         hasDuplicate = true;\r
6207                 }\r
6208                 return ret;\r
6209         };\r
6210 } else if ( document.createRange ) {\r
6211         sortOrder = function( a, b ) {\r
6212                 if ( !a.ownerDocument || !b.ownerDocument ) {\r
6213                         if ( a == b ) {\r
6214                                 hasDuplicate = true;\r
6215                         }\r
6216                         return a.ownerDocument ? -1 : 1;\r
6217                 }\r
6218 \r
6219                 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();\r
6220                 aRange.setStart(a, 0);\r
6221                 aRange.setEnd(a, 0);\r
6222                 bRange.setStart(b, 0);\r
6223                 bRange.setEnd(b, 0);\r
6224                 var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);\r
6225                 if ( ret === 0 ) {\r
6226                         hasDuplicate = true;\r
6227                 }\r
6228                 return ret;\r
6229         };\r
6230 }\r
6231 \r
6232 // Utility function for retreiving the text value of an array of DOM nodes\r
6233 Sizzle.getText = function( elems ) {\r
6234         var ret = "", elem;\r
6235 \r
6236         for ( var i = 0; elems[i]; i++ ) {\r
6237                 elem = elems[i];\r
6238 \r
6239                 // Get the text from text nodes and CDATA nodes\r
6240                 if ( elem.nodeType === 3 || elem.nodeType === 4 ) {\r
6241                         ret += elem.nodeValue;\r
6242 \r
6243                 // Traverse everything else, except comment nodes\r
6244                 } else if ( elem.nodeType !== 8 ) {\r
6245                         ret += Sizzle.getText( elem.childNodes );\r
6246                 }\r
6247         }\r
6248 \r
6249         return ret;\r
6250 };\r
6251 \r
6252 // Check to see if the browser returns elements by name when\r
6253 // querying by getElementById (and provide a workaround)\r
6254 (function(){\r
6255         // We're going to inject a fake input element with a specified name\r
6256         var form = document.createElement("div"),\r
6257                 id = "script" + (new Date()).getTime();\r
6258         form.innerHTML = "<a name='" + id + "'/>";\r
6259 \r
6260         // Inject it into the root element, check its status, and remove it quickly\r
6261         var root = document.documentElement;\r
6262         root.insertBefore( form, root.firstChild );\r
6263 \r
6264         // The workaround has to do additional checks after a getElementById\r
6265         // Which slows things down for other browsers (hence the branching)\r
6266         if ( document.getElementById( id ) ) {\r
6267                 Expr.find.ID = function(match, context, isXML){\r
6268                         if ( typeof context.getElementById !== "undefined" && !isXML ) {\r
6269                                 var m = context.getElementById(match[1]);\r
6270                                 return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];\r
6271                         }\r
6272                 };\r
6273 \r
6274                 Expr.filter.ID = function(elem, match){\r
6275                         var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");\r
6276                         return elem.nodeType === 1 && node && node.nodeValue === match;\r
6277                 };\r
6278         }\r
6279 \r
6280         root.removeChild( form );\r
6281         root = form = null; // release memory in IE\r
6282 })();\r
6283 \r
6284 (function(){\r
6285         // Check to see if the browser returns only elements\r
6286         // when doing getElementsByTagName("*")\r
6287 \r
6288         // Create a fake element\r
6289         var div = document.createElement("div");\r
6290         div.appendChild( document.createComment("") );\r
6291 \r
6292         // Make sure no comments are found\r
6293         if ( div.getElementsByTagName("*").length > 0 ) {\r
6294                 Expr.find.TAG = function(match, context){\r
6295                         var results = context.getElementsByTagName(match[1]);\r
6296 \r
6297                         // Filter out possible comments\r
6298                         if ( match[1] === "*" ) {\r
6299                                 var tmp = [];\r
6300 \r
6301                                 for ( var i = 0; results[i]; i++ ) {\r
6302                                         if ( results[i].nodeType === 1 ) {\r
6303                                                 tmp.push( results[i] );\r
6304                                         }\r
6305                                 }\r
6306 \r
6307                                 results = tmp;\r
6308                         }\r
6309 \r
6310                         return results;\r
6311                 };\r
6312         }\r
6313 \r
6314         // Check to see if an attribute returns normalized href attributes\r
6315         div.innerHTML = "<a href='#'></a>";\r
6316         if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&\r
6317                         div.firstChild.getAttribute("href") !== "#" ) {\r
6318                 Expr.attrHandle.href = function(elem){\r
6319                         return elem.getAttribute("href", 2);\r
6320                 };\r
6321         }\r
6322 \r
6323         div = null; // release memory in IE\r
6324 })();\r
6325 \r
6326 if ( document.querySelectorAll ) {\r
6327         (function(){\r
6328                 var oldSizzle = Sizzle, div = document.createElement("div");\r
6329                 div.innerHTML = "<p class='TEST'></p>";\r
6330 \r
6331                 // Safari can't handle uppercase or unicode characters when\r
6332                 // in quirks mode.\r
6333                 if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {\r
6334                         return;\r
6335                 }\r
6336         \r
6337                 Sizzle = function(query, context, extra, seed){\r
6338                         context = context || document;\r
6339 \r
6340                         // Only use querySelectorAll on non-XML documents\r
6341                         // (ID selectors don't work in non-HTML documents)\r
6342                         if ( !seed && context.nodeType === 9 && !Sizzle.isXML(context) ) {\r
6343                                 try {\r
6344                                         return makeArray( context.querySelectorAll(query), extra );\r
6345                                 } catch(e){}\r
6346                         }\r
6347                 \r
6348                         return oldSizzle(query, context, extra, seed);\r
6349                 };\r
6350 \r
6351                 for ( var prop in oldSizzle ) {\r
6352                         Sizzle[ prop ] = oldSizzle[ prop ];\r
6353                 }\r
6354 \r
6355                 div = null; // release memory in IE\r
6356         })();\r
6357 }\r
6358 \r
6359 (function(){\r
6360         var div = document.createElement("div");\r
6361 \r
6362         div.innerHTML = "<div class='test e'></div><div class='test'></div>";\r
6363 \r
6364         // Opera can't find a second classname (in 9.6)\r
6365         // Also, make sure that getElementsByClassName actually exists\r
6366         if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {\r
6367                 return;\r
6368         }\r
6369 \r
6370         // Safari caches class attributes, doesn't catch changes (in 3.2)\r
6371         div.lastChild.className = "e";\r
6372 \r
6373         if ( div.getElementsByClassName("e").length === 1 ) {\r
6374                 return;\r
6375         }\r
6376         \r
6377         Expr.order.splice(1, 0, "CLASS");\r
6378         Expr.find.CLASS = function(match, context, isXML) {\r
6379                 if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {\r
6380                         return context.getElementsByClassName(match[1]);\r
6381                 }\r
6382         };\r
6383 \r
6384         div = null; // release memory in IE\r
6385 })();\r
6386 \r
6387 function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\r
6388         for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
6389                 var elem = checkSet[i];\r
6390                 if ( elem ) {\r
6391                         elem = elem[dir];\r
6392                         var match = false;\r
6393 \r
6394                         while ( elem ) {\r
6395                                 if ( elem.sizcache === doneName ) {\r
6396                                         match = checkSet[elem.sizset];\r
6397                                         break;\r
6398                                 }\r
6399 \r
6400                                 if ( elem.nodeType === 1 && !isXML ){\r
6401                                         elem.sizcache = doneName;\r
6402                                         elem.sizset = i;\r
6403                                 }\r
6404 \r
6405                                 if ( elem.nodeName.toLowerCase() === cur ) {\r
6406                                         match = elem;\r
6407                                         break;\r
6408                                 }\r
6409 \r
6410                                 elem = elem[dir];\r
6411                         }\r
6412 \r
6413                         checkSet[i] = match;\r
6414                 }\r
6415         }\r
6416 }\r
6417 \r
6418 function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\r
6419         for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
6420                 var elem = checkSet[i];\r
6421                 if ( elem ) {\r
6422                         elem = elem[dir];\r
6423                         var match = false;\r
6424 \r
6425                         while ( elem ) {\r
6426                                 if ( elem.sizcache === doneName ) {\r
6427                                         match = checkSet[elem.sizset];\r
6428                                         break;\r
6429                                 }\r
6430 \r
6431                                 if ( elem.nodeType === 1 ) {\r
6432                                         if ( !isXML ) {\r
6433                                                 elem.sizcache = doneName;\r
6434                                                 elem.sizset = i;\r
6435                                         }\r
6436                                         if ( typeof cur !== "string" ) {\r
6437                                                 if ( elem === cur ) {\r
6438                                                         match = true;\r
6439                                                         break;\r
6440                                                 }\r
6441 \r
6442                                         } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {\r
6443                                                 match = elem;\r
6444                                                 break;\r
6445                                         }\r
6446                                 }\r
6447 \r
6448                                 elem = elem[dir];\r
6449                         }\r
6450 \r
6451                         checkSet[i] = match;\r
6452                 }\r
6453         }\r
6454 }\r
6455 \r
6456 Sizzle.contains = document.compareDocumentPosition ? function(a, b){\r
6457         return !!(a.compareDocumentPosition(b) & 16);\r
6458 } : function(a, b){\r
6459         return a !== b && (a.contains ? a.contains(b) : true);\r
6460 };\r
6461 \r
6462 Sizzle.isXML = function(elem){\r
6463         // documentElement is verified for cases where it doesn't yet exist\r
6464         // (such as loading iframes in IE - #4833) \r
6465         var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;\r
6466         return documentElement ? documentElement.nodeName !== "HTML" : false;\r
6467 };\r
6468 \r
6469 var posProcess = function(selector, context){\r
6470         var tmpSet = [], later = "", match,\r
6471                 root = context.nodeType ? [context] : context;\r
6472 \r
6473         // Position selectors must be done after the filter\r
6474         // And so must :not(positional) so we move all PSEUDOs to the end\r
6475         while ( (match = Expr.match.PSEUDO.exec( selector )) ) {\r
6476                 later += match[0];\r
6477                 selector = selector.replace( Expr.match.PSEUDO, "" );\r
6478         }\r
6479 \r
6480         selector = Expr.relative[selector] ? selector + "*" : selector;\r
6481 \r
6482         for ( var i = 0, l = root.length; i < l; i++ ) {\r
6483                 Sizzle( selector, root[i], tmpSet );\r
6484         }\r
6485 \r
6486         return Sizzle.filter( later, tmpSet );\r
6487 };\r
6488 \r
6489 // EXPOSE\r
6490 \r
6491 window.tinymce.dom.Sizzle = Sizzle;\r
6492 \r
6493 })();\r
6494 \r
6495 \r
6496 (function(tinymce) {\r
6497         // Shorten names\r
6498         var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event;\r
6499 \r
6500         tinymce.create('tinymce.dom.EventUtils', {\r
6501                 EventUtils : function() {\r
6502                         this.inits = [];\r
6503                         this.events = [];\r
6504                 },\r
6505 \r
6506                 add : function(o, n, f, s) {\r
6507                         var cb, t = this, el = t.events, r;\r
6508 \r
6509                         if (n instanceof Array) {\r
6510                                 r = [];\r
6511 \r
6512                                 each(n, function(n) {\r
6513                                         r.push(t.add(o, n, f, s));\r
6514                                 });\r
6515 \r
6516                                 return r;\r
6517                         }\r
6518 \r
6519                         // Handle array\r
6520                         if (o && o.hasOwnProperty && o instanceof Array) {\r
6521                                 r = [];\r
6522 \r
6523                                 each(o, function(o) {\r
6524                                         o = DOM.get(o);\r
6525                                         r.push(t.add(o, n, f, s));\r
6526                                 });\r
6527 \r
6528                                 return r;\r
6529                         }\r
6530 \r
6531                         o = DOM.get(o);\r
6532 \r
6533                         if (!o)\r
6534                                 return;\r
6535 \r
6536                         // Setup event callback\r
6537                         cb = function(e) {\r
6538                                 // Is all events disabled\r
6539                                 if (t.disabled)\r
6540                                         return;\r
6541 \r
6542                                 e = e || window.event;\r
6543 \r
6544                                 // Patch in target, preventDefault and stopPropagation in IE it's W3C valid\r
6545                                 if (e && isIE) {\r
6546                                         if (!e.target)\r
6547                                                 e.target = e.srcElement;\r
6548 \r
6549                                         // Patch in preventDefault, stopPropagation methods for W3C compatibility\r
6550                                         tinymce.extend(e, t._stoppers);\r
6551                                 }\r
6552 \r
6553                                 if (!s)\r
6554                                         return f(e);\r
6555 \r
6556                                 return f.call(s, e);\r
6557                         };\r
6558 \r
6559                         if (n == 'unload') {\r
6560                                 tinymce.unloads.unshift({func : cb});\r
6561                                 return cb;\r
6562                         }\r
6563 \r
6564                         if (n == 'init') {\r
6565                                 if (t.domLoaded)\r
6566                                         cb();\r
6567                                 else\r
6568                                         t.inits.push(cb);\r
6569 \r
6570                                 return cb;\r
6571                         }\r
6572 \r
6573                         // Store away listener reference\r
6574                         el.push({\r
6575                                 obj : o,\r
6576                                 name : n,\r
6577                                 func : f,\r
6578                                 cfunc : cb,\r
6579                                 scope : s\r
6580                         });\r
6581 \r
6582                         t._add(o, n, cb);\r
6583 \r
6584                         return f;\r
6585                 },\r
6586 \r
6587                 remove : function(o, n, f) {\r
6588                         var t = this, a = t.events, s = false, r;\r
6589 \r
6590                         // Handle array\r
6591                         if (o && o.hasOwnProperty && o instanceof Array) {\r
6592                                 r = [];\r
6593 \r
6594                                 each(o, function(o) {\r
6595                                         o = DOM.get(o);\r
6596                                         r.push(t.remove(o, n, f));\r
6597                                 });\r
6598 \r
6599                                 return r;\r
6600                         }\r
6601 \r
6602                         o = DOM.get(o);\r
6603 \r
6604                         each(a, function(e, i) {\r
6605                                 if (e.obj == o && e.name == n && (!f || (e.func == f || e.cfunc == f))) {\r
6606                                         a.splice(i, 1);\r
6607                                         t._remove(o, n, e.cfunc);\r
6608                                         s = true;\r
6609                                         return false;\r
6610                                 }\r
6611                         });\r
6612 \r
6613                         return s;\r
6614                 },\r
6615 \r
6616                 clear : function(o) {\r
6617                         var t = this, a = t.events, i, e;\r
6618 \r
6619                         if (o) {\r
6620                                 o = DOM.get(o);\r
6621 \r
6622                                 for (i = a.length - 1; i >= 0; i--) {\r
6623                                         e = a[i];\r
6624 \r
6625                                         if (e.obj === o) {\r
6626                                                 t._remove(e.obj, e.name, e.cfunc);\r
6627                                                 e.obj = e.cfunc = null;\r
6628                                                 a.splice(i, 1);\r
6629                                         }\r
6630                                 }\r
6631                         }\r
6632                 },\r
6633 \r
6634                 cancel : function(e) {\r
6635                         if (!e)\r
6636                                 return false;\r
6637 \r
6638                         this.stop(e);\r
6639 \r
6640                         return this.prevent(e);\r
6641                 },\r
6642 \r
6643                 stop : function(e) {\r
6644                         if (e.stopPropagation)\r
6645                                 e.stopPropagation();\r
6646                         else\r
6647                                 e.cancelBubble = true;\r
6648 \r
6649                         return false;\r
6650                 },\r
6651 \r
6652                 prevent : function(e) {\r
6653                         if (e.preventDefault)\r
6654                                 e.preventDefault();\r
6655                         else\r
6656                                 e.returnValue = false;\r
6657 \r
6658                         return false;\r
6659                 },\r
6660 \r
6661                 destroy : function() {\r
6662                         var t = this;\r
6663 \r
6664                         each(t.events, function(e, i) {\r
6665                                 t._remove(e.obj, e.name, e.cfunc);\r
6666                                 e.obj = e.cfunc = null;\r
6667                         });\r
6668 \r
6669                         t.events = [];\r
6670                         t = null;\r
6671                 },\r
6672 \r
6673                 _add : function(o, n, f) {\r
6674                         if (o.attachEvent)\r
6675                                 o.attachEvent('on' + n, f);\r
6676                         else if (o.addEventListener)\r
6677                                 o.addEventListener(n, f, false);\r
6678                         else\r
6679                                 o['on' + n] = f;\r
6680                 },\r
6681 \r
6682                 _remove : function(o, n, f) {\r
6683                         if (o) {\r
6684                                 try {\r
6685                                         if (o.detachEvent)\r
6686                                                 o.detachEvent('on' + n, f);\r
6687                                         else if (o.removeEventListener)\r
6688                                                 o.removeEventListener(n, f, false);\r
6689                                         else\r
6690                                                 o['on' + n] = null;\r
6691                                 } catch (ex) {\r
6692                                         // Might fail with permission denined on IE so we just ignore that\r
6693                                 }\r
6694                         }\r
6695                 },\r
6696 \r
6697                 _pageInit : function(win) {\r
6698                         var t = this;\r
6699 \r
6700                         // Keep it from running more than once\r
6701                         if (t.domLoaded)\r
6702                                 return;\r
6703 \r
6704                         t.domLoaded = true;\r
6705 \r
6706                         each(t.inits, function(c) {\r
6707                                 c();\r
6708                         });\r
6709 \r
6710                         t.inits = [];\r
6711                 },\r
6712 \r
6713                 _wait : function(win) {\r
6714                         var t = this, doc = win.document;\r
6715 \r
6716                         // No need since the document is already loaded\r
6717                         if (win.tinyMCE_GZ && tinyMCE_GZ.loaded) {\r
6718                                 t.domLoaded = 1;\r
6719                                 return;\r
6720                         }\r
6721 \r
6722                         // Use IE method\r
6723                         if (doc.attachEvent) {\r
6724                                 doc.attachEvent("onreadystatechange", function() {\r
6725                                         if (doc.readyState === "complete") {\r
6726                                                 doc.detachEvent("onreadystatechange", arguments.callee);\r
6727                                                 t._pageInit(win);\r
6728                                         }\r
6729                                 });\r
6730 \r
6731                                 if (doc.documentElement.doScroll && win == win.top) {\r
6732                                         (function() {\r
6733                                                 if (t.domLoaded)\r
6734                                                         return;\r
6735 \r
6736                                                 try {\r
6737                                                         // If IE is used, use the trick by Diego Perini\r
6738                                                         // http://javascript.nwbox.com/IEContentLoaded/\r
6739                                                         doc.documentElement.doScroll("left");\r
6740                                                 } catch (ex) {\r
6741                                                         setTimeout(arguments.callee, 0);\r
6742                                                         return;\r
6743                                                 }\r
6744 \r
6745                                                 t._pageInit(win);\r
6746                                         })();\r
6747                                 }\r
6748                         } else if (doc.addEventListener) {\r
6749                                 t._add(win, 'DOMContentLoaded', function() {\r
6750                                         t._pageInit(win);\r
6751                                 });\r
6752                         }\r
6753 \r
6754                         t._add(win, 'load', function() {\r
6755                                 t._pageInit(win);\r
6756                         });\r
6757                 },\r
6758 \r
6759                 _stoppers : {\r
6760                         preventDefault : function() {\r
6761                                 this.returnValue = false;\r
6762                         },\r
6763 \r
6764                         stopPropagation : function() {\r
6765                                 this.cancelBubble = true;\r
6766                         }\r
6767                 }\r
6768         });\r
6769 \r
6770         Event = tinymce.dom.Event = new tinymce.dom.EventUtils();\r
6771 \r
6772         // Dispatch DOM content loaded event for IE and Safari\r
6773         Event._wait(window);\r
6774 \r
6775         tinymce.addUnload(function() {\r
6776                 Event.destroy();\r
6777         });\r
6778 })(tinymce);\r
6779 \r
6780 (function(tinymce) {\r
6781         tinymce.dom.Element = function(id, settings) {\r
6782                 var t = this, dom, el;\r
6783 \r
6784                 t.settings = settings = settings || {};\r
6785                 t.id = id;\r
6786                 t.dom = dom = settings.dom || tinymce.DOM;\r
6787 \r
6788                 // Only IE leaks DOM references, this is a lot faster\r
6789                 if (!tinymce.isIE)\r
6790                         el = dom.get(t.id);\r
6791 \r
6792                 tinymce.each(\r
6793                                 ('getPos,getRect,getParent,add,setStyle,getStyle,setStyles,' + \r
6794                                 'setAttrib,setAttribs,getAttrib,addClass,removeClass,' + \r
6795                                 'hasClass,getOuterHTML,setOuterHTML,remove,show,hide,' + \r
6796                                 'isHidden,setHTML,get').split(/,/)\r
6797                         , function(k) {\r
6798                                 t[k] = function() {\r
6799                                         var a = [id], i;\r
6800 \r
6801                                         for (i = 0; i < arguments.length; i++)\r
6802                                                 a.push(arguments[i]);\r
6803 \r
6804                                         a = dom[k].apply(dom, a);\r
6805                                         t.update(k);\r
6806 \r
6807                                         return a;\r
6808                                 };\r
6809                 });\r
6810 \r
6811                 tinymce.extend(t, {\r
6812                         on : function(n, f, s) {\r
6813                                 return tinymce.dom.Event.add(t.id, n, f, s);\r
6814                         },\r
6815 \r
6816                         getXY : function() {\r
6817                                 return {\r
6818                                         x : parseInt(t.getStyle('left')),\r
6819                                         y : parseInt(t.getStyle('top'))\r
6820                                 };\r
6821                         },\r
6822 \r
6823                         getSize : function() {\r
6824                                 var n = dom.get(t.id);\r
6825 \r
6826                                 return {\r
6827                                         w : parseInt(t.getStyle('width') || n.clientWidth),\r
6828                                         h : parseInt(t.getStyle('height') || n.clientHeight)\r
6829                                 };\r
6830                         },\r
6831 \r
6832                         moveTo : function(x, y) {\r
6833                                 t.setStyles({left : x, top : y});\r
6834                         },\r
6835 \r
6836                         moveBy : function(x, y) {\r
6837                                 var p = t.getXY();\r
6838 \r
6839                                 t.moveTo(p.x + x, p.y + y);\r
6840                         },\r
6841 \r
6842                         resizeTo : function(w, h) {\r
6843                                 t.setStyles({width : w, height : h});\r
6844                         },\r
6845 \r
6846                         resizeBy : function(w, h) {\r
6847                                 var s = t.getSize();\r
6848 \r
6849                                 t.resizeTo(s.w + w, s.h + h);\r
6850                         },\r
6851 \r
6852                         update : function(k) {\r
6853                                 var b;\r
6854 \r
6855                                 if (tinymce.isIE6 && settings.blocker) {\r
6856                                         k = k || '';\r
6857 \r
6858                                         // Ignore getters\r
6859                                         if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0)\r
6860                                                 return;\r
6861 \r
6862                                         // Remove blocker on remove\r
6863                                         if (k == 'remove') {\r
6864                                                 dom.remove(t.blocker);\r
6865                                                 return;\r
6866                                         }\r
6867 \r
6868                                         if (!t.blocker) {\r
6869                                                 t.blocker = dom.uniqueId();\r
6870                                                 b = dom.add(settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'});\r
6871                                                 dom.setStyle(b, 'opacity', 0);\r
6872                                         } else\r
6873                                                 b = dom.get(t.blocker);\r
6874 \r
6875                                         dom.setStyles(b, {\r
6876                                                 left : t.getStyle('left', 1),\r
6877                                                 top : t.getStyle('top', 1),\r
6878                                                 width : t.getStyle('width', 1),\r
6879                                                 height : t.getStyle('height', 1),\r
6880                                                 display : t.getStyle('display', 1),\r
6881                                                 zIndex : parseInt(t.getStyle('zIndex', 1) || 0) - 1\r
6882                                         });\r
6883                                 }\r
6884                         }\r
6885                 });\r
6886         };\r
6887 })(tinymce);\r
6888 \r
6889 (function(tinymce) {\r
6890         function trimNl(s) {\r
6891                 return s.replace(/[\n\r]+/g, '');\r
6892         };\r
6893 \r
6894         // Shorten names\r
6895         var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each;\r
6896 \r
6897         tinymce.create('tinymce.dom.Selection', {\r
6898                 Selection : function(dom, win, serializer) {\r
6899                         var t = this;\r
6900 \r
6901                         t.dom = dom;\r
6902                         t.win = win;\r
6903                         t.serializer = serializer;\r
6904 \r
6905                         // Add events\r
6906                         each([\r
6907                                 'onBeforeSetContent',\r
6908 \r
6909                                 'onBeforeGetContent',\r
6910 \r
6911                                 'onSetContent',\r
6912 \r
6913                                 'onGetContent'\r
6914                         ], function(e) {\r
6915                                 t[e] = new tinymce.util.Dispatcher(t);\r
6916                         });\r
6917 \r
6918                         // No W3C Range support\r
6919                         if (!t.win.getSelection)\r
6920                                 t.tridentSel = new tinymce.dom.TridentSelection(t);\r
6921 \r
6922                         if (tinymce.isIE && dom.boxModel)\r
6923                                 this._fixIESelection();\r
6924 \r
6925                         // Prevent leaks\r
6926                         tinymce.addUnload(t.destroy, t);\r
6927                 },\r
6928 \r
6929                 getContent : function(s) {\r
6930                         var t = this, r = t.getRng(), e = t.dom.create("body"), se = t.getSel(), wb, wa, n;\r
6931 \r
6932                         s = s || {};\r
6933                         wb = wa = '';\r
6934                         s.get = true;\r
6935                         s.format = s.format || 'html';\r
6936                         t.onBeforeGetContent.dispatch(t, s);\r
6937 \r
6938                         if (s.format == 'text')\r
6939                                 return t.isCollapsed() ? '' : (r.text || (se.toString ? se.toString() : ''));\r
6940 \r
6941                         if (r.cloneContents) {\r
6942                                 n = r.cloneContents();\r
6943 \r
6944                                 if (n)\r
6945                                         e.appendChild(n);\r
6946                         } else if (is(r.item) || is(r.htmlText))\r
6947                                 e.innerHTML = r.item ? r.item(0).outerHTML : r.htmlText;\r
6948                         else\r
6949                                 e.innerHTML = r.toString();\r
6950 \r
6951                         // Keep whitespace before and after\r
6952                         if (/^\s/.test(e.innerHTML))\r
6953                                 wb = ' ';\r
6954 \r
6955                         if (/\s+$/.test(e.innerHTML))\r
6956                                 wa = ' ';\r
6957 \r
6958                         s.getInner = true;\r
6959 \r
6960                         s.content = t.isCollapsed() ? '' : wb + t.serializer.serialize(e, s) + wa;\r
6961                         t.onGetContent.dispatch(t, s);\r
6962 \r
6963                         return s.content;\r
6964                 },\r
6965 \r
6966                 setContent : function(content, args) {\r
6967                         var self = this, rng = self.getRng(), caretNode, doc = self.win.document, frag, temp;\r
6968 \r
6969                         args = args || {format : 'html'};\r
6970                         args.set = true;\r
6971                         content = args.content = content;\r
6972 \r
6973                         // Dispatch before set content event\r
6974                         if (!args.no_events)\r
6975                                 self.onBeforeSetContent.dispatch(self, args);\r
6976 \r
6977                         content = args.content;\r
6978 \r
6979                         if (rng.insertNode) {\r
6980                                 // Make caret marker since insertNode places the caret in the beginning of text after insert\r
6981                                 content += '<span id="__caret">_</span>';\r
6982 \r
6983                                 // Delete and insert new node\r
6984                                 if (rng.startContainer == doc && rng.endContainer == doc) {\r
6985                                         // WebKit will fail if the body is empty since the range is then invalid and it can't insert contents\r
6986                                         doc.body.innerHTML = content;\r
6987                                 } else {\r
6988                                         rng.deleteContents();\r
6989 \r
6990                                         if (doc.body.childNodes.length == 0) {\r
6991                                                 doc.body.innerHTML = content;\r
6992                                         } else {\r
6993                                                 // createContextualFragment doesn't exists in IE 9 DOMRanges\r
6994                                                 if (rng.createContextualFragment) {\r
6995                                                         rng.insertNode(rng.createContextualFragment(content));\r
6996                                                 } else {\r
6997                                                         // Fake createContextualFragment call in IE 9\r
6998                                                         frag = doc.createDocumentFragment();\r
6999                                                         temp = doc.createElement('div');\r
7000 \r
7001                                                         frag.appendChild(temp);\r
7002                                                         temp.outerHTML = content;\r
7003 \r
7004                                                         rng.insertNode(frag);\r
7005                                                 }\r
7006                                         }\r
7007                                 }\r
7008 \r
7009                                 // Move to caret marker\r
7010                                 caretNode = self.dom.get('__caret');\r
7011 \r
7012                                 // Make sure we wrap it compleatly, Opera fails with a simple select call\r
7013                                 rng = doc.createRange();\r
7014                                 rng.setStartBefore(caretNode);\r
7015                                 rng.setEndBefore(caretNode);\r
7016                                 self.setRng(rng);\r
7017 \r
7018                                 // Remove the caret position\r
7019                                 self.dom.remove('__caret');\r
7020                                 self.setRng(rng);\r
7021                         } else {\r
7022                                 if (rng.item) {\r
7023                                         // Delete content and get caret text selection\r
7024                                         doc.execCommand('Delete', false, null);\r
7025                                         rng = self.getRng();\r
7026                                 }\r
7027 \r
7028                                 rng.pasteHTML(content);\r
7029                         }\r
7030 \r
7031                         // Dispatch set content event\r
7032                         if (!args.no_events)\r
7033                                 self.onSetContent.dispatch(self, args);\r
7034                 },\r
7035 \r
7036                 getStart : function() {\r
7037                         var rng = this.getRng(), startElement, parentElement, checkRng, node;\r
7038 \r
7039                         if (rng.duplicate || rng.item) {\r
7040                                 // Control selection, return first item\r
7041                                 if (rng.item)\r
7042                                         return rng.item(0);\r
7043 \r
7044                                 // Get start element\r
7045                                 checkRng = rng.duplicate();\r
7046                                 checkRng.collapse(1);\r
7047                                 startElement = checkRng.parentElement();\r
7048 \r
7049                                 // Check if range parent is inside the start element, then return the inner parent element\r
7050                                 // This will fix issues when a single element is selected, IE would otherwise return the wrong start element\r
7051                                 parentElement = node = rng.parentElement();\r
7052                                 while (node = node.parentNode) {\r
7053                                         if (node == startElement) {\r
7054                                                 startElement = parentElement;\r
7055                                                 break;\r
7056                                         }\r
7057                                 }\r
7058 \r
7059                                 return startElement;\r
7060                         } else {\r
7061                                 startElement = rng.startContainer;\r
7062 \r
7063                                 if (startElement.nodeType == 1 && startElement.hasChildNodes())\r
7064                                         startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];\r
7065 \r
7066                                 if (startElement && startElement.nodeType == 3)\r
7067                                         return startElement.parentNode;\r
7068 \r
7069                                 return startElement;\r
7070                         }\r
7071                 },\r
7072 \r
7073                 getEnd : function() {\r
7074                         var t = this, r = t.getRng(), e, eo;\r
7075 \r
7076                         if (r.duplicate || r.item) {\r
7077                                 if (r.item)\r
7078                                         return r.item(0);\r
7079 \r
7080                                 r = r.duplicate();\r
7081                                 r.collapse(0);\r
7082                                 e = r.parentElement();\r
7083 \r
7084                                 if (e && e.nodeName == 'BODY')\r
7085                                         return e.lastChild || e;\r
7086 \r
7087                                 return e;\r
7088                         } else {\r
7089                                 e = r.endContainer;\r
7090                                 eo = r.endOffset;\r
7091 \r
7092                                 if (e.nodeType == 1 && e.hasChildNodes())\r
7093                                         e = e.childNodes[eo > 0 ? eo - 1 : eo];\r
7094 \r
7095                                 if (e && e.nodeType == 3)\r
7096                                         return e.parentNode;\r
7097 \r
7098                                 return e;\r
7099                         }\r
7100                 },\r
7101 \r
7102                 getBookmark : function(type, normalized) {\r
7103                         var t = this, dom = t.dom, rng, rng2, id, collapsed, name, element, index, chr = '\uFEFF', styles;\r
7104 \r
7105                         function findIndex(name, element) {\r
7106                                 var index = 0;\r
7107 \r
7108                                 each(dom.select(name), function(node, i) {\r
7109                                         if (node == element)\r
7110                                                 index = i;\r
7111                                 });\r
7112 \r
7113                                 return index;\r
7114                         };\r
7115 \r
7116                         if (type == 2) {\r
7117                                 function getLocation() {\r
7118                                         var rng = t.getRng(true), root = dom.getRoot(), bookmark = {};\r
7119 \r
7120                                         function getPoint(rng, start) {\r
7121                                                 var container = rng[start ? 'startContainer' : 'endContainer'],\r
7122                                                         offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;\r
7123 \r
7124                                                 if (container.nodeType == 3) {\r
7125                                                         if (normalized) {\r
7126                                                                 for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling)\r
7127                                                                         offset += node.nodeValue.length;\r
7128                                                         }\r
7129 \r
7130                                                         point.push(offset);\r
7131                                                 } else {\r
7132                                                         childNodes = container.childNodes;\r
7133 \r
7134                                                         if (offset >= childNodes.length && childNodes.length) {\r
7135                                                                 after = 1;\r
7136                                                                 offset = Math.max(0, childNodes.length - 1);\r
7137                                                         }\r
7138 \r
7139                                                         point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after);\r
7140                                                 }\r
7141 \r
7142                                                 for (; container && container != root; container = container.parentNode)\r
7143                                                         point.push(t.dom.nodeIndex(container, normalized));\r
7144 \r
7145                                                 return point;\r
7146                                         };\r
7147 \r
7148                                         bookmark.start = getPoint(rng, true);\r
7149 \r
7150                                         if (!t.isCollapsed())\r
7151                                                 bookmark.end = getPoint(rng);\r
7152 \r
7153                                         return bookmark;\r
7154                                 };\r
7155 \r
7156                                 return getLocation();\r
7157                         }\r
7158 \r
7159                         // Handle simple range\r
7160                         if (type)\r
7161                                 return {rng : t.getRng()};\r
7162 \r
7163                         rng = t.getRng();\r
7164                         id = dom.uniqueId();\r
7165                         collapsed = tinyMCE.activeEditor.selection.isCollapsed();\r
7166                         styles = 'overflow:hidden;line-height:0px';\r
7167 \r
7168                         // Explorer method\r
7169                         if (rng.duplicate || rng.item) {\r
7170                                 // Text selection\r
7171                                 if (!rng.item) {\r
7172                                         rng2 = rng.duplicate();\r
7173 \r
7174                                         try {\r
7175                                                 // Insert start marker\r
7176                                                 rng.collapse();\r
7177                                                 rng.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');\r
7178 \r
7179                                                 // Insert end marker\r
7180                                                 if (!collapsed) {\r
7181                                                         rng2.collapse(false);\r
7182 \r
7183                                                         // Detect the empty space after block elements in IE and move the end back one character <p></p>] becomes <p>]</p>\r
7184                                                         rng.moveToElementText(rng2.parentElement());\r
7185                                                         if (rng.compareEndPoints('StartToEnd', rng2) == 0)\r
7186                                                                 rng2.move('character', -1);\r
7187 \r
7188                                                         rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');\r
7189                                                 }\r
7190                                         } catch (ex) {\r
7191                                                 // IE might throw unspecified error so lets ignore it\r
7192                                                 return null;\r
7193                                         }\r
7194                                 } else {\r
7195                                         // Control selection\r
7196                                         element = rng.item(0);\r
7197                                         name = element.nodeName;\r
7198 \r
7199                                         return {name : name, index : findIndex(name, element)};\r
7200                                 }\r
7201                         } else {\r
7202                                 element = t.getNode();\r
7203                                 name = element.nodeName;\r
7204                                 if (name == 'IMG')\r
7205                                         return {name : name, index : findIndex(name, element)};\r
7206 \r
7207                                 // W3C method\r
7208                                 rng2 = rng.cloneRange();\r
7209 \r
7210                                 // Insert end marker\r
7211                                 if (!collapsed) {\r
7212                                         rng2.collapse(false);\r
7213                                         rng2.insertNode(dom.create('span', {'data-mce-type' : "bookmark", id : id + '_end', style : styles}, chr));\r
7214                                 }\r
7215 \r
7216                                 rng.collapse(true);\r
7217                                 rng.insertNode(dom.create('span', {'data-mce-type' : "bookmark", id : id + '_start', style : styles}, chr));\r
7218                         }\r
7219 \r
7220                         t.moveToBookmark({id : id, keep : 1});\r
7221 \r
7222                         return {id : id};\r
7223                 },\r
7224 \r
7225                 moveToBookmark : function(bookmark) {\r
7226                         var t = this, dom = t.dom, marker1, marker2, rng, root, startContainer, endContainer, startOffset, endOffset;\r
7227 \r
7228                         // Clear selection cache\r
7229                         if (t.tridentSel)\r
7230                                 t.tridentSel.destroy();\r
7231 \r
7232                         if (bookmark) {\r
7233                                 if (bookmark.start) {\r
7234                                         rng = dom.createRng();\r
7235                                         root = dom.getRoot();\r
7236 \r
7237                                         function setEndPoint(start) {\r
7238                                                 var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;\r
7239 \r
7240                                                 if (point) {\r
7241                                                         offset = point[0];\r
7242 \r
7243                                                         // Find container node\r
7244                                                         for (node = root, i = point.length - 1; i >= 1; i--) {\r
7245                                                                 children = node.childNodes;\r
7246 \r
7247                                                                 if (point[i] > children.length - 1)\r
7248                                                                         return;\r
7249 \r
7250                                                                 node = children[point[i]];\r
7251                                                         }\r
7252 \r
7253                                                         // Move text offset to best suitable location\r
7254                                                         if (node.nodeType === 3)\r
7255                                                                 offset = Math.min(point[0], node.nodeValue.length);\r
7256 \r
7257                                                         // Move element offset to best suitable location\r
7258                                                         if (node.nodeType === 1)\r
7259                                                                 offset = Math.min(point[0], node.childNodes.length);\r
7260 \r
7261                                                         // Set offset within container node\r
7262                                                         if (start)\r
7263                                                                 rng.setStart(node, offset);\r
7264                                                         else\r
7265                                                                 rng.setEnd(node, offset);\r
7266                                                 }\r
7267 \r
7268                                                 return true;\r
7269                                         };\r
7270 \r
7271                                         if (setEndPoint(true) && setEndPoint()) {\r
7272                                                 t.setRng(rng);\r
7273                                         }\r
7274                                 } else if (bookmark.id) {\r
7275                                         function restoreEndPoint(suffix) {\r
7276                                                 var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;\r
7277 \r
7278                                                 if (marker) {\r
7279                                                         node = marker.parentNode;\r
7280 \r
7281                                                         if (suffix == 'start') {\r
7282                                                                 if (!keep) {\r
7283                                                                         idx = dom.nodeIndex(marker);\r
7284                                                                 } else {\r
7285                                                                         node = marker.firstChild;\r
7286                                                                         idx = 1;\r
7287                                                                 }\r
7288 \r
7289                                                                 startContainer = endContainer = node;\r
7290                                                                 startOffset = endOffset = idx;\r
7291                                                         } else {\r
7292                                                                 if (!keep) {\r
7293                                                                         idx = dom.nodeIndex(marker);\r
7294                                                                 } else {\r
7295                                                                         node = marker.firstChild;\r
7296                                                                         idx = 1;\r
7297                                                                 }\r
7298 \r
7299                                                                 endContainer = node;\r
7300                                                                 endOffset = idx;\r
7301                                                         }\r
7302 \r
7303                                                         if (!keep) {\r
7304                                                                 prev = marker.previousSibling;\r
7305                                                                 next = marker.nextSibling;\r
7306 \r
7307                                                                 // Remove all marker text nodes\r
7308                                                                 each(tinymce.grep(marker.childNodes), function(node) {\r
7309                                                                         if (node.nodeType == 3)\r
7310                                                                                 node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');\r
7311                                                                 });\r
7312 \r
7313                                                                 // Remove marker but keep children if for example contents where inserted into the marker\r
7314                                                                 // Also remove duplicated instances of the marker for example by a split operation or by WebKit auto split on paste feature\r
7315                                                                 while (marker = dom.get(bookmark.id + '_' + suffix))\r
7316                                                                         dom.remove(marker, 1);\r
7317 \r
7318                                                                 // If siblings are text nodes then merge them unless it's Opera since it some how removes the node\r
7319                                                                 // and we are sniffing since adding a lot of detection code for a browser with 3% of the market isn't worth the effort. Sorry, Opera but it's just a fact\r
7320                                                                 if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !tinymce.isOpera) {\r
7321                                                                         idx = prev.nodeValue.length;\r
7322                                                                         prev.appendData(next.nodeValue);\r
7323                                                                         dom.remove(next);\r
7324 \r
7325                                                                         if (suffix == 'start') {\r
7326                                                                                 startContainer = endContainer = prev;\r
7327                                                                                 startOffset = endOffset = idx;\r
7328                                                                         } else {\r
7329                                                                                 endContainer = prev;\r
7330                                                                                 endOffset = idx;\r
7331                                                                         }\r
7332                                                                 }\r
7333                                                         }\r
7334                                                 }\r
7335                                         };\r
7336 \r
7337                                         function addBogus(node) {\r
7338                                                 // Adds a bogus BR element for empty block elements or just a space on IE since it renders BR elements incorrectly\r
7339                                                 if (dom.isBlock(node) && !node.innerHTML)\r
7340                                                         node.innerHTML = !isIE ? '<br data-mce-bogus="1" />' : ' ';\r
7341 \r
7342                                                 return node;\r
7343                                         };\r
7344 \r
7345                                         // Restore start/end points\r
7346                                         restoreEndPoint('start');\r
7347                                         restoreEndPoint('end');\r
7348 \r
7349                                         if (startContainer) {\r
7350                                                 rng = dom.createRng();\r
7351                                                 rng.setStart(addBogus(startContainer), startOffset);\r
7352                                                 rng.setEnd(addBogus(endContainer), endOffset);\r
7353                                                 t.setRng(rng);\r
7354                                         }\r
7355                                 } else if (bookmark.name) {\r
7356                                         t.select(dom.select(bookmark.name)[bookmark.index]);\r
7357                                 } else if (bookmark.rng)\r
7358                                         t.setRng(bookmark.rng);\r
7359                         }\r
7360                 },\r
7361 \r
7362                 select : function(node, content) {\r
7363                         var t = this, dom = t.dom, rng = dom.createRng(), idx;\r
7364 \r
7365                         if (node) {\r
7366                                 idx = dom.nodeIndex(node);\r
7367                                 rng.setStart(node.parentNode, idx);\r
7368                                 rng.setEnd(node.parentNode, idx + 1);\r
7369 \r
7370                                 // Find first/last text node or BR element\r
7371                                 if (content) {\r
7372                                         function setPoint(node, start) {\r
7373                                                 var walker = new tinymce.dom.TreeWalker(node, node);\r
7374 \r
7375                                                 do {\r
7376                                                         // Text node\r
7377                                                         if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length != 0) {\r
7378                                                                 if (start)\r
7379                                                                         rng.setStart(node, 0);\r
7380                                                                 else\r
7381                                                                         rng.setEnd(node, node.nodeValue.length);\r
7382 \r
7383                                                                 return;\r
7384                                                         }\r
7385 \r
7386                                                         // BR element\r
7387                                                         if (node.nodeName == 'BR') {\r
7388                                                                 if (start)\r
7389                                                                         rng.setStartBefore(node);\r
7390                                                                 else\r
7391                                                                         rng.setEndBefore(node);\r
7392 \r
7393                                                                 return;\r
7394                                                         }\r
7395                                                 } while (node = (start ? walker.next() : walker.prev()));\r
7396                                         };\r
7397 \r
7398                                         setPoint(node, 1);\r
7399                                         setPoint(node);\r
7400                                 }\r
7401 \r
7402                                 t.setRng(rng);\r
7403                         }\r
7404 \r
7405                         return node;\r
7406                 },\r
7407 \r
7408                 isCollapsed : function() {\r
7409                         var t = this, r = t.getRng(), s = t.getSel();\r
7410 \r
7411                         if (!r || r.item)\r
7412                                 return false;\r
7413 \r
7414                         if (r.compareEndPoints)\r
7415                                 return r.compareEndPoints('StartToEnd', r) === 0;\r
7416 \r
7417                         return !s || r.collapsed;\r
7418                 },\r
7419 \r
7420                 collapse : function(to_start) {\r
7421                         var self = this, rng = self.getRng(), node;\r
7422 \r
7423                         // Control range on IE\r
7424                         if (rng.item) {\r
7425                                 node = rng.item(0);\r
7426                                 rng = self.win.document.body.createTextRange();\r
7427                                 rng.moveToElementText(node);\r
7428                         }\r
7429 \r
7430                         rng.collapse(!!to_start);\r
7431                         self.setRng(rng);\r
7432                 },\r
7433 \r
7434                 getSel : function() {\r
7435                         var t = this, w = this.win;\r
7436 \r
7437                         return w.getSelection ? w.getSelection() : w.document.selection;\r
7438                 },\r
7439 \r
7440                 getRng : function(w3c) {\r
7441                         var t = this, s, r, elm, doc = t.win.document;\r
7442 \r
7443                         // Found tridentSel object then we need to use that one\r
7444                         if (w3c && t.tridentSel)\r
7445                                 return t.tridentSel.getRangeAt(0);\r
7446 \r
7447                         try {\r
7448                                 if (s = t.getSel())\r
7449                                         r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : doc.createRange());\r
7450                         } catch (ex) {\r
7451                                 // IE throws unspecified error here if TinyMCE is placed in a frame/iframe\r
7452                         }\r
7453 \r
7454                         // We have W3C ranges and it's IE then fake control selection since IE9 doesn't handle that correctly yet\r
7455                         if (tinymce.isIE && r && r.setStart && doc.selection.createRange().item) {\r
7456                                 elm = doc.selection.createRange().item(0);\r
7457                                 r = doc.createRange();\r
7458                                 r.setStartBefore(elm);\r
7459                                 r.setEndAfter(elm);\r
7460                         }\r
7461 \r
7462                         // No range found then create an empty one\r
7463                         // This can occur when the editor is placed in a hidden container element on Gecko\r
7464                         // Or on IE when there was an exception\r
7465                         if (!r)\r
7466                                 r = doc.createRange ? doc.createRange() : doc.body.createTextRange();\r
7467 \r
7468                         if (t.selectedRange && t.explicitRange) {\r
7469                                 if (r.compareBoundaryPoints(r.START_TO_START, t.selectedRange) === 0 && r.compareBoundaryPoints(r.END_TO_END, t.selectedRange) === 0) {\r
7470                                         // Safari, Opera and Chrome only ever select text which causes the range to change.\r
7471                                         // This lets us use the originally set range if the selection hasn't been changed by the user.\r
7472                                         r = t.explicitRange;\r
7473                                 } else {\r
7474                                         t.selectedRange = null;\r
7475                                         t.explicitRange = null;\r
7476                                 }\r
7477                         }\r
7478 \r
7479                         return r;\r
7480                 },\r
7481 \r
7482                 setRng : function(r) {\r
7483                         var s, t = this;\r
7484                         \r
7485                         if (!t.tridentSel) {\r
7486                                 s = t.getSel();\r
7487 \r
7488                                 if (s) {\r
7489                                         t.explicitRange = r;\r
7490 \r
7491                                         try {\r
7492                                                 s.removeAllRanges();\r
7493                                         } catch (ex) {\r
7494                                                 // IE9 might throw errors here don't know why\r
7495                                         }\r
7496 \r
7497                                         s.addRange(r);\r
7498                                         t.selectedRange = s.getRangeAt(0);\r
7499                                 }\r
7500                         } else {\r
7501                                 // Is W3C Range\r
7502                                 if (r.cloneRange) {\r
7503                                         t.tridentSel.addRange(r);\r
7504                                         return;\r
7505                                 }\r
7506 \r
7507                                 // Is IE specific range\r
7508                                 try {\r
7509                                         r.select();\r
7510                                 } catch (ex) {\r
7511                                         // Needed for some odd IE bug #1843306\r
7512                                 }\r
7513                         }\r
7514                 },\r
7515 \r
7516                 setNode : function(n) {\r
7517                         var t = this;\r
7518 \r
7519                         t.setContent(t.dom.getOuterHTML(n));\r
7520 \r
7521                         return n;\r
7522                 },\r
7523 \r
7524                 getNode : function() {\r
7525                         var t = this, rng = t.getRng(), sel = t.getSel(), elm, start = rng.startContainer, end = rng.endContainer;\r
7526 \r
7527                         // Range maybe lost after the editor is made visible again\r
7528                         if (!rng)\r
7529                                 return t.dom.getRoot();\r
7530 \r
7531                         if (rng.setStart) {\r
7532                                 elm = rng.commonAncestorContainer;\r
7533 \r
7534                                 // Handle selection a image or other control like element such as anchors\r
7535                                 if (!rng.collapsed) {\r
7536                                         if (rng.startContainer == rng.endContainer) {\r
7537                                                 if (rng.endOffset - rng.startOffset < 2) {\r
7538                                                         if (rng.startContainer.hasChildNodes())\r
7539                                                                 elm = rng.startContainer.childNodes[rng.startOffset];\r
7540                                                 }\r
7541                                         }\r
7542 \r
7543                                         // If the anchor node is a element instead of a text node then return this element\r
7544                                         //if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1) \r
7545                                         //      return sel.anchorNode.childNodes[sel.anchorOffset];\r
7546 \r
7547                                         // Handle cases where the selection is immediately wrapped around a node and return that node instead of it's parent.\r
7548                                         // This happens when you double click an underlined word in FireFox.\r
7549                                         if (start.nodeType === 3 && end.nodeType === 3) {\r
7550                                                 function skipEmptyTextNodes(n, forwards) {\r
7551                                                         var orig = n;\r
7552                                                         while (n && n.nodeType === 3 && n.length === 0) {\r
7553                                                                 n = forwards ? n.nextSibling : n.previousSibling;\r
7554                                                         }\r
7555                                                         return n || orig;\r
7556                                                 }\r
7557                                                 if (start.length === rng.startOffset) {\r
7558                                                         start = skipEmptyTextNodes(start.nextSibling, true);\r
7559                                                 } else {\r
7560                                                         start = start.parentNode;\r
7561                                                 }\r
7562                                                 if (rng.endOffset === 0) {\r
7563                                                         end = skipEmptyTextNodes(end.previousSibling, false);\r
7564                                                 } else {\r
7565                                                         end = end.parentNode;\r
7566                                                 }\r
7567 \r
7568                                                 if (start && start === end)\r
7569                                                         return start;\r
7570                                         }\r
7571                                 }\r
7572 \r
7573                                 if (elm && elm.nodeType == 3)\r
7574                                         return elm.parentNode;\r
7575 \r
7576                                 return elm;\r
7577                         }\r
7578 \r
7579                         return rng.item ? rng.item(0) : rng.parentElement();\r
7580                 },\r
7581 \r
7582                 getSelectedBlocks : function(st, en) {\r
7583                         var t = this, dom = t.dom, sb, eb, n, bl = [];\r
7584 \r
7585                         sb = dom.getParent(st || t.getStart(), dom.isBlock);\r
7586                         eb = dom.getParent(en || t.getEnd(), dom.isBlock);\r
7587 \r
7588                         if (sb)\r
7589                                 bl.push(sb);\r
7590 \r
7591                         if (sb && eb && sb != eb) {\r
7592                                 n = sb;\r
7593 \r
7594                                 while ((n = n.nextSibling) && n != eb) {\r
7595                                         if (dom.isBlock(n))\r
7596                                                 bl.push(n);\r
7597                                 }\r
7598                         }\r
7599 \r
7600                         if (eb && sb != eb)\r
7601                                 bl.push(eb);\r
7602 \r
7603                         return bl;\r
7604                 },\r
7605 \r
7606                 destroy : function(s) {\r
7607                         var t = this;\r
7608 \r
7609                         t.win = null;\r
7610 \r
7611                         if (t.tridentSel)\r
7612                                 t.tridentSel.destroy();\r
7613 \r
7614                         // Manual destroy then remove unload handler\r
7615                         if (!s)\r
7616                                 tinymce.removeUnload(t.destroy);\r
7617                 },\r
7618 \r
7619                 // IE has an issue where you can't select/move the caret by clicking outside the body if the document is in standards mode\r
7620                 _fixIESelection : function() {\r
7621                         var dom = this.dom, doc = dom.doc, body = doc.body, started, startRng, htmlElm;\r
7622 \r
7623                         // Make HTML element unselectable since we are going to handle selection by hand\r
7624                         doc.documentElement.unselectable = true;\r
7625 \r
7626                         // Return range from point or null if it failed\r
7627                         function rngFromPoint(x, y) {\r
7628                                 var rng = body.createTextRange();\r
7629 \r
7630                                 try {\r
7631                                         rng.moveToPoint(x, y);\r
7632                                 } catch (ex) {\r
7633                                         // IE sometimes throws and exception, so lets just ignore it\r
7634                                         rng = null;\r
7635                                 }\r
7636 \r
7637                                 return rng;\r
7638                         };\r
7639 \r
7640                         // Fires while the selection is changing\r
7641                         function selectionChange(e) {\r
7642                                 var pointRng;\r
7643 \r
7644                                 // Check if the button is down or not\r
7645                                 if (e.button) {\r
7646                                         // Create range from mouse position\r
7647                                         pointRng = rngFromPoint(e.x, e.y);\r
7648 \r
7649                                         if (pointRng) {\r
7650                                                 // Check if pointRange is before/after selection then change the endPoint\r
7651                                                 if (pointRng.compareEndPoints('StartToStart', startRng) > 0)\r
7652                                                         pointRng.setEndPoint('StartToStart', startRng);\r
7653                                                 else\r
7654                                                         pointRng.setEndPoint('EndToEnd', startRng);\r
7655 \r
7656                                                 pointRng.select();\r
7657                                         }\r
7658                                 } else\r
7659                                         endSelection();\r
7660                         }\r
7661 \r
7662                         // Removes listeners\r
7663                         function endSelection() {\r
7664                                 var rng = doc.selection.createRange();\r
7665 \r
7666                                 // If the range is collapsed then use the last start range\r
7667                                 if (startRng && !rng.item && rng.compareEndPoints('StartToEnd', rng) === 0)\r
7668                                         startRng.select();\r
7669 \r
7670                                 dom.unbind(doc, 'mouseup', endSelection);\r
7671                                 dom.unbind(doc, 'mousemove', selectionChange);\r
7672                                 startRng = started = 0;\r
7673                         };\r
7674 \r
7675                         // Detect when user selects outside BODY\r
7676                         dom.bind(doc, ['mousedown', 'contextmenu'], function(e) {\r
7677                                 if (e.target.nodeName === 'HTML') {\r
7678                                         if (started)\r
7679                                                 endSelection();\r
7680 \r
7681                                         // Detect vertical scrollbar, since IE will fire a mousedown on the scrollbar and have target set as HTML\r
7682                                         htmlElm = doc.documentElement;\r
7683                                         if (htmlElm.scrollHeight > htmlElm.clientHeight)\r
7684                                                 return;\r
7685 \r
7686                                         started = 1;\r
7687                                         // Setup start position\r
7688                                         startRng = rngFromPoint(e.x, e.y);\r
7689                                         if (startRng) {\r
7690                                                 // Listen for selection change events\r
7691                                                 dom.bind(doc, 'mouseup', endSelection);\r
7692                                                 dom.bind(doc, 'mousemove', selectionChange);\r
7693 \r
7694                                                 dom.win.focus();\r
7695                                                 startRng.select();\r
7696                                         }\r
7697                                 }\r
7698                         });\r
7699                 }\r
7700         });\r
7701 })(tinymce);\r
7702 \r
7703 (function(tinymce) {\r
7704         tinymce.dom.Serializer = function(settings, dom, schema) {\r
7705                 var onPreProcess, onPostProcess, isIE = tinymce.isIE, each = tinymce.each, htmlParser;\r
7706 \r
7707                 // Support the old apply_source_formatting option\r
7708                 if (!settings.apply_source_formatting)\r
7709                         settings.indent = false;\r
7710 \r
7711                 settings.remove_trailing_brs = true;\r
7712 \r
7713                 // Default DOM and Schema if they are undefined\r
7714                 dom = dom || tinymce.DOM;\r
7715                 schema = schema || new tinymce.html.Schema(settings);\r
7716                 settings.entity_encoding = settings.entity_encoding || 'named';\r
7717 \r
7718                 onPreProcess = new tinymce.util.Dispatcher(self);\r
7719 \r
7720                 onPostProcess = new tinymce.util.Dispatcher(self);\r
7721 \r
7722                 htmlParser = new tinymce.html.DomParser(settings, schema);\r
7723 \r
7724                 // Convert move data-mce-src, data-mce-href and data-mce-style into nodes or process them if needed\r
7725                 htmlParser.addAttributeFilter('src,href,style', function(nodes, name) {\r
7726                         var i = nodes.length, node, value, internalName = 'data-mce-' + name, urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope, undef;\r
7727 \r
7728                         while (i--) {\r
7729                                 node = nodes[i];\r
7730 \r
7731                                 value = node.attributes.map[internalName];\r
7732                                 if (value !== undef) {\r
7733                                         // Set external name to internal value and remove internal\r
7734                                         node.attr(name, value.length > 0 ? value : null);\r
7735                                         node.attr(internalName, null);\r
7736                                 } else {\r
7737                                         // No internal attribute found then convert the value we have in the DOM\r
7738                                         value = node.attributes.map[name];\r
7739 \r
7740                                         if (name === "style")\r
7741                                                 value = dom.serializeStyle(dom.parseStyle(value), node.name);\r
7742                                         else if (urlConverter)\r
7743                                                 value = urlConverter.call(urlConverterScope, value, name, node.name);\r
7744 \r
7745                                         node.attr(name, value.length > 0 ? value : null);\r
7746                                 }\r
7747                         }\r
7748                 });\r
7749 \r
7750                 // Remove internal classes mceItem<..>\r
7751                 htmlParser.addAttributeFilter('class', function(nodes, name) {\r
7752                         var i = nodes.length, node, value;\r
7753 \r
7754                         while (i--) {\r
7755                                 node = nodes[i];\r
7756                                 value = node.attr('class').replace(/\s*mce(Item\w+|Selected)\s*/g, '');\r
7757                                 node.attr('class', value.length > 0 ? value : null);\r
7758                         }\r
7759                 });\r
7760 \r
7761                 // Remove bookmark elements\r
7762                 htmlParser.addAttributeFilter('data-mce-type', function(nodes, name, args) {\r
7763                         var i = nodes.length, node;\r
7764 \r
7765                         while (i--) {\r
7766                                 node = nodes[i];\r
7767 \r
7768                                 if (node.attributes.map['data-mce-type'] === 'bookmark' && !args.cleanup)\r
7769                                         node.remove();\r
7770                         }\r
7771                 });\r
7772 \r
7773                 // Force script into CDATA sections and remove the mce- prefix also add comments around styles\r
7774                 htmlParser.addNodeFilter('script,style', function(nodes, name) {\r
7775                         var i = nodes.length, node, value;\r
7776 \r
7777                         function trim(value) {\r
7778                                 return value.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n')\r
7779                                                 .replace(/^[\r\n]*|[\r\n]*$/g, '')\r
7780                                                 .replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, '')\r
7781                                                 .replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, '');\r
7782                         };\r
7783 \r
7784                         while (i--) {\r
7785                                 node = nodes[i];\r
7786                                 value = node.firstChild ? node.firstChild.value : '';\r
7787 \r
7788                                 if (name === "script") {\r
7789                                         // Remove mce- prefix from script elements\r
7790                                         node.attr('type', (node.attr('type') || 'text/javascript').replace(/^mce\-/, ''));\r
7791 \r
7792                                         if (value.length > 0)\r
7793                                                 node.firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>';\r
7794                                 } else {\r
7795                                         if (value.length > 0)\r
7796                                                 node.firstChild.value = '<!--\n' + trim(value) + '\n-->';\r
7797                                 }\r
7798                         }\r
7799                 });\r
7800 \r
7801                 // Convert comments to cdata and handle protected comments\r
7802                 htmlParser.addNodeFilter('#comment', function(nodes, name) {\r
7803                         var i = nodes.length, node;\r
7804 \r
7805                         while (i--) {\r
7806                                 node = nodes[i];\r
7807 \r
7808                                 if (node.value.indexOf('[CDATA[') === 0) {\r
7809                                         node.name = '#cdata';\r
7810                                         node.type = 4;\r
7811                                         node.value = node.value.replace(/^\[CDATA\[|\]\]$/g, '');\r
7812                                 } else if (node.value.indexOf('mce:protected ') === 0) {\r
7813                                         node.name = "#text";\r
7814                                         node.type = 3;\r
7815                                         node.raw = true;\r
7816                                         node.value = unescape(node.value).substr(14);\r
7817                                 }\r
7818                         }\r
7819                 });\r
7820 \r
7821                 htmlParser.addNodeFilter('xml:namespace,input', function(nodes, name) {\r
7822                         var i = nodes.length, node;\r
7823 \r
7824                         while (i--) {\r
7825                                 node = nodes[i];\r
7826                                 if (node.type === 7)\r
7827                                         node.remove();\r
7828                                 else if (node.type === 1) {\r
7829                                         if (name === "input" && !("type" in node.attributes.map))\r
7830                                                 node.attr('type', 'text');\r
7831                                 }\r
7832                         }\r
7833                 });\r
7834 \r
7835                 // Fix list elements, TODO: Replace this later\r
7836                 if (settings.fix_list_elements) {\r
7837                         htmlParser.addNodeFilter('ul,ol', function(nodes, name) {\r
7838                                 var i = nodes.length, node, parentNode;\r
7839 \r
7840                                 while (i--) {\r
7841                                         node = nodes[i];\r
7842                                         parentNode = node.parent;\r
7843 \r
7844                                         if (parentNode.name === 'ul' || parentNode.name === 'ol') {\r
7845                                                 if (node.prev && node.prev.name === 'li') {\r
7846                                                         node.prev.append(node);\r
7847                                                 }\r
7848                                         }\r
7849                                 }\r
7850                         });\r
7851                 }\r
7852 \r
7853                 // Remove internal data attributes\r
7854                 htmlParser.addAttributeFilter('data-mce-src,data-mce-href,data-mce-style', function(nodes, name) {\r
7855                         var i = nodes.length;\r
7856 \r
7857                         while (i--) {\r
7858                                 nodes[i].attr(name, null);\r
7859                         }\r
7860                 });\r
7861 \r
7862                 // Return public methods\r
7863                 return {\r
7864                         schema : schema,\r
7865 \r
7866                         addNodeFilter : htmlParser.addNodeFilter,\r
7867 \r
7868                         addAttributeFilter : htmlParser.addAttributeFilter,\r
7869 \r
7870                         onPreProcess : onPreProcess,\r
7871 \r
7872                         onPostProcess : onPostProcess,\r
7873 \r
7874                         serialize : function(node, args) {\r
7875                                 var impl, doc, oldDoc, htmlSerializer, content;\r
7876 \r
7877                                 // Explorer won't clone contents of script and style and the\r
7878                                 // selected index of select elements are cleared on a clone operation.\r
7879                                 if (isIE && dom.select('script,style,select').length > 0) {\r
7880                                         content = node.innerHTML;\r
7881                                         node = node.cloneNode(false);\r
7882                                         dom.setHTML(node, content);\r
7883                                 } else\r
7884                                         node = node.cloneNode(true);\r
7885 \r
7886                                 // Nodes needs to be attached to something in WebKit/Opera\r
7887                                 // Older builds of Opera crashes if you attach the node to an document created dynamically\r
7888                                 // and since we can't feature detect a crash we need to sniff the acutal build number\r
7889                                 // This fix will make DOM ranges and make Sizzle happy!\r
7890                                 impl = node.ownerDocument.implementation;\r
7891                                 if (impl.createHTMLDocument) {\r
7892                                         // Create an empty HTML document\r
7893                                         doc = impl.createHTMLDocument("");\r
7894 \r
7895                                         // Add the element or it's children if it's a body element to the new document\r
7896                                         each(node.nodeName == 'BODY' ? node.childNodes : [node], function(node) {\r
7897                                                 doc.body.appendChild(doc.importNode(node, true));\r
7898                                         });\r
7899 \r
7900                                         // Grab first child or body element for serialization\r
7901                                         if (node.nodeName != 'BODY')\r
7902                                                 node = doc.body.firstChild;\r
7903                                         else\r
7904                                                 node = doc.body;\r
7905 \r
7906                                         // set the new document in DOMUtils so createElement etc works\r
7907                                         oldDoc = dom.doc;\r
7908                                         dom.doc = doc;\r
7909                                 }\r
7910 \r
7911                                 args = args || {};\r
7912                                 args.format = args.format || 'html';\r
7913 \r
7914                                 // Pre process\r
7915                                 if (!args.no_events) {\r
7916                                         args.node = node;\r
7917                                         onPreProcess.dispatch(self, args);\r
7918                                 }\r
7919 \r
7920                                 // Setup serializer\r
7921                                 htmlSerializer = new tinymce.html.Serializer(settings, schema);\r
7922 \r
7923                                 // Parse and serialize HTML\r
7924                                 args.content = htmlSerializer.serialize(\r
7925                                         htmlParser.parse(args.getInner ? node.innerHTML : tinymce.trim(dom.getOuterHTML(node), args), args)\r
7926                                 );\r
7927 \r
7928                                 // Replace all BOM characters for now until we can find a better solution\r
7929                                 if (!args.cleanup)\r
7930                                         args.content = args.content.replace(/\uFEFF/g, '');\r
7931 \r
7932                                 // Post process\r
7933                                 if (!args.no_events)\r
7934                                         onPostProcess.dispatch(self, args);\r
7935 \r
7936                                 // Restore the old document if it was changed\r
7937                                 if (oldDoc)\r
7938                                         dom.doc = oldDoc;\r
7939 \r
7940                                 args.node = null;\r
7941 \r
7942                                 return args.content;\r
7943                         },\r
7944 \r
7945                         addRules : function(rules) {\r
7946                                 schema.addValidElements(rules);\r
7947                         },\r
7948 \r
7949                         setRules : function(rules) {\r
7950                                 schema.setValidElements(rules);\r
7951                         }\r
7952                 };\r
7953         };\r
7954 })(tinymce);\r
7955 (function(tinymce) {\r
7956         tinymce.dom.ScriptLoader = function(settings) {\r
7957                 var QUEUED = 0,\r
7958                         LOADING = 1,\r
7959                         LOADED = 2,\r
7960                         states = {},\r
7961                         queue = [],\r
7962                         scriptLoadedCallbacks = {},\r
7963                         queueLoadedCallbacks = [],\r
7964                         loading = 0,\r
7965                         undefined;\r
7966 \r
7967                 function loadScript(url, callback) {\r
7968                         var t = this, dom = tinymce.DOM, elm, uri, loc, id;\r
7969 \r
7970                         // Execute callback when script is loaded\r
7971                         function done() {\r
7972                                 dom.remove(id);\r
7973 \r
7974                                 if (elm)\r
7975                                         elm.onreadystatechange = elm.onload = elm = null;\r
7976 \r
7977                                 callback();\r
7978                         };\r
7979                         \r
7980                         function error() {\r
7981                                 // Report the error so it's easier for people to spot loading errors\r
7982                                 if (typeof(console) !== "undefined" && console.log)\r
7983                                         console.log("Failed to load: " + url);\r
7984 \r
7985                                 // We can't mark it as done if there is a load error since\r
7986                                 // A) We don't want to produce 404 errors on the server and\r
7987                                 // B) the onerror event won't fire on all browsers.\r
7988                                 // done();\r
7989                         };\r
7990 \r
7991                         id = dom.uniqueId();\r
7992 \r
7993                         if (tinymce.isIE6) {\r
7994                                 uri = new tinymce.util.URI(url);\r
7995                                 loc = location;\r
7996 \r
7997                                 // If script is from same domain and we\r
7998                                 // use IE 6 then use XHR since it's more reliable\r
7999                                 if (uri.host == loc.hostname && uri.port == loc.port && (uri.protocol + ':') == loc.protocol && uri.protocol.toLowerCase() != 'file') {\r
8000                                         tinymce.util.XHR.send({\r
8001                                                 url : tinymce._addVer(uri.getURI()),\r
8002                                                 success : function(content) {\r
8003                                                         // Create new temp script element\r
8004                                                         var script = dom.create('script', {\r
8005                                                                 type : 'text/javascript'\r
8006                                                         });\r
8007 \r
8008                                                         // Evaluate script in global scope\r
8009                                                         script.text = content;\r
8010                                                         document.getElementsByTagName('head')[0].appendChild(script);\r
8011                                                         dom.remove(script);\r
8012 \r
8013                                                         done();\r
8014                                                 },\r
8015                                                 \r
8016                                                 error : error\r
8017                                         });\r
8018 \r
8019                                         return;\r
8020                                 }\r
8021                         }\r
8022 \r
8023                         // Create new script element\r
8024                         elm = dom.create('script', {\r
8025                                 id : id,\r
8026                                 type : 'text/javascript',\r
8027                                 src : tinymce._addVer(url)\r
8028                         });\r
8029 \r
8030                         // Add onload listener for non IE browsers since IE9\r
8031                         // fires onload event before the script is parsed and executed\r
8032                         if (!tinymce.isIE)\r
8033                                 elm.onload = done;\r
8034 \r
8035                         // Add onerror event will get fired on some browsers but not all of them\r
8036                         elm.onerror = error;\r
8037 \r
8038                         // Opera 9.60 doesn't seem to fire the onreadystate event at correctly\r
8039                         if (!tinymce.isOpera) {\r
8040                                 elm.onreadystatechange = function() {\r
8041                                         var state = elm.readyState;\r
8042 \r
8043                                         // Loaded state is passed on IE 6 however there\r
8044                                         // are known issues with this method but we can't use\r
8045                                         // XHR in a cross domain loading\r
8046                                         if (state == 'complete' || state == 'loaded')\r
8047                                                 done();\r
8048                                 };\r
8049                         }\r
8050 \r
8051                         // Most browsers support this feature so we report errors\r
8052                         // for those at least to help users track their missing plugins etc\r
8053                         // todo: Removed since it produced error if the document is unloaded by navigating away, re-add it as an option\r
8054                         /*elm.onerror = function() {\r
8055                                 alert('Failed to load: ' + url);\r
8056                         };*/\r
8057 \r
8058                         // Add script to document\r
8059                         (document.getElementsByTagName('head')[0] || document.body).appendChild(elm);\r
8060                 };\r
8061 \r
8062                 this.isDone = function(url) {\r
8063                         return states[url] == LOADED;\r
8064                 };\r
8065 \r
8066                 this.markDone = function(url) {\r
8067                         states[url] = LOADED;\r
8068                 };\r
8069 \r
8070                 this.add = this.load = function(url, callback, scope) {\r
8071                         var item, state = states[url];\r
8072 \r
8073                         // Add url to load queue\r
8074                         if (state == undefined) {\r
8075                                 queue.push(url);\r
8076                                 states[url] = QUEUED;\r
8077                         }\r
8078 \r
8079                         if (callback) {\r
8080                                 // Store away callback for later execution\r
8081                                 if (!scriptLoadedCallbacks[url])\r
8082                                         scriptLoadedCallbacks[url] = [];\r
8083 \r
8084                                 scriptLoadedCallbacks[url].push({\r
8085                                         func : callback,\r
8086                                         scope : scope || this\r
8087                                 });\r
8088                         }\r
8089                 };\r
8090 \r
8091                 this.loadQueue = function(callback, scope) {\r
8092                         this.loadScripts(queue, callback, scope);\r
8093                 };\r
8094 \r
8095                 this.loadScripts = function(scripts, callback, scope) {\r
8096                         var loadScripts;\r
8097 \r
8098                         function execScriptLoadedCallbacks(url) {\r
8099                                 // Execute URL callback functions\r
8100                                 tinymce.each(scriptLoadedCallbacks[url], function(callback) {\r
8101                                         callback.func.call(callback.scope);\r
8102                                 });\r
8103 \r
8104                                 scriptLoadedCallbacks[url] = undefined;\r
8105                         };\r
8106 \r
8107                         queueLoadedCallbacks.push({\r
8108                                 func : callback,\r
8109                                 scope : scope || this\r
8110                         });\r
8111 \r
8112                         loadScripts = function() {\r
8113                                 var loadingScripts = tinymce.grep(scripts);\r
8114 \r
8115                                 // Current scripts has been handled\r
8116                                 scripts.length = 0;\r
8117 \r
8118                                 // Load scripts that needs to be loaded\r
8119                                 tinymce.each(loadingScripts, function(url) {\r
8120                                         // Script is already loaded then execute script callbacks directly\r
8121                                         if (states[url] == LOADED) {\r
8122                                                 execScriptLoadedCallbacks(url);\r
8123                                                 return;\r
8124                                         }\r
8125 \r
8126                                         // Is script not loading then start loading it\r
8127                                         if (states[url] != LOADING) {\r
8128                                                 states[url] = LOADING;\r
8129                                                 loading++;\r
8130 \r
8131                                                 loadScript(url, function() {\r
8132                                                         states[url] = LOADED;\r
8133                                                         loading--;\r
8134 \r
8135                                                         execScriptLoadedCallbacks(url);\r
8136 \r
8137                                                         // Load more scripts if they where added by the recently loaded script\r
8138                                                         loadScripts();\r
8139                                                 });\r
8140                                         }\r
8141                                 });\r
8142 \r
8143                                 // No scripts are currently loading then execute all pending queue loaded callbacks\r
8144                                 if (!loading) {\r
8145                                         tinymce.each(queueLoadedCallbacks, function(callback) {\r
8146                                                 callback.func.call(callback.scope);\r
8147                                         });\r
8148 \r
8149                                         queueLoadedCallbacks.length = 0;\r
8150                                 }\r
8151                         };\r
8152 \r
8153                         loadScripts();\r
8154                 };\r
8155         };\r
8156 \r
8157         // Global script loader\r
8158         tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();\r
8159 })(tinymce);\r
8160 \r
8161 tinymce.dom.TreeWalker = function(start_node, root_node) {\r
8162         var node = start_node;\r
8163 \r
8164         function findSibling(node, start_name, sibling_name, shallow) {\r
8165                 var sibling, parent;\r
8166 \r
8167                 if (node) {\r
8168                         // Walk into nodes if it has a start\r
8169                         if (!shallow && node[start_name])\r
8170                                 return node[start_name];\r
8171 \r
8172                         // Return the sibling if it has one\r
8173                         if (node != root_node) {\r
8174                                 sibling = node[sibling_name];\r
8175                                 if (sibling)\r
8176                                         return sibling;\r
8177 \r
8178                                 // Walk up the parents to look for siblings\r
8179                                 for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) {\r
8180                                         sibling = parent[sibling_name];\r
8181                                         if (sibling)\r
8182                                                 return sibling;\r
8183                                 }\r
8184                         }\r
8185                 }\r
8186         };\r
8187 \r
8188         this.current = function() {\r
8189                 return node;\r
8190         };\r
8191 \r
8192         this.next = function(shallow) {\r
8193                 return (node = findSibling(node, 'firstChild', 'nextSibling', shallow));\r
8194         };\r
8195 \r
8196         this.prev = function(shallow) {\r
8197                 return (node = findSibling(node, 'lastChild', 'previousSibling', shallow));\r
8198         };\r
8199 };\r
8200 \r
8201 (function(tinymce) {\r
8202         tinymce.dom.RangeUtils = function(dom) {\r
8203                 var INVISIBLE_CHAR = '\uFEFF';\r
8204 \r
8205                 this.walk = function(rng, callback) {\r
8206                         var startContainer = rng.startContainer,\r
8207                                 startOffset = rng.startOffset,\r
8208                                 endContainer = rng.endContainer,\r
8209                                 endOffset = rng.endOffset,\r
8210                                 ancestor, startPoint,\r
8211                                 endPoint, node, parent, siblings, nodes;\r
8212 \r
8213                         // Handle table cell selection the table plugin enables\r
8214                         // you to fake select table cells and perform formatting actions on them\r
8215                         nodes = dom.select('td.mceSelected,th.mceSelected');\r
8216                         if (nodes.length > 0) {\r
8217                                 tinymce.each(nodes, function(node) {\r
8218                                         callback([node]);\r
8219                                 });\r
8220 \r
8221                                 return;\r
8222                         }\r
8223 \r
8224                         function collectSiblings(node, name, end_node) {\r
8225                                 var siblings = [];\r
8226 \r
8227                                 for (; node && node != end_node; node = node[name])\r
8228                                         siblings.push(node);\r
8229 \r
8230                                 return siblings;\r
8231                         };\r
8232 \r
8233                         function findEndPoint(node, root) {\r
8234                                 do {\r
8235                                         if (node.parentNode == root)\r
8236                                                 return node;\r
8237 \r
8238                                         node = node.parentNode;\r
8239                                 } while(node);\r
8240                         };\r
8241 \r
8242                         function walkBoundary(start_node, end_node, next) {\r
8243                                 var siblingName = next ? 'nextSibling' : 'previousSibling';\r
8244 \r
8245                                 for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) {\r
8246                                         parent = node.parentNode;\r
8247                                         siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName);\r
8248 \r
8249                                         if (siblings.length) {\r
8250                                                 if (!next)\r
8251                                                         siblings.reverse();\r
8252 \r
8253                                                 callback(siblings);\r
8254                                         }\r
8255                                 }\r
8256                         };\r
8257 \r
8258                         // If index based start position then resolve it\r
8259                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes())\r
8260                                 startContainer = startContainer.childNodes[startOffset];\r
8261 \r
8262                         // If index based end position then resolve it\r
8263                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes())\r
8264                                 endContainer = endContainer.childNodes[Math.min(endOffset - 1, endContainer.childNodes.length - 1)];\r
8265 \r
8266                         // Find common ancestor and end points\r
8267                         ancestor = dom.findCommonAncestor(startContainer, endContainer);\r
8268 \r
8269                         // Same container\r
8270                         if (startContainer == endContainer)\r
8271                                 return callback([startContainer]);\r
8272 \r
8273                         // Process left side\r
8274                         for (node = startContainer; node; node = node.parentNode) {\r
8275                                 if (node == endContainer)\r
8276                                         return walkBoundary(startContainer, ancestor, true);\r
8277 \r
8278                                 if (node == ancestor)\r
8279                                         break;\r
8280                         }\r
8281 \r
8282                         // Process right side\r
8283                         for (node = endContainer; node; node = node.parentNode) {\r
8284                                 if (node == startContainer)\r
8285                                         return walkBoundary(endContainer, ancestor);\r
8286 \r
8287                                 if (node == ancestor)\r
8288                                         break;\r
8289                         }\r
8290 \r
8291                         // Find start/end point\r
8292                         startPoint = findEndPoint(startContainer, ancestor) || startContainer;\r
8293                         endPoint = findEndPoint(endContainer, ancestor) || endContainer;\r
8294 \r
8295                         // Walk left leaf\r
8296                         walkBoundary(startContainer, startPoint, true);\r
8297 \r
8298                         // Walk the middle from start to end point\r
8299                         siblings = collectSiblings(\r
8300                                 startPoint == startContainer ? startPoint : startPoint.nextSibling,\r
8301                                 'nextSibling',\r
8302                                 endPoint == endContainer ? endPoint.nextSibling : endPoint\r
8303                         );\r
8304 \r
8305                         if (siblings.length)\r
8306                                 callback(siblings);\r
8307 \r
8308                         // Walk right leaf\r
8309                         walkBoundary(endContainer, endPoint);\r
8310                 };\r
8311 \r
8312                 /*              this.split = function(rng) {\r
8313                         var startContainer = rng.startContainer,\r
8314                                 startOffset = rng.startOffset,\r
8315                                 endContainer = rng.endContainer,\r
8316                                 endOffset = rng.endOffset;\r
8317 \r
8318                         function splitText(node, offset) {\r
8319                                 if (offset == node.nodeValue.length)\r
8320                                         node.appendData(INVISIBLE_CHAR);\r
8321 \r
8322                                 node = node.splitText(offset);\r
8323 \r
8324                                 if (node.nodeValue === INVISIBLE_CHAR)\r
8325                                         node.nodeValue = '';\r
8326 \r
8327                                 return node;\r
8328                         };\r
8329 \r
8330                         // Handle single text node\r
8331                         if (startContainer == endContainer) {\r
8332                                 if (startContainer.nodeType == 3) {\r
8333                                         if (startOffset != 0)\r
8334                                                 startContainer = endContainer = splitText(startContainer, startOffset);\r
8335 \r
8336                                         if (endOffset - startOffset != startContainer.nodeValue.length)\r
8337                                                 splitText(startContainer, endOffset - startOffset);\r
8338                                 }\r
8339                         } else {\r
8340                                 // Split startContainer text node if needed\r
8341                                 if (startContainer.nodeType == 3 && startOffset != 0) {\r
8342                                         startContainer = splitText(startContainer, startOffset);\r
8343                                         startOffset = 0;\r
8344                                 }\r
8345 \r
8346                                 // Split endContainer text node if needed\r
8347                                 if (endContainer.nodeType == 3 && endOffset != endContainer.nodeValue.length) {\r
8348                                         endContainer = splitText(endContainer, endOffset).previousSibling;\r
8349                                         endOffset = endContainer.nodeValue.length;\r
8350                                 }\r
8351                         }\r
8352 \r
8353                         return {\r
8354                                 startContainer : startContainer,\r
8355                                 startOffset : startOffset,\r
8356                                 endContainer : endContainer,\r
8357                                 endOffset : endOffset\r
8358                         };\r
8359                 };\r
8360 */\r
8361         };\r
8362 \r
8363         tinymce.dom.RangeUtils.compareRanges = function(rng1, rng2) {\r
8364                 if (rng1 && rng2) {\r
8365                         // Compare native IE ranges\r
8366                         if (rng1.item || rng1.duplicate) {\r
8367                                 // Both are control ranges and the selected element matches\r
8368                                 if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))\r
8369                                         return true;\r
8370 \r
8371                                 // Both are text ranges and the range matches\r
8372                                 if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1))\r
8373                                         return true;\r
8374                         } else {\r
8375                                 // Compare w3c ranges\r
8376                                 return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;\r
8377                         }\r
8378                 }\r
8379 \r
8380                 return false;\r
8381         };\r
8382 })(tinymce);\r
8383 \r
8384 (function(tinymce) {\r
8385         var Event = tinymce.dom.Event, each = tinymce.each;\r
8386 \r
8387         tinymce.create('tinymce.ui.KeyboardNavigation', {\r
8388                 KeyboardNavigation: function(settings, dom) {\r
8389                         var t = this, root = settings.root, items = settings.items,\r
8390                                         enableUpDown = settings.enableUpDown, enableLeftRight = settings.enableLeftRight || !settings.enableUpDown,\r
8391                                         excludeFromTabOrder = settings.excludeFromTabOrder,\r
8392                                         itemFocussed, itemBlurred, rootKeydown, rootFocussed, focussedId;\r
8393 \r
8394                         dom = dom || tinymce.DOM;\r
8395 \r
8396                         itemFocussed = function(evt) {\r
8397                                 focussedId = evt.target.id;\r
8398                         };\r
8399                         \r
8400                         itemBlurred = function(evt) {\r
8401                                 dom.setAttrib(evt.target.id, 'tabindex', '-1');\r
8402                         };\r
8403                         \r
8404                         rootFocussed = function(evt) {\r
8405                                 var item = dom.get(focussedId);\r
8406                                 dom.setAttrib(item, 'tabindex', '0');\r
8407                                 item.focus();\r
8408                         };\r
8409                         \r
8410                         t.focus = function() {\r
8411                                 dom.get(focussedId).focus();\r
8412                         };\r
8413 \r
8414                         t.destroy = function() {\r
8415                                 each(items, function(item) {\r
8416                                         dom.unbind(dom.get(item.id), 'focus', itemFocussed);\r
8417                                         dom.unbind(dom.get(item.id), 'blur', itemBlurred);\r
8418                                 });\r
8419 \r
8420                                 dom.unbind(dom.get(root), 'focus', rootFocussed);\r
8421                                 dom.unbind(dom.get(root), 'keydown', rootKeydown);\r
8422 \r
8423                                 items = dom = root = t.focus = itemFocussed = itemBlurred = rootKeydown = rootFocussed = null;\r
8424                                 t.destroy = function() {};\r
8425                         };\r
8426                         \r
8427                         t.moveFocus = function(dir, evt) {\r
8428                                 var idx = -1, controls = t.controls, newFocus;\r
8429 \r
8430                                 if (!focussedId)\r
8431                                         return;\r
8432 \r
8433                                 each(items, function(item, index) {\r
8434                                         if (item.id === focussedId) {\r
8435                                                 idx = index;\r
8436                                                 return false;\r
8437                                         }\r
8438                                 });\r
8439 \r
8440                                 idx += dir;\r
8441                                 if (idx < 0) {\r
8442                                         idx = items.length - 1;\r
8443                                 } else if (idx >= items.length) {\r
8444                                         idx = 0;\r
8445                                 }\r
8446                                 \r
8447                                 newFocus = items[idx];\r
8448                                 dom.setAttrib(focussedId, 'tabindex', '-1');\r
8449                                 dom.setAttrib(newFocus.id, 'tabindex', '0');\r
8450                                 dom.get(newFocus.id).focus();\r
8451 \r
8452                                 if (settings.actOnFocus) {\r
8453                                         settings.onAction(newFocus.id);\r
8454                                 }\r
8455 \r
8456                                 if (evt)\r
8457                                         Event.cancel(evt);\r
8458                         };\r
8459                         \r
8460                         rootKeydown = function(evt) {\r
8461                                 var DOM_VK_LEFT = 37, DOM_VK_RIGHT = 39, DOM_VK_UP = 38, DOM_VK_DOWN = 40, DOM_VK_ESCAPE = 27, DOM_VK_ENTER = 14, DOM_VK_RETURN = 13, DOM_VK_SPACE = 32;\r
8462                                 \r
8463                                 switch (evt.keyCode) {\r
8464                                         case DOM_VK_LEFT:\r
8465                                                 if (enableLeftRight) t.moveFocus(-1);\r
8466                                                 break;\r
8467         \r
8468                                         case DOM_VK_RIGHT:\r
8469                                                 if (enableLeftRight) t.moveFocus(1);\r
8470                                                 break;\r
8471         \r
8472                                         case DOM_VK_UP:\r
8473                                                 if (enableUpDown) t.moveFocus(-1);\r
8474                                                 break;\r
8475 \r
8476                                         case DOM_VK_DOWN:\r
8477                                                 if (enableUpDown) t.moveFocus(1);\r
8478                                                 break;\r
8479 \r
8480                                         case DOM_VK_ESCAPE:\r
8481                                                 if (settings.onCancel) {\r
8482                                                         settings.onCancel();\r
8483                                                         Event.cancel(evt);\r
8484                                                 }\r
8485                                                 break;\r
8486 \r
8487                                         case DOM_VK_ENTER:\r
8488                                         case DOM_VK_RETURN:\r
8489                                         case DOM_VK_SPACE:\r
8490                                                 if (settings.onAction) {\r
8491                                                         settings.onAction(focussedId);\r
8492                                                         Event.cancel(evt);\r
8493                                                 }\r
8494                                                 break;\r
8495                                 }\r
8496                         };\r
8497 \r
8498                         // Set up state and listeners for each item.\r
8499                         each(items, function(item, idx) {\r
8500                                 var tabindex;\r
8501 \r
8502                                 if (!item.id) {\r
8503                                         item.id = dom.uniqueId('_mce_item_');\r
8504                                 }\r
8505 \r
8506                                 if (excludeFromTabOrder) {\r
8507                                         dom.bind(item.id, 'blur', itemBlurred);\r
8508                                         tabindex = '-1';\r
8509                                 } else {\r
8510                                         tabindex = (idx === 0 ? '0' : '-1');\r
8511                                 }\r
8512 \r
8513                                 dom.setAttrib(item.id, 'tabindex', tabindex);\r
8514                                 dom.bind(dom.get(item.id), 'focus', itemFocussed);\r
8515                         });\r
8516                         \r
8517                         // Setup initial state for root element.\r
8518                         if (items[0]){\r
8519                                 focussedId = items[0].id;\r
8520                         }\r
8521 \r
8522                         dom.setAttrib(root, 'tabindex', '-1');\r
8523                         \r
8524                         // Setup listeners for root element.\r
8525                         dom.bind(dom.get(root), 'focus', rootFocussed);\r
8526                         dom.bind(dom.get(root), 'keydown', rootKeydown);\r
8527                 }\r
8528         });\r
8529 })(tinymce);\r
8530 (function(tinymce) {\r
8531         // Shorten class names\r
8532         var DOM = tinymce.DOM, is = tinymce.is;\r
8533 \r
8534         tinymce.create('tinymce.ui.Control', {\r
8535                 Control : function(id, s, editor) {\r
8536                         this.id = id;\r
8537                         this.settings = s = s || {};\r
8538                         this.rendered = false;\r
8539                         this.onRender = new tinymce.util.Dispatcher(this);\r
8540                         this.classPrefix = '';\r
8541                         this.scope = s.scope || this;\r
8542                         this.disabled = 0;\r
8543                         this.active = 0;\r
8544                         this.editor = editor;\r
8545                 },\r
8546                 \r
8547                 setAriaProperty : function(property, value) {\r
8548                         var element = DOM.get(this.id + '_aria') || DOM.get(this.id);\r
8549                         if (element) {\r
8550                                 DOM.setAttrib(element, 'aria-' + property, !!value);\r
8551                         }\r
8552                 },\r
8553                 \r
8554                 focus : function() {\r
8555                         DOM.get(this.id).focus();\r
8556                 },\r
8557 \r
8558                 setDisabled : function(s) {\r
8559                         if (s != this.disabled) {\r
8560                                 this.setAriaProperty('disabled', s);\r
8561 \r
8562                                 this.setState('Disabled', s);\r
8563                                 this.setState('Enabled', !s);\r
8564                                 this.disabled = s;\r
8565                         }\r
8566                 },\r
8567 \r
8568                 isDisabled : function() {\r
8569                         return this.disabled;\r
8570                 },\r
8571 \r
8572                 setActive : function(s) {\r
8573                         if (s != this.active) {\r
8574                                 this.setState('Active', s);\r
8575                                 this.active = s;\r
8576                                 this.setAriaProperty('pressed', s);\r
8577                         }\r
8578                 },\r
8579 \r
8580                 isActive : function() {\r
8581                         return this.active;\r
8582                 },\r
8583 \r
8584                 setState : function(c, s) {\r
8585                         var n = DOM.get(this.id);\r
8586 \r
8587                         c = this.classPrefix + c;\r
8588 \r
8589                         if (s)\r
8590                                 DOM.addClass(n, c);\r
8591                         else\r
8592                                 DOM.removeClass(n, c);\r
8593                 },\r
8594 \r
8595                 isRendered : function() {\r
8596                         return this.rendered;\r
8597                 },\r
8598 \r
8599                 renderHTML : function() {\r
8600                 },\r
8601 \r
8602                 renderTo : function(n) {\r
8603                         DOM.setHTML(n, this.renderHTML());\r
8604                 },\r
8605 \r
8606                 postRender : function() {\r
8607                         var t = this, b;\r
8608 \r
8609                         // Set pending states\r
8610                         if (is(t.disabled)) {\r
8611                                 b = t.disabled;\r
8612                                 t.disabled = -1;\r
8613                                 t.setDisabled(b);\r
8614                         }\r
8615 \r
8616                         if (is(t.active)) {\r
8617                                 b = t.active;\r
8618                                 t.active = -1;\r
8619                                 t.setActive(b);\r
8620                         }\r
8621                 },\r
8622 \r
8623                 remove : function() {\r
8624                         DOM.remove(this.id);\r
8625                         this.destroy();\r
8626                 },\r
8627 \r
8628                 destroy : function() {\r
8629                         tinymce.dom.Event.clear(this.id);\r
8630                 }\r
8631         });\r
8632 })(tinymce);\r
8633 tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {\r
8634         Container : function(id, s, editor) {\r
8635                 this.parent(id, s, editor);\r
8636 \r
8637                 this.controls = [];\r
8638 \r
8639                 this.lookup = {};\r
8640         },\r
8641 \r
8642         add : function(c) {\r
8643                 this.lookup[c.id] = c;\r
8644                 this.controls.push(c);\r
8645 \r
8646                 return c;\r
8647         },\r
8648 \r
8649         get : function(n) {\r
8650                 return this.lookup[n];\r
8651         }\r
8652 });\r
8653 \r
8654 \r
8655 tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {\r
8656         Separator : function(id, s) {\r
8657                 this.parent(id, s);\r
8658                 this.classPrefix = 'mceSeparator';\r
8659                 this.setDisabled(true);\r
8660         },\r
8661 \r
8662         renderHTML : function() {\r
8663                 return tinymce.DOM.createHTML('span', {'class' : this.classPrefix, role : 'separator', 'aria-orientation' : 'vertical', tabindex : '-1'});\r
8664         }\r
8665 });\r
8666 \r
8667 (function(tinymce) {\r
8668         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
8669 \r
8670         tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', {\r
8671                 MenuItem : function(id, s) {\r
8672                         this.parent(id, s);\r
8673                         this.classPrefix = 'mceMenuItem';\r
8674                 },\r
8675 \r
8676                 setSelected : function(s) {\r
8677                         this.setState('Selected', s);\r
8678                         this.setAriaProperty('checked', !!s);\r
8679                         this.selected = s;\r
8680                 },\r
8681 \r
8682                 isSelected : function() {\r
8683                         return this.selected;\r
8684                 },\r
8685 \r
8686                 postRender : function() {\r
8687                         var t = this;\r
8688                         \r
8689                         t.parent();\r
8690 \r
8691                         // Set pending state\r
8692                         if (is(t.selected))\r
8693                                 t.setSelected(t.selected);\r
8694                 }\r
8695         });\r
8696 })(tinymce);\r
8697 \r
8698 (function(tinymce) {\r
8699         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
8700 \r
8701         tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', {\r
8702                 Menu : function(id, s) {\r
8703                         var t = this;\r
8704 \r
8705                         t.parent(id, s);\r
8706                         t.items = {};\r
8707                         t.collapsed = false;\r
8708                         t.menuCount = 0;\r
8709                         t.onAddItem = new tinymce.util.Dispatcher(this);\r
8710                 },\r
8711 \r
8712                 expand : function(d) {\r
8713                         var t = this;\r
8714 \r
8715                         if (d) {\r
8716                                 walk(t, function(o) {\r
8717                                         if (o.expand)\r
8718                                                 o.expand();\r
8719                                 }, 'items', t);\r
8720                         }\r
8721 \r
8722                         t.collapsed = false;\r
8723                 },\r
8724 \r
8725                 collapse : function(d) {\r
8726                         var t = this;\r
8727 \r
8728                         if (d) {\r
8729                                 walk(t, function(o) {\r
8730                                         if (o.collapse)\r
8731                                                 o.collapse();\r
8732                                 }, 'items', t);\r
8733                         }\r
8734 \r
8735                         t.collapsed = true;\r
8736                 },\r
8737 \r
8738                 isCollapsed : function() {\r
8739                         return this.collapsed;\r
8740                 },\r
8741 \r
8742                 add : function(o) {\r
8743                         if (!o.settings)\r
8744                                 o = new tinymce.ui.MenuItem(o.id || DOM.uniqueId(), o);\r
8745 \r
8746                         this.onAddItem.dispatch(this, o);\r
8747 \r
8748                         return this.items[o.id] = o;\r
8749                 },\r
8750 \r
8751                 addSeparator : function() {\r
8752                         return this.add({separator : true});\r
8753                 },\r
8754 \r
8755                 addMenu : function(o) {\r
8756                         if (!o.collapse)\r
8757                                 o = this.createMenu(o);\r
8758 \r
8759                         this.menuCount++;\r
8760 \r
8761                         return this.add(o);\r
8762                 },\r
8763 \r
8764                 hasMenus : function() {\r
8765                         return this.menuCount !== 0;\r
8766                 },\r
8767 \r
8768                 remove : function(o) {\r
8769                         delete this.items[o.id];\r
8770                 },\r
8771 \r
8772                 removeAll : function() {\r
8773                         var t = this;\r
8774 \r
8775                         walk(t, function(o) {\r
8776                                 if (o.removeAll)\r
8777                                         o.removeAll();\r
8778                                 else\r
8779                                         o.remove();\r
8780 \r
8781                                 o.destroy();\r
8782                         }, 'items', t);\r
8783 \r
8784                         t.items = {};\r
8785                 },\r
8786 \r
8787                 createMenu : function(o) {\r
8788                         var m = new tinymce.ui.Menu(o.id || DOM.uniqueId(), o);\r
8789 \r
8790                         m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem);\r
8791 \r
8792                         return m;\r
8793                 }\r
8794         });\r
8795 })(tinymce);\r
8796 (function(tinymce) {\r
8797         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element;\r
8798 \r
8799         tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', {\r
8800                 DropMenu : function(id, s) {\r
8801                         s = s || {};\r
8802                         s.container = s.container || DOM.doc.body;\r
8803                         s.offset_x = s.offset_x || 0;\r
8804                         s.offset_y = s.offset_y || 0;\r
8805                         s.vp_offset_x = s.vp_offset_x || 0;\r
8806                         s.vp_offset_y = s.vp_offset_y || 0;\r
8807 \r
8808                         if (is(s.icons) && !s.icons)\r
8809                                 s['class'] += ' mceNoIcons';\r
8810 \r
8811                         this.parent(id, s);\r
8812                         this.onShowMenu = new tinymce.util.Dispatcher(this);\r
8813                         this.onHideMenu = new tinymce.util.Dispatcher(this);\r
8814                         this.classPrefix = 'mceMenu';\r
8815                 },\r
8816 \r
8817                 createMenu : function(s) {\r
8818                         var t = this, cs = t.settings, m;\r
8819 \r
8820                         s.container = s.container || cs.container;\r
8821                         s.parent = t;\r
8822                         s.constrain = s.constrain || cs.constrain;\r
8823                         s['class'] = s['class'] || cs['class'];\r
8824                         s.vp_offset_x = s.vp_offset_x || cs.vp_offset_x;\r
8825                         s.vp_offset_y = s.vp_offset_y || cs.vp_offset_y;\r
8826                         s.keyboard_focus = cs.keyboard_focus;\r
8827                         m = new tinymce.ui.DropMenu(s.id || DOM.uniqueId(), s);\r
8828 \r
8829                         m.onAddItem.add(t.onAddItem.dispatch, t.onAddItem);\r
8830 \r
8831                         return m;\r
8832                 },\r
8833                 \r
8834                 focus : function() {\r
8835                         var t = this;\r
8836                         if (t.keyboardNav) {\r
8837                                 t.keyboardNav.focus();\r
8838                         }\r
8839                 },\r
8840 \r
8841                 update : function() {\r
8842                         var t = this, s = t.settings, tb = DOM.get('menu_' + t.id + '_tbl'), co = DOM.get('menu_' + t.id + '_co'), tw, th;\r
8843 \r
8844                         tw = s.max_width ? Math.min(tb.clientWidth, s.max_width) : tb.clientWidth;\r
8845                         th = s.max_height ? Math.min(tb.clientHeight, s.max_height) : tb.clientHeight;\r
8846 \r
8847                         if (!DOM.boxModel)\r
8848                                 t.element.setStyles({width : tw + 2, height : th + 2});\r
8849                         else\r
8850                                 t.element.setStyles({width : tw, height : th});\r
8851 \r
8852                         if (s.max_width)\r
8853                                 DOM.setStyle(co, 'width', tw);\r
8854 \r
8855                         if (s.max_height) {\r
8856                                 DOM.setStyle(co, 'height', th);\r
8857 \r
8858                                 if (tb.clientHeight < s.max_height)\r
8859                                         DOM.setStyle(co, 'overflow', 'hidden');\r
8860                         }\r
8861                 },\r
8862 \r
8863                 showMenu : function(x, y, px) {\r
8864                         var t = this, s = t.settings, co, vp = DOM.getViewPort(), w, h, mx, my, ot = 2, dm, tb, cp = t.classPrefix;\r
8865 \r
8866                         t.collapse(1);\r
8867 \r
8868                         if (t.isMenuVisible)\r
8869                                 return;\r
8870 \r
8871                         if (!t.rendered) {\r
8872                                 co = DOM.add(t.settings.container, t.renderNode());\r
8873 \r
8874                                 each(t.items, function(o) {\r
8875                                         o.postRender();\r
8876                                 });\r
8877 \r
8878                                 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
8879                         } else\r
8880                                 co = DOM.get('menu_' + t.id);\r
8881 \r
8882                         // Move layer out of sight unless it's Opera since it scrolls to top of page due to an bug\r
8883                         if (!tinymce.isOpera)\r
8884                                 DOM.setStyles(co, {left : -0xFFFF , top : -0xFFFF});\r
8885 \r
8886                         DOM.show(co);\r
8887                         t.update();\r
8888 \r
8889                         x += s.offset_x || 0;\r
8890                         y += s.offset_y || 0;\r
8891                         vp.w -= 4;\r
8892                         vp.h -= 4;\r
8893 \r
8894                         // Move inside viewport if not submenu\r
8895                         if (s.constrain) {\r
8896                                 w = co.clientWidth - ot;\r
8897                                 h = co.clientHeight - ot;\r
8898                                 mx = vp.x + vp.w;\r
8899                                 my = vp.y + vp.h;\r
8900 \r
8901                                 if ((x + s.vp_offset_x + w) > mx)\r
8902                                         x = px ? px - w : Math.max(0, (mx - s.vp_offset_x) - w);\r
8903 \r
8904                                 if ((y + s.vp_offset_y + h) > my)\r
8905                                         y = Math.max(0, (my - s.vp_offset_y) - h);\r
8906                         }\r
8907 \r
8908                         DOM.setStyles(co, {left : x , top : y});\r
8909                         t.element.update();\r
8910 \r
8911                         t.isMenuVisible = 1;\r
8912                         t.mouseClickFunc = Event.add(co, 'click', function(e) {\r
8913                                 var m;\r
8914 \r
8915                                 e = e.target;\r
8916 \r
8917                                 if (e && (e = DOM.getParent(e, 'tr')) && !DOM.hasClass(e, cp + 'ItemSub')) {\r
8918                                         m = t.items[e.id];\r
8919 \r
8920                                         if (m.isDisabled())\r
8921                                                 return;\r
8922 \r
8923                                         dm = t;\r
8924 \r
8925                                         while (dm) {\r
8926                                                 if (dm.hideMenu)\r
8927                                                         dm.hideMenu();\r
8928 \r
8929                                                 dm = dm.settings.parent;\r
8930                                         }\r
8931 \r
8932                                         if (m.settings.onclick)\r
8933                                                 m.settings.onclick(e);\r
8934 \r
8935                                         return Event.cancel(e); // Cancel to fix onbeforeunload problem\r
8936                                 }\r
8937                         });\r
8938 \r
8939                         if (t.hasMenus()) {\r
8940                                 t.mouseOverFunc = Event.add(co, 'mouseover', function(e) {\r
8941                                         var m, r, mi;\r
8942 \r
8943                                         e = e.target;\r
8944                                         if (e && (e = DOM.getParent(e, 'tr'))) {\r
8945                                                 m = t.items[e.id];\r
8946 \r
8947                                                 if (t.lastMenu)\r
8948                                                         t.lastMenu.collapse(1);\r
8949 \r
8950                                                 if (m.isDisabled())\r
8951                                                         return;\r
8952 \r
8953                                                 if (e && DOM.hasClass(e, cp + 'ItemSub')) {\r
8954                                                         //p = DOM.getPos(s.container);\r
8955                                                         r = DOM.getRect(e);\r
8956                                                         m.showMenu((r.x + r.w - ot), r.y - ot, r.x);\r
8957                                                         t.lastMenu = m;\r
8958                                                         DOM.addClass(DOM.get(m.id).firstChild, cp + 'ItemActive');\r
8959                                                 }\r
8960                                         }\r
8961                                 });\r
8962                         }\r
8963                         \r
8964                         Event.add(co, 'keydown', t._keyHandler, t);\r
8965 \r
8966                         t.onShowMenu.dispatch(t);\r
8967 \r
8968                         if (s.keyboard_focus) { \r
8969                                 t._setupKeyboardNav(); \r
8970                         }\r
8971                 },\r
8972 \r
8973                 hideMenu : function(c) {\r
8974                         var t = this, co = DOM.get('menu_' + t.id), e;\r
8975 \r
8976                         if (!t.isMenuVisible)\r
8977                                 return;\r
8978 \r
8979                         if (t.keyboardNav) t.keyboardNav.destroy();\r
8980                         Event.remove(co, 'mouseover', t.mouseOverFunc);\r
8981                         Event.remove(co, 'click', t.mouseClickFunc);\r
8982                         Event.remove(co, 'keydown', t._keyHandler);\r
8983                         DOM.hide(co);\r
8984                         t.isMenuVisible = 0;\r
8985 \r
8986                         if (!c)\r
8987                                 t.collapse(1);\r
8988 \r
8989                         if (t.element)\r
8990                                 t.element.hide();\r
8991 \r
8992                         if (e = DOM.get(t.id))\r
8993                                 DOM.removeClass(e.firstChild, t.classPrefix + 'ItemActive');\r
8994 \r
8995                         t.onHideMenu.dispatch(t);\r
8996                 },\r
8997 \r
8998                 add : function(o) {\r
8999                         var t = this, co;\r
9000 \r
9001                         o = t.parent(o);\r
9002 \r
9003                         if (t.isRendered && (co = DOM.get('menu_' + t.id)))\r
9004                                 t._add(DOM.select('tbody', co)[0], o);\r
9005 \r
9006                         return o;\r
9007                 },\r
9008 \r
9009                 collapse : function(d) {\r
9010                         this.parent(d);\r
9011                         this.hideMenu(1);\r
9012                 },\r
9013 \r
9014                 remove : function(o) {\r
9015                         DOM.remove(o.id);\r
9016                         this.destroy();\r
9017 \r
9018                         return this.parent(o);\r
9019                 },\r
9020 \r
9021                 destroy : function() {\r
9022                         var t = this, co = DOM.get('menu_' + t.id);\r
9023 \r
9024                         if (t.keyboardNav) t.keyboardNav.destroy();\r
9025                         Event.remove(co, 'mouseover', t.mouseOverFunc);\r
9026                         Event.remove(DOM.select('a', co), 'focus', t.mouseOverFunc);\r
9027                         Event.remove(co, 'click', t.mouseClickFunc);\r
9028                         Event.remove(co, 'keydown', t._keyHandler);\r
9029 \r
9030                         if (t.element)\r
9031                                 t.element.remove();\r
9032 \r
9033                         DOM.remove(co);\r
9034                 },\r
9035 \r
9036                 renderNode : function() {\r
9037                         var t = this, s = t.settings, n, tb, co, w;\r
9038 \r
9039                         w = DOM.create('div', {role: 'listbox', id : 'menu_' + t.id, 'class' : s['class'], 'style' : 'position:absolute;left:0;top:0;z-index:200000;outline:0'});\r
9040                         if (t.settings.parent) {\r
9041                                 DOM.setAttrib(w, 'aria-parent', 'menu_' + t.settings.parent.id);\r
9042                         }\r
9043                         co = DOM.add(w, 'div', {role: 'presentation', id : 'menu_' + t.id + '_co', 'class' : t.classPrefix + (s['class'] ? ' ' + s['class'] : '')});\r
9044                         t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
9045 \r
9046                         if (s.menu_line)\r
9047                                 DOM.add(co, 'span', {'class' : t.classPrefix + 'Line'});\r
9048 \r
9049 //                      n = DOM.add(co, 'div', {id : 'menu_' + t.id + '_co', 'class' : 'mceMenuContainer'});\r
9050                         n = DOM.add(co, 'table', {role: 'presentation', id : 'menu_' + t.id + '_tbl', border : 0, cellPadding : 0, cellSpacing : 0});\r
9051                         tb = DOM.add(n, 'tbody');\r
9052 \r
9053                         each(t.items, function(o) {\r
9054                                 t._add(tb, o);\r
9055                         });\r
9056 \r
9057                         t.rendered = true;\r
9058 \r
9059                         return w;\r
9060                 },\r
9061 \r
9062                 // Internal functions\r
9063                 _setupKeyboardNav : function(){\r
9064                         var contextMenu, menuItems, t=this; \r
9065                         contextMenu = DOM.select('#menu_' + t.id)[0];\r
9066                         menuItems = DOM.select('a[role=option]', 'menu_' + t.id);\r
9067                         menuItems.splice(0,0,contextMenu);\r
9068                         t.keyboardNav = new tinymce.ui.KeyboardNavigation({\r
9069                                 root: 'menu_' + t.id,\r
9070                                 items: menuItems,\r
9071                                 onCancel: function() {\r
9072                                         t.hideMenu();\r
9073                                 },\r
9074                                 enableUpDown: true\r
9075                         });\r
9076                         contextMenu.focus();\r
9077                 },\r
9078 \r
9079                 _keyHandler : function(evt) {\r
9080                         var t = this, e;\r
9081                         switch (evt.keyCode) {\r
9082                                 case 37: // Left\r
9083                                         if (t.settings.parent) {\r
9084                                                 t.hideMenu();\r
9085                                                 t.settings.parent.focus();\r
9086                                                 Event.cancel(evt);\r
9087                                         }\r
9088                                         break;\r
9089                                 case 39: // Right\r
9090                                         if (t.mouseOverFunc)\r
9091                                                 t.mouseOverFunc(evt);\r
9092                                         break;\r
9093                         }\r
9094                 },\r
9095 \r
9096                 _add : function(tb, o) {\r
9097                         var n, s = o.settings, a, ro, it, cp = this.classPrefix, ic;\r
9098 \r
9099                         if (s.separator) {\r
9100                                 ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'ItemSeparator'});\r
9101                                 DOM.add(ro, 'td', {'class' : cp + 'ItemSeparator'});\r
9102 \r
9103                                 if (n = ro.previousSibling)\r
9104                                         DOM.addClass(n, 'mceLast');\r
9105 \r
9106                                 return;\r
9107                         }\r
9108 \r
9109                         n = ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'Item ' + cp + 'ItemEnabled'});\r
9110                         n = it = DOM.add(n, s.titleItem ? 'th' : 'td');\r
9111                         n = a = DOM.add(n, 'a', {id: o.id + '_aria',  role: s.titleItem ? 'presentation' : 'option', href : 'javascript:;', onclick : "return false;", onmousedown : 'return false;'});\r
9112 \r
9113                         if (s.parent) {\r
9114                                 DOM.setAttrib(a, 'aria-haspopup', 'true');\r
9115                                 DOM.setAttrib(a, 'aria-owns', 'menu_' + o.id);\r
9116                         }\r
9117 \r
9118                         DOM.addClass(it, s['class']);\r
9119 //                      n = DOM.add(n, 'span', {'class' : 'item'});\r
9120 \r
9121                         ic = DOM.add(n, 'span', {'class' : 'mceIcon' + (s.icon ? ' mce_' + s.icon : '')});\r
9122 \r
9123                         if (s.icon_src)\r
9124                                 DOM.add(ic, 'img', {src : s.icon_src});\r
9125 \r
9126                         n = DOM.add(n, s.element || 'span', {'class' : 'mceText', title : o.settings.title}, o.settings.title);\r
9127 \r
9128                         if (o.settings.style)\r
9129                                 DOM.setAttrib(n, 'style', o.settings.style);\r
9130 \r
9131                         if (tb.childNodes.length == 1)\r
9132                                 DOM.addClass(ro, 'mceFirst');\r
9133 \r
9134                         if ((n = ro.previousSibling) && DOM.hasClass(n, cp + 'ItemSeparator'))\r
9135                                 DOM.addClass(ro, 'mceFirst');\r
9136 \r
9137                         if (o.collapse)\r
9138                                 DOM.addClass(ro, cp + 'ItemSub');\r
9139 \r
9140                         if (n = ro.previousSibling)\r
9141                                 DOM.removeClass(n, 'mceLast');\r
9142 \r
9143                         DOM.addClass(ro, 'mceLast');\r
9144                 }\r
9145         });\r
9146 })(tinymce);\r
9147 (function(tinymce) {\r
9148         var DOM = tinymce.DOM;\r
9149 \r
9150         tinymce.create('tinymce.ui.Button:tinymce.ui.Control', {\r
9151                 Button : function(id, s, ed) {\r
9152                         this.parent(id, s, ed);\r
9153                         this.classPrefix = 'mceButton';\r
9154                 },\r
9155 \r
9156                 renderHTML : function() {\r
9157                         var cp = this.classPrefix, s = this.settings, h, l;\r
9158 \r
9159                         l = DOM.encode(s.label || '');\r
9160                         h = '<a role="button" id="' + this.id + '" href="javascript:;" class="' + cp + ' ' + cp + 'Enabled ' + s['class'] + (l ? ' ' + cp + 'Labeled' : '') +'" onmousedown="return false;" onclick="return false;" aria-labelledby="' + this.id + '_voice" title="' + DOM.encode(s.title) + '">';\r
9161 \r
9162                         if (s.image)\r
9163                                 h += '<img class="mceIcon" src="' + s.image + '" alt="' + DOM.encode(s.title) + '" />' + l;\r
9164                         else\r
9165                                 h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '');\r
9166 \r
9167                         h += '<span class="mceVoiceLabel mceIconOnly" style="display: none;" id="' + this.id + '_voice">' + s.title + '</span>'; \r
9168                         h += '</a>';\r
9169                         return h;\r
9170                 },\r
9171 \r
9172                 postRender : function() {\r
9173                         var t = this, s = t.settings;\r
9174 \r
9175                         tinymce.dom.Event.add(t.id, 'click', function(e) {\r
9176                                 if (!t.isDisabled())\r
9177                                         return s.onclick.call(s.scope, e);\r
9178                         });\r
9179                 }\r
9180         });\r
9181 })(tinymce);\r
9182 \r
9183 (function(tinymce) {\r
9184         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
9185 \r
9186         tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {\r
9187                 ListBox : function(id, s, ed) {\r
9188                         var t = this;\r
9189 \r
9190                         t.parent(id, s, ed);\r
9191 \r
9192                         t.items = [];\r
9193 \r
9194                         t.onChange = new Dispatcher(t);\r
9195 \r
9196                         t.onPostRender = new Dispatcher(t);\r
9197 \r
9198                         t.onAdd = new Dispatcher(t);\r
9199 \r
9200                         t.onRenderMenu = new tinymce.util.Dispatcher(this);\r
9201 \r
9202                         t.classPrefix = 'mceListBox';\r
9203                 },\r
9204 \r
9205                 select : function(va) {\r
9206                         var t = this, fv, f;\r
9207 \r
9208                         if (va == undefined)\r
9209                                 return t.selectByIndex(-1);\r
9210 \r
9211                         // Is string or number make function selector\r
9212                         if (va && va.call)\r
9213                                 f = va;\r
9214                         else {\r
9215                                 f = function(v) {\r
9216                                         return v == va;\r
9217                                 };\r
9218                         }\r
9219 \r
9220                         // Do we need to do something?\r
9221                         if (va != t.selectedValue) {\r
9222                                 // Find item\r
9223                                 each(t.items, function(o, i) {\r
9224                                         if (f(o.value)) {\r
9225                                                 fv = 1;\r
9226                                                 t.selectByIndex(i);\r
9227                                                 return false;\r
9228                                         }\r
9229                                 });\r
9230 \r
9231                                 if (!fv)\r
9232                                         t.selectByIndex(-1);\r
9233                         }\r
9234                 },\r
9235 \r
9236                 selectByIndex : function(idx) {\r
9237                         var t = this, e, o;\r
9238 \r
9239                         if (idx != t.selectedIndex) {\r
9240                                 e = DOM.get(t.id + '_text');\r
9241                                 o = t.items[idx];\r
9242 \r
9243                                 if (o) {\r
9244                                         t.selectedValue = o.value;\r
9245                                         t.selectedIndex = idx;\r
9246                                         DOM.setHTML(e, DOM.encode(o.title));\r
9247                                         DOM.removeClass(e, 'mceTitle');\r
9248                                         DOM.setAttrib(t.id, 'aria-valuenow', o.title);\r
9249                                 } else {\r
9250                                         DOM.setHTML(e, DOM.encode(t.settings.title));\r
9251                                         DOM.addClass(e, 'mceTitle');\r
9252                                         t.selectedValue = t.selectedIndex = null;\r
9253                                         DOM.setAttrib(t.id, 'aria-valuenow', t.settings.title);\r
9254                                 }\r
9255                                 e = 0;\r
9256                         }\r
9257                 },\r
9258 \r
9259                 add : function(n, v, o) {\r
9260                         var t = this;\r
9261 \r
9262                         o = o || {};\r
9263                         o = tinymce.extend(o, {\r
9264                                 title : n,\r
9265                                 value : v\r
9266                         });\r
9267 \r
9268                         t.items.push(o);\r
9269                         t.onAdd.dispatch(t, o);\r
9270                 },\r
9271 \r
9272                 getLength : function() {\r
9273                         return this.items.length;\r
9274                 },\r
9275 \r
9276                 renderHTML : function() {\r
9277                         var h = '', t = this, s = t.settings, cp = t.classPrefix;\r
9278 \r
9279                         h = '<span role="button" aria-haspopup="true" aria-labelledby="' + t.id +'_text" aria-describedby="' + t.id + '_voiceDesc"><table role="presentation" tabindex="0" id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>';\r
9280                         h += '<td>' + DOM.createHTML('span', {id: t.id + '_voiceDesc', 'class': 'voiceLabel', style:'display:none;'}, t.settings.title); \r
9281                         h += DOM.createHTML('a', {id : t.id + '_text', tabindex : -1, href : 'javascript:;', 'class' : 'mceText', onclick : "return false;", onmousedown : 'return false;'}, DOM.encode(t.settings.title)) + '</td>';\r
9282                         h += '<td>' + DOM.createHTML('a', {id : t.id + '_open', tabindex : -1, href : 'javascript:;', 'class' : 'mceOpen', onclick : "return false;", onmousedown : 'return false;'}, '<span><span style="display:none;" class="mceIconOnly" aria-hidden="true">\u25BC</span></span>') + '</td>';\r
9283                         h += '</tr></tbody></table></span>';\r
9284 \r
9285                         return h;\r
9286                 },\r
9287 \r
9288                 showMenu : function() {\r
9289                         var t = this, p1, p2, e = DOM.get(this.id), m;\r
9290 \r
9291                         if (t.isDisabled() || t.items.length == 0)\r
9292                                 return;\r
9293 \r
9294                         if (t.menu && t.menu.isMenuVisible)\r
9295                                 return t.hideMenu();\r
9296 \r
9297                         if (!t.isMenuRendered) {\r
9298                                 t.renderMenu();\r
9299                                 t.isMenuRendered = true;\r
9300                         }\r
9301 \r
9302                         p1 = DOM.getPos(this.settings.menu_container);\r
9303                         p2 = DOM.getPos(e);\r
9304 \r
9305                         m = t.menu;\r
9306                         m.settings.offset_x = p2.x;\r
9307                         m.settings.offset_y = p2.y;\r
9308                         m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus\r
9309 \r
9310                         // Select in menu\r
9311                         if (t.oldID)\r
9312                                 m.items[t.oldID].setSelected(0);\r
9313 \r
9314                         each(t.items, function(o) {\r
9315                                 if (o.value === t.selectedValue) {\r
9316                                         m.items[o.id].setSelected(1);\r
9317                                         t.oldID = o.id;\r
9318                                 }\r
9319                         });\r
9320 \r
9321                         m.showMenu(0, e.clientHeight);\r
9322 \r
9323                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
9324                         DOM.addClass(t.id, t.classPrefix + 'Selected');\r
9325 \r
9326                         //DOM.get(t.id + '_text').focus();\r
9327                 },\r
9328 \r
9329                 hideMenu : function(e) {\r
9330                         var t = this;\r
9331 \r
9332                         if (t.menu && t.menu.isMenuVisible) {\r
9333                                 DOM.removeClass(t.id, t.classPrefix + 'Selected');\r
9334 \r
9335                                 // Prevent double toogles by canceling the mouse click event to the button\r
9336                                 if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open'))\r
9337                                         return;\r
9338 \r
9339                                 if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
9340                                         DOM.removeClass(t.id, t.classPrefix + 'Selected');\r
9341                                         Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
9342                                         t.menu.hideMenu();\r
9343                                 }\r
9344                         }\r
9345                 },\r
9346 \r
9347                 renderMenu : function() {\r
9348                         var t = this, m;\r
9349 \r
9350                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
9351                                 menu_line : 1,\r
9352                                 'class' : t.classPrefix + 'Menu mceNoIcons',\r
9353                                 max_width : 150,\r
9354                                 max_height : 150\r
9355                         });\r
9356 \r
9357                         m.onHideMenu.add(function() {\r
9358                                 t.hideMenu();\r
9359                                 t.focus();\r
9360                         });\r
9361 \r
9362                         m.add({\r
9363                                 title : t.settings.title,\r
9364                                 'class' : 'mceMenuItemTitle',\r
9365                                 onclick : function() {\r
9366                                         if (t.settings.onselect('') !== false)\r
9367                                                 t.select(''); // Must be runned after\r
9368                                 }\r
9369                         });\r
9370 \r
9371                         each(t.items, function(o) {\r
9372                                 // No value then treat it as a title\r
9373                                 if (o.value === undefined) {\r
9374                                         m.add({\r
9375                                                 title : o.title,\r
9376                                                 'class' : 'mceMenuItemTitle',\r
9377                                                 onclick : function() {\r
9378                                                         if (t.settings.onselect('') !== false)\r
9379                                                                 t.select(''); // Must be runned after\r
9380                                                 }\r
9381                                         });\r
9382                                 } else {\r
9383                                         o.id = DOM.uniqueId();\r
9384                                         o.onclick = function() {\r
9385                                                 if (t.settings.onselect(o.value) !== false)\r
9386                                                         t.select(o.value); // Must be runned after\r
9387                                         };\r
9388 \r
9389                                         m.add(o);\r
9390                                 }\r
9391                         });\r
9392 \r
9393                         t.onRenderMenu.dispatch(t, m);\r
9394                         t.menu = m;\r
9395                 },\r
9396 \r
9397                 postRender : function() {\r
9398                         var t = this, cp = t.classPrefix;\r
9399 \r
9400                         Event.add(t.id, 'click', t.showMenu, t);\r
9401                         Event.add(t.id, 'keydown', function(evt) {\r
9402                                 if (evt.keyCode == 32) { // Space\r
9403                                         t.showMenu(evt);\r
9404                                         Event.cancel(evt);\r
9405                                 }\r
9406                         });\r
9407                         Event.add(t.id, 'focus', function() {\r
9408                                 if (!t._focused) {\r
9409                                         t.keyDownHandler = Event.add(t.id, 'keydown', function(e) {\r
9410                                                 if (e.keyCode == 40) {\r
9411                                                         t.showMenu();\r
9412                                                         Event.cancel(e);\r
9413                                                 }\r
9414                                         });\r
9415                                         t.keyPressHandler = Event.add(t.id, 'keypress', function(e) {\r
9416                                                 var v;\r
9417                                                 if (e.keyCode == 13) {\r
9418                                                         // Fake select on enter\r
9419                                                         v = t.selectedValue;\r
9420                                                         t.selectedValue = null; // Needs to be null to fake change\r
9421                                                         Event.cancel(e);\r
9422                                                         t.settings.onselect(v);\r
9423                                                 }\r
9424                                         });\r
9425                                 }\r
9426 \r
9427                                 t._focused = 1;\r
9428                         });\r
9429                         Event.add(t.id, 'blur', function() {\r
9430                                 Event.remove(t.id, 'keydown', t.keyDownHandler);\r
9431                                 Event.remove(t.id, 'keypress', t.keyPressHandler);\r
9432                                 t._focused = 0;\r
9433                         });\r
9434 \r
9435                         // Old IE doesn't have hover on all elements\r
9436                         if (tinymce.isIE6 || !DOM.boxModel) {\r
9437                                 Event.add(t.id, 'mouseover', function() {\r
9438                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
9439                                                 DOM.addClass(t.id, cp + 'Hover');\r
9440                                 });\r
9441 \r
9442                                 Event.add(t.id, 'mouseout', function() {\r
9443                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
9444                                                 DOM.removeClass(t.id, cp + 'Hover');\r
9445                                 });\r
9446                         }\r
9447 \r
9448                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
9449                 },\r
9450 \r
9451                 destroy : function() {\r
9452                         this.parent();\r
9453 \r
9454                         Event.clear(this.id + '_text');\r
9455                         Event.clear(this.id + '_open');\r
9456                 }\r
9457         });\r
9458 })(tinymce);\r
9459 (function(tinymce) {\r
9460         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
9461 \r
9462         tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {\r
9463                 NativeListBox : function(id, s) {\r
9464                         this.parent(id, s);\r
9465                         this.classPrefix = 'mceNativeListBox';\r
9466                 },\r
9467 \r
9468                 setDisabled : function(s) {\r
9469                         DOM.get(this.id).disabled = s;\r
9470                         this.setAriaProperty('disabled', s);\r
9471                 },\r
9472 \r
9473                 isDisabled : function() {\r
9474                         return DOM.get(this.id).disabled;\r
9475                 },\r
9476 \r
9477                 select : function(va) {\r
9478                         var t = this, fv, f;\r
9479 \r
9480                         if (va == undefined)\r
9481                                 return t.selectByIndex(-1);\r
9482 \r
9483                         // Is string or number make function selector\r
9484                         if (va && va.call)\r
9485                                 f = va;\r
9486                         else {\r
9487                                 f = function(v) {\r
9488                                         return v == va;\r
9489                                 };\r
9490                         }\r
9491 \r
9492                         // Do we need to do something?\r
9493                         if (va != t.selectedValue) {\r
9494                                 // Find item\r
9495                                 each(t.items, function(o, i) {\r
9496                                         if (f(o.value)) {\r
9497                                                 fv = 1;\r
9498                                                 t.selectByIndex(i);\r
9499                                                 return false;\r
9500                                         }\r
9501                                 });\r
9502 \r
9503                                 if (!fv)\r
9504                                         t.selectByIndex(-1);\r
9505                         }\r
9506                 },\r
9507 \r
9508                 selectByIndex : function(idx) {\r
9509                         DOM.get(this.id).selectedIndex = idx + 1;\r
9510                         this.selectedValue = this.items[idx] ? this.items[idx].value : null;\r
9511                 },\r
9512 \r
9513                 add : function(n, v, a) {\r
9514                         var o, t = this;\r
9515 \r
9516                         a = a || {};\r
9517                         a.value = v;\r
9518 \r
9519                         if (t.isRendered())\r
9520                                 DOM.add(DOM.get(this.id), 'option', a, n);\r
9521 \r
9522                         o = {\r
9523                                 title : n,\r
9524                                 value : v,\r
9525                                 attribs : a\r
9526                         };\r
9527 \r
9528                         t.items.push(o);\r
9529                         t.onAdd.dispatch(t, o);\r
9530                 },\r
9531 \r
9532                 getLength : function() {\r
9533                         return this.items.length;\r
9534                 },\r
9535 \r
9536                 renderHTML : function() {\r
9537                         var h, t = this;\r
9538 \r
9539                         h = DOM.createHTML('option', {value : ''}, '-- ' + t.settings.title + ' --');\r
9540 \r
9541                         each(t.items, function(it) {\r
9542                                 h += DOM.createHTML('option', {value : it.value}, it.title);\r
9543                         });\r
9544 \r
9545                         h = DOM.createHTML('select', {id : t.id, 'class' : 'mceNativeListBox', 'aria-labelledby': t.id + '_aria'}, h);\r
9546                         h += DOM.createHTML('span', {id : t.id + '_aria', 'style': 'display: none'}, t.settings.title);\r
9547                         return h;\r
9548                 },\r
9549 \r
9550                 postRender : function() {\r
9551                         var t = this, ch, changeListenerAdded = true;\r
9552 \r
9553                         t.rendered = true;\r
9554 \r
9555                         function onChange(e) {\r
9556                                 var v = t.items[e.target.selectedIndex - 1];\r
9557 \r
9558                                 if (v && (v = v.value)) {\r
9559                                         t.onChange.dispatch(t, v);\r
9560 \r
9561                                         if (t.settings.onselect)\r
9562                                                 t.settings.onselect(v);\r
9563                                 }\r
9564                         };\r
9565 \r
9566                         Event.add(t.id, 'change', onChange);\r
9567 \r
9568                         // Accessibility keyhandler\r
9569                         Event.add(t.id, 'keydown', function(e) {\r
9570                                 var bf;\r
9571 \r
9572                                 Event.remove(t.id, 'change', ch);\r
9573                                 changeListenerAdded = false;\r
9574 \r
9575                                 bf = Event.add(t.id, 'blur', function() {\r
9576                                         if (changeListenerAdded) return;\r
9577                                         changeListenerAdded = true;\r
9578                                         Event.add(t.id, 'change', onChange);\r
9579                                         Event.remove(t.id, 'blur', bf);\r
9580                                 });\r
9581 \r
9582                                 if (e.keyCode == 13 || e.keyCode == 32) {\r
9583                                         onChange(e);\r
9584                                         return Event.cancel(e);\r
9585                                 }\r
9586                         });\r
9587 \r
9588                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
9589                 }\r
9590         });\r
9591 })(tinymce);\r
9592 (function(tinymce) {\r
9593         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
9594 \r
9595         tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', {\r
9596                 MenuButton : function(id, s, ed) {\r
9597                         this.parent(id, s, ed);\r
9598 \r
9599                         this.onRenderMenu = new tinymce.util.Dispatcher(this);\r
9600 \r
9601                         s.menu_container = s.menu_container || DOM.doc.body;\r
9602                 },\r
9603 \r
9604                 showMenu : function() {\r
9605                         var t = this, p1, p2, e = DOM.get(t.id), m;\r
9606 \r
9607                         if (t.isDisabled())\r
9608                                 return;\r
9609 \r
9610                         if (!t.isMenuRendered) {\r
9611                                 t.renderMenu();\r
9612                                 t.isMenuRendered = true;\r
9613                         }\r
9614 \r
9615                         if (t.isMenuVisible)\r
9616                                 return t.hideMenu();\r
9617 \r
9618                         p1 = DOM.getPos(t.settings.menu_container);\r
9619                         p2 = DOM.getPos(e);\r
9620 \r
9621                         m = t.menu;\r
9622                         m.settings.offset_x = p2.x;\r
9623                         m.settings.offset_y = p2.y;\r
9624                         m.settings.vp_offset_x = p2.x;\r
9625                         m.settings.vp_offset_y = p2.y;\r
9626                         m.settings.keyboard_focus = t._focused;\r
9627                         m.showMenu(0, e.clientHeight);\r
9628 \r
9629                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
9630                         t.setState('Selected', 1);\r
9631 \r
9632                         t.isMenuVisible = 1;\r
9633                 },\r
9634 \r
9635                 renderMenu : function() {\r
9636                         var t = this, m;\r
9637 \r
9638                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
9639                                 menu_line : 1,\r
9640                                 'class' : this.classPrefix + 'Menu',\r
9641                                 icons : t.settings.icons\r
9642                         });\r
9643 \r
9644                         m.onHideMenu.add(function() {\r
9645                                 t.hideMenu();\r
9646                                 t.focus();\r
9647                         });\r
9648 \r
9649                         t.onRenderMenu.dispatch(t, m);\r
9650                         t.menu = m;\r
9651                 },\r
9652 \r
9653                 hideMenu : function(e) {\r
9654                         var t = this;\r
9655 \r
9656                         // Prevent double toogles by canceling the mouse click event to the button\r
9657                         if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id || e.id === t.id + '_open';}))\r
9658                                 return;\r
9659 \r
9660                         if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
9661                                 t.setState('Selected', 0);\r
9662                                 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
9663                                 if (t.menu)\r
9664                                         t.menu.hideMenu();\r
9665                         }\r
9666 \r
9667                         t.isMenuVisible = 0;\r
9668                 },\r
9669 \r
9670                 postRender : function() {\r
9671                         var t = this, s = t.settings;\r
9672 \r
9673                         Event.add(t.id, 'click', function() {\r
9674                                 if (!t.isDisabled()) {\r
9675                                         if (s.onclick)\r
9676                                                 s.onclick(t.value);\r
9677 \r
9678                                         t.showMenu();\r
9679                                 }\r
9680                         });\r
9681                 }\r
9682         });\r
9683 })(tinymce);\r
9684 \r
9685 (function(tinymce) {\r
9686         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
9687 \r
9688         tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', {\r
9689                 SplitButton : function(id, s, ed) {\r
9690                         this.parent(id, s, ed);\r
9691                         this.classPrefix = 'mceSplitButton';\r
9692                 },\r
9693 \r
9694                 renderHTML : function() {\r
9695                         var h, t = this, s = t.settings, h1;\r
9696 \r
9697                         h = '<tbody><tr>';\r
9698 \r
9699                         if (s.image)\r
9700                                 h1 = DOM.createHTML('img ', {src : s.image, role: 'presentation', 'class' : 'mceAction ' + s['class']});\r
9701                         else\r
9702                                 h1 = DOM.createHTML('span', {'class' : 'mceAction ' + s['class']}, '');\r
9703 \r
9704                         h1 += DOM.createHTML('span', {'class': 'mceVoiceLabel mceIconOnly', id: t.id + '_voice', style: 'display:none;'}, s.title);\r
9705                         h += '<td >' + DOM.createHTML('a', {role: 'button', id : t.id + '_action', tabindex: '-1', href : 'javascript:;', 'class' : 'mceAction ' + s['class'], onclick : "return false;", onmousedown : 'return false;', title : s.title}, h1) + '</td>';\r
9706         \r
9707                         h1 = DOM.createHTML('span', {'class' : 'mceOpen ' + s['class']}, '<span style="display:none;" class="mceIconOnly" aria-hidden="true">\u25BC</span>');\r
9708                         h += '<td >' + DOM.createHTML('a', {role: 'button', id : t.id + '_open', tabindex: '-1', href : 'javascript:;', 'class' : 'mceOpen ' + s['class'], onclick : "return false;", onmousedown : 'return false;', title : s.title}, h1) + '</td>';\r
9709 \r
9710                         h += '</tr></tbody>';\r
9711                         h = DOM.createHTML('table', {id : t.id, role: 'presentation', tabindex: '0',  'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', title : s.title}, h);\r
9712                         return DOM.createHTML('span', {role: 'button', 'aria-labelledby': t.id + '_voice', 'aria-haspopup': 'true'}, h);\r
9713                 },\r
9714 \r
9715                 postRender : function() {\r
9716                         var t = this, s = t.settings, activate;\r
9717 \r
9718                         if (s.onclick) {\r
9719                                 activate = function(evt) {\r
9720                                         if (!t.isDisabled()) {\r
9721                                                 s.onclick(t.value);\r
9722                                                 Event.cancel(evt);\r
9723                                         }\r
9724                                 };\r
9725                                 Event.add(t.id + '_action', 'click', activate);\r
9726                                 Event.add(t.id, ['click', 'keydown'], function(evt) {\r
9727                                         var DOM_VK_SPACE = 32, DOM_VK_ENTER = 14, DOM_VK_RETURN = 13, DOM_VK_UP = 38, DOM_VK_DOWN = 40;\r
9728                                         if ((evt.keyCode === 32 || evt.keyCode === 13 || evt.keyCode === 14) && !evt.altKey && !evt.ctrlKey && !evt.metaKey) {\r
9729                                                 activate();\r
9730                                                 Event.cancel(evt);\r
9731                                         } else if (evt.type === 'click' || evt.keyCode === DOM_VK_DOWN) {\r
9732                                                 t.showMenu();\r
9733                                                 Event.cancel(evt);\r
9734                                         }\r
9735                                 });\r
9736                         }\r
9737 \r
9738                         Event.add(t.id + '_open', 'click', function (evt) {\r
9739                                 t.showMenu();\r
9740                                 Event.cancel(evt);\r
9741                         });\r
9742                         Event.add([t.id, t.id + '_open'], 'focus', function() {t._focused = 1;});\r
9743                         Event.add([t.id, t.id + '_open'], 'blur', function() {t._focused = 0;});\r
9744 \r
9745                         // Old IE doesn't have hover on all elements\r
9746                         if (tinymce.isIE6 || !DOM.boxModel) {\r
9747                                 Event.add(t.id, 'mouseover', function() {\r
9748                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
9749                                                 DOM.addClass(t.id, 'mceSplitButtonHover');\r
9750                                 });\r
9751 \r
9752                                 Event.add(t.id, 'mouseout', function() {\r
9753                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
9754                                                 DOM.removeClass(t.id, 'mceSplitButtonHover');\r
9755                                 });\r
9756                         }\r
9757                 },\r
9758 \r
9759                 destroy : function() {\r
9760                         this.parent();\r
9761 \r
9762                         Event.clear(this.id + '_action');\r
9763                         Event.clear(this.id + '_open');\r
9764                         Event.clear(this.id);\r
9765                 }\r
9766         });\r
9767 })(tinymce);\r
9768 \r
9769 (function(tinymce) {\r
9770         var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each;\r
9771 \r
9772         tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', {\r
9773                 ColorSplitButton : function(id, s, ed) {\r
9774                         var t = this;\r
9775 \r
9776                         t.parent(id, s, ed);\r
9777 \r
9778                         t.settings = s = tinymce.extend({\r
9779                                 colors : '000000,993300,333300,003300,003366,000080,333399,333333,800000,FF6600,808000,008000,008080,0000FF,666699,808080,FF0000,FF9900,99CC00,339966,33CCCC,3366FF,800080,999999,FF00FF,FFCC00,FFFF00,00FF00,00FFFF,00CCFF,993366,C0C0C0,FF99CC,FFCC99,FFFF99,CCFFCC,CCFFFF,99CCFF,CC99FF,FFFFFF',\r
9780                                 grid_width : 8,\r
9781                                 default_color : '#888888'\r
9782                         }, t.settings);\r
9783 \r
9784                         t.onShowMenu = new tinymce.util.Dispatcher(t);\r
9785 \r
9786                         t.onHideMenu = new tinymce.util.Dispatcher(t);\r
9787 \r
9788                         t.value = s.default_color;\r
9789                 },\r
9790 \r
9791                 showMenu : function() {\r
9792                         var t = this, r, p, e, p2;\r
9793 \r
9794                         if (t.isDisabled())\r
9795                                 return;\r
9796 \r
9797                         if (!t.isMenuRendered) {\r
9798                                 t.renderMenu();\r
9799                                 t.isMenuRendered = true;\r
9800                         }\r
9801 \r
9802                         if (t.isMenuVisible)\r
9803                                 return t.hideMenu();\r
9804 \r
9805                         e = DOM.get(t.id);\r
9806                         DOM.show(t.id + '_menu');\r
9807                         DOM.addClass(e, 'mceSplitButtonSelected');\r
9808                         p2 = DOM.getPos(e);\r
9809                         DOM.setStyles(t.id + '_menu', {\r
9810                                 left : p2.x,\r
9811                                 top : p2.y + e.clientHeight,\r
9812                                 zIndex : 200000\r
9813                         });\r
9814                         e = 0;\r
9815 \r
9816                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
9817                         t.onShowMenu.dispatch(t);\r
9818 \r
9819                         if (t._focused) {\r
9820                                 t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) {\r
9821                                         if (e.keyCode == 27)\r
9822                                                 t.hideMenu();\r
9823                                 });\r
9824 \r
9825                                 DOM.select('a', t.id + '_menu')[0].focus(); // Select first link\r
9826                         }\r
9827 \r
9828                         t.isMenuVisible = 1;\r
9829                 },\r
9830 \r
9831                 hideMenu : function(e) {\r
9832                         var t = this;\r
9833 \r
9834                         if (t.isMenuVisible) {\r
9835                                 // Prevent double toogles by canceling the mouse click event to the button\r
9836                                 if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';}))\r
9837                                         return;\r
9838 \r
9839                                 if (!e || !DOM.getParent(e.target, '.mceSplitButtonMenu')) {\r
9840                                         DOM.removeClass(t.id, 'mceSplitButtonSelected');\r
9841                                         Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
9842                                         Event.remove(t.id + '_menu', 'keydown', t._keyHandler);\r
9843                                         DOM.hide(t.id + '_menu');\r
9844                                 }\r
9845 \r
9846                                 t.isMenuVisible = 0;\r
9847                         }\r
9848                 },\r
9849 \r
9850                 renderMenu : function() {\r
9851                         var t = this, m, i = 0, s = t.settings, n, tb, tr, w, context;\r
9852 \r
9853                         w = DOM.add(s.menu_container, 'div', {role: 'listbox', id : t.id + '_menu', 'class' : s['menu_class'] + ' ' + s['class'], style : 'position:absolute;left:0;top:-1000px;'});\r
9854                         m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'});\r
9855                         DOM.add(m, 'span', {'class' : 'mceMenuLine'});\r
9856 \r
9857                         n = DOM.add(m, 'table', {role: 'presentation', 'class' : 'mceColorSplitMenu'});\r
9858                         tb = DOM.add(n, 'tbody');\r
9859 \r
9860                         // Generate color grid\r
9861                         i = 0;\r
9862                         each(is(s.colors, 'array') ? s.colors : s.colors.split(','), function(c) {\r
9863                                 c = c.replace(/^#/, '');\r
9864 \r
9865                                 if (!i--) {\r
9866                                         tr = DOM.add(tb, 'tr');\r
9867                                         i = s.grid_width - 1;\r
9868                                 }\r
9869 \r
9870                                 n = DOM.add(tr, 'td');\r
9871                                 n = DOM.add(n, 'a', {\r
9872                                         role : 'option',\r
9873                                         href : 'javascript:;',\r
9874                                         style : {\r
9875                                                 backgroundColor : '#' + c\r
9876                                         },\r
9877                                         'title': t.editor.getLang('colors.' + c, c),\r
9878                                         'data-mce-color' : '#' + c\r
9879                                 });\r
9880 \r
9881                                 if (t.editor.forcedHighContrastMode) {\r
9882                                         n = DOM.add(n, 'canvas', { width: 16, height: 16, 'aria-hidden': 'true' });\r
9883                                         if (n.getContext && (context = n.getContext("2d"))) {\r
9884                                                 context.fillStyle = '#' + c;\r
9885                                                 context.fillRect(0, 0, 16, 16);\r
9886                                         } else {\r
9887                                                 // No point leaving a canvas element around if it's not supported for drawing on anyway.\r
9888                                                 DOM.remove(n);\r
9889                                         }\r
9890                                 }\r
9891                         });\r
9892 \r
9893                         if (s.more_colors_func) {\r
9894                                 n = DOM.add(tb, 'tr');\r
9895                                 n = DOM.add(n, 'td', {colspan : s.grid_width, 'class' : 'mceMoreColors'});\r
9896                                 n = DOM.add(n, 'a', {role: 'option', id : t.id + '_more', href : 'javascript:;', onclick : 'return false;', 'class' : 'mceMoreColors'}, s.more_colors_title);\r
9897 \r
9898                                 Event.add(n, 'click', function(e) {\r
9899                                         s.more_colors_func.call(s.more_colors_scope || this);\r
9900                                         return Event.cancel(e); // Cancel to fix onbeforeunload problem\r
9901                                 });\r
9902                         }\r
9903 \r
9904                         DOM.addClass(m, 'mceColorSplitMenu');\r
9905                         \r
9906                         new tinymce.ui.KeyboardNavigation({\r
9907                                 root: t.id + '_menu',\r
9908                                 items: DOM.select('a', t.id + '_menu'),\r
9909                                 onCancel: function() {\r
9910                                         t.hideMenu();\r
9911                                         t.focus();\r
9912                                 }\r
9913                         });\r
9914 \r
9915                         // Prevent IE from scrolling and hindering click to occur #4019\r
9916                         Event.add(t.id + '_menu', 'mousedown', function(e) {return Event.cancel(e);});\r
9917 \r
9918                         Event.add(t.id + '_menu', 'click', function(e) {\r
9919                                 var c;\r
9920 \r
9921                                 e = DOM.getParent(e.target, 'a', tb);\r
9922 \r
9923                                 if (e && e.nodeName.toLowerCase() == 'a' && (c = e.getAttribute('data-mce-color')))\r
9924                                         t.setColor(c);\r
9925 \r
9926                                 return Event.cancel(e); // Prevent IE auto save warning\r
9927                         });\r
9928 \r
9929                         return w;\r
9930                 },\r
9931 \r
9932                 setColor : function(c) {\r
9933                         this.displayColor(c);\r
9934                         this.hideMenu();\r
9935                         this.settings.onselect(c);\r
9936                 },\r
9937                 \r
9938                 displayColor : function(c) {\r
9939                         var t = this;\r
9940 \r
9941                         DOM.setStyle(t.id + '_preview', 'backgroundColor', c);\r
9942 \r
9943                         t.value = c;\r
9944                 },\r
9945 \r
9946                 postRender : function() {\r
9947                         var t = this, id = t.id;\r
9948 \r
9949                         t.parent();\r
9950                         DOM.add(id + '_action', 'div', {id : id + '_preview', 'class' : 'mceColorPreview'});\r
9951                         DOM.setStyle(t.id + '_preview', 'backgroundColor', t.value);\r
9952                 },\r
9953 \r
9954                 destroy : function() {\r
9955                         this.parent();\r
9956 \r
9957                         Event.clear(this.id + '_menu');\r
9958                         Event.clear(this.id + '_more');\r
9959                         DOM.remove(this.id + '_menu');\r
9960                 }\r
9961         });\r
9962 })(tinymce);\r
9963 \r
9964 (function(tinymce) {\r
9965 // Shorten class names\r
9966 var dom = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event;\r
9967 tinymce.create('tinymce.ui.ToolbarGroup:tinymce.ui.Container', {\r
9968         renderHTML : function() {\r
9969                 var t = this, h = [], controls = t.controls, each = tinymce.each, settings = t.settings;\r
9970 \r
9971                 h.push('<div id="' + t.id + '" role="group" aria-labelledby="' + t.id + '_voice">');\r
9972                 //TODO: ACC test this out - adding a role = application for getting the landmarks working well.\r
9973                 h.push("<span role='application'>");\r
9974                 h.push('<span id="' + t.id + '_voice" class="mceVoiceLabel" style="display:none;">' + dom.encode(settings.name) + '</span>');\r
9975                 each(controls, function(toolbar) {\r
9976                         h.push(toolbar.renderHTML());\r
9977                 });\r
9978                 h.push("</span>");\r
9979                 h.push('</div>');\r
9980 \r
9981                 return h.join('');\r
9982         },\r
9983         \r
9984         focus : function() {\r
9985                 this.keyNav.focus();\r
9986         },\r
9987         \r
9988         postRender : function() {\r
9989                 var t = this, items = [];\r
9990 \r
9991                 each(t.controls, function(toolbar) {\r
9992                         each (toolbar.controls, function(control) {\r
9993                                 if (control.id) {\r
9994                                         items.push(control);\r
9995                                 }\r
9996                         });\r
9997                 });\r
9998 \r
9999                 t.keyNav = new tinymce.ui.KeyboardNavigation({\r
10000                         root: t.id,\r
10001                         items: items,\r
10002                         onCancel: function() {\r
10003                                 t.editor.focus();\r
10004                         },\r
10005                         excludeFromTabOrder: !t.settings.tab_focus_toolbar\r
10006                 });\r
10007         },\r
10008         \r
10009         destroy : function() {\r
10010                 var self = this;\r
10011 \r
10012                 self.parent();\r
10013                 self.keyNav.destroy();\r
10014                 Event.clear(self.id);\r
10015         }\r
10016 });\r
10017 })(tinymce);\r
10018 \r
10019 (function(tinymce) {\r
10020 // Shorten class names\r
10021 var dom = tinymce.DOM, each = tinymce.each;\r
10022 tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {\r
10023         renderHTML : function() {\r
10024                 var t = this, h = '', c, co, s = t.settings, i, pr, nx, cl;\r
10025 \r
10026                 cl = t.controls;\r
10027                 for (i=0; i<cl.length; i++) {\r
10028                         // Get current control, prev control, next control and if the control is a list box or not\r
10029                         co = cl[i];\r
10030                         pr = cl[i - 1];\r
10031                         nx = cl[i + 1];\r
10032 \r
10033                         // Add toolbar start\r
10034                         if (i === 0) {\r
10035                                 c = 'mceToolbarStart';\r
10036 \r
10037                                 if (co.Button)\r
10038                                         c += ' mceToolbarStartButton';\r
10039                                 else if (co.SplitButton)\r
10040                                         c += ' mceToolbarStartSplitButton';\r
10041                                 else if (co.ListBox)\r
10042                                         c += ' mceToolbarStartListBox';\r
10043 \r
10044                                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
10045                         }\r
10046 \r
10047                         // Add toolbar end before list box and after the previous button\r
10048                         // This is to fix the o2k7 editor skins\r
10049                         if (pr && co.ListBox) {\r
10050                                 if (pr.Button || pr.SplitButton)\r
10051                                         h += dom.createHTML('td', {'class' : 'mceToolbarEnd'}, dom.createHTML('span', null, '<!-- IE -->'));\r
10052                         }\r
10053 \r
10054                         // Render control HTML\r
10055 \r
10056                         // IE 8 quick fix, needed to propertly generate a hit area for anchors\r
10057                         if (dom.stdMode)\r
10058                                 h += '<td style="position: relative">' + co.renderHTML() + '</td>';\r
10059                         else\r
10060                                 h += '<td>' + co.renderHTML() + '</td>';\r
10061 \r
10062                         // Add toolbar start after list box and before the next button\r
10063                         // This is to fix the o2k7 editor skins\r
10064                         if (nx && co.ListBox) {\r
10065                                 if (nx.Button || nx.SplitButton)\r
10066                                         h += dom.createHTML('td', {'class' : 'mceToolbarStart'}, dom.createHTML('span', null, '<!-- IE -->'));\r
10067                         }\r
10068                 }\r
10069 \r
10070                 c = 'mceToolbarEnd';\r
10071 \r
10072                 if (co.Button)\r
10073                         c += ' mceToolbarEndButton';\r
10074                 else if (co.SplitButton)\r
10075                         c += ' mceToolbarEndSplitButton';\r
10076                 else if (co.ListBox)\r
10077                         c += ' mceToolbarEndListBox';\r
10078 \r
10079                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
10080 \r
10081                 return dom.createHTML('table', {id : t.id, 'class' : 'mceToolbar' + (s['class'] ? ' ' + s['class'] : ''), cellpadding : '0', cellspacing : '0', align : t.settings.align || '', role: 'presentation', tabindex: '-1'}, '<tbody><tr>' + h + '</tr></tbody>');\r
10082         }\r
10083 });\r
10084 })(tinymce);\r
10085 \r
10086 (function(tinymce) {\r
10087         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;\r
10088 \r
10089         tinymce.create('tinymce.AddOnManager', {\r
10090                 AddOnManager : function() {\r
10091                         var self = this;\r
10092 \r
10093                         self.items = [];\r
10094                         self.urls = {};\r
10095                         self.lookup = {};\r
10096                         self.onAdd = new Dispatcher(self);\r
10097                 },\r
10098 \r
10099                 get : function(n) {\r
10100                         return this.lookup[n];\r
10101                 },\r
10102 \r
10103                 requireLangPack : function(n) {\r
10104                         var s = tinymce.settings;\r
10105 \r
10106                         if (s && s.language && s.language_load !== false)\r
10107                                 tinymce.ScriptLoader.add(this.urls[n] + '/langs/' + s.language + '.js');\r
10108                 },\r
10109 \r
10110                 add : function(id, o) {\r
10111                         this.items.push(o);\r
10112                         this.lookup[id] = o;\r
10113                         this.onAdd.dispatch(this, id, o);\r
10114 \r
10115                         return o;\r
10116                 },\r
10117 \r
10118                 load : function(n, u, cb, s) {\r
10119                         var t = this;\r
10120 \r
10121                         if (t.urls[n])\r
10122                                 return;\r
10123 \r
10124                         if (u.indexOf('/') != 0 && u.indexOf('://') == -1)\r
10125                                 u = tinymce.baseURL + '/' + u;\r
10126 \r
10127                         t.urls[n] = u.substring(0, u.lastIndexOf('/'));\r
10128 \r
10129                         if (!t.lookup[n])\r
10130                                 tinymce.ScriptLoader.add(u, cb, s);\r
10131                 }\r
10132         });\r
10133 \r
10134         // Create plugin and theme managers\r
10135         tinymce.PluginManager = new tinymce.AddOnManager();\r
10136         tinymce.ThemeManager = new tinymce.AddOnManager();\r
10137 }(tinymce));\r
10138 \r
10139 (function(tinymce) {\r
10140         // Shorten names\r
10141         var each = tinymce.each, extend = tinymce.extend,\r
10142                 DOM = tinymce.DOM, Event = tinymce.dom.Event,\r
10143                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
10144                 explode = tinymce.explode,\r
10145                 Dispatcher = tinymce.util.Dispatcher, undefined, instanceCounter = 0;\r
10146 \r
10147         // Setup some URLs where the editor API is located and where the document is\r
10148         tinymce.documentBaseURL = window.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');\r
10149         if (!/[\/\\]$/.test(tinymce.documentBaseURL))\r
10150                 tinymce.documentBaseURL += '/';\r
10151 \r
10152         tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL);\r
10153 \r
10154         tinymce.baseURI = new tinymce.util.URI(tinymce.baseURL);\r
10155 \r
10156         // Add before unload listener\r
10157         // This was required since IE was leaking memory if you added and removed beforeunload listeners\r
10158         // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event\r
10159         tinymce.onBeforeUnload = new Dispatcher(tinymce);\r
10160 \r
10161         // Must be on window or IE will leak if the editor is placed in frame or iframe\r
10162         Event.add(window, 'beforeunload', function(e) {\r
10163                 tinymce.onBeforeUnload.dispatch(tinymce, e);\r
10164         });\r
10165 \r
10166         tinymce.onAddEditor = new Dispatcher(tinymce);\r
10167 \r
10168         tinymce.onRemoveEditor = new Dispatcher(tinymce);\r
10169 \r
10170         tinymce.EditorManager = extend(tinymce, {\r
10171                 editors : [],\r
10172 \r
10173                 i18n : {},\r
10174 \r
10175                 activeEditor : null,\r
10176 \r
10177                 init : function(s) {\r
10178                         var t = this, pl, sl = tinymce.ScriptLoader, e, el = [], ed;\r
10179 \r
10180                         function execCallback(se, n, s) {\r
10181                                 var f = se[n];\r
10182 \r
10183                                 if (!f)\r
10184                                         return;\r
10185 \r
10186                                 if (tinymce.is(f, 'string')) {\r
10187                                         s = f.replace(/\.\w+$/, '');\r
10188                                         s = s ? tinymce.resolve(s) : 0;\r
10189                                         f = tinymce.resolve(f);\r
10190                                 }\r
10191 \r
10192                                 return f.apply(s || this, Array.prototype.slice.call(arguments, 2));\r
10193                         };\r
10194 \r
10195                         s = extend({\r
10196                                 theme : "simple",\r
10197                                 language : "en"\r
10198                         }, s);\r
10199 \r
10200                         t.settings = s;\r
10201 \r
10202                         // Legacy call\r
10203                         Event.add(document, 'init', function() {\r
10204                                 var l, co;\r
10205 \r
10206                                 execCallback(s, 'onpageload');\r
10207 \r
10208                                 switch (s.mode) {\r
10209                                         case "exact":\r
10210                                                 l = s.elements || '';\r
10211 \r
10212                                                 if(l.length > 0) {\r
10213                                                         each(explode(l), function(v) {\r
10214                                                                 if (DOM.get(v)) {\r
10215                                                                         ed = new tinymce.Editor(v, s);\r
10216                                                                         el.push(ed);\r
10217                                                                         ed.render(1);\r
10218                                                                 } else {\r
10219                                                                         each(document.forms, function(f) {\r
10220                                                                                 each(f.elements, function(e) {\r
10221                                                                                         if (e.name === v) {\r
10222                                                                                                 v = 'mce_editor_' + instanceCounter++;\r
10223                                                                                                 DOM.setAttrib(e, 'id', v);\r
10224 \r
10225                                                                                                 ed = new tinymce.Editor(v, s);\r
10226                                                                                                 el.push(ed);\r
10227                                                                                                 ed.render(1);\r
10228                                                                                         }\r
10229                                                                                 });\r
10230                                                                         });\r
10231                                                                 }\r
10232                                                         });\r
10233                                                 }\r
10234                                                 break;\r
10235 \r
10236                                         case "textareas":\r
10237                                         case "specific_textareas":\r
10238                                                 function hasClass(n, c) {\r
10239                                                         return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);\r
10240                                                 };\r
10241 \r
10242                                                 each(DOM.select('textarea'), function(v) {\r
10243                                                         if (s.editor_deselector && hasClass(v, s.editor_deselector))\r
10244                                                                 return;\r
10245 \r
10246                                                         if (!s.editor_selector || hasClass(v, s.editor_selector)) {\r
10247                                                                 // Can we use the name\r
10248                                                                 e = DOM.get(v.name);\r
10249                                                                 if (!v.id && !e)\r
10250                                                                         v.id = v.name;\r
10251 \r
10252                                                                 // Generate unique name if missing or already exists\r
10253                                                                 if (!v.id || t.get(v.id))\r
10254                                                                         v.id = DOM.uniqueId();\r
10255 \r
10256                                                                 ed = new tinymce.Editor(v.id, s);\r
10257                                                                 el.push(ed);\r
10258                                                                 ed.render(1);\r
10259                                                         }\r
10260                                                 });\r
10261                                                 break;\r
10262                                 }\r
10263 \r
10264                                 // Call onInit when all editors are initialized\r
10265                                 if (s.oninit) {\r
10266                                         l = co = 0;\r
10267 \r
10268                                         each(el, function(ed) {\r
10269                                                 co++;\r
10270 \r
10271                                                 if (!ed.initialized) {\r
10272                                                         // Wait for it\r
10273                                                         ed.onInit.add(function() {\r
10274                                                                 l++;\r
10275 \r
10276                                                                 // All done\r
10277                                                                 if (l == co)\r
10278                                                                         execCallback(s, 'oninit');\r
10279                                                         });\r
10280                                                 } else\r
10281                                                         l++;\r
10282 \r
10283                                                 // All done\r
10284                                                 if (l == co)\r
10285                                                         execCallback(s, 'oninit');                                      \r
10286                                         });\r
10287                                 }\r
10288                         });\r
10289                 },\r
10290 \r
10291                 get : function(id) {\r
10292                         if (id === undefined)\r
10293                                 return this.editors;\r
10294 \r
10295                         return this.editors[id];\r
10296                 },\r
10297 \r
10298                 getInstanceById : function(id) {\r
10299                         return this.get(id);\r
10300                 },\r
10301 \r
10302                 add : function(editor) {\r
10303                         var self = this, editors = self.editors;\r
10304 \r
10305                         // Add named and index editor instance\r
10306                         editors[editor.id] = editor;\r
10307                         editors.push(editor);\r
10308 \r
10309                         self._setActive(editor);\r
10310                         self.onAddEditor.dispatch(self, editor);\r
10311 \r
10312 \r
10313                         return editor;\r
10314                 },\r
10315 \r
10316                 remove : function(editor) {\r
10317                         var t = this, i, editors = t.editors;\r
10318 \r
10319                         // Not in the collection\r
10320                         if (!editors[editor.id])\r
10321                                 return null;\r
10322 \r
10323                         delete editors[editor.id];\r
10324 \r
10325                         for (i = 0; i < editors.length; i++) {\r
10326                                 if (editors[i] == editor) {\r
10327                                         editors.splice(i, 1);\r
10328                                         break;\r
10329                                 }\r
10330                         }\r
10331 \r
10332                         // Select another editor since the active one was removed\r
10333                         if (t.activeEditor == editor)\r
10334                                 t._setActive(editors[0]);\r
10335 \r
10336                         editor.destroy();\r
10337                         t.onRemoveEditor.dispatch(t, editor);\r
10338 \r
10339                         return editor;\r
10340                 },\r
10341 \r
10342                 execCommand : function(c, u, v) {\r
10343                         var t = this, ed = t.get(v), w;\r
10344 \r
10345                         // Manager commands\r
10346                         switch (c) {\r
10347                                 case "mceFocus":\r
10348                                         ed.focus();\r
10349                                         return true;\r
10350 \r
10351                                 case "mceAddEditor":\r
10352                                 case "mceAddControl":\r
10353                                         if (!t.get(v))\r
10354                                                 new tinymce.Editor(v, t.settings).render();\r
10355 \r
10356                                         return true;\r
10357 \r
10358                                 case "mceAddFrameControl":\r
10359                                         w = v.window;\r
10360 \r
10361                                         // Add tinyMCE global instance and tinymce namespace to specified window\r
10362                                         w.tinyMCE = tinyMCE;\r
10363                                         w.tinymce = tinymce;\r
10364 \r
10365                                         tinymce.DOM.doc = w.document;\r
10366                                         tinymce.DOM.win = w;\r
10367 \r
10368                                         ed = new tinymce.Editor(v.element_id, v);\r
10369                                         ed.render();\r
10370 \r
10371                                         // Fix IE memory leaks\r
10372                                         if (tinymce.isIE) {\r
10373                                                 function clr() {\r
10374                                                         ed.destroy();\r
10375                                                         w.detachEvent('onunload', clr);\r
10376                                                         w = w.tinyMCE = w.tinymce = null; // IE leak\r
10377                                                 };\r
10378 \r
10379                                                 w.attachEvent('onunload', clr);\r
10380                                         }\r
10381 \r
10382                                         v.page_window = null;\r
10383 \r
10384                                         return true;\r
10385 \r
10386                                 case "mceRemoveEditor":\r
10387                                 case "mceRemoveControl":\r
10388                                         if (ed)\r
10389                                                 ed.remove();\r
10390 \r
10391                                         return true;\r
10392 \r
10393                                 case 'mceToggleEditor':\r
10394                                         if (!ed) {\r
10395                                                 t.execCommand('mceAddControl', 0, v);\r
10396                                                 return true;\r
10397                                         }\r
10398 \r
10399                                         if (ed.isHidden())\r
10400                                                 ed.show();\r
10401                                         else\r
10402                                                 ed.hide();\r
10403 \r
10404                                         return true;\r
10405                         }\r
10406 \r
10407                         // Run command on active editor\r
10408                         if (t.activeEditor)\r
10409                                 return t.activeEditor.execCommand(c, u, v);\r
10410 \r
10411                         return false;\r
10412                 },\r
10413 \r
10414                 execInstanceCommand : function(id, c, u, v) {\r
10415                         var ed = this.get(id);\r
10416 \r
10417                         if (ed)\r
10418                                 return ed.execCommand(c, u, v);\r
10419 \r
10420                         return false;\r
10421                 },\r
10422 \r
10423                 triggerSave : function() {\r
10424                         each(this.editors, function(e) {\r
10425                                 e.save();\r
10426                         });\r
10427                 },\r
10428 \r
10429                 addI18n : function(p, o) {\r
10430                         var lo, i18n = this.i18n;\r
10431 \r
10432                         if (!tinymce.is(p, 'string')) {\r
10433                                 each(p, function(o, lc) {\r
10434                                         each(o, function(o, g) {\r
10435                                                 each(o, function(o, k) {\r
10436                                                         if (g === 'common')\r
10437                                                                 i18n[lc + '.' + k] = o;\r
10438                                                         else\r
10439                                                                 i18n[lc + '.' + g + '.' + k] = o;\r
10440                                                 });\r
10441                                         });\r
10442                                 });\r
10443                         } else {\r
10444                                 each(o, function(o, k) {\r
10445                                         i18n[p + '.' + k] = o;\r
10446                                 });\r
10447                         }\r
10448                 },\r
10449 \r
10450                 // Private methods\r
10451 \r
10452                 _setActive : function(editor) {\r
10453                         this.selectedInstance = this.activeEditor = editor;\r
10454                 }\r
10455         });\r
10456 })(tinymce);\r
10457 \r
10458 (function(tinymce) {\r
10459         // Shorten these names\r
10460         var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend,\r
10461                 Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isGecko = tinymce.isGecko,\r
10462                 isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, is = tinymce.is,\r
10463                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
10464                 inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;\r
10465 \r
10466         tinymce.create('tinymce.Editor', {\r
10467                 Editor : function(id, s) {\r
10468                         var t = this;\r
10469 \r
10470                         t.id = t.editorId = id;\r
10471 \r
10472                         t.execCommands = {};\r
10473                         t.queryStateCommands = {};\r
10474                         t.queryValueCommands = {};\r
10475 \r
10476                         t.isNotDirty = false;\r
10477 \r
10478                         t.plugins = {};\r
10479 \r
10480                         // Add events to the editor\r
10481                         each([\r
10482                                 'onPreInit',\r
10483 \r
10484                                 'onBeforeRenderUI',\r
10485 \r
10486                                 'onPostRender',\r
10487 \r
10488                                 'onInit',\r
10489 \r
10490                                 'onRemove',\r
10491 \r
10492                                 'onActivate',\r
10493 \r
10494                                 'onDeactivate',\r
10495 \r
10496                                 'onClick',\r
10497 \r
10498                                 'onEvent',\r
10499 \r
10500                                 'onMouseUp',\r
10501 \r
10502                                 'onMouseDown',\r
10503 \r
10504                                 'onDblClick',\r
10505 \r
10506                                 'onKeyDown',\r
10507 \r
10508                                 'onKeyUp',\r
10509 \r
10510                                 'onKeyPress',\r
10511 \r
10512                                 'onContextMenu',\r
10513 \r
10514                                 'onSubmit',\r
10515 \r
10516                                 'onReset',\r
10517 \r
10518                                 'onPaste',\r
10519 \r
10520                                 'onPreProcess',\r
10521 \r
10522                                 'onPostProcess',\r
10523 \r
10524                                 'onBeforeSetContent',\r
10525 \r
10526                                 'onBeforeGetContent',\r
10527 \r
10528                                 'onSetContent',\r
10529 \r
10530                                 'onGetContent',\r
10531 \r
10532                                 'onLoadContent',\r
10533 \r
10534                                 'onSaveContent',\r
10535 \r
10536                                 'onNodeChange',\r
10537 \r
10538                                 'onChange',\r
10539 \r
10540                                 'onBeforeExecCommand',\r
10541 \r
10542                                 'onExecCommand',\r
10543 \r
10544                                 'onUndo',\r
10545 \r
10546                                 'onRedo',\r
10547 \r
10548                                 'onVisualAid',\r
10549 \r
10550                                 'onSetProgressState'\r
10551                         ], function(e) {\r
10552                                 t[e] = new Dispatcher(t);\r
10553                         });\r
10554 \r
10555                         t.settings = s = extend({\r
10556                                 id : id,\r
10557                                 language : 'en',\r
10558                                 docs_language : 'en',\r
10559                                 theme : 'simple',\r
10560                                 skin : 'default',\r
10561                                 delta_width : 0,\r
10562                                 delta_height : 0,\r
10563                                 popup_css : '',\r
10564                                 plugins : '',\r
10565                                 document_base_url : tinymce.documentBaseURL,\r
10566                                 add_form_submit_trigger : 1,\r
10567                                 submit_patch : 1,\r
10568                                 add_unload_trigger : 1,\r
10569                                 convert_urls : 1,\r
10570                                 relative_urls : 1,\r
10571                                 remove_script_host : 1,\r
10572                                 table_inline_editing : 0,\r
10573                                 object_resizing : 1,\r
10574                                 cleanup : 1,\r
10575                                 accessibility_focus : 1,\r
10576                                 custom_shortcuts : 1,\r
10577                                 custom_undo_redo_keyboard_shortcuts : 1,\r
10578                                 custom_undo_redo_restore_selection : 1,\r
10579                                 custom_undo_redo : 1,\r
10580                                 doctype : tinymce.isIE6 ? '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">' : '<!DOCTYPE>', // Use old doctype on IE 6 to avoid horizontal scroll\r
10581                                 visual_table_class : 'mceItemTable',\r
10582                                 visual : 1,\r
10583                                 font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',\r
10584                                 apply_source_formatting : 1,\r
10585                                 directionality : 'ltr',\r
10586                                 forced_root_block : 'p',\r
10587                                 hidden_input : 1,\r
10588                                 padd_empty_editor : 1,\r
10589                                 render_ui : 1,\r
10590                                 init_theme : 1,\r
10591                                 force_p_newlines : 1,\r
10592                                 indentation : '30px',\r
10593                                 keep_styles : 1,\r
10594                                 fix_table_elements : 1,\r
10595                                 inline_styles : 1,\r
10596                                 convert_fonts_to_spans : true,\r
10597                                 indent : 'simple',\r
10598                                 indent_before : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr',\r
10599                                 indent_after : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr',\r
10600                                 validate : true,\r
10601                                 entity_encoding : 'named',\r
10602                                 url_converter : t.convertURL,\r
10603                                 url_converter_scope : t,\r
10604                                 ie7_compat : true\r
10605                         }, s);\r
10606 \r
10607                         t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {\r
10608                                 base_uri : tinyMCE.baseURI\r
10609                         });\r
10610 \r
10611                         t.baseURI = tinymce.baseURI;\r
10612 \r
10613                         t.contentCSS = [];\r
10614 \r
10615                         // Call setup\r
10616                         t.execCallback('setup', t);\r
10617                 },\r
10618 \r
10619                 render : function(nst) {\r
10620                         var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader;\r
10621 \r
10622                         // Page is not loaded yet, wait for it\r
10623                         if (!Event.domLoaded) {\r
10624                                 Event.add(document, 'init', function() {\r
10625                                         t.render();\r
10626                                 });\r
10627                                 return;\r
10628                         }\r
10629 \r
10630                         tinyMCE.settings = s;\r
10631 \r
10632                         // Element not found, then skip initialization\r
10633                         if (!t.getElement())\r
10634                                 return;\r
10635 \r
10636                         // Is a iPad/iPhone, then skip initialization. We need to sniff here since the\r
10637                         // browser says it has contentEditable support but there is no visible caret\r
10638                         // We will remove this check ones Apple implements full contentEditable support\r
10639                         if (tinymce.isIDevice)\r
10640                                 return;\r
10641 \r
10642                         // Add hidden input for non input elements inside form elements\r
10643                         if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))\r
10644                                 DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);\r
10645 \r
10646                         if (tinymce.WindowManager)\r
10647                                 t.windowManager = new tinymce.WindowManager(t);\r
10648 \r
10649                         if (s.encoding == 'xml') {\r
10650                                 t.onGetContent.add(function(ed, o) {\r
10651                                         if (o.save)\r
10652                                                 o.content = DOM.encode(o.content);\r
10653                                 });\r
10654                         }\r
10655 \r
10656                         if (s.add_form_submit_trigger) {\r
10657                                 t.onSubmit.addToTop(function() {\r
10658                                         if (t.initialized) {\r
10659                                                 t.save();\r
10660                                                 t.isNotDirty = 1;\r
10661                                         }\r
10662                                 });\r
10663                         }\r
10664 \r
10665                         if (s.add_unload_trigger) {\r
10666                                 t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {\r
10667                                         if (t.initialized && !t.destroyed && !t.isHidden())\r
10668                                                 t.save({format : 'raw', no_events : true});\r
10669                                 });\r
10670                         }\r
10671 \r
10672                         tinymce.addUnload(t.destroy, t);\r
10673 \r
10674                         if (s.submit_patch) {\r
10675                                 t.onBeforeRenderUI.add(function() {\r
10676                                         var n = t.getElement().form;\r
10677 \r
10678                                         if (!n)\r
10679                                                 return;\r
10680 \r
10681                                         // Already patched\r
10682                                         if (n._mceOldSubmit)\r
10683                                                 return;\r
10684 \r
10685                                         // Check page uses id="submit" or name="submit" for it's submit button\r
10686                                         if (!n.submit.nodeType && !n.submit.length) {\r
10687                                                 t.formElement = n;\r
10688                                                 n._mceOldSubmit = n.submit;\r
10689                                                 n.submit = function() {\r
10690                                                         // Save all instances\r
10691                                                         tinymce.triggerSave();\r
10692                                                         t.isNotDirty = 1;\r
10693 \r
10694                                                         return t.formElement._mceOldSubmit(t.formElement);\r
10695                                                 };\r
10696                                         }\r
10697 \r
10698                                         n = null;\r
10699                                 });\r
10700                         }\r
10701 \r
10702                         // Load scripts\r
10703                         function loadScripts() {\r
10704                                 if (s.language && s.language_load !== false)\r
10705                                         sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');\r
10706 \r
10707                                 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])\r
10708                                         ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');\r
10709 \r
10710                                 each(explode(s.plugins), function(p) {\r
10711                                         if (p && p.charAt(0) != '-' && !PluginManager.urls[p]) {\r
10712                                                 // Skip safari plugin, since it is removed as of 3.3b1\r
10713                                                 if (p == 'safari')\r
10714                                                         return;\r
10715 \r
10716                                                 PluginManager.load(p, 'plugins/' + p + '/editor_plugin' + tinymce.suffix + '.js');\r
10717                                         }\r
10718                                 });\r
10719 \r
10720                                 // Init when que is loaded\r
10721                                 sl.loadQueue(function() {\r
10722                                         if (!t.removed)\r
10723                                                 t.init();\r
10724                                 });\r
10725                         };\r
10726 \r
10727                         loadScripts();\r
10728                 },\r
10729 \r
10730                 init : function() {\r
10731                         var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re, i;\r
10732 \r
10733                         tinymce.add(t);\r
10734 \r
10735                         s.aria_label = s.aria_label || DOM.getAttrib(e, 'aria-label', t.getLang('aria.rich_text_area'));\r
10736 \r
10737                         if (s.theme) {\r
10738                                 s.theme = s.theme.replace(/-/, '');\r
10739                                 o = ThemeManager.get(s.theme);\r
10740                                 t.theme = new o();\r
10741 \r
10742                                 if (t.theme.init && s.init_theme)\r
10743                                         t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));\r
10744                         }\r
10745 \r
10746                         // Create all plugins\r
10747                         each(explode(s.plugins.replace(/\-/g, '')), function(p) {\r
10748                                 var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;\r
10749 \r
10750                                 if (c) {\r
10751                                         po = new c(t, u);\r
10752 \r
10753                                         t.plugins[p] = po;\r
10754 \r
10755                                         if (po.init)\r
10756                                                 po.init(t, u);\r
10757                                 }\r
10758                         });\r
10759 \r
10760                         // Setup popup CSS path(s)\r
10761                         if (s.popup_css !== false) {\r
10762                                 if (s.popup_css)\r
10763                                         s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);\r
10764                                 else\r
10765                                         s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");\r
10766                         }\r
10767 \r
10768                         if (s.popup_css_add)\r
10769                                 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);\r
10770 \r
10771                         t.controlManager = new tinymce.ControlManager(t);\r
10772 \r
10773                         if (s.custom_undo_redo) {\r
10774                                 t.onBeforeExecCommand.add(function(ed, cmd, ui, val, a) {\r
10775                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))\r
10776                                                 t.undoManager.beforeChange();\r
10777                                 });\r
10778 \r
10779                                 t.onExecCommand.add(function(ed, cmd, ui, val, a) {\r
10780                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))\r
10781                                                 t.undoManager.add();\r
10782                                 });\r
10783                         }\r
10784 \r
10785                         t.onExecCommand.add(function(ed, c) {\r
10786                                 // Don't refresh the select lists until caret move\r
10787                                 if (!/^(FontName|FontSize)$/.test(c))\r
10788                                         t.nodeChanged();\r
10789                         });\r
10790 \r
10791                         // Remove ghost selections on images and tables in Gecko\r
10792                         if (isGecko) {\r
10793                                 function repaint(a, o) {\r
10794                                         if (!o || !o.initial)\r
10795                                                 t.execCommand('mceRepaint');\r
10796                                 };\r
10797 \r
10798                                 t.onUndo.add(repaint);\r
10799                                 t.onRedo.add(repaint);\r
10800                                 t.onSetContent.add(repaint);\r
10801                         }\r
10802 \r
10803                         // Enables users to override the control factory\r
10804                         t.onBeforeRenderUI.dispatch(t, t.controlManager);\r
10805 \r
10806                         // Measure box\r
10807                         if (s.render_ui) {\r
10808                                 w = s.width || e.style.width || e.offsetWidth;\r
10809                                 h = s.height || e.style.height || e.offsetHeight;\r
10810                                 t.orgDisplay = e.style.display;\r
10811                                 re = /^[0-9\.]+(|px)$/i;\r
10812 \r
10813                                 if (re.test('' + w))\r
10814                                         w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);\r
10815 \r
10816                                 if (re.test('' + h))\r
10817                                         h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);\r
10818 \r
10819                                 // Render UI\r
10820                                 o = t.theme.renderUI({\r
10821                                         targetNode : e,\r
10822                                         width : w,\r
10823                                         height : h,\r
10824                                         deltaWidth : s.delta_width,\r
10825                                         deltaHeight : s.delta_height\r
10826                                 });\r
10827 \r
10828                                 t.editorContainer = o.editorContainer;\r
10829                         }\r
10830 \r
10831 \r
10832                         // User specified a document.domain value\r
10833                         if (document.domain && location.hostname != document.domain)\r
10834                                 tinymce.relaxedDomain = document.domain;\r
10835 \r
10836                         // Resize editor\r
10837                         DOM.setStyles(o.sizeContainer || o.editorContainer, {\r
10838                                 width : w,\r
10839                                 height : h\r
10840                         });\r
10841 \r
10842                         // Load specified content CSS last\r
10843                         if (s.content_css) {\r
10844                                 tinymce.each(explode(s.content_css), function(u) {\r
10845                                         t.contentCSS.push(t.documentBaseURI.toAbsolute(u));\r
10846                                 });\r
10847                         }\r
10848 \r
10849                         h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');\r
10850                         if (h < 100)\r
10851                                 h = 100;\r
10852 \r
10853                         t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">';\r
10854 \r
10855                         // We only need to override paths if we have to\r
10856                         // IE has a bug where it remove site absolute urls to relative ones if this is specified\r
10857                         if (s.document_base_url != tinymce.documentBaseURL)\r
10858                                 t.iframeHTML += '<base href="' + t.documentBaseURI.getURI() + '" />';\r
10859 \r
10860                         // IE8 doesn't support carets behind images setting ie7_compat would force IE8+ to run in IE7 compat mode.\r
10861                         if (s.ie7_compat)\r
10862                                 t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" />';\r
10863                         else\r
10864                                 t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=edge" />';\r
10865 \r
10866                         t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';\r
10867 \r
10868                         // Firefox 2 doesn't load stylesheets correctly this way\r
10869                         if (!isGecko || !/Firefox\/2/.test(navigator.userAgent)) {\r
10870                                 for (i = 0; i < t.contentCSS.length; i++)\r
10871                                         t.iframeHTML += '<link type="text/css" rel="stylesheet" href="' + t.contentCSS[i] + '" />';\r
10872 \r
10873                                 t.contentCSS = [];\r
10874                         }\r
10875 \r
10876                         bi = s.body_id || 'tinymce';\r
10877                         if (bi.indexOf('=') != -1) {\r
10878                                 bi = t.getParam('body_id', '', 'hash');\r
10879                                 bi = bi[t.id] || bi;\r
10880                         }\r
10881 \r
10882                         bc = s.body_class || '';\r
10883                         if (bc.indexOf('=') != -1) {\r
10884                                 bc = t.getParam('body_class', '', 'hash');\r
10885                                 bc = bc[t.id] || '';\r
10886                         }\r
10887 \r
10888                         t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"></body></html>';\r
10889 \r
10890                         // Domain relaxing enabled, then set document domain\r
10891                         if (tinymce.relaxedDomain && (isIE || (tinymce.isOpera && parseFloat(opera.version()) < 11))) {\r
10892                                 // We need to write the contents here in IE since multiple writes messes up refresh button and back button\r
10893                                 u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()';                         \r
10894                         }\r
10895 \r
10896                         // Create iframe\r
10897                         // TODO: ACC add the appropriate description on this.\r
10898                         n = DOM.add(o.iframeContainer, 'iframe', { \r
10899                                 id : t.id + "_ifr",\r
10900                                 src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7\r
10901                                 frameBorder : '0', \r
10902                                 title : s.aria_label,\r
10903                                 style : {\r
10904                                         width : '100%',\r
10905                                         height : h\r
10906                                 }\r
10907                         });\r
10908 \r
10909                         t.contentAreaContainer = o.iframeContainer;\r
10910                         DOM.get(o.editorContainer).style.display = t.orgDisplay;\r
10911                         DOM.get(t.id).style.display = 'none';\r
10912                         DOM.setAttrib(t.id, 'aria-hidden', true);\r
10913 \r
10914                         if (!tinymce.relaxedDomain || !u)\r
10915                                 t.setupIframe();\r
10916 \r
10917                         e = n = o = null; // Cleanup\r
10918                 },\r
10919 \r
10920                 setupIframe : function() {\r
10921                         var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;\r
10922 \r
10923                         // Setup iframe body\r
10924                         if (!isIE || !tinymce.relaxedDomain) {\r
10925                                 d.open();\r
10926                                 d.write(t.iframeHTML);\r
10927                                 d.close();\r
10928 \r
10929                                 if (tinymce.relaxedDomain)\r
10930                                         d.domain = tinymce.relaxedDomain;\r
10931                         }\r
10932 \r
10933                         // Design mode needs to be added here Ctrl+A will fail otherwise\r
10934                         if (!isIE) {\r
10935                                 try {\r
10936                                         if (!s.readonly)\r
10937                                                 d.designMode = 'On';\r
10938                                 } catch (ex) {\r
10939                                         // Will fail on Gecko if the editor is placed in an hidden container element\r
10940                                         // The design mode will be set ones the editor is focused\r
10941                                 }\r
10942                         }\r
10943 \r
10944                         // IE needs to use contentEditable or it will display non secure items for HTTPS\r
10945                         if (isIE) {\r
10946                                 // It will not steal focus if we hide it while setting contentEditable\r
10947                                 b = t.getBody();\r
10948                                 DOM.hide(b);\r
10949 \r
10950                                 if (!s.readonly)\r
10951                                         b.contentEditable = true;\r
10952 \r
10953                                 DOM.show(b);\r
10954                         }\r
10955 \r
10956                         t.schema = new tinymce.html.Schema(s);\r
10957 \r
10958                         t.dom = new tinymce.dom.DOMUtils(t.getDoc(), {\r
10959                                 keep_values : true,\r
10960                                 url_converter : t.convertURL,\r
10961                                 url_converter_scope : t,\r
10962                                 hex_colors : s.force_hex_style_colors,\r
10963                                 class_filter : s.class_filter,\r
10964                                 update_styles : 1,\r
10965                                 fix_ie_paragraphs : 1,\r
10966                                 schema : t.schema\r
10967                         });\r
10968 \r
10969                         t.parser = new tinymce.html.DomParser(s, t.schema);\r
10970 \r
10971                         // Force anchor names closed\r
10972                         t.parser.addAttributeFilter('name', function(nodes, name) {\r
10973                                 var i = nodes.length, sibling, prevSibling, parent, node;\r
10974 \r
10975                                 while (i--) {\r
10976                                         node = nodes[i];\r
10977                                         if (node.name === 'a' && node.firstChild) {\r
10978                                                 parent = node.parent;\r
10979 \r
10980                                                 // Move children after current node\r
10981                                                 sibling = node.lastChild;\r
10982                                                 do {\r
10983                                                         prevSibling = sibling.prev;\r
10984                                                         parent.insert(sibling, node);\r
10985                                                         sibling = prevSibling;\r
10986                                                 } while (sibling);\r
10987                                         }\r
10988                                 }\r
10989                         });\r
10990 \r
10991                         // Convert src and href into data-mce-src, data-mce-href and data-mce-style\r
10992                         t.parser.addAttributeFilter('src,href,style', function(nodes, name) {\r
10993                                 var i = nodes.length, node, dom = t.dom, value;\r
10994 \r
10995                                 while (i--) {\r
10996                                         node = nodes[i];\r
10997                                         value = node.attr(name);\r
10998 \r
10999                                         if (name === "style")\r
11000                                                 node.attr('data-mce-style', dom.serializeStyle(dom.parseStyle(value), node.name));\r
11001                                         else\r
11002                                                 node.attr('data-mce-' + name, t.convertURL(value, name, node.name));\r
11003                                 }\r
11004                         });\r
11005 \r
11006                         // Keep scripts from executing\r
11007                         t.parser.addNodeFilter('script', function(nodes, name) {\r
11008                                 var i = nodes.length;\r
11009 \r
11010                                 while (i--)\r
11011                                         nodes[i].attr('type', 'mce-text/javascript');\r
11012                         });\r
11013 \r
11014                         t.parser.addNodeFilter('#cdata', function(nodes, name) {\r
11015                                 var i = nodes.length, node;\r
11016 \r
11017                                 while (i--) {\r
11018                                         node = nodes[i];\r
11019                                         node.type = 8;\r
11020                                         node.name = '#comment';\r
11021                                         node.value = '[CDATA[' + node.value + ']]';\r
11022                                 }\r
11023                         });\r
11024 \r
11025                         t.parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes, name) {\r
11026                                 var i = nodes.length, node, nonEmptyElements = t.schema.getNonEmptyElements();\r
11027 \r
11028                                 while (i--) {\r
11029                                         node = nodes[i];\r
11030 \r
11031                                         if (node.isEmpty(nonEmptyElements))\r
11032                                                 node.empty().append(new tinymce.html.Node('br', 1)).shortEnded = true;\r
11033                                 }\r
11034                         });\r
11035 \r
11036                         t.serializer = new tinymce.dom.Serializer(s, t.dom, t.schema);\r
11037 \r
11038                         t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);\r
11039 \r
11040                         t.formatter = new tinymce.Formatter(this);\r
11041 \r
11042                         // Register default formats\r
11043                         t.formatter.register({\r
11044                                 alignleft : [\r
11045                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}},\r
11046                                         {selector : 'img,table', collapsed : false, styles : {'float' : 'left'}}\r
11047                                 ],\r
11048 \r
11049                                 aligncenter : [\r
11050                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}},\r
11051                                         {selector : 'img', collapsed : false, styles : {display : 'block', marginLeft : 'auto', marginRight : 'auto'}},\r
11052                                         {selector : 'table', collapsed : false, styles : {marginLeft : 'auto', marginRight : 'auto'}}\r
11053                                 ],\r
11054 \r
11055                                 alignright : [\r
11056                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}},\r
11057                                         {selector : 'img,table', collapsed : false, styles : {'float' : 'right'}}\r
11058                                 ],\r
11059 \r
11060                                 alignfull : [\r
11061                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'justify'}}\r
11062                                 ],\r
11063 \r
11064                                 bold : [\r
11065                                         {inline : 'strong', remove : 'all'},\r
11066                                         {inline : 'span', styles : {fontWeight : 'bold'}},\r
11067                                         {inline : 'b', remove : 'all'}\r
11068                                 ],\r
11069 \r
11070                                 italic : [\r
11071                                         {inline : 'em', remove : 'all'},\r
11072                                         {inline : 'span', styles : {fontStyle : 'italic'}},\r
11073                                         {inline : 'i', remove : 'all'}\r
11074                                 ],\r
11075 \r
11076                                 underline : [\r
11077                                         {inline : 'span', styles : {textDecoration : 'underline'}, exact : true},\r
11078                                         {inline : 'u', remove : 'all'}\r
11079                                 ],\r
11080 \r
11081                                 strikethrough : [\r
11082                                         {inline : 'span', styles : {textDecoration : 'line-through'}, exact : true},\r
11083                                         {inline : 'strike', remove : 'all'}\r
11084                                 ],\r
11085 \r
11086                                 forecolor : {inline : 'span', styles : {color : '%value'}, wrap_links : false},\r
11087                                 hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}, wrap_links : false},\r
11088                                 fontname : {inline : 'span', styles : {fontFamily : '%value'}},\r
11089                                 fontsize : {inline : 'span', styles : {fontSize : '%value'}},\r
11090                                 fontsize_class : {inline : 'span', attributes : {'class' : '%value'}},\r
11091                                 blockquote : {block : 'blockquote', wrapper : 1, remove : 'all'},\r
11092                                 subscript : {inline : 'sub'},\r
11093                                 superscript : {inline : 'sup'},\r
11094 \r
11095                                 removeformat : [\r
11096                                         {selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},\r
11097                                         {selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},\r
11098                                         {selector : '*', attributes : ['style', 'class'], split : false, expand : false, deep : true}\r
11099                                 ]\r
11100                         });\r
11101 \r
11102                         // Register default block formats\r
11103                         each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) {\r
11104                                 t.formatter.register(name, {block : name, remove : 'all'});\r
11105                         });\r
11106 \r
11107                         // Register user defined formats\r
11108                         t.formatter.register(t.settings.formats);\r
11109 \r
11110                         t.undoManager = new tinymce.UndoManager(t);\r
11111 \r
11112                         // Pass through\r
11113                         t.undoManager.onAdd.add(function(um, l) {\r
11114                                 if (um.hasUndo())\r
11115                                         return t.onChange.dispatch(t, l, um);\r
11116                         });\r
11117 \r
11118                         t.undoManager.onUndo.add(function(um, l) {\r
11119                                 return t.onUndo.dispatch(t, l, um);\r
11120                         });\r
11121 \r
11122                         t.undoManager.onRedo.add(function(um, l) {\r
11123                                 return t.onRedo.dispatch(t, l, um);\r
11124                         });\r
11125 \r
11126                         t.forceBlocks = new tinymce.ForceBlocks(t, {\r
11127                                 forced_root_block : s.forced_root_block\r
11128                         });\r
11129 \r
11130                         t.editorCommands = new tinymce.EditorCommands(t);\r
11131 \r
11132                         // Pass through\r
11133                         t.serializer.onPreProcess.add(function(se, o) {\r
11134                                 return t.onPreProcess.dispatch(t, o, se);\r
11135                         });\r
11136 \r
11137                         t.serializer.onPostProcess.add(function(se, o) {\r
11138                                 return t.onPostProcess.dispatch(t, o, se);\r
11139                         });\r
11140 \r
11141                         t.onPreInit.dispatch(t);\r
11142 \r
11143                         if (!s.gecko_spellcheck)\r
11144                                 t.getBody().spellcheck = 0;\r
11145 \r
11146                         if (!s.readonly)\r
11147                                 t._addEvents();\r
11148 \r
11149                         t.controlManager.onPostRender.dispatch(t, t.controlManager);\r
11150                         t.onPostRender.dispatch(t);\r
11151 \r
11152                         if (s.directionality)\r
11153                                 t.getBody().dir = s.directionality;\r
11154 \r
11155                         if (s.nowrap)\r
11156                                 t.getBody().style.whiteSpace = "nowrap";\r
11157 \r
11158                         if (s.handle_node_change_callback) {\r
11159                                 t.onNodeChange.add(function(ed, cm, n) {\r
11160                                         t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());\r
11161                                 });\r
11162                         }\r
11163 \r
11164                         if (s.save_callback) {\r
11165                                 t.onSaveContent.add(function(ed, o) {\r
11166                                         var h = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
11167 \r
11168                                         if (h)\r
11169                                                 o.content = h;\r
11170                                 });\r
11171                         }\r
11172 \r
11173                         if (s.onchange_callback) {\r
11174                                 t.onChange.add(function(ed, l) {\r
11175                                         t.execCallback('onchange_callback', t, l);\r
11176                                 });\r
11177                         }\r
11178 \r
11179                         if (s.protect) {\r
11180                                 t.onBeforeSetContent.add(function(ed, o) {\r
11181                                         if (s.protect) {\r
11182                                                 each(s.protect, function(pattern) {\r
11183                                                         o.content = o.content.replace(pattern, function(str) {\r
11184                                                                 return '<!--mce:protected ' + escape(str) + '-->';\r
11185                                                         });\r
11186                                                 });\r
11187                                         }\r
11188                                 });\r
11189                         }\r
11190 \r
11191                         if (s.convert_newlines_to_brs) {\r
11192                                 t.onBeforeSetContent.add(function(ed, o) {\r
11193                                         if (o.initial)\r
11194                                                 o.content = o.content.replace(/\r?\n/g, '<br />');\r
11195                                 });\r
11196                         }\r
11197 \r
11198                         if (s.preformatted) {\r
11199                                 t.onPostProcess.add(function(ed, o) {\r
11200                                         o.content = o.content.replace(/^\s*<pre.*?>/, '');\r
11201                                         o.content = o.content.replace(/<\/pre>\s*$/, '');\r
11202 \r
11203                                         if (o.set)\r
11204                                                 o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';\r
11205                                 });\r
11206                         }\r
11207 \r
11208                         if (s.verify_css_classes) {\r
11209                                 t.serializer.attribValueFilter = function(n, v) {\r
11210                                         var s, cl;\r
11211 \r
11212                                         if (n == 'class') {\r
11213                                                 // Build regexp for classes\r
11214                                                 if (!t.classesRE) {\r
11215                                                         cl = t.dom.getClasses();\r
11216 \r
11217                                                         if (cl.length > 0) {\r
11218                                                                 s = '';\r
11219 \r
11220                                                                 each (cl, function(o) {\r
11221                                                                         s += (s ? '|' : '') + o['class'];\r
11222                                                                 });\r
11223 \r
11224                                                                 t.classesRE = new RegExp('(' + s + ')', 'gi');\r
11225                                                         }\r
11226                                                 }\r
11227 \r
11228                                                 return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';\r
11229                                         }\r
11230 \r
11231                                         return v;\r
11232                                 };\r
11233                         }\r
11234 \r
11235                         if (s.cleanup_callback) {\r
11236                                 t.onBeforeSetContent.add(function(ed, o) {\r
11237                                         o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
11238                                 });\r
11239 \r
11240                                 t.onPreProcess.add(function(ed, o) {\r
11241                                         if (o.set)\r
11242                                                 t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);\r
11243 \r
11244                                         if (o.get)\r
11245                                                 t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);\r
11246                                 });\r
11247 \r
11248                                 t.onPostProcess.add(function(ed, o) {\r
11249                                         if (o.set)\r
11250                                                 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
11251 \r
11252                                         if (o.get)                                              \r
11253                                                 o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);\r
11254                                 });\r
11255                         }\r
11256 \r
11257                         if (s.save_callback) {\r
11258                                 t.onGetContent.add(function(ed, o) {\r
11259                                         if (o.save)\r
11260                                                 o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
11261                                 });\r
11262                         }\r
11263 \r
11264                         if (s.handle_event_callback) {\r
11265                                 t.onEvent.add(function(ed, e, o) {\r
11266                                         if (t.execCallback('handle_event_callback', e, ed, o) === false)\r
11267                                                 Event.cancel(e);\r
11268                                 });\r
11269                         }\r
11270 \r
11271                         // Add visual aids when new contents is added\r
11272                         t.onSetContent.add(function() {\r
11273                                 t.addVisual(t.getBody());\r
11274                         });\r
11275 \r
11276                         // Remove empty contents\r
11277                         if (s.padd_empty_editor) {\r
11278                                 t.onPostProcess.add(function(ed, o) {\r
11279                                         o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');\r
11280                                 });\r
11281                         }\r
11282 \r
11283                         if (isGecko) {\r
11284                                 // Fix gecko link bug, when a link is placed at the end of block elements there is\r
11285                                 // no way to move the caret behind the link. This fix adds a bogus br element after the link\r
11286                                 function fixLinks(ed, o) {\r
11287                                         each(ed.dom.select('a'), function(n) {\r
11288                                                 var pn = n.parentNode;\r
11289 \r
11290                                                 if (ed.dom.isBlock(pn) && pn.lastChild === n)\r
11291                                                         ed.dom.add(pn, 'br', {'data-mce-bogus' : 1});\r
11292                                         });\r
11293                                 };\r
11294 \r
11295                                 t.onExecCommand.add(function(ed, cmd) {\r
11296                                         if (cmd === 'CreateLink')\r
11297                                                 fixLinks(ed);\r
11298                                 });\r
11299 \r
11300                                 t.onSetContent.add(t.selection.onSetContent.add(fixLinks));\r
11301 \r
11302                                 if (!s.readonly) {\r
11303                                         try {\r
11304                                                 // Design mode must be set here once again to fix a bug where\r
11305                                                 // Ctrl+A/Delete/Backspace didn't work if the editor was added using mceAddControl then removed then added again\r
11306                                                 d.designMode = 'Off';\r
11307                                                 d.designMode = 'On';\r
11308                                         } catch (ex) {\r
11309                                                 // Will fail on Gecko if the editor is placed in an hidden container element\r
11310                                                 // The design mode will be set ones the editor is focused\r
11311                                         }\r
11312                                 }\r
11313                         }\r
11314 \r
11315                         // A small timeout was needed since firefox will remove. Bug: #1838304\r
11316                         setTimeout(function () {\r
11317                                 if (t.removed)\r
11318                                         return;\r
11319 \r
11320                                 t.load({initial : true, format : 'html'});\r
11321                                 t.startContent = t.getContent({format : 'raw'});\r
11322                                 t.undoManager.add();\r
11323                                 t.initialized = true;\r
11324 \r
11325                                 t.onInit.dispatch(t);\r
11326                                 t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());\r
11327                                 t.execCallback('init_instance_callback', t);\r
11328                                 t.focus(true);\r
11329                                 t.nodeChanged({initial : 1});\r
11330 \r
11331                                 // Load specified content CSS last\r
11332                                 each(t.contentCSS, function(u) {\r
11333                                         t.dom.loadCSS(u);\r
11334                                 });\r
11335 \r
11336                                 // Handle auto focus\r
11337                                 if (s.auto_focus) {\r
11338                                         setTimeout(function () {\r
11339                                                 var ed = tinymce.get(s.auto_focus);\r
11340 \r
11341                                                 ed.selection.select(ed.getBody(), 1);\r
11342                                                 ed.selection.collapse(1);\r
11343                                                 ed.getWin().focus();\r
11344                                         }, 100);\r
11345                                 }\r
11346                         }, 1);\r
11347         \r
11348                         e = null;\r
11349                 },\r
11350 \r
11351 \r
11352                 focus : function(sf) {\r
11353                         var oed, t = this, ce = t.settings.content_editable, ieRng, controlElm, doc = t.getDoc();\r
11354 \r
11355                         if (!sf) {\r
11356                                 // Get selected control element\r
11357                                 ieRng = t.selection.getRng();\r
11358                                 if (ieRng.item) {\r
11359                                         controlElm = ieRng.item(0);\r
11360                                 }\r
11361 \r
11362                                 // Is not content editable\r
11363                                 if (!ce)\r
11364                                         t.getWin().focus();\r
11365 \r
11366                                 // Restore selected control element\r
11367                                 // This is needed when for example an image is selected within a\r
11368                                 // layer a call to focus will then remove the control selection\r
11369                                 if (controlElm && controlElm.ownerDocument == doc) {\r
11370                                         ieRng = doc.body.createControlRange();\r
11371                                         ieRng.addElement(controlElm);\r
11372                                         ieRng.select();\r
11373                                 }\r
11374 \r
11375                         }\r
11376 \r
11377                         if (tinymce.activeEditor != t) {\r
11378                                 if ((oed = tinymce.activeEditor) != null)\r
11379                                         oed.onDeactivate.dispatch(oed, t);\r
11380 \r
11381                                 t.onActivate.dispatch(t, oed);\r
11382                         }\r
11383 \r
11384                         tinymce._setActive(t);\r
11385                 },\r
11386 \r
11387                 execCallback : function(n) {\r
11388                         var t = this, f = t.settings[n], s;\r
11389 \r
11390                         if (!f)\r
11391                                 return;\r
11392 \r
11393                         // Look through lookup\r
11394                         if (t.callbackLookup && (s = t.callbackLookup[n])) {\r
11395                                 f = s.func;\r
11396                                 s = s.scope;\r
11397                         }\r
11398 \r
11399                         if (is(f, 'string')) {\r
11400                                 s = f.replace(/\.\w+$/, '');\r
11401                                 s = s ? tinymce.resolve(s) : 0;\r
11402                                 f = tinymce.resolve(f);\r
11403                                 t.callbackLookup = t.callbackLookup || {};\r
11404                                 t.callbackLookup[n] = {func : f, scope : s};\r
11405                         }\r
11406 \r
11407                         return f.apply(s || t, Array.prototype.slice.call(arguments, 1));\r
11408                 },\r
11409 \r
11410                 translate : function(s) {\r
11411                         var c = this.settings.language || 'en', i18n = tinymce.i18n;\r
11412 \r
11413                         if (!s)\r
11414                                 return '';\r
11415 \r
11416                         return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {\r
11417                                 return i18n[c + '.' + b] || '{#' + b + '}';\r
11418                         });\r
11419                 },\r
11420 \r
11421                 getLang : function(n, dv) {\r
11422                         return tinymce.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}');\r
11423                 },\r
11424 \r
11425                 getParam : function(n, dv, ty) {\r
11426                         var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;\r
11427 \r
11428                         if (ty === 'hash') {\r
11429                                 o = {};\r
11430 \r
11431                                 if (is(v, 'string')) {\r
11432                                         each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {\r
11433                                                 v = v.split('=');\r
11434 \r
11435                                                 if (v.length > 1)\r
11436                                                         o[tr(v[0])] = tr(v[1]);\r
11437                                                 else\r
11438                                                         o[tr(v[0])] = tr(v);\r
11439                                         });\r
11440                                 } else\r
11441                                         o = v;\r
11442 \r
11443                                 return o;\r
11444                         }\r
11445 \r
11446                         return v;\r
11447                 },\r
11448 \r
11449                 nodeChanged : function(o) {\r
11450                         var t = this, s = t.selection, n = s.getStart() || t.getBody();\r
11451 \r
11452                         // Fix for bug #1896577 it seems that this can not be fired while the editor is loading\r
11453                         if (t.initialized) {\r
11454                                 o = o || {};\r
11455                                 n = isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n; // Fix for IE initial state\r
11456 \r
11457                                 // Get parents and add them to object\r
11458                                 o.parents = [];\r
11459                                 t.dom.getParent(n, function(node) {\r
11460                                         if (node.nodeName == 'BODY')\r
11461                                                 return true;\r
11462 \r
11463                                         o.parents.push(node);\r
11464                                 });\r
11465 \r
11466                                 t.onNodeChange.dispatch(\r
11467                                         t,\r
11468                                         o ? o.controlManager || t.controlManager : t.controlManager,\r
11469                                         n,\r
11470                                         s.isCollapsed(),\r
11471                                         o\r
11472                                 );\r
11473                         }\r
11474                 },\r
11475 \r
11476                 addButton : function(n, s) {\r
11477                         var t = this;\r
11478 \r
11479                         t.buttons = t.buttons || {};\r
11480                         t.buttons[n] = s;\r
11481                 },\r
11482 \r
11483                 addCommand : function(name, callback, scope) {\r
11484                         this.execCommands[name] = {func : callback, scope : scope || this};\r
11485                 },\r
11486 \r
11487                 addQueryStateHandler : function(name, callback, scope) {\r
11488                         this.queryStateCommands[name] = {func : callback, scope : scope || this};\r
11489                 },\r
11490 \r
11491                 addQueryValueHandler : function(name, callback, scope) {\r
11492                         this.queryValueCommands[name] = {func : callback, scope : scope || this};\r
11493                 },\r
11494 \r
11495                 addShortcut : function(pa, desc, cmd_func, sc) {\r
11496                         var t = this, c;\r
11497 \r
11498                         if (!t.settings.custom_shortcuts)\r
11499                                 return false;\r
11500 \r
11501                         t.shortcuts = t.shortcuts || {};\r
11502 \r
11503                         if (is(cmd_func, 'string')) {\r
11504                                 c = cmd_func;\r
11505 \r
11506                                 cmd_func = function() {\r
11507                                         t.execCommand(c, false, null);\r
11508                                 };\r
11509                         }\r
11510 \r
11511                         if (is(cmd_func, 'object')) {\r
11512                                 c = cmd_func;\r
11513 \r
11514                                 cmd_func = function() {\r
11515                                         t.execCommand(c[0], c[1], c[2]);\r
11516                                 };\r
11517                         }\r
11518 \r
11519                         each(explode(pa), function(pa) {\r
11520                                 var o = {\r
11521                                         func : cmd_func,\r
11522                                         scope : sc || this,\r
11523                                         desc : desc,\r
11524                                         alt : false,\r
11525                                         ctrl : false,\r
11526                                         shift : false\r
11527                                 };\r
11528 \r
11529                                 each(explode(pa, '+'), function(v) {\r
11530                                         switch (v) {\r
11531                                                 case 'alt':\r
11532                                                 case 'ctrl':\r
11533                                                 case 'shift':\r
11534                                                         o[v] = true;\r
11535                                                         break;\r
11536 \r
11537                                                 default:\r
11538                                                         o.charCode = v.charCodeAt(0);\r
11539                                                         o.keyCode = v.toUpperCase().charCodeAt(0);\r
11540                                         }\r
11541                                 });\r
11542 \r
11543                                 t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;\r
11544                         });\r
11545 \r
11546                         return true;\r
11547                 },\r
11548 \r
11549                 execCommand : function(cmd, ui, val, a) {\r
11550                         var t = this, s = 0, o, st;\r
11551 \r
11552                         if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))\r
11553                                 t.focus();\r
11554 \r
11555                         o = {};\r
11556                         t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);\r
11557                         if (o.terminate)\r
11558                                 return false;\r
11559 \r
11560                         // Command callback\r
11561                         if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {\r
11562                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
11563                                 return true;\r
11564                         }\r
11565 \r
11566                         // Registred commands\r
11567                         if (o = t.execCommands[cmd]) {\r
11568                                 st = o.func.call(o.scope, ui, val);\r
11569 \r
11570                                 // Fall through on true\r
11571                                 if (st !== true) {\r
11572                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
11573                                         return st;\r
11574                                 }\r
11575                         }\r
11576 \r
11577                         // Plugin commands\r
11578                         each(t.plugins, function(p) {\r
11579                                 if (p.execCommand && p.execCommand(cmd, ui, val)) {\r
11580                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
11581                                         s = 1;\r
11582                                         return false;\r
11583                                 }\r
11584                         });\r
11585 \r
11586                         if (s)\r
11587                                 return true;\r
11588 \r
11589                         // Theme commands\r
11590                         if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {\r
11591                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
11592                                 return true;\r
11593                         }\r
11594 \r
11595                         // Editor commands\r
11596                         if (t.editorCommands.execCommand(cmd, ui, val)) {\r
11597                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
11598                                 return true;\r
11599                         }\r
11600 \r
11601                         // Browser commands\r
11602                         t.getDoc().execCommand(cmd, ui, val);\r
11603                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
11604                 },\r
11605 \r
11606                 queryCommandState : function(cmd) {\r
11607                         var t = this, o, s;\r
11608 \r
11609                         // Is hidden then return undefined\r
11610                         if (t._isHidden())\r
11611                                 return;\r
11612 \r
11613                         // Registred commands\r
11614                         if (o = t.queryStateCommands[cmd]) {\r
11615                                 s = o.func.call(o.scope);\r
11616 \r
11617                                 // Fall though on true\r
11618                                 if (s !== true)\r
11619                                         return s;\r
11620                         }\r
11621 \r
11622                         // Registred commands\r
11623                         o = t.editorCommands.queryCommandState(cmd);\r
11624                         if (o !== -1)\r
11625                                 return o;\r
11626 \r
11627                         // Browser commands\r
11628                         try {\r
11629                                 return this.getDoc().queryCommandState(cmd);\r
11630                         } catch (ex) {\r
11631                                 // Fails sometimes see bug: 1896577\r
11632                         }\r
11633                 },\r
11634 \r
11635                 queryCommandValue : function(c) {\r
11636                         var t = this, o, s;\r
11637 \r
11638                         // Is hidden then return undefined\r
11639                         if (t._isHidden())\r
11640                                 return;\r
11641 \r
11642                         // Registred commands\r
11643                         if (o = t.queryValueCommands[c]) {\r
11644                                 s = o.func.call(o.scope);\r
11645 \r
11646                                 // Fall though on true\r
11647                                 if (s !== true)\r
11648                                         return s;\r
11649                         }\r
11650 \r
11651                         // Registred commands\r
11652                         o = t.editorCommands.queryCommandValue(c);\r
11653                         if (is(o))\r
11654                                 return o;\r
11655 \r
11656                         // Browser commands\r
11657                         try {\r
11658                                 return this.getDoc().queryCommandValue(c);\r
11659                         } catch (ex) {\r
11660                                 // Fails sometimes see bug: 1896577\r
11661                         }\r
11662                 },\r
11663 \r
11664                 show : function() {\r
11665                         var t = this;\r
11666 \r
11667                         DOM.show(t.getContainer());\r
11668                         DOM.hide(t.id);\r
11669                         t.load();\r
11670                 },\r
11671 \r
11672                 hide : function() {\r
11673                         var t = this, d = t.getDoc();\r
11674 \r
11675                         // Fixed bug where IE has a blinking cursor left from the editor\r
11676                         if (isIE && d)\r
11677                                 d.execCommand('SelectAll');\r
11678 \r
11679                         // We must save before we hide so Safari doesn't crash\r
11680                         t.save();\r
11681                         DOM.hide(t.getContainer());\r
11682                         DOM.setStyle(t.id, 'display', t.orgDisplay);\r
11683                 },\r
11684 \r
11685                 isHidden : function() {\r
11686                         return !DOM.isHidden(this.id);\r
11687                 },\r
11688 \r
11689                 setProgressState : function(b, ti, o) {\r
11690                         this.onSetProgressState.dispatch(this, b, ti, o);\r
11691 \r
11692                         return b;\r
11693                 },\r
11694 \r
11695                 load : function(o) {\r
11696                         var t = this, e = t.getElement(), h;\r
11697 \r
11698                         if (e) {\r
11699                                 o = o || {};\r
11700                                 o.load = true;\r
11701 \r
11702                                 // Double encode existing entities in the value\r
11703                                 h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);\r
11704                                 o.element = e;\r
11705 \r
11706                                 if (!o.no_events)\r
11707                                         t.onLoadContent.dispatch(t, o);\r
11708 \r
11709                                 o.element = e = null;\r
11710 \r
11711                                 return h;\r
11712                         }\r
11713                 },\r
11714 \r
11715                 save : function(o) {\r
11716                         var t = this, e = t.getElement(), h, f;\r
11717 \r
11718                         if (!e || !t.initialized)\r
11719                                 return;\r
11720 \r
11721                         o = o || {};\r
11722                         o.save = true;\r
11723 \r
11724                         // Add undo level will trigger onchange event\r
11725                         if (!o.no_events) {\r
11726                                 t.undoManager.typing = false;\r
11727                                 t.undoManager.add();\r
11728                         }\r
11729 \r
11730                         o.element = e;\r
11731                         h = o.content = t.getContent(o);\r
11732 \r
11733                         if (!o.no_events)\r
11734                                 t.onSaveContent.dispatch(t, o);\r
11735 \r
11736                         h = o.content;\r
11737 \r
11738                         if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {\r
11739                                 e.innerHTML = h;\r
11740 \r
11741                                 // Update hidden form element\r
11742                                 if (f = DOM.getParent(t.id, 'form')) {\r
11743                                         each(f.elements, function(e) {\r
11744                                                 if (e.name == t.id) {\r
11745                                                         e.value = h;\r
11746                                                         return false;\r
11747                                                 }\r
11748                                         });\r
11749                                 }\r
11750                         } else\r
11751                                 e.value = h;\r
11752 \r
11753                         o.element = e = null;\r
11754 \r
11755                         return h;\r
11756                 },\r
11757 \r
11758                 setContent : function(content, args) {\r
11759                         var self = this, rootNode, body = self.getBody();\r
11760 \r
11761                         // Setup args object\r
11762                         args = args || {};\r
11763                         args.format = args.format || 'html';\r
11764                         args.set = true;\r
11765                         args.content = content;\r
11766 \r
11767                         // Do preprocessing\r
11768                         if (!args.no_events)\r
11769                                 self.onBeforeSetContent.dispatch(self, args);\r
11770 \r
11771                         content = args.content;\r
11772 \r
11773                         // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content\r
11774                         // It will also be impossible to place the caret in the editor unless there is a BR element present\r
11775                         if (!tinymce.isIE && (content.length === 0 || /^\s+$/.test(content))) {\r
11776                                 body.innerHTML = '<br data-mce-bogus="1" />';\r
11777                                 return;\r
11778                         }\r
11779 \r
11780                         // Parse and serialize the html\r
11781                         if (args.format !== 'raw') {\r
11782                                 content = new tinymce.html.Serializer({}, self.schema).serialize(\r
11783                                         self.parser.parse(content)\r
11784                                 );\r
11785                         }\r
11786 \r
11787                         // Set the new cleaned contents to the editor\r
11788                         args.content = tinymce.trim(content);\r
11789                         self.dom.setHTML(body, args.content);\r
11790 \r
11791                         // Do post processing\r
11792                         if (!args.no_events)\r
11793                                 self.onSetContent.dispatch(self, args);\r
11794 \r
11795                         return args.content;\r
11796                 },\r
11797 \r
11798                 getContent : function(args) {\r
11799                         var self = this, content;\r
11800 \r
11801                         // Setup args object\r
11802                         args = args || {};\r
11803                         args.format = args.format || 'html';\r
11804                         args.get = true;\r
11805 \r
11806                         // Do preprocessing\r
11807                         if (!args.no_events)\r
11808                                 self.onBeforeGetContent.dispatch(self, args);\r
11809 \r
11810                         // Get raw contents or by default the cleaned contents\r
11811                         if (args.format == 'raw')\r
11812                                 content = self.getBody().innerHTML;\r
11813                         else\r
11814                                 content = self.serializer.serialize(self.getBody(), args);\r
11815 \r
11816                         args.content = tinymce.trim(content);\r
11817 \r
11818                         // Do post processing\r
11819                         if (!args.no_events)\r
11820                                 self.onGetContent.dispatch(self, args);\r
11821 \r
11822                         return args.content;\r
11823                 },\r
11824 \r
11825                 isDirty : function() {\r
11826                         var self = this;\r
11827 \r
11828                         return tinymce.trim(self.startContent) != tinymce.trim(self.getContent({format : 'raw', no_events : 1})) && !self.isNotDirty;\r
11829                 },\r
11830 \r
11831                 getContainer : function() {\r
11832                         var t = this;\r
11833 \r
11834                         if (!t.container)\r
11835                                 t.container = DOM.get(t.editorContainer || t.id + '_parent');\r
11836 \r
11837                         return t.container;\r
11838                 },\r
11839 \r
11840                 getContentAreaContainer : function() {\r
11841                         return this.contentAreaContainer;\r
11842                 },\r
11843 \r
11844                 getElement : function() {\r
11845                         return DOM.get(this.settings.content_element || this.id);\r
11846                 },\r
11847 \r
11848                 getWin : function() {\r
11849                         var t = this, e;\r
11850 \r
11851                         if (!t.contentWindow) {\r
11852                                 e = DOM.get(t.id + "_ifr");\r
11853 \r
11854                                 if (e)\r
11855                                         t.contentWindow = e.contentWindow;\r
11856                         }\r
11857 \r
11858                         return t.contentWindow;\r
11859                 },\r
11860 \r
11861                 getDoc : function() {\r
11862                         var t = this, w;\r
11863 \r
11864                         if (!t.contentDocument) {\r
11865                                 w = t.getWin();\r
11866 \r
11867                                 if (w)\r
11868                                         t.contentDocument = w.document;\r
11869                         }\r
11870 \r
11871                         return t.contentDocument;\r
11872                 },\r
11873 \r
11874                 getBody : function() {\r
11875                         return this.bodyElement || this.getDoc().body;\r
11876                 },\r
11877 \r
11878                 convertURL : function(u, n, e) {\r
11879                         var t = this, s = t.settings;\r
11880 \r
11881                         // Use callback instead\r
11882                         if (s.urlconverter_callback)\r
11883                                 return t.execCallback('urlconverter_callback', u, e, true, n);\r
11884 \r
11885                         // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs\r
11886                         if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)\r
11887                                 return u;\r
11888 \r
11889                         // Convert to relative\r
11890                         if (s.relative_urls)\r
11891                                 return t.documentBaseURI.toRelative(u);\r
11892 \r
11893                         // Convert to absolute\r
11894                         u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);\r
11895 \r
11896                         return u;\r
11897                 },\r
11898 \r
11899                 addVisual : function(e) {\r
11900                         var t = this, s = t.settings;\r
11901 \r
11902                         e = e || t.getBody();\r
11903 \r
11904                         if (!is(t.hasVisual))\r
11905                                 t.hasVisual = s.visual;\r
11906 \r
11907                         each(t.dom.select('table,a', e), function(e) {\r
11908                                 var v;\r
11909 \r
11910                                 switch (e.nodeName) {\r
11911                                         case 'TABLE':\r
11912                                                 v = t.dom.getAttrib(e, 'border');\r
11913 \r
11914                                                 if (!v || v == '0') {\r
11915                                                         if (t.hasVisual)\r
11916                                                                 t.dom.addClass(e, s.visual_table_class);\r
11917                                                         else\r
11918                                                                 t.dom.removeClass(e, s.visual_table_class);\r
11919                                                 }\r
11920 \r
11921                                                 return;\r
11922 \r
11923                                         case 'A':\r
11924                                                 v = t.dom.getAttrib(e, 'name');\r
11925 \r
11926                                                 if (v) {\r
11927                                                         if (t.hasVisual)\r
11928                                                                 t.dom.addClass(e, 'mceItemAnchor');\r
11929                                                         else\r
11930                                                                 t.dom.removeClass(e, 'mceItemAnchor');\r
11931                                                 }\r
11932 \r
11933                                                 return;\r
11934                                 }\r
11935                         });\r
11936 \r
11937                         t.onVisualAid.dispatch(t, e, t.hasVisual);\r
11938                 },\r
11939 \r
11940                 remove : function() {\r
11941                         var t = this, e = t.getContainer();\r
11942 \r
11943                         t.removed = 1; // Cancels post remove event execution\r
11944                         t.hide();\r
11945 \r
11946                         t.execCallback('remove_instance_callback', t);\r
11947                         t.onRemove.dispatch(t);\r
11948 \r
11949                         // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command\r
11950                         t.onExecCommand.listeners = [];\r
11951 \r
11952                         tinymce.remove(t);\r
11953                         DOM.remove(e);\r
11954                 },\r
11955 \r
11956                 destroy : function(s) {\r
11957                         var t = this;\r
11958 \r
11959                         // One time is enough\r
11960                         if (t.destroyed)\r
11961                                 return;\r
11962 \r
11963                         if (!s) {\r
11964                                 tinymce.removeUnload(t.destroy);\r
11965                                 tinyMCE.onBeforeUnload.remove(t._beforeUnload);\r
11966 \r
11967                                 // Manual destroy\r
11968                                 if (t.theme && t.theme.destroy)\r
11969                                         t.theme.destroy();\r
11970 \r
11971                                 // Destroy controls, selection and dom\r
11972                                 t.controlManager.destroy();\r
11973                                 t.selection.destroy();\r
11974                                 t.dom.destroy();\r
11975 \r
11976                                 // Remove all events\r
11977 \r
11978                                 // Don't clear the window or document if content editable\r
11979                                 // is enabled since other instances might still be present\r
11980                                 if (!t.settings.content_editable) {\r
11981                                         Event.clear(t.getWin());\r
11982                                         Event.clear(t.getDoc());\r
11983                                 }\r
11984 \r
11985                                 Event.clear(t.getBody());\r
11986                                 Event.clear(t.formElement);\r
11987                         }\r
11988 \r
11989                         if (t.formElement) {\r
11990                                 t.formElement.submit = t.formElement._mceOldSubmit;\r
11991                                 t.formElement._mceOldSubmit = null;\r
11992                         }\r
11993 \r
11994                         t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;\r
11995 \r
11996                         if (t.selection)\r
11997                                 t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;\r
11998 \r
11999                         t.destroyed = 1;\r
12000                 },\r
12001 \r
12002                 // Internal functions\r
12003 \r
12004                 _addEvents : function() {\r
12005                         // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset\r
12006                         var t = this, i, s = t.settings, dom = t.dom, lo = {\r
12007                                 mouseup : 'onMouseUp',\r
12008                                 mousedown : 'onMouseDown',\r
12009                                 click : 'onClick',\r
12010                                 keyup : 'onKeyUp',\r
12011                                 keydown : 'onKeyDown',\r
12012                                 keypress : 'onKeyPress',\r
12013                                 submit : 'onSubmit',\r
12014                                 reset : 'onReset',\r
12015                                 contextmenu : 'onContextMenu',\r
12016                                 dblclick : 'onDblClick',\r
12017                                 paste : 'onPaste' // Doesn't work in all browsers yet\r
12018                         };\r
12019 \r
12020                         function eventHandler(e, o) {\r
12021                                 var ty = e.type;\r
12022 \r
12023                                 // Don't fire events when it's removed\r
12024                                 if (t.removed)\r
12025                                         return;\r
12026 \r
12027                                 // Generic event handler\r
12028                                 if (t.onEvent.dispatch(t, e, o) !== false) {\r
12029                                         // Specific event handler\r
12030                                         t[lo[e.fakeType || e.type]].dispatch(t, e, o);\r
12031                                 }\r
12032                         };\r
12033 \r
12034                         // Add DOM events\r
12035                         each(lo, function(v, k) {\r
12036                                 switch (k) {\r
12037                                         case 'contextmenu':\r
12038                                                 dom.bind(t.getDoc(), k, eventHandler);\r
12039                                                 break;\r
12040 \r
12041                                         case 'paste':\r
12042                                                 dom.bind(t.getBody(), k, function(e) {\r
12043                                                         eventHandler(e);\r
12044                                                 });\r
12045                                                 break;\r
12046 \r
12047                                         case 'submit':\r
12048                                         case 'reset':\r
12049                                                 dom.bind(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);\r
12050                                                 break;\r
12051 \r
12052                                         default:\r
12053                                                 dom.bind(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler);\r
12054                                 }\r
12055                         });\r
12056 \r
12057                         dom.bind(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) {\r
12058                                 t.focus(true);\r
12059                         });\r
12060 \r
12061 \r
12062                         // Fixes bug where a specified document_base_uri could result in broken images\r
12063                         // This will also fix drag drop of images in Gecko\r
12064                         if (tinymce.isGecko) {\r
12065                                 dom.bind(t.getDoc(), 'DOMNodeInserted', function(e) {\r
12066                                         var v;\r
12067 \r
12068                                         e = e.target;\r
12069 \r
12070                                         if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('data-mce-src')))\r
12071                                                 e.src = t.documentBaseURI.toAbsolute(v);\r
12072                                 });\r
12073                         }\r
12074 \r
12075                         // Set various midas options in Gecko\r
12076                         if (isGecko) {\r
12077                                 function setOpts() {\r
12078                                         var t = this, d = t.getDoc(), s = t.settings;\r
12079 \r
12080                                         if (isGecko && !s.readonly) {\r
12081                                                 if (t._isHidden()) {\r
12082                                                         try {\r
12083                                                                 if (!s.content_editable)\r
12084                                                                         d.designMode = 'On';\r
12085                                                         } catch (ex) {\r
12086                                                                 // Fails if it's hidden\r
12087                                                         }\r
12088                                                 }\r
12089 \r
12090                                                 try {\r
12091                                                         // Try new Gecko method\r
12092                                                         d.execCommand("styleWithCSS", 0, false);\r
12093                                                 } catch (ex) {\r
12094                                                         // Use old method\r
12095                                                         if (!t._isHidden())\r
12096                                                                 try {d.execCommand("useCSS", 0, true);} catch (ex) {}\r
12097                                                 }\r
12098 \r
12099                                                 if (!s.table_inline_editing)\r
12100                                                         try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {}\r
12101 \r
12102                                                 if (!s.object_resizing)\r
12103                                                         try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {}\r
12104                                         }\r
12105                                 };\r
12106 \r
12107                                 t.onBeforeExecCommand.add(setOpts);\r
12108                                 t.onMouseDown.add(setOpts);\r
12109                         }\r
12110 \r
12111                         // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250\r
12112                         // WebKit can't even do simple things like selecting an image\r
12113                         // This also fixes so it's possible to select mceItemAnchors\r
12114                         if (tinymce.isWebKit) {\r
12115                                 t.onClick.add(function(ed, e) {\r
12116                                         e = e.target;\r
12117 \r
12118                                         // Needs tobe the setBaseAndExtend or it will fail to select floated images\r
12119                                         if (e.nodeName == 'IMG' || (e.nodeName == 'A' && dom.hasClass(e, 'mceItemAnchor'))) {\r
12120                                                 t.selection.getSel().setBaseAndExtent(e, 0, e, 1);\r
12121                                                 t.nodeChanged();\r
12122                                         }\r
12123                                 });\r
12124                         }\r
12125 \r
12126                         // Add node change handlers\r
12127                         t.onMouseUp.add(t.nodeChanged);\r
12128                         //t.onClick.add(t.nodeChanged);\r
12129                         t.onKeyUp.add(function(ed, e) {\r
12130                                 var c = e.keyCode;\r
12131 \r
12132                                 if ((c >= 33 && c <= 36) || (c >= 37 && c <= 40) || c == 13 || c == 45 || c == 46 || c == 8 || (tinymce.isMac && (c == 91 || c == 93)) || e.ctrlKey)\r
12133                                         t.nodeChanged();\r
12134                         });\r
12135 \r
12136                         // Add reset handler\r
12137                         t.onReset.add(function() {\r
12138                                 t.setContent(t.startContent, {format : 'raw'});\r
12139                         });\r
12140 \r
12141                         // Add shortcuts\r
12142                         if (s.custom_shortcuts) {\r
12143                                 if (s.custom_undo_redo_keyboard_shortcuts) {\r
12144                                         t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo');\r
12145                                         t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo');\r
12146                                 }\r
12147 \r
12148                                 // Add default shortcuts for gecko\r
12149                                 t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');\r
12150                                 t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');\r
12151                                 t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');\r
12152 \r
12153                                 // BlockFormat shortcuts keys\r
12154                                 for (i=1; i<=6; i++)\r
12155                                         t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);\r
12156 \r
12157                                 t.addShortcut('ctrl+7', '', ['FormatBlock', false, '<p>']);\r
12158                                 t.addShortcut('ctrl+8', '', ['FormatBlock', false, '<div>']);\r
12159                                 t.addShortcut('ctrl+9', '', ['FormatBlock', false, '<address>']);\r
12160 \r
12161                                 function find(e) {\r
12162                                         var v = null;\r
12163 \r
12164                                         if (!e.altKey && !e.ctrlKey && !e.metaKey)\r
12165                                                 return v;\r
12166 \r
12167                                         each(t.shortcuts, function(o) {\r
12168                                                 if (tinymce.isMac && o.ctrl != e.metaKey)\r
12169                                                         return;\r
12170                                                 else if (!tinymce.isMac && o.ctrl != e.ctrlKey)\r
12171                                                         return;\r
12172 \r
12173                                                 if (o.alt != e.altKey)\r
12174                                                         return;\r
12175 \r
12176                                                 if (o.shift != e.shiftKey)\r
12177                                                         return;\r
12178 \r
12179                                                 if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {\r
12180                                                         v = o;\r
12181                                                         return false;\r
12182                                                 }\r
12183                                         });\r
12184 \r
12185                                         return v;\r
12186                                 };\r
12187 \r
12188                                 t.onKeyUp.add(function(ed, e) {\r
12189                                         var o = find(e);\r
12190 \r
12191                                         if (o)\r
12192                                                 return Event.cancel(e);\r
12193                                 });\r
12194 \r
12195                                 t.onKeyPress.add(function(ed, e) {\r
12196                                         var o = find(e);\r
12197 \r
12198                                         if (o)\r
12199                                                 return Event.cancel(e);\r
12200                                 });\r
12201 \r
12202                                 t.onKeyDown.add(function(ed, e) {\r
12203                                         var o = find(e);\r
12204 \r
12205                                         if (o) {\r
12206                                                 o.func.call(o.scope);\r
12207                                                 return Event.cancel(e);\r
12208                                         }\r
12209                                 });\r
12210                         }\r
12211 \r
12212                         if (tinymce.isIE) {\r
12213                                 // Fix so resize will only update the width and height attributes not the styles of an image\r
12214                                 // It will also block mceItemNoResize items\r
12215                                 dom.bind(t.getDoc(), 'controlselect', function(e) {\r
12216                                         var re = t.resizeInfo, cb;\r
12217 \r
12218                                         e = e.target;\r
12219 \r
12220                                         // Don't do this action for non image elements\r
12221                                         if (e.nodeName !== 'IMG')\r
12222                                                 return;\r
12223 \r
12224                                         if (re)\r
12225                                                 dom.unbind(re.node, re.ev, re.cb);\r
12226 \r
12227                                         if (!dom.hasClass(e, 'mceItemNoResize')) {\r
12228                                                 ev = 'resizeend';\r
12229                                                 cb = dom.bind(e, ev, function(e) {\r
12230                                                         var v;\r
12231 \r
12232                                                         e = e.target;\r
12233 \r
12234                                                         if (v = dom.getStyle(e, 'width')) {\r
12235                                                                 dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, ''));\r
12236                                                                 dom.setStyle(e, 'width', '');\r
12237                                                         }\r
12238 \r
12239                                                         if (v = dom.getStyle(e, 'height')) {\r
12240                                                                 dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, ''));\r
12241                                                                 dom.setStyle(e, 'height', '');\r
12242                                                         }\r
12243                                                 });\r
12244                                         } else {\r
12245                                                 ev = 'resizestart';\r
12246                                                 cb = dom.bind(e, 'resizestart', Event.cancel, Event);\r
12247                                         }\r
12248 \r
12249                                         re = t.resizeInfo = {\r
12250                                                 node : e,\r
12251                                                 ev : ev,\r
12252                                                 cb : cb\r
12253                                         };\r
12254                                 });\r
12255 \r
12256                                 t.onKeyDown.add(function(ed, e) {\r
12257                                         var sel;\r
12258 \r
12259                                         switch (e.keyCode) {\r
12260                                                 case 8:\r
12261                                                         sel = t.getDoc().selection;\r
12262 \r
12263                                                         // Fix IE control + backspace browser bug\r
12264                                                         if (sel.createRange && sel.createRange().item) {\r
12265                                                                 ed.dom.remove(sel.createRange().item(0));\r
12266                                                                 return Event.cancel(e);\r
12267                                                         }\r
12268                                         }\r
12269                                 });\r
12270                         }\r
12271 \r
12272                         if (tinymce.isOpera) {\r
12273                                 t.onClick.add(function(ed, e) {\r
12274                                         Event.prevent(e);\r
12275                                 });\r
12276                         }\r
12277 \r
12278                         // Add custom undo/redo handlers\r
12279                         if (s.custom_undo_redo) {\r
12280                                 function addUndo() {\r
12281                                         t.undoManager.typing = false;\r
12282                                         t.undoManager.add();\r
12283                                 };\r
12284 \r
12285                                 dom.bind(t.getDoc(), 'focusout', function(e) {\r
12286                                         if (!t.removed && t.undoManager.typing)\r
12287                                                 addUndo();\r
12288                                 });\r
12289 \r
12290                                 // Add undo level when contents is drag/dropped within the editor\r
12291                                 t.dom.bind(t.dom.getRoot(), 'dragend', function(e) {\r
12292                                         addUndo();\r
12293                                 });\r
12294 \r
12295                                 t.onKeyUp.add(function(ed, e) {\r
12296                                         var rng, parent, bookmark;\r
12297 \r
12298                                         // Fix for bug #3168, to remove odd ".." nodes from the DOM we need to get/set the HTML of the parent node.\r
12299                                         if (isIE && e.keyCode == 8) {\r
12300                                                 rng = t.selection.getRng();\r
12301                                                 if (rng.parentElement) {\r
12302                                                         parent = rng.parentElement();\r
12303                                                         bookmark = t.selection.getBookmark();\r
12304                                                         parent.innerHTML = parent.innerHTML;\r
12305                                                         t.selection.moveToBookmark(bookmark);\r
12306                                                 }\r
12307                                         }\r
12308 \r
12309                                         if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45 || e.ctrlKey)\r
12310                                                 addUndo();\r
12311                                 });\r
12312 \r
12313                                 t.onKeyDown.add(function(ed, e) {\r
12314                                         var rng, parent, bookmark, keyCode = e.keyCode;\r
12315 \r
12316                                         // IE has a really odd bug where the DOM might include an node that doesn't have\r
12317                                         // a proper structure. If you try to access nodeValue it would throw an illegal value exception.\r
12318                                         // This seems to only happen when you delete contents and it seems to be avoidable if you refresh the element\r
12319                                         // after you delete contents from it. See: #3008923\r
12320                                         if (isIE && keyCode == 46) {\r
12321                                                 rng = t.selection.getRng();\r
12322 \r
12323                                                 if (rng.parentElement) {\r
12324                                                         parent = rng.parentElement();\r
12325 \r
12326                                                         if (!t.undoManager.typing) {\r
12327                                                                 t.undoManager.beforeChange();\r
12328                                                                 t.undoManager.typing = true;\r
12329                                                                 t.undoManager.add();\r
12330                                                         }\r
12331 \r
12332                                                         // Select next word when ctrl key is used in combo with delete\r
12333                                                         if (e.ctrlKey) {\r
12334                                                                 rng.moveEnd('word', 1);\r
12335                                                                 rng.select();\r
12336                                                         }\r
12337 \r
12338                                                         // Delete contents\r
12339                                                         t.selection.getSel().clear();\r
12340 \r
12341                                                         // Check if we are within the same parent\r
12342                                                         if (rng.parentElement() == parent) {\r
12343                                                                 bookmark = t.selection.getBookmark();\r
12344 \r
12345                                                                 try {\r
12346                                                                         // Update the HTML and hopefully it will remove the artifacts\r
12347                                                                         parent.innerHTML = parent.innerHTML;\r
12348                                                                 } catch (ex) {\r
12349                                                                         // And since it's IE it can sometimes produce an unknown runtime error\r
12350                                                                 }\r
12351 \r
12352                                                                 // Restore the caret position\r
12353                                                                 t.selection.moveToBookmark(bookmark);\r
12354                                                         }\r
12355 \r
12356                                                         // Block the default delete behavior since it might be broken\r
12357                                                         e.preventDefault();\r
12358                                                         return;\r
12359                                                 }\r
12360                                         }\r
12361 \r
12362                                         // Is caracter positon keys left,right,up,down,home,end,pgdown,pgup,enter\r
12363                                         if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 13 || keyCode == 45) {\r
12364                                                 // Add position before enter key is pressed, used by IE since it still uses the default browser behavior\r
12365                                                 // Todo: Remove this once we normalize enter behavior on IE\r
12366                                                 if (tinymce.isIE && keyCode == 13)\r
12367                                                         t.undoManager.beforeChange();\r
12368 \r
12369                                                 if (t.undoManager.typing)\r
12370                                                         addUndo();\r
12371 \r
12372                                                 return;\r
12373                                         }\r
12374 \r
12375                                         // If key isn't shift,ctrl,alt,capslock,metakey\r
12376                                         if ((keyCode < 16 || keyCode > 20) && keyCode != 224 && keyCode != 91 && !t.undoManager.typing) {\r
12377                                                 t.undoManager.beforeChange();\r
12378                                                 t.undoManager.add();\r
12379                                                 t.undoManager.typing = true;\r
12380                                         }\r
12381                                 });\r
12382 \r
12383                                 t.onMouseDown.add(function() {\r
12384                                         if (t.undoManager.typing)\r
12385                                                 addUndo();\r
12386                                 });\r
12387                         }\r
12388                         \r
12389                         // Bug fix for FireFox keeping styles from end of selection instead of start.\r
12390                         if (tinymce.isGecko) {\r
12391                                 function getAttributeApplyFunction() {\r
12392                                         var template = t.dom.getAttribs(t.selection.getStart().cloneNode(false));\r
12393 \r
12394                                         return function() {\r
12395                                                 var target = t.selection.getStart();\r
12396                                                 t.dom.removeAllAttribs(target);\r
12397                                                 each(template, function(attr) {\r
12398                                                         target.setAttributeNode(attr.cloneNode(true));\r
12399                                                 });\r
12400                                         };\r
12401                                 }\r
12402 \r
12403                                 function isSelectionAcrossElements() {\r
12404                                         var s = t.selection;\r
12405 \r
12406                                         return !s.isCollapsed() && s.getStart() != s.getEnd();\r
12407                                 }\r
12408 \r
12409                                 t.onKeyPress.add(function(ed, e) {\r
12410                                         var applyAttributes;\r
12411 \r
12412                                         if ((e.keyCode == 8 || e.keyCode == 46) && isSelectionAcrossElements()) {\r
12413                                                 applyAttributes = getAttributeApplyFunction();\r
12414                                                 t.getDoc().execCommand('delete', false, null);\r
12415                                                 applyAttributes();\r
12416 \r
12417                                                 return Event.cancel(e);\r
12418                                         }\r
12419                                 });\r
12420 \r
12421                                 t.dom.bind(t.getDoc(), 'cut', function(e) {\r
12422                                         var applyAttributes;\r
12423 \r
12424                                         if (isSelectionAcrossElements()) {\r
12425                                                 applyAttributes = getAttributeApplyFunction();\r
12426                                                 t.onKeyUp.addToTop(Event.cancel, Event);\r
12427 \r
12428                                                 setTimeout(function() {\r
12429                                                         applyAttributes();\r
12430                                                         t.onKeyUp.remove(Event.cancel, Event);\r
12431                                                 }, 0);\r
12432                                         }\r
12433                                 });\r
12434                         }\r
12435                 },\r
12436 \r
12437                 _isHidden : function() {\r
12438                         var s;\r
12439 \r
12440                         if (!isGecko)\r
12441                                 return 0;\r
12442 \r
12443                         // Weird, wheres that cursor selection?\r
12444                         s = this.selection.getSel();\r
12445                         return (!s || !s.rangeCount || s.rangeCount == 0);\r
12446                 }\r
12447         });\r
12448 })(tinymce);\r
12449 \r
12450 (function(tinymce) {\r
12451         // Added for compression purposes\r
12452         var each = tinymce.each, undefined, TRUE = true, FALSE = false;\r
12453 \r
12454         tinymce.EditorCommands = function(editor) {\r
12455                 var dom = editor.dom,\r
12456                         selection = editor.selection,\r
12457                         commands = {state: {}, exec : {}, value : {}},\r
12458                         settings = editor.settings,\r
12459                         bookmark;\r
12460 \r
12461                 function execCommand(command, ui, value) {\r
12462                         var func;\r
12463 \r
12464                         command = command.toLowerCase();\r
12465                         if (func = commands.exec[command]) {\r
12466                                 func(command, ui, value);\r
12467                                 return TRUE;\r
12468                         }\r
12469 \r
12470                         return FALSE;\r
12471                 };\r
12472 \r
12473                 function queryCommandState(command) {\r
12474                         var func;\r
12475 \r
12476                         command = command.toLowerCase();\r
12477                         if (func = commands.state[command])\r
12478                                 return func(command);\r
12479 \r
12480                         return -1;\r
12481                 };\r
12482 \r
12483                 function queryCommandValue(command) {\r
12484                         var func;\r
12485 \r
12486                         command = command.toLowerCase();\r
12487                         if (func = commands.value[command])\r
12488                                 return func(command);\r
12489 \r
12490                         return FALSE;\r
12491                 };\r
12492 \r
12493                 function addCommands(command_list, type) {\r
12494                         type = type || 'exec';\r
12495 \r
12496                         each(command_list, function(callback, command) {\r
12497                                 each(command.toLowerCase().split(','), function(command) {\r
12498                                         commands[type][command] = callback;\r
12499                                 });\r
12500                         });\r
12501                 };\r
12502 \r
12503                 // Expose public methods\r
12504                 tinymce.extend(this, {\r
12505                         execCommand : execCommand,\r
12506                         queryCommandState : queryCommandState,\r
12507                         queryCommandValue : queryCommandValue,\r
12508                         addCommands : addCommands\r
12509                 });\r
12510 \r
12511                 // Private methods\r
12512 \r
12513                 function execNativeCommand(command, ui, value) {\r
12514                         if (ui === undefined)\r
12515                                 ui = FALSE;\r
12516 \r
12517                         if (value === undefined)\r
12518                                 value = null;\r
12519 \r
12520                         return editor.getDoc().execCommand(command, ui, value);\r
12521                 };\r
12522 \r
12523                 function isFormatMatch(name) {\r
12524                         return editor.formatter.match(name);\r
12525                 };\r
12526 \r
12527                 function toggleFormat(name, value) {\r
12528                         editor.formatter.toggle(name, value ? {value : value} : undefined);\r
12529                 };\r
12530 \r
12531                 function storeSelection(type) {\r
12532                         bookmark = selection.getBookmark(type);\r
12533                 };\r
12534 \r
12535                 function restoreSelection() {\r
12536                         selection.moveToBookmark(bookmark);\r
12537                 };\r
12538 \r
12539                 // Add execCommand overrides\r
12540                 addCommands({\r
12541                         // Ignore these, added for compatibility\r
12542                         'mceResetDesignMode,mceBeginUndoLevel' : function() {},\r
12543 \r
12544                         // Add undo manager logic\r
12545                         'mceEndUndoLevel,mceAddUndoLevel' : function() {\r
12546                                 editor.undoManager.add();\r
12547                         },\r
12548 \r
12549                         'Cut,Copy,Paste' : function(command) {\r
12550                                 var doc = editor.getDoc(), failed;\r
12551 \r
12552                                 // Try executing the native command\r
12553                                 try {\r
12554                                         execNativeCommand(command);\r
12555                                 } catch (ex) {\r
12556                                         // Command failed\r
12557                                         failed = TRUE;\r
12558                                 }\r
12559 \r
12560                                 // Present alert message about clipboard access not being available\r
12561                                 if (failed || !doc.queryCommandSupported(command)) {\r
12562                                         if (tinymce.isGecko) {\r
12563                                                 editor.windowManager.confirm(editor.getLang('clipboard_msg'), function(state) {\r
12564                                                         if (state)\r
12565                                                                 open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', '_blank');\r
12566                                                 });\r
12567                                         } else\r
12568                                                 editor.windowManager.alert(editor.getLang('clipboard_no_support'));\r
12569                                 }\r
12570                         },\r
12571 \r
12572                         // Override unlink command\r
12573                         unlink : function(command) {\r
12574                                 if (selection.isCollapsed())\r
12575                                         selection.select(selection.getNode());\r
12576 \r
12577                                 execNativeCommand(command);\r
12578                                 selection.collapse(FALSE);\r
12579                         },\r
12580 \r
12581                         // Override justify commands to use the text formatter engine\r
12582                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
12583                                 var align = command.substring(7);\r
12584 \r
12585                                 // Remove all other alignments first\r
12586                                 each('left,center,right,full'.split(','), function(name) {\r
12587                                         if (align != name)\r
12588                                                 editor.formatter.remove('align' + name);\r
12589                                 });\r
12590 \r
12591                                 toggleFormat('align' + align);\r
12592                                 execCommand('mceRepaint');\r
12593                         },\r
12594 \r
12595                         // Override list commands to fix WebKit bug\r
12596                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
12597                                 var listElm, listParent;\r
12598 \r
12599                                 execNativeCommand(command);\r
12600 \r
12601                                 // WebKit produces lists within block elements so we need to split them\r
12602                                 // we will replace the native list creation logic to custom logic later on\r
12603                                 // TODO: Remove this when the list creation logic is removed\r
12604                                 listElm = dom.getParent(selection.getNode(), 'ol,ul');\r
12605                                 if (listElm) {\r
12606                                         listParent = listElm.parentNode;\r
12607 \r
12608                                         // If list is within a text block then split that block\r
12609                                         if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {\r
12610                                                 storeSelection();\r
12611                                                 dom.split(listParent, listElm);\r
12612                                                 restoreSelection();\r
12613                                         }\r
12614                                 }\r
12615                         },\r
12616 \r
12617                         // Override commands to use the text formatter engine\r
12618                         'Bold,Italic,Underline,Strikethrough,Superscript,Subscript' : function(command) {\r
12619                                 toggleFormat(command);\r
12620                         },\r
12621 \r
12622                         // Override commands to use the text formatter engine\r
12623                         'ForeColor,HiliteColor,FontName' : function(command, ui, value) {\r
12624                                 toggleFormat(command, value);\r
12625                         },\r
12626 \r
12627                         FontSize : function(command, ui, value) {\r
12628                                 var fontClasses, fontSizes;\r
12629 \r
12630                                 // Convert font size 1-7 to styles\r
12631                                 if (value >= 1 && value <= 7) {\r
12632                                         fontSizes = tinymce.explode(settings.font_size_style_values);\r
12633                                         fontClasses = tinymce.explode(settings.font_size_classes);\r
12634 \r
12635                                         if (fontClasses)\r
12636                                                 value = fontClasses[value - 1] || value;\r
12637                                         else\r
12638                                                 value = fontSizes[value - 1] || value;\r
12639                                 }\r
12640 \r
12641                                 toggleFormat(command, value);\r
12642                         },\r
12643 \r
12644                         RemoveFormat : function(command) {\r
12645                                 editor.formatter.remove(command);\r
12646                         },\r
12647 \r
12648                         mceBlockQuote : function(command) {\r
12649                                 toggleFormat('blockquote');\r
12650                         },\r
12651 \r
12652                         FormatBlock : function(command, ui, value) {\r
12653                                 return toggleFormat(value || 'p');\r
12654                         },\r
12655 \r
12656                         mceCleanup : function() {\r
12657                                 var bookmark = selection.getBookmark();\r
12658 \r
12659                                 editor.setContent(editor.getContent({cleanup : TRUE}), {cleanup : TRUE});\r
12660 \r
12661                                 selection.moveToBookmark(bookmark);\r
12662                         },\r
12663 \r
12664                         mceRemoveNode : function(command, ui, value) {\r
12665                                 var node = value || selection.getNode();\r
12666 \r
12667                                 // Make sure that the body node isn't removed\r
12668                                 if (node != editor.getBody()) {\r
12669                                         storeSelection();\r
12670                                         editor.dom.remove(node, TRUE);\r
12671                                         restoreSelection();\r
12672                                 }\r
12673                         },\r
12674 \r
12675                         mceSelectNodeDepth : function(command, ui, value) {\r
12676                                 var counter = 0;\r
12677 \r
12678                                 dom.getParent(selection.getNode(), function(node) {\r
12679                                         if (node.nodeType == 1 && counter++ == value) {\r
12680                                                 selection.select(node);\r
12681                                                 return FALSE;\r
12682                                         }\r
12683                                 }, editor.getBody());\r
12684                         },\r
12685 \r
12686                         mceSelectNode : function(command, ui, value) {\r
12687                                 selection.select(value);\r
12688                         },\r
12689 \r
12690                         mceInsertContent : function(command, ui, value) {\r
12691                                 var caretNode, rng, rootNode, parent, node, rng, nodeRect, viewPortRect, args;\r
12692 \r
12693                                 function findSuitableCaretNode(node, root_node, next) {\r
12694                                         var walker = new tinymce.dom.TreeWalker(next ? node.nextSibling : node.previousSibling, root_node);\r
12695 \r
12696                                         while ((node = walker.current())) {\r
12697                                                 if ((node.nodeType == 3 && tinymce.trim(node.nodeValue).length) || node.nodeName == 'BR' || node.nodeName == 'IMG')\r
12698                                                         return node;\r
12699 \r
12700                                                 if (next)\r
12701                                                         walker.next();\r
12702                                                 else\r
12703                                                         walker.prev();\r
12704                                         }\r
12705                                 };\r
12706 \r
12707                                 args = {content: value, format: 'html'};\r
12708                                 selection.onBeforeSetContent.dispatch(selection, args);\r
12709                                 value = args.content;\r
12710 \r
12711                                 // Add caret at end of contents if it's missing\r
12712                                 if (value.indexOf('{$caret}') == -1)\r
12713                                         value += '{$caret}';\r
12714 \r
12715                                 // Set the content at selection to a span and replace it's contents with the value\r
12716                                 selection.setContent('<span id="__mce">\uFEFF</span>', {no_events : false});\r
12717                                 dom.setOuterHTML('__mce', value.replace(/\{\$caret\}/, '<span data-mce-type="bookmark" id="__mce">\uFEFF</span>'));\r
12718 \r
12719                                 caretNode = dom.select('#__mce')[0];\r
12720                                 rootNode = dom.getRoot();\r
12721 \r
12722                                 // Move the caret into the last suitable location within the previous sibling if it's a block since the block might be split\r
12723                                 if (caretNode.previousSibling && dom.isBlock(caretNode.previousSibling) || caretNode.parentNode == rootNode) {\r
12724                                         node = findSuitableCaretNode(caretNode, rootNode);\r
12725                                         if (node) {\r
12726                                                 if (node.nodeName == 'BR')\r
12727                                                         node.parentNode.insertBefore(caretNode, node);\r
12728                                                 else\r
12729                                                         dom.insertAfter(caretNode, node);\r
12730                                         }\r
12731                                 }\r
12732 \r
12733                                 // Find caret root parent and clean it up using the serializer to avoid nesting\r
12734                                 while (caretNode) {\r
12735                                         if (caretNode === rootNode) {\r
12736                                                 // Clean up the parent element by parsing and serializing it\r
12737                                                 // This will remove invalid elements/attributes and fix nesting issues\r
12738                                                 dom.setOuterHTML(parent, \r
12739                                                         new tinymce.html.Serializer({}, editor.schema).serialize(\r
12740                                                                 editor.parser.parse(dom.getOuterHTML(parent))\r
12741                                                         )\r
12742                                                 );\r
12743 \r
12744                                                 break;\r
12745                                         }\r
12746 \r
12747                                         parent = caretNode;\r
12748                                         caretNode = caretNode.parentNode;\r
12749                                 }\r
12750 \r
12751                                 // Find caret after cleanup and move selection to that location\r
12752                                 caretNode = dom.select('#__mce')[0];\r
12753                                 if (caretNode) {\r
12754                                         node = findSuitableCaretNode(caretNode, rootNode) || findSuitableCaretNode(caretNode, rootNode, true);\r
12755                                         dom.remove(caretNode);\r
12756 \r
12757                                         if (node) {\r
12758                                                 rng = dom.createRng();\r
12759 \r
12760                                                 if (node.nodeType == 3) {\r
12761                                                         rng.setStart(node, node.length);\r
12762                                                         rng.setEnd(node, node.length);\r
12763                                                 } else {\r
12764                                                         if (node.nodeName == 'BR') {\r
12765                                                                 rng.setStartBefore(node);\r
12766                                                                 rng.setEndBefore(node);\r
12767                                                         } else {\r
12768                                                                 rng.setStartAfter(node);\r
12769                                                                 rng.setEndAfter(node);\r
12770                                                         }\r
12771                                                 }\r
12772 \r
12773                                                 selection.setRng(rng);\r
12774 \r
12775                                                 // Scroll range into view scrollIntoView on element can't be used since it will scroll the main view port as well\r
12776                                                 if (!tinymce.isIE) {\r
12777                                                         node = dom.create('span', null, '\u00a0');\r
12778                                                         rng.insertNode(node);\r
12779                                                         nodeRect = dom.getRect(node);\r
12780                                                         viewPortRect = dom.getViewPort(editor.getWin());\r
12781 \r
12782                                                         // Check if node is out side the viewport if it is then scroll to it\r
12783                                                         if ((nodeRect.y > viewPortRect.y + viewPortRect.h || nodeRect.y < viewPortRect.y) ||\r
12784                                                                 (nodeRect.x > viewPortRect.x + viewPortRect.w || nodeRect.x < viewPortRect.x)) {\r
12785                                                                 editor.getBody().scrollLeft = nodeRect.x;\r
12786                                                                 editor.getBody().scrollTop = nodeRect.y;\r
12787                                                         }\r
12788 \r
12789                                                         dom.remove(node);\r
12790                                                 }\r
12791 \r
12792                                                 // Make sure that the selection is collapsed after we removed the node fixes a WebKit bug\r
12793                                                 // where WebKit would place the endContainer/endOffset at a different location than the startContainer/startOffset\r
12794                                                 selection.collapse(true);\r
12795                                         }\r
12796                                 }\r
12797 \r
12798                                 selection.onSetContent.dispatch(selection, args);\r
12799                                 editor.addVisual();\r
12800                         },\r
12801 \r
12802                         mceInsertRawHTML : function(command, ui, value) {\r
12803                                 selection.setContent('tiny_mce_marker');\r
12804                                 editor.setContent(editor.getContent().replace(/tiny_mce_marker/g, function() { return value }));\r
12805                         },\r
12806 \r
12807                         mceSetContent : function(command, ui, value) {\r
12808                                 editor.setContent(value);\r
12809                         },\r
12810 \r
12811                         'Indent,Outdent' : function(command) {\r
12812                                 var intentValue, indentUnit, value;\r
12813 \r
12814                                 // Setup indent level\r
12815                                 intentValue = settings.indentation;\r
12816                                 indentUnit = /[a-z%]+$/i.exec(intentValue);\r
12817                                 intentValue = parseInt(intentValue);\r
12818 \r
12819                                 if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) {\r
12820                                         each(selection.getSelectedBlocks(), function(element) {\r
12821                                                 if (command == 'outdent') {\r
12822                                                         value = Math.max(0, parseInt(element.style.paddingLeft || 0) - intentValue);\r
12823                                                         dom.setStyle(element, 'paddingLeft', value ? value + indentUnit : '');\r
12824                                                 } else\r
12825                                                         dom.setStyle(element, 'paddingLeft', (parseInt(element.style.paddingLeft || 0) + intentValue) + indentUnit);\r
12826                                         });\r
12827                                 } else\r
12828                                         execNativeCommand(command);\r
12829                         },\r
12830 \r
12831                         mceRepaint : function() {\r
12832                                 var bookmark;\r
12833 \r
12834                                 if (tinymce.isGecko) {\r
12835                                         try {\r
12836                                                 storeSelection(TRUE);\r
12837 \r
12838                                                 if (selection.getSel())\r
12839                                                         selection.getSel().selectAllChildren(editor.getBody());\r
12840 \r
12841                                                 selection.collapse(TRUE);\r
12842                                                 restoreSelection();\r
12843                                         } catch (ex) {\r
12844                                                 // Ignore\r
12845                                         }\r
12846                                 }\r
12847                         },\r
12848 \r
12849                         mceToggleFormat : function(command, ui, value) {\r
12850                                 editor.formatter.toggle(value);\r
12851                         },\r
12852 \r
12853                         InsertHorizontalRule : function() {\r
12854                                 editor.execCommand('mceInsertContent', false, '<hr />');\r
12855                         },\r
12856 \r
12857                         mceToggleVisualAid : function() {\r
12858                                 editor.hasVisual = !editor.hasVisual;\r
12859                                 editor.addVisual();\r
12860                         },\r
12861 \r
12862                         mceReplaceContent : function(command, ui, value) {\r
12863                                 editor.execCommand('mceInsertContent', false, value.replace(/\{\$selection\}/g, selection.getContent({format : 'text'})));\r
12864                         },\r
12865 \r
12866                         mceInsertLink : function(command, ui, value) {\r
12867                                 var link = dom.getParent(selection.getNode(), 'a'), img, floatVal;\r
12868 \r
12869                                 if (tinymce.is(value, 'string'))\r
12870                                         value = {href : value};\r
12871 \r
12872                                 // Spaces are never valid in URLs and it's a very common mistake for people to make so we fix it here.\r
12873                                 value.href = value.href.replace(' ', '%20');\r
12874 \r
12875                                 if (!link) {\r
12876                                         // WebKit can't create links on float images for some odd reason so just remove it and restore it later\r
12877                                         if (tinymce.isWebKit) {\r
12878                                                 img = dom.getParent(selection.getNode(), 'img');\r
12879 \r
12880                                                 if (img) {\r
12881                                                         floatVal = img.style.cssFloat;\r
12882                                                         img.style.cssFloat = null;\r
12883                                                 }\r
12884                                         }\r
12885 \r
12886                                         execNativeCommand('CreateLink', FALSE, 'javascript:mctmp(0);');\r
12887 \r
12888                                         // Restore float value\r
12889                                         if (floatVal)\r
12890                                                 img.style.cssFloat = floatVal;\r
12891 \r
12892                                         each(dom.select("a[href='javascript:mctmp(0);']"), function(link) {\r
12893                                                 dom.setAttribs(link, value);\r
12894                                         });\r
12895                                 } else {\r
12896                                         if (value.href)\r
12897                                                 dom.setAttribs(link, value);\r
12898                                         else\r
12899                                                 editor.dom.remove(link, TRUE);\r
12900                                 }\r
12901                         },\r
12902                         \r
12903                         selectAll : function() {\r
12904                                 var root = dom.getRoot(), rng = dom.createRng();\r
12905 \r
12906                                 rng.setStart(root, 0);\r
12907                                 rng.setEnd(root, root.childNodes.length);\r
12908 \r
12909                                 editor.selection.setRng(rng);\r
12910                         }\r
12911                 });\r
12912 \r
12913                 // Add queryCommandState overrides\r
12914                 addCommands({\r
12915                         // Override justify commands\r
12916                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
12917                                 return isFormatMatch('align' + command.substring(7));\r
12918                         },\r
12919 \r
12920                         'Bold,Italic,Underline,Strikethrough,Superscript,Subscript' : function(command) {\r
12921                                 return isFormatMatch(command);\r
12922                         },\r
12923 \r
12924                         mceBlockQuote : function() {\r
12925                                 return isFormatMatch('blockquote');\r
12926                         },\r
12927 \r
12928                         Outdent : function() {\r
12929                                 var node;\r
12930 \r
12931                                 if (settings.inline_styles) {\r
12932                                         if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
12933                                                 return TRUE;\r
12934 \r
12935                                         if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
12936                                                 return TRUE;\r
12937                                 }\r
12938 \r
12939                                 return queryCommandState('InsertUnorderedList') || queryCommandState('InsertOrderedList') || (!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE'));\r
12940                         },\r
12941 \r
12942                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
12943                                 return dom.getParent(selection.getNode(), command == 'insertunorderedlist' ? 'UL' : 'OL');\r
12944                         }\r
12945                 }, 'state');\r
12946 \r
12947                 // Add queryCommandValue overrides\r
12948                 addCommands({\r
12949                         'FontSize,FontName' : function(command) {\r
12950                                 var value = 0, parent;\r
12951 \r
12952                                 if (parent = dom.getParent(selection.getNode(), 'span')) {\r
12953                                         if (command == 'fontsize')\r
12954                                                 value = parent.style.fontSize;\r
12955                                         else\r
12956                                                 value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();\r
12957                                 }\r
12958 \r
12959                                 return value;\r
12960                         }\r
12961                 }, 'value');\r
12962 \r
12963                 // Add undo manager logic\r
12964                 if (settings.custom_undo_redo) {\r
12965                         addCommands({\r
12966                                 Undo : function() {\r
12967                                         editor.undoManager.undo();\r
12968                                 },\r
12969 \r
12970                                 Redo : function() {\r
12971                                         editor.undoManager.redo();\r
12972                                 }\r
12973                         });\r
12974                 }\r
12975         };\r
12976 })(tinymce);\r
12977 \r
12978 (function(tinymce) {\r
12979         var Dispatcher = tinymce.util.Dispatcher;\r
12980 \r
12981         tinymce.UndoManager = function(editor) {\r
12982                 var self, index = 0, data = [];\r
12983 \r
12984                 function getContent() {\r
12985                         return tinymce.trim(editor.getContent({format : 'raw', no_events : 1}));\r
12986                 };\r
12987 \r
12988                 return self = {\r
12989                         typing : false,\r
12990 \r
12991                         onAdd : new Dispatcher(self),\r
12992 \r
12993                         onUndo : new Dispatcher(self),\r
12994 \r
12995                         onRedo : new Dispatcher(self),\r
12996 \r
12997                         beforeChange : function() {\r
12998                                 // Set before bookmark on previous level\r
12999                                 if (data[index])\r
13000                                         data[index].beforeBookmark = editor.selection.getBookmark(2, true);\r
13001                         },\r
13002 \r
13003                         add : function(level) {\r
13004                                 var i, settings = editor.settings, lastLevel;\r
13005 \r
13006                                 level = level || {};\r
13007                                 level.content = getContent();\r
13008 \r
13009                                 // Add undo level if needed\r
13010                                 lastLevel = data[index];\r
13011                                 if (lastLevel && lastLevel.content == level.content)\r
13012                                         return null;\r
13013 \r
13014                                 // Time to compress\r
13015                                 if (settings.custom_undo_redo_levels) {\r
13016                                         if (data.length > settings.custom_undo_redo_levels) {\r
13017                                                 for (i = 0; i < data.length - 1; i++)\r
13018                                                         data[i] = data[i + 1];\r
13019 \r
13020                                                 data.length--;\r
13021                                                 index = data.length;\r
13022                                         }\r
13023                                 }\r
13024 \r
13025                                 // Get a non intrusive normalized bookmark\r
13026                                 level.bookmark = editor.selection.getBookmark(2, true);\r
13027 \r
13028                                 // Crop array if needed\r
13029                                 if (index < data.length - 1)\r
13030                                         data.length = index + 1;\r
13031 \r
13032                                 data.push(level);\r
13033                                 index = data.length - 1;\r
13034 \r
13035                                 self.onAdd.dispatch(self, level);\r
13036                                 editor.isNotDirty = 0;\r
13037 \r
13038                                 return level;\r
13039                         },\r
13040 \r
13041                         undo : function() {\r
13042                                 var level, i;\r
13043 \r
13044                                 if (self.typing) {\r
13045                                         self.add();\r
13046                                         self.typing = false;\r
13047                                 }\r
13048 \r
13049                                 if (index > 0) {\r
13050                                         level = data[--index];\r
13051 \r
13052                                         editor.setContent(level.content, {format : 'raw'});\r
13053                                         editor.selection.moveToBookmark(level.beforeBookmark);\r
13054 \r
13055                                         self.onUndo.dispatch(self, level);\r
13056                                 }\r
13057 \r
13058                                 return level;\r
13059                         },\r
13060 \r
13061                         redo : function() {\r
13062                                 var level;\r
13063 \r
13064                                 if (index < data.length - 1) {\r
13065                                         level = data[++index];\r
13066 \r
13067                                         editor.setContent(level.content, {format : 'raw'});\r
13068                                         editor.selection.moveToBookmark(level.bookmark);\r
13069 \r
13070                                         self.onRedo.dispatch(self, level);\r
13071                                 }\r
13072 \r
13073                                 return level;\r
13074                         },\r
13075 \r
13076                         clear : function() {\r
13077                                 data = [];\r
13078                                 index = 0;\r
13079                                 self.typing = false;\r
13080                         },\r
13081 \r
13082                         hasUndo : function() {\r
13083                                 return index > 0 || this.typing;\r
13084                         },\r
13085 \r
13086                         hasRedo : function() {\r
13087                                 return index < data.length - 1 && !this.typing;\r
13088                         }\r
13089                 };\r
13090         };\r
13091 })(tinymce);\r
13092 \r
13093 (function(tinymce) {\r
13094         // Shorten names\r
13095         var Event = tinymce.dom.Event,\r
13096                 isIE = tinymce.isIE,\r
13097                 isGecko = tinymce.isGecko,\r
13098                 isOpera = tinymce.isOpera,\r
13099                 each = tinymce.each,\r
13100                 extend = tinymce.extend,\r
13101                 TRUE = true,\r
13102                 FALSE = false;\r
13103 \r
13104         function cloneFormats(node) {\r
13105                 var clone, temp, inner;\r
13106 \r
13107                 do {\r
13108                         if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) {\r
13109                                 if (clone) {\r
13110                                         temp = node.cloneNode(false);\r
13111                                         temp.appendChild(clone);\r
13112                                         clone = temp;\r
13113                                 } else {\r
13114                                         clone = inner = node.cloneNode(false);\r
13115                                 }\r
13116 \r
13117                                 clone.removeAttribute('id');\r
13118                         }\r
13119                 } while (node = node.parentNode);\r
13120 \r
13121                 if (clone)\r
13122                         return {wrapper : clone, inner : inner};\r
13123         };\r
13124 \r
13125         // Checks if the selection/caret is at the end of the specified block element\r
13126         function isAtEnd(rng, par) {\r
13127                 var rng2 = par.ownerDocument.createRange();\r
13128 \r
13129                 rng2.setStart(rng.endContainer, rng.endOffset);\r
13130                 rng2.setEndAfter(par);\r
13131 \r
13132                 // Get number of characters to the right of the cursor if it's zero then we are at the end and need to merge the next block element\r
13133                 return rng2.cloneContents().textContent.length == 0;\r
13134         };\r
13135 \r
13136         function splitList(selection, dom, li) {\r
13137                 var listBlock, block;\r
13138 \r
13139                 if (dom.isEmpty(li)) {\r
13140                         listBlock = dom.getParent(li, 'ul,ol');\r
13141 \r
13142                         if (!dom.getParent(listBlock.parentNode, 'ul,ol')) {\r
13143                                 dom.split(listBlock, li);\r
13144                                 block = dom.create('p', 0, '<br data-mce-bogus="1" />');\r
13145                                 dom.replace(block, li);\r
13146                                 selection.select(block, 1);\r
13147                         }\r
13148 \r
13149                         return FALSE;\r
13150                 }\r
13151 \r
13152                 return TRUE;\r
13153         };\r
13154 \r
13155         tinymce.create('tinymce.ForceBlocks', {\r
13156                 ForceBlocks : function(ed) {\r
13157                         var t = this, s = ed.settings, elm;\r
13158 \r
13159                         t.editor = ed;\r
13160                         t.dom = ed.dom;\r
13161                         elm = (s.forced_root_block || 'p').toLowerCase();\r
13162                         s.element = elm.toUpperCase();\r
13163 \r
13164                         ed.onPreInit.add(t.setup, t);\r
13165 \r
13166                         if (s.forced_root_block) {\r
13167                                 ed.onInit.add(t.forceRoots, t);\r
13168                                 ed.onSetContent.add(t.forceRoots, t);\r
13169                                 ed.onBeforeGetContent.add(t.forceRoots, t);\r
13170                                 ed.onExecCommand.add(function(ed, cmd) {\r
13171                                         if (cmd == 'mceInsertContent') {\r
13172                                                 t.forceRoots();\r
13173                                                 ed.nodeChanged();\r
13174                                         }\r
13175                                 });\r
13176                         }\r
13177                 },\r
13178 \r
13179                 setup : function() {\r
13180                         var t = this, ed = t.editor, s = ed.settings, dom = ed.dom, selection = ed.selection;\r
13181 \r
13182                         // Force root blocks when typing and when getting output\r
13183                         if (s.forced_root_block) {\r
13184                                 ed.onBeforeExecCommand.add(t.forceRoots, t);\r
13185                                 ed.onKeyUp.add(t.forceRoots, t);\r
13186                                 ed.onPreProcess.add(t.forceRoots, t);\r
13187                         }\r
13188 \r
13189                         if (s.force_br_newlines) {\r
13190                                 // Force IE to produce BRs on enter\r
13191                                 if (isIE) {\r
13192                                         ed.onKeyPress.add(function(ed, e) {\r
13193                                                 var n;\r
13194 \r
13195                                                 if (e.keyCode == 13 && selection.getNode().nodeName != 'LI') {\r
13196                                                         selection.setContent('<br id="__" /> ', {format : 'raw'});\r
13197                                                         n = dom.get('__');\r
13198                                                         n.removeAttribute('id');\r
13199                                                         selection.select(n);\r
13200                                                         selection.collapse();\r
13201                                                         return Event.cancel(e);\r
13202                                                 }\r
13203                                         });\r
13204                                 }\r
13205                         }\r
13206 \r
13207                         if (s.force_p_newlines) {\r
13208                                 if (!isIE) {\r
13209                                         ed.onKeyPress.add(function(ed, e) {\r
13210                                                 if (e.keyCode == 13 && !e.shiftKey && !t.insertPara(e))\r
13211                                                         Event.cancel(e);\r
13212                                         });\r
13213                                 } else {\r
13214                                         // Ungly hack to for IE to preserve the formatting when you press\r
13215                                         // enter at the end of a block element with formatted contents\r
13216                                         // This logic overrides the browsers default logic with\r
13217                                         // custom logic that enables us to control the output\r
13218                                         tinymce.addUnload(function() {\r
13219                                                 t._previousFormats = 0; // Fix IE leak\r
13220                                         });\r
13221 \r
13222                                         ed.onKeyPress.add(function(ed, e) {\r
13223                                                 t._previousFormats = 0;\r
13224 \r
13225                                                 // Clone the current formats, this will later be applied to the new block contents\r
13226                                                 if (e.keyCode == 13 && !e.shiftKey && ed.selection.isCollapsed() && s.keep_styles)\r
13227                                                         t._previousFormats = cloneFormats(ed.selection.getStart());\r
13228                                         });\r
13229 \r
13230                                         ed.onKeyUp.add(function(ed, e) {\r
13231                                                 // Let IE break the element and the wrap the new caret location in the previous formats\r
13232                                                 if (e.keyCode == 13 && !e.shiftKey) {\r
13233                                                         var parent = ed.selection.getStart(), fmt = t._previousFormats;\r
13234 \r
13235                                                         // Parent is an empty block\r
13236                                                         if (!parent.hasChildNodes() && fmt) {\r
13237                                                                 parent = dom.getParent(parent, dom.isBlock);\r
13238 \r
13239                                                                 if (parent && parent.nodeName != 'LI') {\r
13240                                                                         parent.innerHTML = '';\r
13241 \r
13242                                                                         if (t._previousFormats) {\r
13243                                                                                 parent.appendChild(fmt.wrapper);\r
13244                                                                                 fmt.inner.innerHTML = '\uFEFF';\r
13245                                                                         } else\r
13246                                                                                 parent.innerHTML = '\uFEFF';\r
13247 \r
13248                                                                         selection.select(parent, 1);\r
13249                                                                         selection.collapse(true);\r
13250                                                                         ed.getDoc().execCommand('Delete', false, null);\r
13251                                                                         t._previousFormats = 0;\r
13252                                                                 }\r
13253                                                         }\r
13254                                                 }\r
13255                                         });\r
13256                                 }\r
13257 \r
13258                                 if (isGecko) {\r
13259                                         ed.onKeyDown.add(function(ed, e) {\r
13260                                                 if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey)\r
13261                                                         t.backspaceDelete(e, e.keyCode == 8);\r
13262                                         });\r
13263                                 }\r
13264                         }\r
13265 \r
13266                         // Workaround for missing shift+enter support, http://bugs.webkit.org/show_bug.cgi?id=16973\r
13267                         if (tinymce.isWebKit) {\r
13268                                 function insertBr(ed) {\r
13269                                         var rng = selection.getRng(), br, div = dom.create('div', null, ' '), divYPos, vpHeight = dom.getViewPort(ed.getWin()).h;\r
13270 \r
13271                                         // Insert BR element\r
13272                                         rng.insertNode(br = dom.create('br'));\r
13273 \r
13274                                         // Place caret after BR\r
13275                                         rng.setStartAfter(br);\r
13276                                         rng.setEndAfter(br);\r
13277                                         selection.setRng(rng);\r
13278 \r
13279                                         // Could not place caret after BR then insert an nbsp entity and move the caret\r
13280                                         if (selection.getSel().focusNode == br.previousSibling) {\r
13281                                                 selection.select(dom.insertAfter(dom.doc.createTextNode('\u00a0'), br));\r
13282                                                 selection.collapse(TRUE);\r
13283                                         }\r
13284 \r
13285                                         // Create a temporary DIV after the BR and get the position as it\r
13286                                         // seems like getPos() returns 0 for text nodes and BR elements.\r
13287                                         dom.insertAfter(div, br);\r
13288                                         divYPos = dom.getPos(div).y;\r
13289                                         dom.remove(div);\r
13290 \r
13291                                         // Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117\r
13292                                         if (divYPos > vpHeight) // It is not necessary to scroll if the DIV is inside the view port.\r
13293                                                 ed.getWin().scrollTo(0, divYPos);\r
13294                                 };\r
13295 \r
13296                                 ed.onKeyPress.add(function(ed, e) {\r
13297                                         if (e.keyCode == 13 && (e.shiftKey || (s.force_br_newlines && !dom.getParent(selection.getNode(), 'h1,h2,h3,h4,h5,h6,ol,ul')))) {\r
13298                                                 insertBr(ed);\r
13299                                                 Event.cancel(e);\r
13300                                         }\r
13301                                 });\r
13302                         }\r
13303 \r
13304                         // IE specific fixes\r
13305                         if (isIE) {\r
13306                                 // Replaces IE:s auto generated paragraphs with the specified element name\r
13307                                 if (s.element != 'P') {\r
13308                                         ed.onKeyPress.add(function(ed, e) {\r
13309                                                 t.lastElm = selection.getNode().nodeName;\r
13310                                         });\r
13311 \r
13312                                         ed.onKeyUp.add(function(ed, e) {\r
13313                                                 var bl, n = selection.getNode(), b = ed.getBody();\r
13314 \r
13315                                                 if (b.childNodes.length === 1 && n.nodeName == 'P') {\r
13316                                                         n = dom.rename(n, s.element);\r
13317                                                         selection.select(n);\r
13318                                                         selection.collapse();\r
13319                                                         ed.nodeChanged();\r
13320                                                 } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') {\r
13321                                                         bl = dom.getParent(n, 'p');\r
13322 \r
13323                                                         if (bl) {\r
13324                                                                 dom.rename(bl, s.element);\r
13325                                                                 ed.nodeChanged();\r
13326                                                         }\r
13327                                                 }\r
13328                                         });\r
13329                                 }\r
13330                         }\r
13331                 },\r
13332 \r
13333                 find : function(n, t, s) {\r
13334                         var ed = this.editor, w = ed.getDoc().createTreeWalker(n, 4, null, FALSE), c = -1;\r
13335 \r
13336                         while (n = w.nextNode()) {\r
13337                                 c++;\r
13338 \r
13339                                 // Index by node\r
13340                                 if (t == 0 && n == s)\r
13341                                         return c;\r
13342 \r
13343                                 // Node by index\r
13344                                 if (t == 1 && c == s)\r
13345                                         return n;\r
13346                         }\r
13347 \r
13348                         return -1;\r
13349                 },\r
13350 \r
13351                 forceRoots : function(ed, e) {\r
13352                         var t = this, ed = t.editor, b = ed.getBody(), d = ed.getDoc(), se = ed.selection, s = se.getSel(), r = se.getRng(), si = -2, ei, so, eo, tr, c = -0xFFFFFF;\r
13353                         var nx, bl, bp, sp, le, nl = b.childNodes, i, n, eid;\r
13354 \r
13355                         // Fix for bug #1863847\r
13356                         //if (e && e.keyCode == 13)\r
13357                         //      return TRUE;\r
13358 \r
13359                         // Wrap non blocks into blocks\r
13360                         for (i = nl.length - 1; i >= 0; i--) {\r
13361                                 nx = nl[i];\r
13362 \r
13363                                 // Ignore internal elements\r
13364                                 if (nx.nodeType === 1 && nx.getAttribute('data-mce-type')) {\r
13365                                         bl = null;\r
13366                                         continue;\r
13367                                 }\r
13368 \r
13369                                 // Is text or non block element\r
13370                                 if (nx.nodeType === 3 || (!t.dom.isBlock(nx) && nx.nodeType !== 8 && !/^(script|mce:script|style|mce:style)$/i.test(nx.nodeName))) {\r
13371                                         if (!bl) {\r
13372                                                 // Create new block but ignore whitespace\r
13373                                                 if (nx.nodeType != 3 || /[^\s]/g.test(nx.nodeValue)) {\r
13374                                                         // Store selection\r
13375                                                         if (si == -2 && r) {\r
13376                                                                 if (!isIE || r.setStart) {\r
13377                                                                         // If selection is element then mark it\r
13378                                                                         if (r.startContainer.nodeType == 1 && (n = r.startContainer.childNodes[r.startOffset]) && n.nodeType == 1) {\r
13379                                                                                 // Save the id of the selected element\r
13380                                                                                 eid = n.getAttribute("id");\r
13381                                                                                 n.setAttribute("id", "__mce");\r
13382                                                                         } else {\r
13383                                                                                 // If element is inside body, might not be the case in contentEdiable mode\r
13384                                                                                 if (ed.dom.getParent(r.startContainer, function(e) {return e === b;})) {\r
13385                                                                                         so = r.startOffset;\r
13386                                                                                         eo = r.endOffset;\r
13387                                                                                         si = t.find(b, 0, r.startContainer);\r
13388                                                                                         ei = t.find(b, 0, r.endContainer);\r
13389                                                                                 }\r
13390                                                                         }\r
13391                                                                 } else {\r
13392                                                                         // Force control range into text range\r
13393                                                                         if (r.item) {\r
13394                                                                                 tr = d.body.createTextRange();\r
13395                                                                                 tr.moveToElementText(r.item(0));\r
13396                                                                                 r = tr;\r
13397                                                                         }\r
13398 \r
13399                                                                         tr = d.body.createTextRange();\r
13400                                                                         tr.moveToElementText(b);\r
13401                                                                         tr.collapse(1);\r
13402                                                                         bp = tr.move('character', c) * -1;\r
13403 \r
13404                                                                         tr = r.duplicate();\r
13405                                                                         tr.collapse(1);\r
13406                                                                         sp = tr.move('character', c) * -1;\r
13407 \r
13408                                                                         tr = r.duplicate();\r
13409                                                                         tr.collapse(0);\r
13410                                                                         le = (tr.move('character', c) * -1) - sp;\r
13411 \r
13412                                                                         si = sp - bp;\r
13413                                                                         ei = le;\r
13414                                                                 }\r
13415                                                         }\r
13416 \r
13417                                                         // Uses replaceChild instead of cloneNode since it removes selected attribute from option elements on IE\r
13418                                                         // See: http://support.microsoft.com/kb/829907\r
13419                                                         bl = ed.dom.create(ed.settings.forced_root_block);\r
13420                                                         nx.parentNode.replaceChild(bl, nx);\r
13421                                                         bl.appendChild(nx);\r
13422                                                 }\r
13423                                         } else {\r
13424                                                 if (bl.hasChildNodes())\r
13425                                                         bl.insertBefore(nx, bl.firstChild);\r
13426                                                 else\r
13427                                                         bl.appendChild(nx);\r
13428                                         }\r
13429                                 } else\r
13430                                         bl = null; // Time to create new block\r
13431                         }\r
13432 \r
13433                         // Restore selection\r
13434                         if (si != -2) {\r
13435                                 if (!isIE || r.setStart) {\r
13436                                         bl = b.getElementsByTagName(ed.settings.element)[0];\r
13437                                         r = d.createRange();\r
13438 \r
13439                                         // Select last location or generated block\r
13440                                         if (si != -1)\r
13441                                                 r.setStart(t.find(b, 1, si), so);\r
13442                                         else\r
13443                                                 r.setStart(bl, 0);\r
13444 \r
13445                                         // Select last location or generated block\r
13446                                         if (ei != -1)\r
13447                                                 r.setEnd(t.find(b, 1, ei), eo);\r
13448                                         else\r
13449                                                 r.setEnd(bl, 0);\r
13450 \r
13451                                         if (s) {\r
13452                                                 s.removeAllRanges();\r
13453                                                 s.addRange(r);\r
13454                                         }\r
13455                                 } else {\r
13456                                         try {\r
13457                                                 r = s.createRange();\r
13458                                                 r.moveToElementText(b);\r
13459                                                 r.collapse(1);\r
13460                                                 r.moveStart('character', si);\r
13461                                                 r.moveEnd('character', ei);\r
13462                                                 r.select();\r
13463                                         } catch (ex) {\r
13464                                                 // Ignore\r
13465                                         }\r
13466                                 }\r
13467                         } else if ((!isIE || r.setStart) && (n = ed.dom.get('__mce'))) {\r
13468                                 // Restore the id of the selected element\r
13469                                 if (eid)\r
13470                                         n.setAttribute('id', eid);\r
13471                                 else\r
13472                                         n.removeAttribute('id');\r
13473 \r
13474                                 // Move caret before selected element\r
13475                                 r = d.createRange();\r
13476                                 r.setStartBefore(n);\r
13477                                 r.setEndBefore(n);\r
13478                                 se.setRng(r);\r
13479                         }\r
13480                 },\r
13481 \r
13482                 getParentBlock : function(n) {\r
13483                         var d = this.dom;\r
13484 \r
13485                         return d.getParent(n, d.isBlock);\r
13486                 },\r
13487 \r
13488                 insertPara : function(e) {\r
13489                         var t = this, ed = t.editor, dom = ed.dom, d = ed.getDoc(), se = ed.settings, s = ed.selection.getSel(), r = s.getRangeAt(0), b = d.body;\r
13490                         var rb, ra, dir, sn, so, en, eo, sb, eb, bn, bef, aft, sc, ec, n, vp = dom.getViewPort(ed.getWin()), y, ch, car;\r
13491 \r
13492                         ed.undoManager.beforeChange();\r
13493 \r
13494                         // If root blocks are forced then use Operas default behavior since it's really good\r
13495 // Removed due to bug: #1853816\r
13496 //                      if (se.forced_root_block && isOpera)\r
13497 //                              return TRUE;\r
13498 \r
13499                         // Setup before range\r
13500                         rb = d.createRange();\r
13501 \r
13502                         // If is before the first block element and in body, then move it into first block element\r
13503                         rb.setStart(s.anchorNode, s.anchorOffset);\r
13504                         rb.collapse(TRUE);\r
13505 \r
13506                         // Setup after range\r
13507                         ra = d.createRange();\r
13508 \r
13509                         // If is before the first block element and in body, then move it into first block element\r
13510                         ra.setStart(s.focusNode, s.focusOffset);\r
13511                         ra.collapse(TRUE);\r
13512 \r
13513                         // Setup start/end points\r
13514                         dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0;\r
13515                         sn = dir ? s.anchorNode : s.focusNode;\r
13516                         so = dir ? s.anchorOffset : s.focusOffset;\r
13517                         en = dir ? s.focusNode : s.anchorNode;\r
13518                         eo = dir ? s.focusOffset : s.anchorOffset;\r
13519 \r
13520                         // If selection is in empty table cell\r
13521                         if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) {\r
13522                                 if (sn.firstChild.nodeName == 'BR')\r
13523                                         dom.remove(sn.firstChild); // Remove BR\r
13524 \r
13525                                 // Create two new block elements\r
13526                                 if (sn.childNodes.length == 0) {\r
13527                                         ed.dom.add(sn, se.element, null, '<br />');\r
13528                                         aft = ed.dom.add(sn, se.element, null, '<br />');\r
13529                                 } else {\r
13530                                         n = sn.innerHTML;\r
13531                                         sn.innerHTML = '';\r
13532                                         ed.dom.add(sn, se.element, null, n);\r
13533                                         aft = ed.dom.add(sn, se.element, null, '<br />');\r
13534                                 }\r
13535 \r
13536                                 // Move caret into the last one\r
13537                                 r = d.createRange();\r
13538                                 r.selectNodeContents(aft);\r
13539                                 r.collapse(1);\r
13540                                 ed.selection.setRng(r);\r
13541 \r
13542                                 return FALSE;\r
13543                         }\r
13544 \r
13545                         // If the caret is in an invalid location in FF we need to move it into the first block\r
13546                         if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) {\r
13547                                 sn = en = sn.firstChild;\r
13548                                 so = eo = 0;\r
13549                                 rb = d.createRange();\r
13550                                 rb.setStart(sn, 0);\r
13551                                 ra = d.createRange();\r
13552                                 ra.setStart(en, 0);\r
13553                         }\r
13554 \r
13555                         // Never use body as start or end node\r
13556                         sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes\r
13557                         sn = sn.nodeName == "BODY" ? sn.firstChild : sn;\r
13558                         en = en.nodeName == "HTML" ? d.body : en; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes\r
13559                         en = en.nodeName == "BODY" ? en.firstChild : en;\r
13560 \r
13561                         // Get start and end blocks\r
13562                         sb = t.getParentBlock(sn);\r
13563                         eb = t.getParentBlock(en);\r
13564                         bn = sb ? sb.nodeName : se.element; // Get block name to create\r
13565 \r
13566                         // Return inside list use default browser behavior\r
13567                         if (n = t.dom.getParent(sb, 'li,pre')) {\r
13568                                 if (n.nodeName == 'LI')\r
13569                                         return splitList(ed.selection, t.dom, n);\r
13570 \r
13571                                 return TRUE;\r
13572                         }\r
13573 \r
13574                         // If caption or absolute layers then always generate new blocks within\r
13575                         if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {\r
13576                                 bn = se.element;\r
13577                                 sb = null;\r
13578                         }\r
13579 \r
13580                         // If caption or absolute layers then always generate new blocks within\r
13581                         if (eb && (eb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {\r
13582                                 bn = se.element;\r
13583                                 eb = null;\r
13584                         }\r
13585 \r
13586                         // Use P instead\r
13587                         if (/(TD|TABLE|TH|CAPTION)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(dom.getStyle(sb, 'float', 1)))) {\r
13588                                 bn = se.element;\r
13589                                 sb = eb = null;\r
13590                         }\r
13591 \r
13592                         // Setup new before and after blocks\r
13593                         bef = (sb && sb.nodeName == bn) ? sb.cloneNode(0) : ed.dom.create(bn);\r
13594                         aft = (eb && eb.nodeName == bn) ? eb.cloneNode(0) : ed.dom.create(bn);\r
13595 \r
13596                         // Remove id from after clone\r
13597                         aft.removeAttribute('id');\r
13598 \r
13599                         // Is header and cursor is at the end, then force paragraph under\r
13600                         if (/^(H[1-6])$/.test(bn) && isAtEnd(r, sb))\r
13601                                 aft = ed.dom.create(se.element);\r
13602 \r
13603                         // Find start chop node\r
13604                         n = sc = sn;\r
13605                         do {\r
13606                                 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))\r
13607                                         break;\r
13608 \r
13609                                 sc = n;\r
13610                         } while ((n = n.previousSibling ? n.previousSibling : n.parentNode));\r
13611 \r
13612                         // Find end chop node\r
13613                         n = ec = en;\r
13614                         do {\r
13615                                 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))\r
13616                                         break;\r
13617 \r
13618                                 ec = n;\r
13619                         } while ((n = n.nextSibling ? n.nextSibling : n.parentNode));\r
13620 \r
13621                         // Place first chop part into before block element\r
13622                         if (sc.nodeName == bn)\r
13623                                 rb.setStart(sc, 0);\r
13624                         else\r
13625                                 rb.setStartBefore(sc);\r
13626 \r
13627                         rb.setEnd(sn, so);\r
13628                         bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari\r
13629 \r
13630                         // Place secnd chop part within new block element\r
13631                         try {\r
13632                                 ra.setEndAfter(ec);\r
13633                         } catch(ex) {\r
13634                                 //console.debug(s.focusNode, s.focusOffset);\r
13635                         }\r
13636 \r
13637                         ra.setStart(en, eo);\r
13638                         aft.appendChild(ra.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari\r
13639 \r
13640                         // Create range around everything\r
13641                         r = d.createRange();\r
13642                         if (!sc.previousSibling && sc.parentNode.nodeName == bn) {\r
13643                                 r.setStartBefore(sc.parentNode);\r
13644                         } else {\r
13645                                 if (rb.startContainer.nodeName == bn && rb.startOffset == 0)\r
13646                                         r.setStartBefore(rb.startContainer);\r
13647                                 else\r
13648                                         r.setStart(rb.startContainer, rb.startOffset);\r
13649                         }\r
13650 \r
13651                         if (!ec.nextSibling && ec.parentNode.nodeName == bn)\r
13652                                 r.setEndAfter(ec.parentNode);\r
13653                         else\r
13654                                 r.setEnd(ra.endContainer, ra.endOffset);\r
13655 \r
13656                         // Delete and replace it with new block elements\r
13657                         r.deleteContents();\r
13658 \r
13659                         if (isOpera)\r
13660                                 ed.getWin().scrollTo(0, vp.y);\r
13661 \r
13662                         // Never wrap blocks in blocks\r
13663                         if (bef.firstChild && bef.firstChild.nodeName == bn)\r
13664                                 bef.innerHTML = bef.firstChild.innerHTML;\r
13665 \r
13666                         if (aft.firstChild && aft.firstChild.nodeName == bn)\r
13667                                 aft.innerHTML = aft.firstChild.innerHTML;\r
13668 \r
13669                         // Padd empty blocks\r
13670                         if (dom.isEmpty(bef))\r
13671                                 bef.innerHTML = '<br />';\r
13672 \r
13673                         function appendStyles(e, en) {\r
13674                                 var nl = [], nn, n, i;\r
13675 \r
13676                                 e.innerHTML = '';\r
13677 \r
13678                                 // Make clones of style elements\r
13679                                 if (se.keep_styles) {\r
13680                                         n = en;\r
13681                                         do {\r
13682                                                 // We only want style specific elements\r
13683                                                 if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) {\r
13684                                                         nn = n.cloneNode(FALSE);\r
13685                                                         dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique\r
13686                                                         nl.push(nn);\r
13687                                                 }\r
13688                                         } while (n = n.parentNode);\r
13689                                 }\r
13690 \r
13691                                 // Append style elements to aft\r
13692                                 if (nl.length > 0) {\r
13693                                         for (i = nl.length - 1, nn = e; i >= 0; i--)\r
13694                                                 nn = nn.appendChild(nl[i]);\r
13695 \r
13696                                         // Padd most inner style element\r
13697                                         nl[0].innerHTML = isOpera ? '\u00a0' : '<br />'; // Extra space for Opera so that the caret can move there\r
13698                                         return nl[0]; // Move caret to most inner element\r
13699                                 } else\r
13700                                         e.innerHTML = isOpera ? '\u00a0' : '<br />'; // Extra space for Opera so that the caret can move there\r
13701                         };\r
13702 \r
13703                         // Fill empty afterblook with current style\r
13704                         if (dom.isEmpty(aft))\r
13705                                 car = appendStyles(aft, en);\r
13706 \r
13707                         // Opera needs this one backwards for older versions\r
13708                         if (isOpera && parseFloat(opera.version()) < 9.5) {\r
13709                                 r.insertNode(bef);\r
13710                                 r.insertNode(aft);\r
13711                         } else {\r
13712                                 r.insertNode(aft);\r
13713                                 r.insertNode(bef);\r
13714                         }\r
13715 \r
13716                         // Normalize\r
13717                         aft.normalize();\r
13718                         bef.normalize();\r
13719 \r
13720                         function first(n) {\r
13721                                 return d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE).nextNode() || n;\r
13722                         };\r
13723 \r
13724                         // Move cursor and scroll into view\r
13725                         r = d.createRange();\r
13726                         r.selectNodeContents(isGecko ? first(car || aft) : car || aft);\r
13727                         r.collapse(1);\r
13728                         s.removeAllRanges();\r
13729                         s.addRange(r);\r
13730 \r
13731                         // scrollIntoView seems to scroll the parent window in most browsers now including FF 3.0b4 so it's time to stop using it and do it our selfs\r
13732                         y = ed.dom.getPos(aft).y;\r
13733                         //ch = aft.clientHeight;\r
13734 \r
13735                         // Is element within viewport\r
13736                         if (y < vp.y || y + 25 > vp.y + vp.h) {\r
13737                                 ed.getWin().scrollTo(0, y < vp.y ? y : y - vp.h + 25); // Needs to be hardcoded to roughly one line of text if a huge text block is broken into two blocks\r
13738 \r
13739                                 /*console.debug(\r
13740                                         'Element: y=' + y + ', h=' + ch + ', ' +\r
13741                                         'Viewport: y=' + vp.y + ", h=" + vp.h + ', bottom=' + (vp.y + vp.h)\r
13742                                 );*/\r
13743                         }\r
13744 \r
13745                         ed.undoManager.add();\r
13746 \r
13747                         return FALSE;\r
13748                 },\r
13749 \r
13750                 backspaceDelete : function(e, bs) {\r
13751                         var t = this, ed = t.editor, b = ed.getBody(), dom = ed.dom, n, se = ed.selection, r = se.getRng(), sc = r.startContainer, n, w, tn, walker;\r
13752 \r
13753                         // Delete when caret is behind a element doesn't work correctly on Gecko see #3011651\r
13754                         if (!bs && r.collapsed && sc.nodeType == 1 && r.startOffset == sc.childNodes.length) {\r
13755                                 walker = new tinymce.dom.TreeWalker(sc.lastChild, sc);\r
13756 \r
13757                                 // Walk the dom backwards until we find a text node\r
13758                                 for (n = sc.lastChild; n; n = walker.prev()) {\r
13759                                         if (n.nodeType == 3) {\r
13760                                                 r.setStart(n, n.nodeValue.length);\r
13761                                                 r.collapse(true);\r
13762                                                 se.setRng(r);\r
13763                                                 return;\r
13764                                         }\r
13765                                 }\r
13766                         }\r
13767 \r
13768                         // The caret sometimes gets stuck in Gecko if you delete empty paragraphs\r
13769                         // This workaround removes the element by hand and moves the caret to the previous element\r
13770                         if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) {\r
13771                                 if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) {\r
13772                                         // Find previous block element\r
13773                                         n = sc;\r
13774                                         while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ;\r
13775 \r
13776                                         if (n) {\r
13777                                                 if (sc != b.firstChild) {\r
13778                                                         // Find last text node\r
13779                                                         w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE);\r
13780                                                         while (tn = w.nextNode())\r
13781                                                                 n = tn;\r
13782 \r
13783                                                         // Place caret at the end of last text node\r
13784                                                         r = ed.getDoc().createRange();\r
13785                                                         r.setStart(n, n.nodeValue ? n.nodeValue.length : 0);\r
13786                                                         r.setEnd(n, n.nodeValue ? n.nodeValue.length : 0);\r
13787                                                         se.setRng(r);\r
13788 \r
13789                                                         // Remove the target container\r
13790                                                         ed.dom.remove(sc);\r
13791                                                 }\r
13792 \r
13793                                                 return Event.cancel(e);\r
13794                                         }\r
13795                                 }\r
13796                         }\r
13797                 }\r
13798         });\r
13799 })(tinymce);\r
13800 \r
13801 (function(tinymce) {\r
13802         // Shorten names\r
13803         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;\r
13804 \r
13805         tinymce.create('tinymce.ControlManager', {\r
13806                 ControlManager : function(ed, s) {\r
13807                         var t = this, i;\r
13808 \r
13809                         s = s || {};\r
13810                         t.editor = ed;\r
13811                         t.controls = {};\r
13812                         t.onAdd = new tinymce.util.Dispatcher(t);\r
13813                         t.onPostRender = new tinymce.util.Dispatcher(t);\r
13814                         t.prefix = s.prefix || ed.id + '_';\r
13815                         t._cls = {};\r
13816 \r
13817                         t.onPostRender.add(function() {\r
13818                                 each(t.controls, function(c) {\r
13819                                         c.postRender();\r
13820                                 });\r
13821                         });\r
13822                 },\r
13823 \r
13824                 get : function(id) {\r
13825                         return this.controls[this.prefix + id] || this.controls[id];\r
13826                 },\r
13827 \r
13828                 setActive : function(id, s) {\r
13829                         var c = null;\r
13830 \r
13831                         if (c = this.get(id))\r
13832                                 c.setActive(s);\r
13833 \r
13834                         return c;\r
13835                 },\r
13836 \r
13837                 setDisabled : function(id, s) {\r
13838                         var c = null;\r
13839 \r
13840                         if (c = this.get(id))\r
13841                                 c.setDisabled(s);\r
13842 \r
13843                         return c;\r
13844                 },\r
13845 \r
13846                 add : function(c) {\r
13847                         var t = this;\r
13848 \r
13849                         if (c) {\r
13850                                 t.controls[c.id] = c;\r
13851                                 t.onAdd.dispatch(c, t);\r
13852                         }\r
13853 \r
13854                         return c;\r
13855                 },\r
13856 \r
13857                 createControl : function(n) {\r
13858                         var c, t = this, ed = t.editor;\r
13859 \r
13860                         each(ed.plugins, function(p) {\r
13861                                 if (p.createControl) {\r
13862                                         c = p.createControl(n, t);\r
13863 \r
13864                                         if (c)\r
13865                                                 return false;\r
13866                                 }\r
13867                         });\r
13868 \r
13869                         switch (n) {\r
13870                                 case "|":\r
13871                                 case "separator":\r
13872                                         return t.createSeparator();\r
13873                         }\r
13874 \r
13875                         if (!c && ed.buttons && (c = ed.buttons[n]))\r
13876                                 return t.createButton(n, c);\r
13877 \r
13878                         return t.add(c);\r
13879                 },\r
13880 \r
13881                 createDropMenu : function(id, s, cc) {\r
13882                         var t = this, ed = t.editor, c, bm, v, cls;\r
13883 \r
13884                         s = extend({\r
13885                                 'class' : 'mceDropDown',\r
13886                                 constrain : ed.settings.constrain_menus\r
13887                         }, s);\r
13888 \r
13889                         s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin';\r
13890                         if (v = ed.getParam('skin_variant'))\r
13891                                 s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);\r
13892 \r
13893                         id = t.prefix + id;\r
13894                         cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;\r
13895                         c = t.controls[id] = new cls(id, s);\r
13896                         c.onAddItem.add(function(c, o) {\r
13897                                 var s = o.settings;\r
13898 \r
13899                                 s.title = ed.getLang(s.title, s.title);\r
13900 \r
13901                                 if (!s.onclick) {\r
13902                                         s.onclick = function(v) {\r
13903                                                 if (s.cmd)\r
13904                                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
13905                                         };\r
13906                                 }\r
13907                         });\r
13908 \r
13909                         ed.onRemove.add(function() {\r
13910                                 c.destroy();\r
13911                         });\r
13912 \r
13913                         // Fix for bug #1897785, #1898007\r
13914                         if (tinymce.isIE) {\r
13915                                 c.onShowMenu.add(function() {\r
13916                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
13917                                         ed.focus();\r
13918 \r
13919                                         bm = ed.selection.getBookmark(1);\r
13920                                 });\r
13921 \r
13922                                 c.onHideMenu.add(function() {\r
13923                                         if (bm) {\r
13924                                                 ed.selection.moveToBookmark(bm);\r
13925                                                 bm = 0;\r
13926                                         }\r
13927                                 });\r
13928                         }\r
13929 \r
13930                         return t.add(c);\r
13931                 },\r
13932 \r
13933                 createListBox : function(id, s, cc) {\r
13934                         var t = this, ed = t.editor, cmd, c, cls;\r
13935 \r
13936                         if (t.get(id))\r
13937                                 return null;\r
13938 \r
13939                         s.title = ed.translate(s.title);\r
13940                         s.scope = s.scope || ed;\r
13941 \r
13942                         if (!s.onselect) {\r
13943                                 s.onselect = function(v) {\r
13944                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
13945                                 };\r
13946                         }\r
13947 \r
13948                         s = extend({\r
13949                                 title : s.title,\r
13950                                 'class' : 'mce_' + id,\r
13951                                 scope : s.scope,\r
13952                                 control_manager : t\r
13953                         }, s);\r
13954 \r
13955                         id = t.prefix + id;\r
13956 \r
13957                         if (ed.settings.use_native_selects)\r
13958                                 c = new tinymce.ui.NativeListBox(id, s);\r
13959                         else {\r
13960                                 cls = cc || t._cls.listbox || tinymce.ui.ListBox;\r
13961                                 c = new cls(id, s, ed);\r
13962                         }\r
13963 \r
13964                         t.controls[id] = c;\r
13965 \r
13966                         // Fix focus problem in Safari\r
13967                         if (tinymce.isWebKit) {\r
13968                                 c.onPostRender.add(function(c, n) {\r
13969                                         // Store bookmark on mousedown\r
13970                                         Event.add(n, 'mousedown', function() {\r
13971                                                 ed.bookmark = ed.selection.getBookmark(1);\r
13972                                         });\r
13973 \r
13974                                         // Restore on focus, since it might be lost\r
13975                                         Event.add(n, 'focus', function() {\r
13976                                                 ed.selection.moveToBookmark(ed.bookmark);\r
13977                                                 ed.bookmark = null;\r
13978                                         });\r
13979                                 });\r
13980                         }\r
13981 \r
13982                         if (c.hideMenu)\r
13983                                 ed.onMouseDown.add(c.hideMenu, c);\r
13984 \r
13985                         return t.add(c);\r
13986                 },\r
13987 \r
13988                 createButton : function(id, s, cc) {\r
13989                         var t = this, ed = t.editor, o, c, cls;\r
13990 \r
13991                         if (t.get(id))\r
13992                                 return null;\r
13993 \r
13994                         s.title = ed.translate(s.title);\r
13995                         s.label = ed.translate(s.label);\r
13996                         s.scope = s.scope || ed;\r
13997 \r
13998                         if (!s.onclick && !s.menu_button) {\r
13999                                 s.onclick = function() {\r
14000                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
14001                                 };\r
14002                         }\r
14003 \r
14004                         s = extend({\r
14005                                 title : s.title,\r
14006                                 'class' : 'mce_' + id,\r
14007                                 unavailable_prefix : ed.getLang('unavailable', ''),\r
14008                                 scope : s.scope,\r
14009                                 control_manager : t\r
14010                         }, s);\r
14011 \r
14012                         id = t.prefix + id;\r
14013 \r
14014                         if (s.menu_button) {\r
14015                                 cls = cc || t._cls.menubutton || tinymce.ui.MenuButton;\r
14016                                 c = new cls(id, s, ed);\r
14017                                 ed.onMouseDown.add(c.hideMenu, c);\r
14018                         } else {\r
14019                                 cls = t._cls.button || tinymce.ui.Button;\r
14020                                 c = new cls(id, s);\r
14021                         }\r
14022 \r
14023                         return t.add(c);\r
14024                 },\r
14025 \r
14026                 createMenuButton : function(id, s, cc) {\r
14027                         s = s || {};\r
14028                         s.menu_button = 1;\r
14029 \r
14030                         return this.createButton(id, s, cc);\r
14031                 },\r
14032 \r
14033                 createSplitButton : function(id, s, cc) {\r
14034                         var t = this, ed = t.editor, cmd, c, cls;\r
14035 \r
14036                         if (t.get(id))\r
14037                                 return null;\r
14038 \r
14039                         s.title = ed.translate(s.title);\r
14040                         s.scope = s.scope || ed;\r
14041 \r
14042                         if (!s.onclick) {\r
14043                                 s.onclick = function(v) {\r
14044                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
14045                                 };\r
14046                         }\r
14047 \r
14048                         if (!s.onselect) {\r
14049                                 s.onselect = function(v) {\r
14050                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
14051                                 };\r
14052                         }\r
14053 \r
14054                         s = extend({\r
14055                                 title : s.title,\r
14056                                 'class' : 'mce_' + id,\r
14057                                 scope : s.scope,\r
14058                                 control_manager : t\r
14059                         }, s);\r
14060 \r
14061                         id = t.prefix + id;\r
14062                         cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton;\r
14063                         c = t.add(new cls(id, s, ed));\r
14064                         ed.onMouseDown.add(c.hideMenu, c);\r
14065 \r
14066                         return c;\r
14067                 },\r
14068 \r
14069                 createColorSplitButton : function(id, s, cc) {\r
14070                         var t = this, ed = t.editor, cmd, c, cls, bm;\r
14071 \r
14072                         if (t.get(id))\r
14073                                 return null;\r
14074 \r
14075                         s.title = ed.translate(s.title);\r
14076                         s.scope = s.scope || ed;\r
14077 \r
14078                         if (!s.onclick) {\r
14079                                 s.onclick = function(v) {\r
14080                                         if (tinymce.isIE)\r
14081                                                 bm = ed.selection.getBookmark(1);\r
14082 \r
14083                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
14084                                 };\r
14085                         }\r
14086 \r
14087                         if (!s.onselect) {\r
14088                                 s.onselect = function(v) {\r
14089                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
14090                                 };\r
14091                         }\r
14092 \r
14093                         s = extend({\r
14094                                 title : s.title,\r
14095                                 'class' : 'mce_' + id,\r
14096                                 'menu_class' : ed.getParam('skin') + 'Skin',\r
14097                                 scope : s.scope,\r
14098                                 more_colors_title : ed.getLang('more_colors')\r
14099                         }, s);\r
14100 \r
14101                         id = t.prefix + id;\r
14102                         cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton;\r
14103                         c = new cls(id, s, ed);\r
14104                         ed.onMouseDown.add(c.hideMenu, c);\r
14105 \r
14106                         // Remove the menu element when the editor is removed\r
14107                         ed.onRemove.add(function() {\r
14108                                 c.destroy();\r
14109                         });\r
14110 \r
14111                         // Fix for bug #1897785, #1898007\r
14112                         if (tinymce.isIE) {\r
14113                                 c.onShowMenu.add(function() {\r
14114                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
14115                                         ed.focus();\r
14116                                         bm = ed.selection.getBookmark(1);\r
14117                                 });\r
14118 \r
14119                                 c.onHideMenu.add(function() {\r
14120                                         if (bm) {\r
14121                                                 ed.selection.moveToBookmark(bm);\r
14122                                                 bm = 0;\r
14123                                         }\r
14124                                 });\r
14125                         }\r
14126 \r
14127                         return t.add(c);\r
14128                 },\r
14129 \r
14130                 createToolbar : function(id, s, cc) {\r
14131                         var c, t = this, cls;\r
14132 \r
14133                         id = t.prefix + id;\r
14134                         cls = cc || t._cls.toolbar || tinymce.ui.Toolbar;\r
14135                         c = new cls(id, s, t.editor);\r
14136 \r
14137                         if (t.get(id))\r
14138                                 return null;\r
14139 \r
14140                         return t.add(c);\r
14141                 },\r
14142                 \r
14143                 createToolbarGroup : function(id, s, cc) {\r
14144                         var c, t = this, cls;\r
14145                         id = t.prefix + id;\r
14146                         cls = cc || this._cls.toolbarGroup || tinymce.ui.ToolbarGroup;\r
14147                         c = new cls(id, s, t.editor);\r
14148                         \r
14149                         if (t.get(id))\r
14150                                 return null;\r
14151                         \r
14152                         return t.add(c);\r
14153                 },\r
14154 \r
14155                 createSeparator : function(cc) {\r
14156                         var cls = cc || this._cls.separator || tinymce.ui.Separator;\r
14157 \r
14158                         return new cls();\r
14159                 },\r
14160 \r
14161                 setControlType : function(n, c) {\r
14162                         return this._cls[n.toLowerCase()] = c;\r
14163                 },\r
14164         \r
14165                 destroy : function() {\r
14166                         each(this.controls, function(c) {\r
14167                                 c.destroy();\r
14168                         });\r
14169 \r
14170                         this.controls = null;\r
14171                 }\r
14172         });\r
14173 })(tinymce);\r
14174 \r
14175 (function(tinymce) {\r
14176         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;\r
14177 \r
14178         tinymce.create('tinymce.WindowManager', {\r
14179                 WindowManager : function(ed) {\r
14180                         var t = this;\r
14181 \r
14182                         t.editor = ed;\r
14183                         t.onOpen = new Dispatcher(t);\r
14184                         t.onClose = new Dispatcher(t);\r
14185                         t.params = {};\r
14186                         t.features = {};\r
14187                 },\r
14188 \r
14189                 open : function(s, p) {\r
14190                         var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u;\r
14191 \r
14192                         // Default some options\r
14193                         s = s || {};\r
14194                         p = p || {};\r
14195                         sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window\r
14196                         sh = isOpera ? vp.h : screen.height;\r
14197                         s.name = s.name || 'mc_' + new Date().getTime();\r
14198                         s.width = parseInt(s.width || 320);\r
14199                         s.height = parseInt(s.height || 240);\r
14200                         s.resizable = true;\r
14201                         s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0);\r
14202                         s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0);\r
14203                         p.inline = false;\r
14204                         p.mce_width = s.width;\r
14205                         p.mce_height = s.height;\r
14206                         p.mce_auto_focus = s.auto_focus;\r
14207 \r
14208                         if (mo) {\r
14209                                 if (isIE) {\r
14210                                         s.center = true;\r
14211                                         s.help = false;\r
14212                                         s.dialogWidth = s.width + 'px';\r
14213                                         s.dialogHeight = s.height + 'px';\r
14214                                         s.scroll = s.scrollbars || false;\r
14215                                 }\r
14216                         }\r
14217 \r
14218                         // Build features string\r
14219                         each(s, function(v, k) {\r
14220                                 if (tinymce.is(v, 'boolean'))\r
14221                                         v = v ? 'yes' : 'no';\r
14222 \r
14223                                 if (!/^(name|url)$/.test(k)) {\r
14224                                         if (isIE && mo)\r
14225                                                 f += (f ? ';' : '') + k + ':' + v;\r
14226                                         else\r
14227                                                 f += (f ? ',' : '') + k + '=' + v;\r
14228                                 }\r
14229                         });\r
14230 \r
14231                         t.features = s;\r
14232                         t.params = p;\r
14233                         t.onOpen.dispatch(t, s, p);\r
14234 \r
14235                         u = s.url || s.file;\r
14236                         u = tinymce._addVer(u);\r
14237 \r
14238                         try {\r
14239                                 if (isIE && mo) {\r
14240                                         w = 1;\r
14241                                         window.showModalDialog(u, window, f);\r
14242                                 } else\r
14243                                         w = window.open(u, s.name, f);\r
14244                         } catch (ex) {\r
14245                                 // Ignore\r
14246                         }\r
14247 \r
14248                         if (!w)\r
14249                                 alert(t.editor.getLang('popup_blocked'));\r
14250                 },\r
14251 \r
14252                 close : function(w) {\r
14253                         w.close();\r
14254                         this.onClose.dispatch(this);\r
14255                 },\r
14256 \r
14257                 createInstance : function(cl, a, b, c, d, e) {\r
14258                         var f = tinymce.resolve(cl);\r
14259 \r
14260                         return new f(a, b, c, d, e);\r
14261                 },\r
14262 \r
14263                 confirm : function(t, cb, s, w) {\r
14264                         w = w || window;\r
14265 \r
14266                         cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t))));\r
14267                 },\r
14268 \r
14269                 alert : function(tx, cb, s, w) {\r
14270                         var t = this;\r
14271 \r
14272                         w = w || window;\r
14273                         w.alert(t._decode(t.editor.getLang(tx, tx)));\r
14274 \r
14275                         if (cb)\r
14276                                 cb.call(s || t);\r
14277                 },\r
14278 \r
14279                 resizeBy : function(dw, dh, win) {\r
14280                         win.resizeBy(dw, dh);\r
14281                 },\r
14282 \r
14283                 // Internal functions\r
14284 \r
14285                 _decode : function(s) {\r
14286                         return tinymce.DOM.decode(s).replace(/\\n/g, '\n');\r
14287                 }\r
14288         });\r
14289 }(tinymce));\r
14290 (function(tinymce) {\r
14291         tinymce.Formatter = function(ed) {\r
14292                 var formats = {},\r
14293                         each = tinymce.each,\r
14294                         dom = ed.dom,\r
14295                         selection = ed.selection,\r
14296                         TreeWalker = tinymce.dom.TreeWalker,\r
14297                         rangeUtils = new tinymce.dom.RangeUtils(dom),\r
14298                         isValid = ed.schema.isValidChild,\r
14299                         isBlock = dom.isBlock,\r
14300                         forcedRootBlock = ed.settings.forced_root_block,\r
14301                         nodeIndex = dom.nodeIndex,\r
14302                         INVISIBLE_CHAR = '\uFEFF',\r
14303                         MCE_ATTR_RE = /^(src|href|style)$/,\r
14304                         FALSE = false,\r
14305                         TRUE = true,\r
14306                         undefined,\r
14307                         pendingFormats = {apply : [], remove : []};\r
14308 \r
14309                 function isArray(obj) {\r
14310                         return obj instanceof Array;\r
14311                 };\r
14312 \r
14313                 function getParents(node, selector) {\r
14314                         return dom.getParents(node, selector, dom.getRoot());\r
14315                 };\r
14316 \r
14317                 function isCaretNode(node) {\r
14318                         return node.nodeType === 1 && (node.face === 'mceinline' || node.style.fontFamily === 'mceinline');\r
14319                 };\r
14320 \r
14321                 // Public functions\r
14322 \r
14323                 function get(name) {\r
14324                         return name ? formats[name] : formats;\r
14325                 };\r
14326 \r
14327                 function register(name, format) {\r
14328                         if (name) {\r
14329                                 if (typeof(name) !== 'string') {\r
14330                                         each(name, function(format, name) {\r
14331                                                 register(name, format);\r
14332                                         });\r
14333                                 } else {\r
14334                                         // Force format into array and add it to internal collection\r
14335                                         format = format.length ? format : [format];\r
14336 \r
14337                                         each(format, function(format) {\r
14338                                                 // Set deep to false by default on selector formats this to avoid removing\r
14339                                                 // alignment on images inside paragraphs when alignment is changed on paragraphs\r
14340                                                 if (format.deep === undefined)\r
14341                                                         format.deep = !format.selector;\r
14342 \r
14343                                                 // Default to true\r
14344                                                 if (format.split === undefined)\r
14345                                                         format.split = !format.selector || format.inline;\r
14346 \r
14347                                                 // Default to true\r
14348                                                 if (format.remove === undefined && format.selector && !format.inline)\r
14349                                                         format.remove = 'none';\r
14350 \r
14351                                                 // Mark format as a mixed format inline + block level\r
14352                                                 if (format.selector && format.inline) {\r
14353                                                         format.mixed = true;\r
14354                                                         format.block_expand = true;\r
14355                                                 }\r
14356 \r
14357                                                 // Split classes if needed\r
14358                                                 if (typeof(format.classes) === 'string')\r
14359                                                         format.classes = format.classes.split(/\s+/);\r
14360                                         });\r
14361 \r
14362                                         formats[name] = format;\r
14363                                 }\r
14364                         }\r
14365                 };\r
14366 \r
14367                 var getTextDecoration = function(node) {\r
14368                         var decoration;\r
14369 \r
14370                         ed.dom.getParent(node, function(n) {\r
14371                                 decoration = ed.dom.getStyle(n, 'text-decoration');\r
14372                                 return decoration && decoration !== 'none';\r
14373                         });\r
14374 \r
14375                         return decoration;\r
14376                 };\r
14377 \r
14378                 var processUnderlineAndColor = function(node) {\r
14379                         var textDecoration;\r
14380                         if (node.nodeType === 1 && node.parentNode && node.parentNode.nodeType === 1) {\r
14381                                 textDecoration = getTextDecoration(node.parentNode);\r
14382                                 if (ed.dom.getStyle(node, 'color') && textDecoration) {\r
14383                                         ed.dom.setStyle(node, 'text-decoration', textDecoration);\r
14384                                 } else if (ed.dom.getStyle(node, 'textdecoration') === textDecoration) {\r
14385                                         ed.dom.setStyle(node, 'text-decoration', null);\r
14386                                 }\r
14387                         }\r
14388                 };\r
14389 \r
14390                 function apply(name, vars, node) {\r
14391                         var formatList = get(name), format = formatList[0], bookmark, rng, i, isCollapsed = selection.isCollapsed();\r
14392 \r
14393                         function moveStart(rng) {\r
14394                                 var container = rng.startContainer,\r
14395                                         offset = rng.startOffset,\r
14396                                         walker, node;\r
14397 \r
14398                                 // Move startContainer/startOffset in to a suitable node\r
14399                                 if (container.nodeType == 1 || container.nodeValue === "") {\r
14400                                         container = container.nodeType == 1 ? container.childNodes[offset] : container;\r
14401 \r
14402                                         // Might fail if the offset is behind the last element in it's container\r
14403                                         if (container) {\r
14404                                                 walker = new TreeWalker(container, container.parentNode);\r
14405                                                 for (node = walker.current(); node; node = walker.next()) {\r
14406                                                         if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {\r
14407                                                                 rng.setStart(node, 0);\r
14408                                                                 break;\r
14409                                                         }\r
14410                                                 }\r
14411                                         }\r
14412                                 }\r
14413 \r
14414                                 return rng;\r
14415                         };\r
14416 \r
14417                         function setElementFormat(elm, fmt) {\r
14418                                 fmt = fmt || format;\r
14419 \r
14420                                 if (elm) {\r
14421                                         each(fmt.styles, function(value, name) {\r
14422                                                 dom.setStyle(elm, name, replaceVars(value, vars));\r
14423                                         });\r
14424 \r
14425                                         each(fmt.attributes, function(value, name) {\r
14426                                                 dom.setAttrib(elm, name, replaceVars(value, vars));\r
14427                                         });\r
14428 \r
14429                                         each(fmt.classes, function(value) {\r
14430                                                 value = replaceVars(value, vars);\r
14431 \r
14432                                                 if (!dom.hasClass(elm, value))\r
14433                                                         dom.addClass(elm, value);\r
14434                                         });\r
14435                                 }\r
14436                         };\r
14437 \r
14438                         function applyRngStyle(rng) {\r
14439                                 var newWrappers = [], wrapName, wrapElm;\r
14440 \r
14441                                 // Setup wrapper element\r
14442                                 wrapName = format.inline || format.block;\r
14443                                 wrapElm = dom.create(wrapName);\r
14444                                 setElementFormat(wrapElm);\r
14445 \r
14446                                 rangeUtils.walk(rng, function(nodes) {\r
14447                                         var currentWrapElm;\r
14448 \r
14449                                         function process(node) {\r
14450                                                 var nodeName = node.nodeName.toLowerCase(), parentName = node.parentNode.nodeName.toLowerCase(), found;\r
14451 \r
14452                                                 // Stop wrapping on br elements\r
14453                                                 if (isEq(nodeName, 'br')) {\r
14454                                                         currentWrapElm = 0;\r
14455 \r
14456                                                         // Remove any br elements when we wrap things\r
14457                                                         if (format.block)\r
14458                                                                 dom.remove(node);\r
14459 \r
14460                                                         return;\r
14461                                                 }\r
14462 \r
14463                                                 // If node is wrapper type\r
14464                                                 if (format.wrapper && matchNode(node, name, vars)) {\r
14465                                                         currentWrapElm = 0;\r
14466                                                         return;\r
14467                                                 }\r
14468 \r
14469                                                 // Can we rename the block\r
14470                                                 if (format.block && !format.wrapper && isTextBlock(nodeName)) {\r
14471                                                         node = dom.rename(node, wrapName);\r
14472                                                         setElementFormat(node);\r
14473                                                         newWrappers.push(node);\r
14474                                                         currentWrapElm = 0;\r
14475                                                         return;\r
14476                                                 }\r
14477 \r
14478                                                 // Handle selector patterns\r
14479                                                 if (format.selector) {\r
14480                                                         // Look for matching formats\r
14481                                                         each(formatList, function(format) {\r
14482                                                                 // Check collapsed state if it exists\r
14483                                                                 if ('collapsed' in format && format.collapsed !== isCollapsed) {\r
14484                                                                         return;\r
14485                                                                 }\r
14486 \r
14487                                                                 if (dom.is(node, format.selector) && !isCaretNode(node)) {\r
14488                                                                         setElementFormat(node, format);\r
14489                                                                         found = true;\r
14490                                                                 }\r
14491                                                         });\r
14492 \r
14493                                                         // Continue processing if a selector match wasn't found and a inline element is defined\r
14494                                                         if (!format.inline || found) {\r
14495                                                                 currentWrapElm = 0;\r
14496                                                                 return;\r
14497                                                         }\r
14498                                                 }\r
14499 \r
14500                                                 // Is it valid to wrap this item\r
14501                                                 if (isValid(wrapName, nodeName) && isValid(parentName, wrapName) &&\r
14502                                                                 !(node.nodeType === 3 && node.nodeValue.length === 1 && node.nodeValue.charCodeAt(0) === 65279)) {\r
14503                                                         // Start wrapping\r
14504                                                         if (!currentWrapElm) {\r
14505                                                                 // Wrap the node\r
14506                                                                 currentWrapElm = wrapElm.cloneNode(FALSE);\r
14507                                                                 node.parentNode.insertBefore(currentWrapElm, node);\r
14508                                                                 newWrappers.push(currentWrapElm);\r
14509                                                         }\r
14510 \r
14511                                                         currentWrapElm.appendChild(node);\r
14512                                                 } else {\r
14513                                                         // Start a new wrapper for possible children\r
14514                                                         currentWrapElm = 0;\r
14515 \r
14516                                                         each(tinymce.grep(node.childNodes), process);\r
14517 \r
14518                                                         // End the last wrapper\r
14519                                                         currentWrapElm = 0;\r
14520                                                 }\r
14521                                         };\r
14522 \r
14523                                         // Process siblings from range\r
14524                                         each(nodes, process);\r
14525                                 });\r
14526 \r
14527                                 // Wrap links inside as well, for example color inside a link when the wrapper is around the link\r
14528                                 if (format.wrap_links === false) {\r
14529                                         each(newWrappers, function(node) {\r
14530                                                 function process(node) {\r
14531                                                         var i, currentWrapElm, children;\r
14532 \r
14533                                                         if (node.nodeName === 'A') {\r
14534                                                                 currentWrapElm = wrapElm.cloneNode(FALSE);\r
14535                                                                 newWrappers.push(currentWrapElm);\r
14536 \r
14537                                                                 children = tinymce.grep(node.childNodes);\r
14538                                                                 for (i = 0; i < children.length; i++)\r
14539                                                                         currentWrapElm.appendChild(children[i]);\r
14540 \r
14541                                                                 node.appendChild(currentWrapElm);\r
14542                                                         }\r
14543 \r
14544                                                         each(tinymce.grep(node.childNodes), process);\r
14545                                                 };\r
14546 \r
14547                                                 process(node);\r
14548                                         });\r
14549                                 }\r
14550 \r
14551                                 // Cleanup\r
14552                                 each(newWrappers, function(node) {\r
14553                                         var childCount;\r
14554 \r
14555                                         function getChildCount(node) {\r
14556                                                 var count = 0;\r
14557 \r
14558                                                 each(node.childNodes, function(node) {\r
14559                                                         if (!isWhiteSpaceNode(node) && !isBookmarkNode(node))\r
14560                                                                 count++;\r
14561                                                 });\r
14562 \r
14563                                                 return count;\r
14564                                         };\r
14565 \r
14566                                         function mergeStyles(node) {\r
14567                                                 var child, clone;\r
14568 \r
14569                                                 each(node.childNodes, function(node) {\r
14570                                                         if (node.nodeType == 1 && !isBookmarkNode(node) && !isCaretNode(node)) {\r
14571                                                                 child = node;\r
14572                                                                 return FALSE; // break loop\r
14573                                                         }\r
14574                                                 });\r
14575 \r
14576                                                 // If child was found and of the same type as the current node\r
14577                                                 if (child && matchName(child, format)) {\r
14578                                                         clone = child.cloneNode(FALSE);\r
14579                                                         setElementFormat(clone);\r
14580 \r
14581                                                         dom.replace(clone, node, TRUE);\r
14582                                                         dom.remove(child, 1);\r
14583                                                 }\r
14584 \r
14585                                                 return clone || node;\r
14586                                         };\r
14587 \r
14588                                         childCount = getChildCount(node);\r
14589 \r
14590                                         // Remove empty nodes but only if there is multiple wrappers and they are not block\r
14591                                         // elements so never remove single <h1></h1> since that would remove the currrent empty block element where the caret is at\r
14592                                         if ((newWrappers.length > 1 || !isBlock(node)) && childCount === 0) {\r
14593                                                 dom.remove(node, 1);\r
14594                                                 return;\r
14595                                         }\r
14596 \r
14597                                         if (format.inline || format.wrapper) {\r
14598                                                 // Merges the current node with it's children of similar type to reduce the number of elements\r
14599                                                 if (!format.exact && childCount === 1)\r
14600                                                         node = mergeStyles(node);\r
14601 \r
14602                                                 // Remove/merge children\r
14603                                                 each(formatList, function(format) {\r
14604                                                         // Merge all children of similar type will move styles from child to parent\r
14605                                                         // this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span>\r
14606                                                         // will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span>\r
14607                                                         each(dom.select(format.inline, node), function(child) {\r
14608                                                                 var parent;\r
14609 \r
14610                                                                 // When wrap_links is set to false we don't want\r
14611                                                                 // to remove the format on children within links\r
14612                                                                 if (format.wrap_links === false) {\r
14613                                                                         parent = child.parentNode;\r
14614 \r
14615                                                                         do {\r
14616                                                                                 if (parent.nodeName === 'A')\r
14617                                                                                         return;\r
14618                                                                         } while (parent = parent.parentNode);\r
14619                                                                 }\r
14620 \r
14621                                                                 removeFormat(format, vars, child, format.exact ? child : null);\r
14622                                                         });\r
14623                                                 });\r
14624 \r
14625                                                 // Remove child if direct parent is of same type\r
14626                                                 if (matchNode(node.parentNode, name, vars)) {\r
14627                                                         dom.remove(node, 1);\r
14628                                                         node = 0;\r
14629                                                         return TRUE;\r
14630                                                 }\r
14631 \r
14632                                                 // Look for parent with similar style format\r
14633                                                 if (format.merge_with_parents) {\r
14634                                                         dom.getParent(node.parentNode, function(parent) {\r
14635                                                                 if (matchNode(parent, name, vars)) {\r
14636                                                                         dom.remove(node, 1);\r
14637                                                                         node = 0;\r
14638                                                                         return TRUE;\r
14639                                                                 }\r
14640                                                         });\r
14641                                                 }\r
14642 \r
14643                                                 // Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>\r
14644                                                 if (node) {\r
14645                                                         node = mergeSiblings(getNonWhiteSpaceSibling(node), node);\r
14646                                                         node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE));\r
14647                                                 }\r
14648                                         }\r
14649                                 });\r
14650                         };\r
14651 \r
14652                         if (format) {\r
14653                                 if (node) {\r
14654                                         rng = dom.createRng();\r
14655 \r
14656                                         rng.setStartBefore(node);\r
14657                                         rng.setEndAfter(node);\r
14658 \r
14659                                         applyRngStyle(expandRng(rng, formatList));\r
14660                                 } else {\r
14661                                         if (!isCollapsed || !format.inline || dom.select('td.mceSelected,th.mceSelected').length) {\r
14662                                                 // Obtain selection node before selection is unselected by applyRngStyle()\r
14663                                                 var curSelNode = ed.selection.getNode();\r
14664 \r
14665                                                 // Apply formatting to selection\r
14666                                                 bookmark = selection.getBookmark();\r
14667                                                 applyRngStyle(expandRng(selection.getRng(TRUE), formatList));\r
14668 \r
14669                                                 // Colored nodes should be underlined so that the color of the underline matches the text color.\r
14670                                                 if (format.styles && (format.styles.color || format.styles.textDecoration)) {\r
14671                                                         tinymce.walk(curSelNode, processUnderlineAndColor, 'childNodes');\r
14672                                                         processUnderlineAndColor(curSelNode);\r
14673                                                 }\r
14674 \r
14675                                                 selection.moveToBookmark(bookmark);\r
14676                                                 selection.setRng(moveStart(selection.getRng(TRUE)));\r
14677                                                 ed.nodeChanged();\r
14678                                         } else\r
14679                                                 performCaretAction('apply', name, vars);\r
14680                                 }\r
14681                         }\r
14682                 };\r
14683 \r
14684                 function remove(name, vars, node) {\r
14685                         var formatList = get(name), format = formatList[0], bookmark, i, rng;\r
14686 \r
14687                         function moveStart(rng) {\r
14688                                 var container = rng.startContainer,\r
14689                                         offset = rng.startOffset,\r
14690                                         walker, node, nodes, tmpNode;\r
14691 \r
14692                                 // Convert text node into index if possible\r
14693                                 if (container.nodeType == 3 && offset >= container.nodeValue.length - 1) {\r
14694                                         container = container.parentNode;\r
14695                                         offset = nodeIndex(container) + 1;\r
14696                                 }\r
14697 \r
14698                                 // Move startContainer/startOffset in to a suitable node\r
14699                                 if (container.nodeType == 1) {\r
14700                                         nodes = container.childNodes;\r
14701                                         container = nodes[Math.min(offset, nodes.length - 1)];\r
14702                                         walker = new TreeWalker(container);\r
14703 \r
14704                                         // If offset is at end of the parent node walk to the next one\r
14705                                         if (offset > nodes.length - 1)\r
14706                                                 walker.next();\r
14707 \r
14708                                         for (node = walker.current(); node; node = walker.next()) {\r
14709                                                 if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {\r
14710                                                         // IE has a "neat" feature where it moves the start node into the closest element\r
14711                                                         // we can avoid this by inserting an element before it and then remove it after we set the selection\r
14712                                                         tmpNode = dom.create('a', null, INVISIBLE_CHAR);\r
14713                                                         node.parentNode.insertBefore(tmpNode, node);\r
14714 \r
14715                                                         // Set selection and remove tmpNode\r
14716                                                         rng.setStart(node, 0);\r
14717                                                         selection.setRng(rng);\r
14718                                                         dom.remove(tmpNode);\r
14719 \r
14720                                                         return;\r
14721                                                 }\r
14722                                         }\r
14723                                 }\r
14724                         };\r
14725 \r
14726                         // Merges the styles for each node\r
14727                         function process(node) {\r
14728                                 var children, i, l;\r
14729 \r
14730                                 // Grab the children first since the nodelist might be changed\r
14731                                 children = tinymce.grep(node.childNodes);\r
14732 \r
14733                                 // Process current node\r
14734                                 for (i = 0, l = formatList.length; i < l; i++) {\r
14735                                         if (removeFormat(formatList[i], vars, node, node))\r
14736                                                 break;\r
14737                                 }\r
14738 \r
14739                                 // Process the children\r
14740                                 if (format.deep) {\r
14741                                         for (i = 0, l = children.length; i < l; i++)\r
14742                                                 process(children[i]);\r
14743                                 }\r
14744                         };\r
14745 \r
14746                         function findFormatRoot(container) {\r
14747                                 var formatRoot;\r
14748 \r
14749                                 // Find format root\r
14750                                 each(getParents(container.parentNode).reverse(), function(parent) {\r
14751                                         var format;\r
14752 \r
14753                                         // Find format root element\r
14754                                         if (!formatRoot && parent.id != '_start' && parent.id != '_end') {\r
14755                                                 // Is the node matching the format we are looking for\r
14756                                                 format = matchNode(parent, name, vars);\r
14757                                                 if (format && format.split !== false)\r
14758                                                         formatRoot = parent;\r
14759                                         }\r
14760                                 });\r
14761 \r
14762                                 return formatRoot;\r
14763                         };\r
14764 \r
14765                         function wrapAndSplit(format_root, container, target, split) {\r
14766                                 var parent, clone, lastClone, firstClone, i, formatRootParent;\r
14767 \r
14768                                 // Format root found then clone formats and split it\r
14769                                 if (format_root) {\r
14770                                         formatRootParent = format_root.parentNode;\r
14771 \r
14772                                         for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) {\r
14773                                                 clone = parent.cloneNode(FALSE);\r
14774 \r
14775                                                 for (i = 0; i < formatList.length; i++) {\r
14776                                                         if (removeFormat(formatList[i], vars, clone, clone)) {\r
14777                                                                 clone = 0;\r
14778                                                                 break;\r
14779                                                         }\r
14780                                                 }\r
14781 \r
14782                                                 // Build wrapper node\r
14783                                                 if (clone) {\r
14784                                                         if (lastClone)\r
14785                                                                 clone.appendChild(lastClone);\r
14786 \r
14787                                                         if (!firstClone)\r
14788                                                                 firstClone = clone;\r
14789 \r
14790                                                         lastClone = clone;\r
14791                                                 }\r
14792                                         }\r
14793 \r
14794                                         // Never split block elements if the format is mixed\r
14795                                         if (split && (!format.mixed || !isBlock(format_root)))\r
14796                                                 container = dom.split(format_root, container);\r
14797 \r
14798                                         // Wrap container in cloned formats\r
14799                                         if (lastClone) {\r
14800                                                 target.parentNode.insertBefore(lastClone, target);\r
14801                                                 firstClone.appendChild(target);\r
14802                                         }\r
14803                                 }\r
14804 \r
14805                                 return container;\r
14806                         };\r
14807 \r
14808                         function splitToFormatRoot(container) {\r
14809                                 return wrapAndSplit(findFormatRoot(container), container, container, true);\r
14810                         };\r
14811 \r
14812                         function unwrap(start) {\r
14813                                 var node = dom.get(start ? '_start' : '_end'),\r
14814                                         out = node[start ? 'firstChild' : 'lastChild'];\r
14815 \r
14816                                 // If the end is placed within the start the result will be removed\r
14817                                 // So this checks if the out node is a bookmark node if it is it\r
14818                                 // checks for another more suitable node\r
14819                                 if (isBookmarkNode(out))\r
14820                                         out = out[start ? 'firstChild' : 'lastChild'];\r
14821 \r
14822                                 dom.remove(node, true);\r
14823 \r
14824                                 return out;\r
14825                         };\r
14826 \r
14827                         function removeRngStyle(rng) {\r
14828                                 var startContainer, endContainer;\r
14829 \r
14830                                 rng = expandRng(rng, formatList, TRUE);\r
14831 \r
14832                                 if (format.split) {\r
14833                                         startContainer = getContainer(rng, TRUE);\r
14834                                         endContainer = getContainer(rng);\r
14835 \r
14836                                         if (startContainer != endContainer) {\r
14837                                                 // Wrap start/end nodes in span element since these might be cloned/moved\r
14838                                                 startContainer = wrap(startContainer, 'span', {id : '_start', 'data-mce-type' : 'bookmark'});\r
14839                                                 endContainer = wrap(endContainer, 'span', {id : '_end', 'data-mce-type' : 'bookmark'});\r
14840 \r
14841                                                 // Split start/end\r
14842                                                 splitToFormatRoot(startContainer);\r
14843                                                 splitToFormatRoot(endContainer);\r
14844 \r
14845                                                 // Unwrap start/end to get real elements again\r
14846                                                 startContainer = unwrap(TRUE);\r
14847                                                 endContainer = unwrap();\r
14848                                         } else\r
14849                                                 startContainer = endContainer = splitToFormatRoot(startContainer);\r
14850 \r
14851                                         // Update range positions since they might have changed after the split operations\r
14852                                         rng.startContainer = startContainer.parentNode;\r
14853                                         rng.startOffset = nodeIndex(startContainer);\r
14854                                         rng.endContainer = endContainer.parentNode;\r
14855                                         rng.endOffset = nodeIndex(endContainer) + 1;\r
14856                                 }\r
14857 \r
14858                                 // Remove items between start/end\r
14859                                 rangeUtils.walk(rng, function(nodes) {\r
14860                                         each(nodes, function(node) {\r
14861                                                 process(node);\r
14862 \r
14863                                                 // Remove parent span if it only contains text-decoration: underline, yet a parent node is also underlined.\r
14864                                                 if (node.nodeType === 1 && ed.dom.getStyle(node, 'text-decoration') === 'underline' && node.parentNode && getTextDecoration(node.parentNode) === 'underline') {\r
14865                                                         removeFormat({'deep': false, 'exact': true, 'inline': 'span', 'styles': {'textDecoration' : 'underline'}}, null, node);\r
14866                                                 }\r
14867                                         });\r
14868                                 });\r
14869                         };\r
14870 \r
14871                         // Handle node\r
14872                         if (node) {\r
14873                                 rng = dom.createRng();\r
14874                                 rng.setStartBefore(node);\r
14875                                 rng.setEndAfter(node);\r
14876                                 removeRngStyle(rng);\r
14877                                 return;\r
14878                         }\r
14879 \r
14880                         if (!selection.isCollapsed() || !format.inline || dom.select('td.mceSelected,th.mceSelected').length) {\r
14881                                 bookmark = selection.getBookmark();\r
14882                                 removeRngStyle(selection.getRng(TRUE));\r
14883                                 selection.moveToBookmark(bookmark);\r
14884 \r
14885                                 // Check if start element still has formatting then we are at: "<b>text|</b>text" and need to move the start into the next text node\r
14886                                 if (match(name, vars, selection.getStart())) {\r
14887                                         moveStart(selection.getRng(true));\r
14888                                 }\r
14889 \r
14890                                 ed.nodeChanged();\r
14891                         } else\r
14892                                 performCaretAction('remove', name, vars);\r
14893                 };\r
14894 \r
14895                 function toggle(name, vars, node) {\r
14896                         var fmt = get(name);\r
14897 \r
14898                         if (match(name, vars, node) && (!('toggle' in fmt[0]) || fmt[0]['toggle']))\r
14899                                 remove(name, vars, node);\r
14900                         else\r
14901                                 apply(name, vars, node);\r
14902                 };\r
14903 \r
14904                 function matchNode(node, name, vars, similar) {\r
14905                         var formatList = get(name), format, i, classes;\r
14906 \r
14907                         function matchItems(node, format, item_name) {\r
14908                                 var key, value, items = format[item_name], i;\r
14909 \r
14910                                 // Check all items\r
14911                                 if (items) {\r
14912                                         // Non indexed object\r
14913                                         if (items.length === undefined) {\r
14914                                                 for (key in items) {\r
14915                                                         if (items.hasOwnProperty(key)) {\r
14916                                                                 if (item_name === 'attributes')\r
14917                                                                         value = dom.getAttrib(node, key);\r
14918                                                                 else\r
14919                                                                         value = getStyle(node, key);\r
14920 \r
14921                                                                 if (similar && !value && !format.exact)\r
14922                                                                         return;\r
14923 \r
14924                                                                 if ((!similar || format.exact) && !isEq(value, replaceVars(items[key], vars)))\r
14925                                                                         return;\r
14926                                                         }\r
14927                                                 }\r
14928                                         } else {\r
14929                                                 // Only one match needed for indexed arrays\r
14930                                                 for (i = 0; i < items.length; i++) {\r
14931                                                         if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i]))\r
14932                                                                 return format;\r
14933                                                 }\r
14934                                         }\r
14935                                 }\r
14936 \r
14937                                 return format;\r
14938                         };\r
14939 \r
14940                         if (formatList && node) {\r
14941                                 // Check each format in list\r
14942                                 for (i = 0; i < formatList.length; i++) {\r
14943                                         format = formatList[i];\r
14944 \r
14945                                         // Name name, attributes, styles and classes\r
14946                                         if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) {\r
14947                                                 // Match classes\r
14948                                                 if (classes = format.classes) {\r
14949                                                         for (i = 0; i < classes.length; i++) {\r
14950                                                                 if (!dom.hasClass(node, classes[i]))\r
14951                                                                         return;\r
14952                                                         }\r
14953                                                 }\r
14954 \r
14955                                                 return format;\r
14956                                         }\r
14957                                 }\r
14958                         }\r
14959                 };\r
14960 \r
14961                 function match(name, vars, node) {\r
14962                         var startNode, i;\r
14963 \r
14964                         function matchParents(node) {\r
14965                                 // Find first node with similar format settings\r
14966                                 node = dom.getParent(node, function(node) {\r
14967                                         return !!matchNode(node, name, vars, true);\r
14968                                 });\r
14969 \r
14970                                 // Do an exact check on the similar format element\r
14971                                 return matchNode(node, name, vars);\r
14972                         };\r
14973 \r
14974                         // Check specified node\r
14975                         if (node)\r
14976                                 return matchParents(node);\r
14977 \r
14978                         // Check pending formats\r
14979                         if (selection.isCollapsed()) {\r
14980                                 for (i = pendingFormats.apply.length - 1; i >= 0; i--) {\r
14981                                         if (pendingFormats.apply[i].name == name)\r
14982                                                 return true;\r
14983                                 }\r
14984 \r
14985                                 for (i = pendingFormats.remove.length - 1; i >= 0; i--) {\r
14986                                         if (pendingFormats.remove[i].name == name)\r
14987                                                 return false;\r
14988                                 }\r
14989 \r
14990                                 return matchParents(selection.getNode());\r
14991                         }\r
14992 \r
14993                         // Check selected node\r
14994                         node = selection.getNode();\r
14995                         if (matchParents(node))\r
14996                                 return TRUE;\r
14997 \r
14998                         // Check start node if it's different\r
14999                         startNode = selection.getStart();\r
15000                         if (startNode != node) {\r
15001                                 if (matchParents(startNode))\r
15002                                         return TRUE;\r
15003                         }\r
15004 \r
15005                         return FALSE;\r
15006                 };\r
15007 \r
15008                 function matchAll(names, vars) {\r
15009                         var startElement, matchedFormatNames = [], checkedMap = {}, i, ni, name;\r
15010 \r
15011                         // If the selection is collapsed then check pending formats\r
15012                         if (selection.isCollapsed()) {\r
15013                                 for (ni = 0; ni < names.length; ni++) {\r
15014                                         // If the name is to be removed, then stop it from being added\r
15015                                         for (i = pendingFormats.remove.length - 1; i >= 0; i--) {\r
15016                                                 name = names[ni];\r
15017 \r
15018                                                 if (pendingFormats.remove[i].name == name) {\r
15019                                                         checkedMap[name] = true;\r
15020                                                         break;\r
15021                                                 }\r
15022                                         }\r
15023                                 }\r
15024 \r
15025                                 // If the format is to be applied\r
15026                                 for (i = pendingFormats.apply.length - 1; i >= 0; i--) {\r
15027                                         for (ni = 0; ni < names.length; ni++) {\r
15028                                                 name = names[ni];\r
15029 \r
15030                                                 if (!checkedMap[name] && pendingFormats.apply[i].name == name) {\r
15031                                                         checkedMap[name] = true;\r
15032                                                         matchedFormatNames.push(name);\r
15033                                                 }\r
15034                                         }\r
15035                                 }\r
15036                         }\r
15037 \r
15038                         // Check start of selection for formats\r
15039                         startElement = selection.getStart();\r
15040                         dom.getParent(startElement, function(node) {\r
15041                                 var i, name;\r
15042 \r
15043                                 for (i = 0; i < names.length; i++) {\r
15044                                         name = names[i];\r
15045 \r
15046                                         if (!checkedMap[name] && matchNode(node, name, vars)) {\r
15047                                                 checkedMap[name] = true;\r
15048                                                 matchedFormatNames.push(name);\r
15049                                         }\r
15050                                 }\r
15051                         });\r
15052 \r
15053                         return matchedFormatNames;\r
15054                 };\r
15055 \r
15056                 function canApply(name) {\r
15057                         var formatList = get(name), startNode, parents, i, x, selector;\r
15058 \r
15059                         if (formatList) {\r
15060                                 startNode = selection.getStart();\r
15061                                 parents = getParents(startNode);\r
15062 \r
15063                                 for (x = formatList.length - 1; x >= 0; x--) {\r
15064                                         selector = formatList[x].selector;\r
15065 \r
15066                                         // Format is not selector based, then always return TRUE\r
15067                                         if (!selector)\r
15068                                                 return TRUE;\r
15069 \r
15070                                         for (i = parents.length - 1; i >= 0; i--) {\r
15071                                                 if (dom.is(parents[i], selector))\r
15072                                                         return TRUE;\r
15073                                         }\r
15074                                 }\r
15075                         }\r
15076 \r
15077                         return FALSE;\r
15078                 };\r
15079 \r
15080                 // Expose to public\r
15081                 tinymce.extend(this, {\r
15082                         get : get,\r
15083                         register : register,\r
15084                         apply : apply,\r
15085                         remove : remove,\r
15086                         toggle : toggle,\r
15087                         match : match,\r
15088                         matchAll : matchAll,\r
15089                         matchNode : matchNode,\r
15090                         canApply : canApply\r
15091                 });\r
15092 \r
15093                 // Private functions\r
15094 \r
15095                 function matchName(node, format) {\r
15096                         // Check for inline match\r
15097                         if (isEq(node, format.inline))\r
15098                                 return TRUE;\r
15099 \r
15100                         // Check for block match\r
15101                         if (isEq(node, format.block))\r
15102                                 return TRUE;\r
15103 \r
15104                         // Check for selector match\r
15105                         if (format.selector)\r
15106                                 return dom.is(node, format.selector);\r
15107                 };\r
15108 \r
15109                 function isEq(str1, str2) {\r
15110                         str1 = str1 || '';\r
15111                         str2 = str2 || '';\r
15112 \r
15113                         str1 = '' + (str1.nodeName || str1);\r
15114                         str2 = '' + (str2.nodeName || str2);\r
15115 \r
15116                         return str1.toLowerCase() == str2.toLowerCase();\r
15117                 };\r
15118 \r
15119                 function getStyle(node, name) {\r
15120                         var styleVal = dom.getStyle(node, name);\r
15121 \r
15122                         // Force the format to hex\r
15123                         if (name == 'color' || name == 'backgroundColor')\r
15124                                 styleVal = dom.toHex(styleVal);\r
15125 \r
15126                         // Opera will return bold as 700\r
15127                         if (name == 'fontWeight' && styleVal == 700)\r
15128                                 styleVal = 'bold';\r
15129 \r
15130                         return '' + styleVal;\r
15131                 };\r
15132 \r
15133                 function replaceVars(value, vars) {\r
15134                         if (typeof(value) != "string")\r
15135                                 value = value(vars);\r
15136                         else if (vars) {\r
15137                                 value = value.replace(/%(\w+)/g, function(str, name) {\r
15138                                         return vars[name] || str;\r
15139                                 });\r
15140                         }\r
15141 \r
15142                         return value;\r
15143                 };\r
15144 \r
15145                 function isWhiteSpaceNode(node) {\r
15146                         return node && node.nodeType === 3 && /^([\s\r\n]+|)$/.test(node.nodeValue);\r
15147                 };\r
15148 \r
15149                 function wrap(node, name, attrs) {\r
15150                         var wrapper = dom.create(name, attrs);\r
15151 \r
15152                         node.parentNode.insertBefore(wrapper, node);\r
15153                         wrapper.appendChild(node);\r
15154 \r
15155                         return wrapper;\r
15156                 };\r
15157 \r
15158                 function expandRng(rng, format, remove) {\r
15159                         var startContainer = rng.startContainer,\r
15160                                 startOffset = rng.startOffset,\r
15161                                 endContainer = rng.endContainer,\r
15162                                 endOffset = rng.endOffset, sibling, lastIdx, leaf;\r
15163 \r
15164                         // This function walks up the tree if there is no siblings before/after the node\r
15165                         function findParentContainer(container, child_name, sibling_name, root) {\r
15166                                 var parent, child;\r
15167 \r
15168                                 root = root || dom.getRoot();\r
15169 \r
15170                                 for (;;) {\r
15171                                         // Check if we can move up are we at root level or body level\r
15172                                         parent = container.parentNode;\r
15173 \r
15174                                         // Stop expanding on block elements or root depending on format\r
15175                                         if (parent == root || (!format[0].block_expand && isBlock(parent)))\r
15176                                                 return container;\r
15177 \r
15178                                         for (sibling = parent[child_name]; sibling && sibling != container; sibling = sibling[sibling_name]) {\r
15179                                                 if (sibling.nodeType == 1 && !isBookmarkNode(sibling))\r
15180                                                         return container;\r
15181 \r
15182                                                 if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling))\r
15183                                                         return container;\r
15184                                         }\r
15185 \r
15186                                         container = container.parentNode;\r
15187                                 }\r
15188 \r
15189                                 return container;\r
15190                         };\r
15191 \r
15192                         // This function walks down the tree to find the leaf at the selection.\r
15193                         // The offset is also returned as if node initially a leaf, the offset may be in the middle of the text node.\r
15194                         function findLeaf(node, offset) {\r
15195                                 if (offset === undefined)\r
15196                                         offset = node.nodeType === 3 ? node.length : node.childNodes.length;\r
15197                                 while (node && node.hasChildNodes()) {\r
15198                                         node = node.childNodes[offset];\r
15199                                         if (node)\r
15200                                                 offset = node.nodeType === 3 ? node.length : node.childNodes.length;\r
15201                                 }\r
15202                                 return { node: node, offset: offset };\r
15203                         }\r
15204 \r
15205                         // If index based start position then resolve it\r
15206                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {\r
15207                                 lastIdx = startContainer.childNodes.length - 1;\r
15208                                 startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset];\r
15209 \r
15210                                 if (startContainer.nodeType == 3)\r
15211                                         startOffset = 0;\r
15212                         }\r
15213 \r
15214                         // If index based end position then resolve it\r
15215                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {\r
15216                                 lastIdx = endContainer.childNodes.length - 1;\r
15217                                 endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1];\r
15218 \r
15219                                 if (endContainer.nodeType == 3)\r
15220                                         endOffset = endContainer.nodeValue.length;\r
15221                         }\r
15222 \r
15223                         // Exclude bookmark nodes if possible\r
15224                         if (isBookmarkNode(startContainer.parentNode))\r
15225                                 startContainer = startContainer.parentNode;\r
15226 \r
15227                         if (isBookmarkNode(startContainer))\r
15228                                 startContainer = startContainer.nextSibling || startContainer;\r
15229 \r
15230                         if (isBookmarkNode(endContainer.parentNode)) {\r
15231                                 endOffset = dom.nodeIndex(endContainer);\r
15232                                 endContainer = endContainer.parentNode;\r
15233                         }\r
15234 \r
15235                         if (isBookmarkNode(endContainer) && endContainer.previousSibling) {\r
15236                                 endContainer = endContainer.previousSibling;\r
15237                                 endOffset = endContainer.length;\r
15238                         }\r
15239 \r
15240                         if (format[0].inline) {\r
15241                                 // Avoid applying formatting to a trailing space.\r
15242                                 leaf = findLeaf(endContainer, endOffset);\r
15243                                 if (leaf.node) {\r
15244                                         while (leaf.node && leaf.offset === 0 && leaf.node.previousSibling)\r
15245                                                 leaf = findLeaf(leaf.node.previousSibling);\r
15246 \r
15247                                         if (leaf.node && leaf.offset > 0 && leaf.node.nodeType === 3 &&\r
15248                                                         leaf.node.nodeValue.charAt(leaf.offset - 1) === ' ') {\r
15249 \r
15250                                                 if (leaf.offset > 1) {\r
15251                                                         endContainer = leaf.node;\r
15252                                                         endContainer.splitText(leaf.offset - 1);\r
15253                                                 } else if (leaf.node.previousSibling) {\r
15254                                                         endContainer = leaf.node.previousSibling;\r
15255                                                 }\r
15256                                         }\r
15257                                 }\r
15258                         }\r
15259                         \r
15260                         // Move start/end point up the tree if the leaves are sharp and if we are in different containers\r
15261                         // Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!\r
15262                         // This will reduce the number of wrapper elements that needs to be created\r
15263                         // Move start point up the tree\r
15264                         if (format[0].inline || format[0].block_expand) {\r
15265                                 startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');\r
15266                                 endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');\r
15267                         }\r
15268 \r
15269                         // Expand start/end container to matching selector\r
15270                         if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) {\r
15271                                 function findSelectorEndPoint(container, sibling_name) {\r
15272                                         var parents, i, y, curFormat;\r
15273 \r
15274                                         if (container.nodeType == 3 && container.nodeValue.length == 0 && container[sibling_name])\r
15275                                                 container = container[sibling_name];\r
15276 \r
15277                                         parents = getParents(container);\r
15278                                         for (i = 0; i < parents.length; i++) {\r
15279                                                 for (y = 0; y < format.length; y++) {\r
15280                                                         curFormat = format[y];\r
15281 \r
15282                                                         // If collapsed state is set then skip formats that doesn't match that\r
15283                                                         if ("collapsed" in curFormat && curFormat.collapsed !== rng.collapsed)\r
15284                                                                 continue;\r
15285 \r
15286                                                         if (dom.is(parents[i], curFormat.selector))\r
15287                                                                 return parents[i];\r
15288                                                 }\r
15289                                         }\r
15290 \r
15291                                         return container;\r
15292                                 };\r
15293 \r
15294                                 // Find new startContainer/endContainer if there is better one\r
15295                                 startContainer = findSelectorEndPoint(startContainer, 'previousSibling');\r
15296                                 endContainer = findSelectorEndPoint(endContainer, 'nextSibling');\r
15297                         }\r
15298 \r
15299                         // Expand start/end container to matching block element or text node\r
15300                         if (format[0].block || format[0].selector) {\r
15301                                 function findBlockEndPoint(container, sibling_name, sibling_name2) {\r
15302                                         var node;\r
15303 \r
15304                                         // Expand to block of similar type\r
15305                                         if (!format[0].wrapper)\r
15306                                                 node = dom.getParent(container, format[0].block);\r
15307 \r
15308                                         // Expand to first wrappable block element or any block element\r
15309                                         if (!node)\r
15310                                                 node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, isBlock);\r
15311 \r
15312                                         // Exclude inner lists from wrapping\r
15313                                         if (node && format[0].wrapper)\r
15314                                                 node = getParents(node, 'ul,ol').reverse()[0] || node;\r
15315 \r
15316                                         // Didn't find a block element look for first/last wrappable element\r
15317                                         if (!node) {\r
15318                                                 node = container;\r
15319 \r
15320                                                 while (node[sibling_name] && !isBlock(node[sibling_name])) {\r
15321                                                         node = node[sibling_name];\r
15322 \r
15323                                                         // Break on BR but include it will be removed later on\r
15324                                                         // we can't remove it now since we need to check if it can be wrapped\r
15325                                                         if (isEq(node, 'br'))\r
15326                                                                 break;\r
15327                                                 }\r
15328                                         }\r
15329 \r
15330                                         return node || container;\r
15331                                 };\r
15332 \r
15333                                 // Find new startContainer/endContainer if there is better one\r
15334                                 startContainer = findBlockEndPoint(startContainer, 'previousSibling');\r
15335                                 endContainer = findBlockEndPoint(endContainer, 'nextSibling');\r
15336 \r
15337                                 // Non block element then try to expand up the leaf\r
15338                                 if (format[0].block) {\r
15339                                         if (!isBlock(startContainer))\r
15340                                                 startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');\r
15341 \r
15342                                         if (!isBlock(endContainer))\r
15343                                                 endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');\r
15344                                 }\r
15345                         }\r
15346 \r
15347                         // Setup index for startContainer\r
15348                         if (startContainer.nodeType == 1) {\r
15349                                 startOffset = nodeIndex(startContainer);\r
15350                                 startContainer = startContainer.parentNode;\r
15351                         }\r
15352 \r
15353                         // Setup index for endContainer\r
15354                         if (endContainer.nodeType == 1) {\r
15355                                 endOffset = nodeIndex(endContainer) + 1;\r
15356                                 endContainer = endContainer.parentNode;\r
15357                         }\r
15358 \r
15359                         // Return new range like object\r
15360                         return {\r
15361                                 startContainer : startContainer,\r
15362                                 startOffset : startOffset,\r
15363                                 endContainer : endContainer,\r
15364                                 endOffset : endOffset\r
15365                         };\r
15366                 }\r
15367 \r
15368                 function removeFormat(format, vars, node, compare_node) {\r
15369                         var i, attrs, stylesModified;\r
15370 \r
15371                         // Check if node matches format\r
15372                         if (!matchName(node, format))\r
15373                                 return FALSE;\r
15374 \r
15375                         // Should we compare with format attribs and styles\r
15376                         if (format.remove != 'all') {\r
15377                                 // Remove styles\r
15378                                 each(format.styles, function(value, name) {\r
15379                                         value = replaceVars(value, vars);\r
15380 \r
15381                                         // Indexed array\r
15382                                         if (typeof(name) === 'number') {\r
15383                                                 name = value;\r
15384                                                 compare_node = 0;\r
15385                                         }\r
15386 \r
15387                                         if (!compare_node || isEq(getStyle(compare_node, name), value))\r
15388                                                 dom.setStyle(node, name, '');\r
15389 \r
15390                                         stylesModified = 1;\r
15391                                 });\r
15392 \r
15393                                 // Remove style attribute if it's empty\r
15394                                 if (stylesModified && dom.getAttrib(node, 'style') == '') {\r
15395                                         node.removeAttribute('style');\r
15396                                         node.removeAttribute('data-mce-style');\r
15397                                 }\r
15398 \r
15399                                 // Remove attributes\r
15400                                 each(format.attributes, function(value, name) {\r
15401                                         var valueOut;\r
15402 \r
15403                                         value = replaceVars(value, vars);\r
15404 \r
15405                                         // Indexed array\r
15406                                         if (typeof(name) === 'number') {\r
15407                                                 name = value;\r
15408                                                 compare_node = 0;\r
15409                                         }\r
15410 \r
15411                                         if (!compare_node || isEq(dom.getAttrib(compare_node, name), value)) {\r
15412                                                 // Keep internal classes\r
15413                                                 if (name == 'class') {\r
15414                                                         value = dom.getAttrib(node, name);\r
15415                                                         if (value) {\r
15416                                                                 // Build new class value where everything is removed except the internal prefixed classes\r
15417                                                                 valueOut = '';\r
15418                                                                 each(value.split(/\s+/), function(cls) {\r
15419                                                                         if (/mce\w+/.test(cls))\r
15420                                                                                 valueOut += (valueOut ? ' ' : '') + cls;\r
15421                                                                 });\r
15422 \r
15423                                                                 // We got some internal classes left\r
15424                                                                 if (valueOut) {\r
15425                                                                         dom.setAttrib(node, name, valueOut);\r
15426                                                                         return;\r
15427                                                                 }\r
15428                                                         }\r
15429                                                 }\r
15430 \r
15431                                                 // IE6 has a bug where the attribute doesn't get removed correctly\r
15432                                                 if (name == "class")\r
15433                                                         node.removeAttribute('className');\r
15434 \r
15435                                                 // Remove mce prefixed attributes\r
15436                                                 if (MCE_ATTR_RE.test(name))\r
15437                                                         node.removeAttribute('data-mce-' + name);\r
15438 \r
15439                                                 node.removeAttribute(name);\r
15440                                         }\r
15441                                 });\r
15442 \r
15443                                 // Remove classes\r
15444                                 each(format.classes, function(value) {\r
15445                                         value = replaceVars(value, vars);\r
15446 \r
15447                                         if (!compare_node || dom.hasClass(compare_node, value))\r
15448                                                 dom.removeClass(node, value);\r
15449                                 });\r
15450 \r
15451                                 // Check for non internal attributes\r
15452                                 attrs = dom.getAttribs(node);\r
15453                                 for (i = 0; i < attrs.length; i++) {\r
15454                                         if (attrs[i].nodeName.indexOf('_') !== 0)\r
15455                                                 return FALSE;\r
15456                                 }\r
15457                         }\r
15458 \r
15459                         // Remove the inline child if it's empty for example <b> or <span>\r
15460                         if (format.remove != 'none') {\r
15461                                 removeNode(node, format);\r
15462                                 return TRUE;\r
15463                         }\r
15464                 };\r
15465 \r
15466                 function removeNode(node, format) {\r
15467                         var parentNode = node.parentNode, rootBlockElm;\r
15468 \r
15469                         if (format.block) {\r
15470                                 if (!forcedRootBlock) {\r
15471                                         function find(node, next, inc) {\r
15472                                                 node = getNonWhiteSpaceSibling(node, next, inc);\r
15473 \r
15474                                                 return !node || (node.nodeName == 'BR' || isBlock(node));\r
15475                                         };\r
15476 \r
15477                                         // Append BR elements if needed before we remove the block\r
15478                                         if (isBlock(node) && !isBlock(parentNode)) {\r
15479                                                 if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1))\r
15480                                                         node.insertBefore(dom.create('br'), node.firstChild);\r
15481 \r
15482                                                 if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1))\r
15483                                                         node.appendChild(dom.create('br'));\r
15484                                         }\r
15485                                 } else {\r
15486                                         // Wrap the block in a forcedRootBlock if we are at the root of document\r
15487                                         if (parentNode == dom.getRoot()) {\r
15488                                                 if (!format.list_block || !isEq(node, format.list_block)) {\r
15489                                                         each(tinymce.grep(node.childNodes), function(node) {\r
15490                                                                 if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) {\r
15491                                                                         if (!rootBlockElm)\r
15492                                                                                 rootBlockElm = wrap(node, forcedRootBlock);\r
15493                                                                         else\r
15494                                                                                 rootBlockElm.appendChild(node);\r
15495                                                                 } else\r
15496                                                                         rootBlockElm = 0;\r
15497                                                         });\r
15498                                                 }\r
15499                                         }\r
15500                                 }\r
15501                         }\r
15502 \r
15503                         // Never remove nodes that isn't the specified inline element if a selector is specified too\r
15504                         if (format.selector && format.inline && !isEq(format.inline, node))\r
15505                                 return;\r
15506 \r
15507                         dom.remove(node, 1);\r
15508                 };\r
15509 \r
15510                 function getNonWhiteSpaceSibling(node, next, inc) {\r
15511                         if (node) {\r
15512                                 next = next ? 'nextSibling' : 'previousSibling';\r
15513 \r
15514                                 for (node = inc ? node : node[next]; node; node = node[next]) {\r
15515                                         if (node.nodeType == 1 || !isWhiteSpaceNode(node))\r
15516                                                 return node;\r
15517                                 }\r
15518                         }\r
15519                 };\r
15520 \r
15521                 function isBookmarkNode(node) {\r
15522                         return node && node.nodeType == 1 && node.getAttribute('data-mce-type') == 'bookmark';\r
15523                 };\r
15524 \r
15525                 function mergeSiblings(prev, next) {\r
15526                         var marker, sibling, tmpSibling;\r
15527 \r
15528                         function compareElements(node1, node2) {\r
15529                                 // Not the same name\r
15530                                 if (node1.nodeName != node2.nodeName)\r
15531                                         return FALSE;\r
15532 \r
15533                                 function getAttribs(node) {\r
15534                                         var attribs = {};\r
15535 \r
15536                                         each(dom.getAttribs(node), function(attr) {\r
15537                                                 var name = attr.nodeName.toLowerCase();\r
15538 \r
15539                                                 // Don't compare internal attributes or style\r
15540                                                 if (name.indexOf('_') !== 0 && name !== 'style')\r
15541                                                         attribs[name] = dom.getAttrib(node, name);\r
15542                                         });\r
15543 \r
15544                                         return attribs;\r
15545                                 };\r
15546 \r
15547                                 function compareObjects(obj1, obj2) {\r
15548                                         var value, name;\r
15549 \r
15550                                         for (name in obj1) {\r
15551                                                 // Obj1 has item obj2 doesn't have\r
15552                                                 if (obj1.hasOwnProperty(name)) {\r
15553                                                         value = obj2[name];\r
15554 \r
15555                                                         // Obj2 doesn't have obj1 item\r
15556                                                         if (value === undefined)\r
15557                                                                 return FALSE;\r
15558 \r
15559                                                         // Obj2 item has a different value\r
15560                                                         if (obj1[name] != value)\r
15561                                                                 return FALSE;\r
15562 \r
15563                                                         // Delete similar value\r
15564                                                         delete obj2[name];\r
15565                                                 }\r
15566                                         }\r
15567 \r
15568                                         // Check if obj 2 has something obj 1 doesn't have\r
15569                                         for (name in obj2) {\r
15570                                                 // Obj2 has item obj1 doesn't have\r
15571                                                 if (obj2.hasOwnProperty(name))\r
15572                                                         return FALSE;\r
15573                                         }\r
15574 \r
15575                                         return TRUE;\r
15576                                 };\r
15577 \r
15578                                 // Attribs are not the same\r
15579                                 if (!compareObjects(getAttribs(node1), getAttribs(node2)))\r
15580                                         return FALSE;\r
15581 \r
15582                                 // Styles are not the same\r
15583                                 if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style'))))\r
15584                                         return FALSE;\r
15585 \r
15586                                 return TRUE;\r
15587                         };\r
15588 \r
15589                         // Check if next/prev exists and that they are elements\r
15590                         if (prev && next) {\r
15591                                 function findElementSibling(node, sibling_name) {\r
15592                                         for (sibling = node; sibling; sibling = sibling[sibling_name]) {\r
15593                                                 if (sibling.nodeType == 3 && sibling.nodeValue.length !== 0)\r
15594                                                         return node;\r
15595 \r
15596                                                 if (sibling.nodeType == 1 && !isBookmarkNode(sibling))\r
15597                                                         return sibling;\r
15598                                         }\r
15599 \r
15600                                         return node;\r
15601                                 };\r
15602 \r
15603                                 // If previous sibling is empty then jump over it\r
15604                                 prev = findElementSibling(prev, 'previousSibling');\r
15605                                 next = findElementSibling(next, 'nextSibling');\r
15606 \r
15607                                 // Compare next and previous nodes\r
15608                                 if (compareElements(prev, next)) {\r
15609                                         // Append nodes between\r
15610                                         for (sibling = prev.nextSibling; sibling && sibling != next;) {\r
15611                                                 tmpSibling = sibling;\r
15612                                                 sibling = sibling.nextSibling;\r
15613                                                 prev.appendChild(tmpSibling);\r
15614                                         }\r
15615 \r
15616                                         // Remove next node\r
15617                                         dom.remove(next);\r
15618 \r
15619                                         // Move children into prev node\r
15620                                         each(tinymce.grep(next.childNodes), function(node) {\r
15621                                                 prev.appendChild(node);\r
15622                                         });\r
15623 \r
15624                                         return prev;\r
15625                                 }\r
15626                         }\r
15627 \r
15628                         return next;\r
15629                 };\r
15630 \r
15631                 function isTextBlock(name) {\r
15632                         return /^(h[1-6]|p|div|pre|address|dl|dt|dd)$/.test(name);\r
15633                 };\r
15634 \r
15635                 function getContainer(rng, start) {\r
15636                         var container, offset, lastIdx;\r
15637 \r
15638                         container = rng[start ? 'startContainer' : 'endContainer'];\r
15639                         offset = rng[start ? 'startOffset' : 'endOffset'];\r
15640 \r
15641                         if (container.nodeType == 1) {\r
15642                                 lastIdx = container.childNodes.length - 1;\r
15643 \r
15644                                 if (!start && offset)\r
15645                                         offset--;\r
15646 \r
15647                                 container = container.childNodes[offset > lastIdx ? lastIdx : offset];\r
15648                         }\r
15649 \r
15650                         return container;\r
15651                 };\r
15652 \r
15653                 function performCaretAction(type, name, vars) {\r
15654                         var i, currentPendingFormats = pendingFormats[type],\r
15655                                 otherPendingFormats = pendingFormats[type == 'apply' ? 'remove' : 'apply'];\r
15656 \r
15657                         function hasPending() {\r
15658                                 return pendingFormats.apply.length || pendingFormats.remove.length;\r
15659                         };\r
15660 \r
15661                         function resetPending() {\r
15662                                 pendingFormats.apply = [];\r
15663                                 pendingFormats.remove = [];\r
15664                         };\r
15665 \r
15666                         function perform(caret_node) {\r
15667                                 // Apply pending formats\r
15668                                 each(pendingFormats.apply.reverse(), function(item) {\r
15669                                         apply(item.name, item.vars, caret_node);\r
15670 \r
15671                                         // Colored nodes should be underlined so that the color of the underline matches the text color.\r
15672                                         if (item.name === 'forecolor' && item.vars.value)\r
15673                                                 processUnderlineAndColor(caret_node.parentNode);\r
15674                                 });\r
15675 \r
15676                                 // Remove pending formats\r
15677                                 each(pendingFormats.remove.reverse(), function(item) {\r
15678                                         remove(item.name, item.vars, caret_node);\r
15679                                 });\r
15680 \r
15681                                 dom.remove(caret_node, 1);\r
15682                                 resetPending();\r
15683                         };\r
15684 \r
15685                         // Check if it already exists then ignore it\r
15686                         for (i = currentPendingFormats.length - 1; i >= 0; i--) {\r
15687                                 if (currentPendingFormats[i].name == name)\r
15688                                         return;\r
15689                         }\r
15690 \r
15691                         currentPendingFormats.push({name : name, vars : vars});\r
15692 \r
15693                         // Check if it's in the other type, then remove it\r
15694                         for (i = otherPendingFormats.length - 1; i >= 0; i--) {\r
15695                                 if (otherPendingFormats[i].name == name)\r
15696                                         otherPendingFormats.splice(i, 1);\r
15697                         }\r
15698 \r
15699                         // Pending apply or remove formats\r
15700                         if (hasPending()) {\r
15701                                 ed.getDoc().execCommand('FontName', false, 'mceinline');\r
15702                                 pendingFormats.lastRng = selection.getRng();\r
15703 \r
15704                                 // IE will convert the current word\r
15705                                 each(dom.select('font,span'), function(node) {\r
15706                                         var bookmark;\r
15707 \r
15708                                         if (isCaretNode(node)) {\r
15709                                                 bookmark = selection.getBookmark();\r
15710                                                 perform(node);\r
15711                                                 selection.moveToBookmark(bookmark);\r
15712                                                 ed.nodeChanged();\r
15713                                         }\r
15714                                 });\r
15715 \r
15716                                 // Only register listeners once if we need to\r
15717                                 if (!pendingFormats.isListening && hasPending()) {\r
15718                                         pendingFormats.isListening = true;\r
15719 \r
15720                                         each('onKeyDown,onKeyUp,onKeyPress,onMouseUp'.split(','), function(event) {\r
15721                                                 ed[event].addToTop(function(ed, e) {\r
15722                                                         // Do we have pending formats and is the selection moved has moved\r
15723                                                         if (hasPending() && !tinymce.dom.RangeUtils.compareRanges(pendingFormats.lastRng, selection.getRng())) {\r
15724                                                                 each(dom.select('font,span'), function(node) {\r
15725                                                                         var textNode, rng;\r
15726 \r
15727                                                                         // Look for marker\r
15728                                                                         if (isCaretNode(node)) {\r
15729                                                                                 textNode = node.firstChild;\r
15730 \r
15731                                                                                 if (textNode) {\r
15732                                                                                         perform(node);\r
15733 \r
15734                                                                                         rng = dom.createRng();\r
15735                                                                                         rng.setStart(textNode, textNode.nodeValue.length);\r
15736                                                                                         rng.setEnd(textNode, textNode.nodeValue.length);\r
15737                                                                                         selection.setRng(rng);\r
15738                                                                                         ed.nodeChanged();\r
15739                                                                                 } else\r
15740                                                                                         dom.remove(node);\r
15741                                                                         }\r
15742                                                                 });\r
15743 \r
15744                                                                 // Always unbind and clear pending styles on keyup\r
15745                                                                 if (e.type == 'keyup' || e.type == 'mouseup')\r
15746                                                                         resetPending();\r
15747                                                         }\r
15748                                                 });\r
15749                                         });\r
15750                                 }\r
15751                         }\r
15752                 };\r
15753         };\r
15754 })(tinymce);\r
15755 \r
15756 tinymce.onAddEditor.add(function(tinymce, ed) {\r
15757         var filters, fontSizes, dom, settings = ed.settings;\r
15758 \r
15759         if (settings.inline_styles) {\r
15760                 fontSizes = tinymce.explode(settings.font_size_style_values);\r
15761 \r
15762                 function replaceWithSpan(node, styles) {\r
15763                         tinymce.each(styles, function(value, name) {\r
15764                                 if (value)\r
15765                                         dom.setStyle(node, name, value);\r
15766                         });\r
15767 \r
15768                         dom.rename(node, 'span');\r
15769                 };\r
15770 \r
15771                 filters = {\r
15772                         font : function(dom, node) {\r
15773                                 replaceWithSpan(node, {\r
15774                                         backgroundColor : node.style.backgroundColor,\r
15775                                         color : node.color,\r
15776                                         fontFamily : node.face,\r
15777                                         fontSize : fontSizes[parseInt(node.size) - 1]\r
15778                                 });\r
15779                         },\r
15780 \r
15781                         u : function(dom, node) {\r
15782                                 replaceWithSpan(node, {\r
15783                                         textDecoration : 'underline'\r
15784                                 });\r
15785                         },\r
15786 \r
15787                         strike : function(dom, node) {\r
15788                                 replaceWithSpan(node, {\r
15789                                         textDecoration : 'line-through'\r
15790                                 });\r
15791                         }\r
15792                 };\r
15793 \r
15794                 function convert(editor, params) {\r
15795                         dom = editor.dom;\r
15796 \r
15797                         if (settings.convert_fonts_to_spans) {\r
15798                                 tinymce.each(dom.select('font,u,strike', params.node), function(node) {\r
15799                                         filters[node.nodeName.toLowerCase()](ed.dom, node);\r
15800                                 });\r
15801                         }\r
15802                 };\r
15803 \r
15804                 ed.onPreProcess.add(convert);\r
15805                 ed.onSetContent.add(convert);\r
15806 \r
15807                 ed.onInit.add(function() {\r
15808                         ed.selection.onSetContent.add(convert);\r
15809                 });\r
15810         }\r
15811 });\r
15812 \r