]> git.mxchange.org Git - friendica.git/blob - library/tinymce/jscripts/tiny_mce/tiny_mce_src.js
update tinymce to 3.5b2 to fix issues with FF 11 and pasting into code blocks
[friendica.git] / library / tinymce / jscripts / 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 : '5b2',\r
9 \r
10                 releaseDate : '2012-03-15',\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.isIE7 = t.isIE && /MSIE [7]/.test(ua);\r
24 \r
25                         t.isIE8 = t.isIE && /MSIE [8]/.test(ua);\r
26 \r
27                         t.isIE9 = t.isIE && /MSIE [9]/.test(ua);\r
28 \r
29                         t.isGecko = !t.isWebKit && /Gecko/.test(ua);\r
30 \r
31                         t.isMac = ua.indexOf('Mac') != -1;\r
32 \r
33                         t.isAir = /adobeair/i.test(ua);\r
34 \r
35                         t.isIDevice = /(iPad|iPhone)/.test(ua);\r
36                         \r
37                         t.isIOS5 = t.isIDevice && ua.match(/AppleWebKit\/(\d*)/)[1]>=534;\r
38 \r
39                         // TinyMCE .NET webcontrol might be setting the values for TinyMCE\r
40                         if (win.tinyMCEPreInit) {\r
41                                 t.suffix = tinyMCEPreInit.suffix;\r
42                                 t.baseURL = tinyMCEPreInit.base;\r
43                                 t.query = tinyMCEPreInit.query;\r
44                                 return;\r
45                         }\r
46 \r
47                         // Get suffix and base\r
48                         t.suffix = '';\r
49 \r
50                         // If base element found, add that infront of baseURL\r
51                         nl = d.getElementsByTagName('base');\r
52                         for (i=0; i<nl.length; i++) {\r
53                                 if (v = nl[i].href) {\r
54                                         // Host only value like http://site.com or http://site.com:8008\r
55                                         if (/^https?:\/\/[^\/]+$/.test(v))\r
56                                                 v += '/';\r
57 \r
58                                         base = v ? v.match(/.*\//)[0] : ''; // Get only directory\r
59                                 }\r
60                         }\r
61 \r
62                         function getBase(n) {\r
63                                 if (n.src && /tiny_mce(|_gzip|_jquery|_prototype|_full)(_dev|_src)?.js/.test(n.src)) {\r
64                                         if (/_(src|dev)\.js/g.test(n.src))\r
65                                                 t.suffix = '_src';\r
66 \r
67                                         if ((p = n.src.indexOf('?')) != -1)\r
68                                                 t.query = n.src.substring(p + 1);\r
69 \r
70                                         t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));\r
71 \r
72                                         // If path to script is relative and a base href was found add that one infront\r
73                                         // the src property will always be an absolute one on non IE browsers and IE 8\r
74                                         // so this logic will basically only be executed on older IE versions\r
75                                         if (base && t.baseURL.indexOf('://') == -1 && t.baseURL.indexOf('/') !== 0)\r
76                                                 t.baseURL = base + t.baseURL;\r
77 \r
78                                         return t.baseURL;\r
79                                 }\r
80 \r
81                                 return null;\r
82                         };\r
83 \r
84                         // Check document\r
85                         nl = d.getElementsByTagName('script');\r
86                         for (i=0; i<nl.length; i++) {\r
87                                 if (getBase(nl[i]))\r
88                                         return;\r
89                         }\r
90 \r
91                         // Check head\r
92                         n = d.getElementsByTagName('head')[0];\r
93                         if (n) {\r
94                                 nl = n.getElementsByTagName('script');\r
95                                 for (i=0; i<nl.length; i++) {\r
96                                         if (getBase(nl[i]))\r
97                                                 return;\r
98                                 }\r
99                         }\r
100 \r
101                         return;\r
102                 },\r
103 \r
104                 is : function(o, t) {\r
105                         if (!t)\r
106                                 return o !== undefined;\r
107 \r
108                         if (t == 'array' && (o.hasOwnProperty && o instanceof Array))\r
109                                 return true;\r
110 \r
111                         return typeof(o) == t;\r
112                 },\r
113 \r
114                 makeMap : function(items, delim, map) {\r
115                         var i;\r
116 \r
117                         items = items || [];\r
118                         delim = delim || ',';\r
119 \r
120                         if (typeof(items) == "string")\r
121                                 items = items.split(delim);\r
122 \r
123                         map = map || {};\r
124 \r
125                         i = items.length;\r
126                         while (i--)\r
127                                 map[items[i]] = {};\r
128 \r
129                         return map;\r
130                 },\r
131 \r
132                 each : function(o, cb, s) {\r
133                         var n, l;\r
134 \r
135                         if (!o)\r
136                                 return 0;\r
137 \r
138                         s = s || o;\r
139 \r
140                         if (o.length !== undefined) {\r
141                                 // Indexed arrays, needed for Safari\r
142                                 for (n=0, l = o.length; n < l; n++) {\r
143                                         if (cb.call(s, o[n], n, o) === false)\r
144                                                 return 0;\r
145                                 }\r
146                         } else {\r
147                                 // Hashtables\r
148                                 for (n in o) {\r
149                                         if (o.hasOwnProperty(n)) {\r
150                                                 if (cb.call(s, o[n], n, o) === false)\r
151                                                         return 0;\r
152                                         }\r
153                                 }\r
154                         }\r
155 \r
156                         return 1;\r
157                 },\r
158 \r
159 \r
160                 map : function(a, f) {\r
161                         var o = [];\r
162 \r
163                         tinymce.each(a, function(v) {\r
164                                 o.push(f(v));\r
165                         });\r
166 \r
167                         return o;\r
168                 },\r
169 \r
170                 grep : function(a, f) {\r
171                         var o = [];\r
172 \r
173                         tinymce.each(a, function(v) {\r
174                                 if (!f || f(v))\r
175                                         o.push(v);\r
176                         });\r
177 \r
178                         return o;\r
179                 },\r
180 \r
181                 inArray : function(a, v) {\r
182                         var i, l;\r
183 \r
184                         if (a) {\r
185                                 for (i = 0, l = a.length; i < l; i++) {\r
186                                         if (a[i] === v)\r
187                                                 return i;\r
188                                 }\r
189                         }\r
190 \r
191                         return -1;\r
192                 },\r
193 \r
194                 extend : function(o, e) {\r
195                         var i, l, a = arguments;\r
196 \r
197                         for (i = 1, l = a.length; i < l; i++) {\r
198                                 e = a[i];\r
199 \r
200                                 tinymce.each(e, function(v, n) {\r
201                                         if (v !== undefined)\r
202                                                 o[n] = v;\r
203                                 });\r
204                         }\r
205 \r
206                         return o;\r
207                 },\r
208 \r
209 \r
210                 trim : function(s) {\r
211                         return (s ? '' + s : '').replace(whiteSpaceRe, '');\r
212                 },\r
213 \r
214                 create : function(s, p, root) {\r
215                         var t = this, sp, ns, cn, scn, c, de = 0;\r
216 \r
217                         // Parse : <prefix> <class>:<super class>\r
218                         s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);\r
219                         cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name\r
220 \r
221                         // Create namespace for new class\r
222                         ns = t.createNS(s[3].replace(/\.\w+$/, ''), root);\r
223 \r
224                         // Class already exists\r
225                         if (ns[cn])\r
226                                 return;\r
227 \r
228                         // Make pure static class\r
229                         if (s[2] == 'static') {\r
230                                 ns[cn] = p;\r
231 \r
232                                 if (this.onCreate)\r
233                                         this.onCreate(s[2], s[3], ns[cn]);\r
234 \r
235                                 return;\r
236                         }\r
237 \r
238                         // Create default constructor\r
239                         if (!p[cn]) {\r
240                                 p[cn] = function() {};\r
241                                 de = 1;\r
242                         }\r
243 \r
244                         // Add constructor and methods\r
245                         ns[cn] = p[cn];\r
246                         t.extend(ns[cn].prototype, p);\r
247 \r
248                         // Extend\r
249                         if (s[5]) {\r
250                                 sp = t.resolve(s[5]).prototype;\r
251                                 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name\r
252 \r
253                                 // Extend constructor\r
254                                 c = ns[cn];\r
255                                 if (de) {\r
256                                         // Add passthrough constructor\r
257                                         ns[cn] = function() {\r
258                                                 return sp[scn].apply(this, arguments);\r
259                                         };\r
260                                 } else {\r
261                                         // Add inherit constructor\r
262                                         ns[cn] = function() {\r
263                                                 this.parent = sp[scn];\r
264                                                 return c.apply(this, arguments);\r
265                                         };\r
266                                 }\r
267                                 ns[cn].prototype[cn] = ns[cn];\r
268 \r
269                                 // Add super methods\r
270                                 t.each(sp, function(f, n) {\r
271                                         ns[cn].prototype[n] = sp[n];\r
272                                 });\r
273 \r
274                                 // Add overridden methods\r
275                                 t.each(p, function(f, n) {\r
276                                         // Extend methods if needed\r
277                                         if (sp[n]) {\r
278                                                 ns[cn].prototype[n] = function() {\r
279                                                         this.parent = sp[n];\r
280                                                         return f.apply(this, arguments);\r
281                                                 };\r
282                                         } else {\r
283                                                 if (n != cn)\r
284                                                         ns[cn].prototype[n] = f;\r
285                                         }\r
286                                 });\r
287                         }\r
288 \r
289                         // Add static methods\r
290                         t.each(p['static'], function(f, n) {\r
291                                 ns[cn][n] = f;\r
292                         });\r
293 \r
294                         if (this.onCreate)\r
295                                 this.onCreate(s[2], s[3], ns[cn].prototype);\r
296                 },\r
297 \r
298                 walk : function(o, f, n, s) {\r
299                         s = s || this;\r
300 \r
301                         if (o) {\r
302                                 if (n)\r
303                                         o = o[n];\r
304 \r
305                                 tinymce.each(o, function(o, i) {\r
306                                         if (f.call(s, o, i, n) === false)\r
307                                                 return false;\r
308 \r
309                                         tinymce.walk(o, f, n, s);\r
310                                 });\r
311                         }\r
312                 },\r
313 \r
314                 createNS : function(n, o) {\r
315                         var i, v;\r
316 \r
317                         o = o || win;\r
318 \r
319                         n = n.split('.');\r
320                         for (i=0; i<n.length; i++) {\r
321                                 v = n[i];\r
322 \r
323                                 if (!o[v])\r
324                                         o[v] = {};\r
325 \r
326                                 o = o[v];\r
327                         }\r
328 \r
329                         return o;\r
330                 },\r
331 \r
332                 resolve : function(n, o) {\r
333                         var i, l;\r
334 \r
335                         o = o || win;\r
336 \r
337                         n = n.split('.');\r
338                         for (i = 0, l = n.length; i < l; i++) {\r
339                                 o = o[n[i]];\r
340 \r
341                                 if (!o)\r
342                                         break;\r
343                         }\r
344 \r
345                         return o;\r
346                 },\r
347 \r
348                 addUnload : function(f, s) {\r
349                         var t = this;\r
350 \r
351                         f = {func : f, scope : s || this};\r
352 \r
353                         if (!t.unloads) {\r
354                                 function unload() {\r
355                                         var li = t.unloads, o, n;\r
356 \r
357                                         if (li) {\r
358                                                 // Call unload handlers\r
359                                                 for (n in li) {\r
360                                                         o = li[n];\r
361 \r
362                                                         if (o && o.func)\r
363                                                                 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy\r
364                                                 }\r
365 \r
366                                                 // Detach unload function\r
367                                                 if (win.detachEvent) {\r
368                                                         win.detachEvent('onbeforeunload', fakeUnload);\r
369                                                         win.detachEvent('onunload', unload);\r
370                                                 } else if (win.removeEventListener)\r
371                                                         win.removeEventListener('unload', unload, false);\r
372 \r
373                                                 // Destroy references\r
374                                                 t.unloads = o = li = w = unload = 0;\r
375 \r
376                                                 // Run garbarge collector on IE\r
377                                                 if (win.CollectGarbage)\r
378                                                         CollectGarbage();\r
379                                         }\r
380                                 };\r
381 \r
382                                 function fakeUnload() {\r
383                                         var d = document;\r
384 \r
385                                         // Is there things still loading, then do some magic\r
386                                         if (d.readyState == 'interactive') {\r
387                                                 function stop() {\r
388                                                         // Prevent memory leak\r
389                                                         d.detachEvent('onstop', stop);\r
390 \r
391                                                         // Call unload handler\r
392                                                         if (unload)\r
393                                                                 unload();\r
394 \r
395                                                         d = 0;\r
396                                                 };\r
397 \r
398                                                 // Fire unload when the currently loading page is stopped\r
399                                                 if (d)\r
400                                                         d.attachEvent('onstop', stop);\r
401 \r
402                                                 // Remove onstop listener after a while to prevent the unload function\r
403                                                 // to execute if the user presses cancel in an onbeforeunload\r
404                                                 // confirm dialog and then presses the browser stop button\r
405                                                 win.setTimeout(function() {\r
406                                                         if (d)\r
407                                                                 d.detachEvent('onstop', stop);\r
408                                                 }, 0);\r
409                                         }\r
410                                 };\r
411 \r
412                                 // Attach unload handler\r
413                                 if (win.attachEvent) {\r
414                                         win.attachEvent('onunload', unload);\r
415                                         win.attachEvent('onbeforeunload', fakeUnload);\r
416                                 } else if (win.addEventListener)\r
417                                         win.addEventListener('unload', unload, false);\r
418 \r
419                                 // Setup initial unload handler array\r
420                                 t.unloads = [f];\r
421                         } else\r
422                                 t.unloads.push(f);\r
423 \r
424                         return f;\r
425                 },\r
426 \r
427                 removeUnload : function(f) {\r
428                         var u = this.unloads, r = null;\r
429 \r
430                         tinymce.each(u, function(o, i) {\r
431                                 if (o && o.func == f) {\r
432                                         u.splice(i, 1);\r
433                                         r = f;\r
434                                         return false;\r
435                                 }\r
436                         });\r
437 \r
438                         return r;\r
439                 },\r
440 \r
441                 explode : function(s, d) {\r
442                         return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;\r
443                 },\r
444 \r
445                 _addVer : function(u) {\r
446                         var v;\r
447 \r
448                         if (!this.query)\r
449                                 return u;\r
450 \r
451                         v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;\r
452 \r
453                         if (u.indexOf('#') == -1)\r
454                                 return u + v;\r
455 \r
456                         return u.replace('#', v + '#');\r
457                 },\r
458 \r
459                 // Fix function for IE 9 where regexps isn't working correctly\r
460                 // Todo: remove me once MS fixes the bug\r
461                 _replace : function(find, replace, str) {\r
462                         // On IE9 we have to fake $x replacement\r
463                         if (isRegExpBroken) {\r
464                                 return str.replace(find, function() {\r
465                                         var val = replace, args = arguments, i;\r
466 \r
467                                         for (i = 0; i < args.length - 2; i++) {\r
468                                                 if (args[i] === undefined) {\r
469                                                         val = val.replace(new RegExp('\\$' + i, 'g'), '');\r
470                                                 } else {\r
471                                                         val = val.replace(new RegExp('\\$' + i, 'g'), args[i]);\r
472                                                 }\r
473                                         }\r
474 \r
475                                         return val;\r
476                                 });\r
477                         }\r
478 \r
479                         return str.replace(find, replace);\r
480                 }\r
481 \r
482                 };\r
483 \r
484         // Initialize the API\r
485         tinymce._init();\r
486 \r
487         // Expose tinymce namespace to the global namespace (window)\r
488         win.tinymce = win.tinyMCE = tinymce;\r
489 \r
490         // Describe the different namespaces\r
491 \r
492         })(window);\r
493 \r
494 \r
495 \r
496 tinymce.create('tinymce.util.Dispatcher', {\r
497         scope : null,\r
498         listeners : null,\r
499 \r
500         Dispatcher : function(s) {\r
501                 this.scope = s || this;\r
502                 this.listeners = [];\r
503         },\r
504 \r
505         add : function(cb, s) {\r
506                 this.listeners.push({cb : cb, scope : s || this.scope});\r
507 \r
508                 return cb;\r
509         },\r
510 \r
511         addToTop : function(cb, s) {\r
512                 this.listeners.unshift({cb : cb, scope : s || this.scope});\r
513 \r
514                 return cb;\r
515         },\r
516 \r
517         remove : function(cb) {\r
518                 var l = this.listeners, o = null;\r
519 \r
520                 tinymce.each(l, function(c, i) {\r
521                         if (cb == c.cb) {\r
522                                 o = cb;\r
523                                 l.splice(i, 1);\r
524                                 return false;\r
525                         }\r
526                 });\r
527 \r
528                 return o;\r
529         },\r
530 \r
531         dispatch : function() {\r
532                 var s, a = arguments, i, li = this.listeners, c;\r
533 \r
534                 // Needs to be a real loop since the listener count might change while looping\r
535                 // And this is also more efficient\r
536                 for (i = 0; i<li.length; i++) {\r
537                         c = li[i];\r
538                         s = c.cb.apply(c.scope, a.length > 0 ? a : [c.scope]);\r
539 \r
540                         if (s === false)\r
541                                 break;\r
542                 }\r
543 \r
544                 return s;\r
545         }\r
546 \r
547         });\r
548 \r
549 (function() {\r
550         var each = tinymce.each;\r
551 \r
552         tinymce.create('tinymce.util.URI', {\r
553                 URI : function(u, s) {\r
554                         var t = this, o, a, b, base_url;\r
555 \r
556                         // Trim whitespace\r
557                         u = tinymce.trim(u);\r
558 \r
559                         // Default settings\r
560                         s = t.settings = s || {};\r
561 \r
562                         // Strange app protocol that isn't http/https or local anchor\r
563                         // For example: mailto,skype,tel etc.\r
564                         if (/^([\w\-]+):([^\/]{2})/i.test(u) || /^\s*#/.test(u)) {\r
565                                 t.source = u;\r
566                                 return;\r
567                         }\r
568 \r
569                         // Absolute path with no host, fake host and protocol\r
570                         if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)\r
571                                 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;\r
572 \r
573                         // Relative path http:// or protocol relative //path\r
574                         if (!/^[\w-]*:?\/\//.test(u)) {\r
575                                 base_url = s.base_uri ? s.base_uri.path : new tinymce.util.URI(location.href).directory;\r
576                                 u = ((s.base_uri && s.base_uri.protocol) || 'http') + '://mce_host' + t.toAbsPath(base_url, u);\r
577                         }\r
578 \r
579                         // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)\r
580                         u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something\r
581                         u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);\r
582                         each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {\r
583                                 var s = u[i];\r
584 \r
585                                 // Zope 3 workaround, they use @@something\r
586                                 if (s)\r
587                                         s = s.replace(/\(mce_at\)/g, '@@');\r
588 \r
589                                 t[v] = s;\r
590                         });\r
591 \r
592                         if (b = s.base_uri) {\r
593                                 if (!t.protocol)\r
594                                         t.protocol = b.protocol;\r
595 \r
596                                 if (!t.userInfo)\r
597                                         t.userInfo = b.userInfo;\r
598 \r
599                                 if (!t.port && t.host == 'mce_host')\r
600                                         t.port = b.port;\r
601 \r
602                                 if (!t.host || t.host == 'mce_host')\r
603                                         t.host = b.host;\r
604 \r
605                                 t.source = '';\r
606                         }\r
607 \r
608                         //t.path = t.path || '/';\r
609                 },\r
610 \r
611                 setPath : function(p) {\r
612                         var t = this;\r
613 \r
614                         p = /^(.*?)\/?(\w+)?$/.exec(p);\r
615 \r
616                         // Update path parts\r
617                         t.path = p[0];\r
618                         t.directory = p[1];\r
619                         t.file = p[2];\r
620 \r
621                         // Rebuild source\r
622                         t.source = '';\r
623                         t.getURI();\r
624                 },\r
625 \r
626                 toRelative : function(u) {\r
627                         var t = this, o;\r
628 \r
629                         if (u === "./")\r
630                                 return u;\r
631 \r
632                         u = new tinymce.util.URI(u, {base_uri : t});\r
633 \r
634                         // Not on same domain/port or protocol\r
635                         if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)\r
636                                 return u.getURI();\r
637 \r
638                         o = t.toRelPath(t.path, u.path);\r
639 \r
640                         // Add query\r
641                         if (u.query)\r
642                                 o += '?' + u.query;\r
643 \r
644                         // Add anchor\r
645                         if (u.anchor)\r
646                                 o += '#' + u.anchor;\r
647 \r
648                         return o;\r
649                 },\r
650         \r
651                 toAbsolute : function(u, nh) {\r
652                         var u = new tinymce.util.URI(u, {base_uri : this});\r
653 \r
654                         return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0);\r
655                 },\r
656 \r
657                 toRelPath : function(base, path) {\r
658                         var items, bp = 0, out = '', i, l;\r
659 \r
660                         // Split the paths\r
661                         base = base.substring(0, base.lastIndexOf('/'));\r
662                         base = base.split('/');\r
663                         items = path.split('/');\r
664 \r
665                         if (base.length >= items.length) {\r
666                                 for (i = 0, l = base.length; i < l; i++) {\r
667                                         if (i >= items.length || base[i] != items[i]) {\r
668                                                 bp = i + 1;\r
669                                                 break;\r
670                                         }\r
671                                 }\r
672                         }\r
673 \r
674                         if (base.length < items.length) {\r
675                                 for (i = 0, l = items.length; i < l; i++) {\r
676                                         if (i >= base.length || base[i] != items[i]) {\r
677                                                 bp = i + 1;\r
678                                                 break;\r
679                                         }\r
680                                 }\r
681                         }\r
682 \r
683                         if (bp == 1)\r
684                                 return path;\r
685 \r
686                         for (i = 0, l = base.length - (bp - 1); i < l; i++)\r
687                                 out += "../";\r
688 \r
689                         for (i = bp - 1, l = items.length; i < l; i++) {\r
690                                 if (i != bp - 1)\r
691                                         out += "/" + items[i];\r
692                                 else\r
693                                         out += items[i];\r
694                         }\r
695 \r
696                         return out;\r
697                 },\r
698 \r
699                 toAbsPath : function(base, path) {\r
700                         var i, nb = 0, o = [], tr, outPath;\r
701 \r
702                         // Split paths\r
703                         tr = /\/$/.test(path) ? '/' : '';\r
704                         base = base.split('/');\r
705                         path = path.split('/');\r
706 \r
707                         // Remove empty chunks\r
708                         each(base, function(k) {\r
709                                 if (k)\r
710                                         o.push(k);\r
711                         });\r
712 \r
713                         base = o;\r
714 \r
715                         // Merge relURLParts chunks\r
716                         for (i = path.length - 1, o = []; i >= 0; i--) {\r
717                                 // Ignore empty or .\r
718                                 if (path[i].length == 0 || path[i] == ".")\r
719                                         continue;\r
720 \r
721                                 // Is parent\r
722                                 if (path[i] == '..') {\r
723                                         nb++;\r
724                                         continue;\r
725                                 }\r
726 \r
727                                 // Move up\r
728                                 if (nb > 0) {\r
729                                         nb--;\r
730                                         continue;\r
731                                 }\r
732 \r
733                                 o.push(path[i]);\r
734                         }\r
735 \r
736                         i = base.length - nb;\r
737 \r
738                         // If /a/b/c or /\r
739                         if (i <= 0)\r
740                                 outPath = o.reverse().join('/');\r
741                         else\r
742                                 outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');\r
743 \r
744                         // Add front / if it's needed\r
745                         if (outPath.indexOf('/') !== 0)\r
746                                 outPath = '/' + outPath;\r
747 \r
748                         // Add traling / if it's needed\r
749                         if (tr && outPath.lastIndexOf('/') !== outPath.length - 1)\r
750                                 outPath += tr;\r
751 \r
752                         return outPath;\r
753                 },\r
754 \r
755                 getURI : function(nh) {\r
756                         var s, t = this;\r
757 \r
758                         // Rebuild source\r
759                         if (!t.source || nh) {\r
760                                 s = '';\r
761 \r
762                                 if (!nh) {\r
763                                         if (t.protocol)\r
764                                                 s += t.protocol + '://';\r
765 \r
766                                         if (t.userInfo)\r
767                                                 s += t.userInfo + '@';\r
768 \r
769                                         if (t.host)\r
770                                                 s += t.host;\r
771 \r
772                                         if (t.port)\r
773                                                 s += ':' + t.port;\r
774                                 }\r
775 \r
776                                 if (t.path)\r
777                                         s += t.path;\r
778 \r
779                                 if (t.query)\r
780                                         s += '?' + t.query;\r
781 \r
782                                 if (t.anchor)\r
783                                         s += '#' + t.anchor;\r
784 \r
785                                 t.source = s;\r
786                         }\r
787 \r
788                         return t.source;\r
789                 }\r
790         });\r
791 })();\r
792 \r
793 (function() {\r
794         var each = tinymce.each;\r
795 \r
796         tinymce.create('static tinymce.util.Cookie', {\r
797                 getHash : function(n) {\r
798                         var v = this.get(n), h;\r
799 \r
800                         if (v) {\r
801                                 each(v.split('&'), function(v) {\r
802                                         v = v.split('=');\r
803                                         h = h || {};\r
804                                         h[unescape(v[0])] = unescape(v[1]);\r
805                                 });\r
806                         }\r
807 \r
808                         return h;\r
809                 },\r
810 \r
811                 setHash : function(n, v, e, p, d, s) {\r
812                         var o = '';\r
813 \r
814                         each(v, function(v, k) {\r
815                                 o += (!o ? '' : '&') + escape(k) + '=' + escape(v);\r
816                         });\r
817 \r
818                         this.set(n, o, e, p, d, s);\r
819                 },\r
820 \r
821                 get : function(n) {\r
822                         var c = document.cookie, e, p = n + "=", b;\r
823 \r
824                         // Strict mode\r
825                         if (!c)\r
826                                 return;\r
827 \r
828                         b = c.indexOf("; " + p);\r
829 \r
830                         if (b == -1) {\r
831                                 b = c.indexOf(p);\r
832 \r
833                                 if (b != 0)\r
834                                         return null;\r
835                         } else\r
836                                 b += 2;\r
837 \r
838                         e = c.indexOf(";", b);\r
839 \r
840                         if (e == -1)\r
841                                 e = c.length;\r
842 \r
843                         return unescape(c.substring(b + p.length, e));\r
844                 },\r
845 \r
846                 set : function(n, v, e, p, d, s) {\r
847                         document.cookie = n + "=" + escape(v) +\r
848                                 ((e) ? "; expires=" + e.toGMTString() : "") +\r
849                                 ((p) ? "; path=" + escape(p) : "") +\r
850                                 ((d) ? "; domain=" + d : "") +\r
851                                 ((s) ? "; secure" : "");\r
852                 },\r
853 \r
854                 remove : function(n, p) {\r
855                         var d = new Date();\r
856 \r
857                         d.setTime(d.getTime() - 1000);\r
858 \r
859                         this.set(n, '', d, p, d);\r
860                 }\r
861         });\r
862 })();\r
863 \r
864 (function() {\r
865         function serialize(o, quote) {\r
866                 var i, v, t;\r
867 \r
868                 quote = quote || '"';\r
869 \r
870                 if (o == null)\r
871                         return 'null';\r
872 \r
873                 t = typeof o;\r
874 \r
875                 if (t == 'string') {\r
876                         v = '\bb\tt\nn\ff\rr\""\'\'\\\\';\r
877 \r
878                         return quote + o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g, function(a, b) {\r
879                                 // Make sure single quotes never get encoded inside double quotes for JSON compatibility\r
880                                 if (quote === '"' && a === "'")\r
881                                         return a;\r
882 \r
883                                 i = v.indexOf(b);\r
884 \r
885                                 if (i + 1)\r
886                                         return '\\' + v.charAt(i + 1);\r
887 \r
888                                 a = b.charCodeAt().toString(16);\r
889 \r
890                                 return '\\u' + '0000'.substring(a.length) + a;\r
891                         }) + quote;\r
892                 }\r
893 \r
894                 if (t == 'object') {\r
895                         if (o.hasOwnProperty && o instanceof Array) {\r
896                                         for (i=0, v = '['; i<o.length; i++)\r
897                                                 v += (i > 0 ? ',' : '') + serialize(o[i], quote);\r
898 \r
899                                         return v + ']';\r
900                                 }\r
901 \r
902                                 v = '{';\r
903 \r
904                                 for (i in o) {\r
905                                         if (o.hasOwnProperty(i)) {\r
906                                                 v += typeof o[i] != 'function' ? (v.length > 1 ? ',' + quote : quote) + i + quote +':' + serialize(o[i], quote) : '';\r
907                                         }\r
908                                 }\r
909 \r
910                                 return v + '}';\r
911                 }\r
912 \r
913                 return '' + o;\r
914         };\r
915 \r
916         tinymce.util.JSON = {\r
917                 serialize: serialize,\r
918 \r
919                 parse: function(s) {\r
920                         try {\r
921                                 return eval('(' + s + ')');\r
922                         } catch (ex) {\r
923                                 // Ignore\r
924                         }\r
925                 }\r
926 \r
927                 };\r
928 })();\r
929 \r
930 tinymce.create('static tinymce.util.XHR', {\r
931         send : function(o) {\r
932                 var x, t, w = window, c = 0;\r
933 \r
934                 // Default settings\r
935                 o.scope = o.scope || this;\r
936                 o.success_scope = o.success_scope || o.scope;\r
937                 o.error_scope = o.error_scope || o.scope;\r
938                 o.async = o.async === false ? false : true;\r
939                 o.data = o.data || '';\r
940 \r
941                 function get(s) {\r
942                         x = 0;\r
943 \r
944                         try {\r
945                                 x = new ActiveXObject(s);\r
946                         } catch (ex) {\r
947                         }\r
948 \r
949                         return x;\r
950                 };\r
951 \r
952                 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');\r
953 \r
954                 if (x) {\r
955                         if (x.overrideMimeType)\r
956                                 x.overrideMimeType(o.content_type);\r
957 \r
958                         x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);\r
959 \r
960                         if (o.content_type)\r
961                                 x.setRequestHeader('Content-Type', o.content_type);\r
962 \r
963                         x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\r
964 \r
965                         x.send(o.data);\r
966 \r
967                         function ready() {\r
968                                 if (!o.async || x.readyState == 4 || c++ > 10000) {\r
969                                         if (o.success && c < 10000 && x.status == 200)\r
970                                                 o.success.call(o.success_scope, '' + x.responseText, x, o);\r
971                                         else if (o.error)\r
972                                                 o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);\r
973 \r
974                                         x = null;\r
975                                 } else\r
976                                         w.setTimeout(ready, 10);\r
977                         };\r
978 \r
979                         // Syncronous request\r
980                         if (!o.async)\r
981                                 return ready();\r
982 \r
983                         // Wait for response, onReadyStateChange can not be used since it leaks memory in IE\r
984                         t = w.setTimeout(ready, 10);\r
985                 }\r
986         }\r
987 });\r
988 \r
989 (function() {\r
990         var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;\r
991 \r
992         tinymce.create('tinymce.util.JSONRequest', {\r
993                 JSONRequest : function(s) {\r
994                         this.settings = extend({\r
995                         }, s);\r
996                         this.count = 0;\r
997                 },\r
998 \r
999                 send : function(o) {\r
1000                         var ecb = o.error, scb = o.success;\r
1001 \r
1002                         o = extend(this.settings, o);\r
1003 \r
1004                         o.success = function(c, x) {\r
1005                                 c = JSON.parse(c);\r
1006 \r
1007                                 if (typeof(c) == 'undefined') {\r
1008                                         c = {\r
1009                                                 error : 'JSON Parse error.'\r
1010                                         };\r
1011                                 }\r
1012 \r
1013                                 if (c.error)\r
1014                                         ecb.call(o.error_scope || o.scope, c.error, x);\r
1015                                 else\r
1016                                         scb.call(o.success_scope || o.scope, c.result);\r
1017                         };\r
1018 \r
1019                         o.error = function(ty, x) {\r
1020                                 if (ecb)\r
1021                                         ecb.call(o.error_scope || o.scope, ty, x);\r
1022                         };\r
1023 \r
1024                         o.data = JSON.serialize({\r
1025                                 id : o.id || 'c' + (this.count++),\r
1026                                 method : o.method,\r
1027                                 params : o.params\r
1028                         });\r
1029 \r
1030                         // JSON content type for Ruby on rails. Bug: #1883287\r
1031                         o.content_type = 'application/json';\r
1032 \r
1033                         XHR.send(o);\r
1034                 },\r
1035 \r
1036                 'static' : {\r
1037                         sendRPC : function(o) {\r
1038                                 return new tinymce.util.JSONRequest().send(o);\r
1039                         }\r
1040                 }\r
1041         });\r
1042 }());\r
1043 (function(tinymce){\r
1044         tinymce.VK = {\r
1045                 BACKSPACE: 8,\r
1046                 DELETE: 46,\r
1047                 DOWN: 40,\r
1048                 ENTER: 13,\r
1049                 LEFT: 37,\r
1050                 RIGHT: 39,\r
1051                 SPACEBAR: 32,\r
1052                 TAB: 9,\r
1053                 UP: 38,\r
1054 \r
1055                 modifierPressed: function (e) {\r
1056                         return e.shiftKey || e.ctrlKey || e.altKey;\r
1057                 }\r
1058         }\r
1059 })(tinymce);\r
1060 \r
1061 (function(tinymce) {\r
1062         var VK = tinymce.VK, BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE;\r
1063 \r
1064         function setEditorCommandState(editor, cmd, state) {\r
1065                 try {\r
1066                         editor.getDoc().execCommand(cmd, false, state);\r
1067                 } catch (ex) {\r
1068                         // Ignore\r
1069                 }\r
1070         }\r
1071 \r
1072         function cleanupStylesWhenDeleting(ed) {\r
1073                 var dom = ed.dom, selection = ed.selection;\r
1074 \r
1075                 ed.onKeyDown.add(function(ed, e) {\r
1076                         var rng, blockElm, node, clonedSpan, isDelete;\r
1077 \r
1078                         if (e.isDefaultPrevented()) {\r
1079                                 return;\r
1080                         }\r
1081 \r
1082                         isDelete = e.keyCode == DELETE;\r
1083                         if ((isDelete || e.keyCode == BACKSPACE) && !VK.modifierPressed(e)) {\r
1084                                 e.preventDefault();\r
1085                                 rng = selection.getRng();\r
1086 \r
1087                                 // Find root block\r
1088                                 blockElm = dom.getParent(rng.startContainer, dom.isBlock);\r
1089 \r
1090                                 // On delete clone the root span of the next block element\r
1091                                 if (isDelete)\r
1092                                         blockElm = dom.getNext(blockElm, dom.isBlock);\r
1093 \r
1094                                 // Locate root span element and clone it since it would otherwise get merged by the "apple-style-span" on delete/backspace\r
1095                                 if (blockElm) {\r
1096                                         node = blockElm.firstChild;\r
1097 \r
1098                                         // Ignore empty text nodes\r
1099                                         while (node && node.nodeType == 3 && node.nodeValue.length == 0)\r
1100                                                 node = node.nextSibling;\r
1101 \r
1102                                         if (node && node.nodeName === 'SPAN') {\r
1103                                                 clonedSpan = node.cloneNode(false);\r
1104                                         }\r
1105                                 }\r
1106 \r
1107                                 // Do the backspace/delete action\r
1108                                 ed.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);\r
1109 \r
1110                                 // Find all odd apple-style-spans\r
1111                                 blockElm = dom.getParent(rng.startContainer, dom.isBlock);\r
1112                                 tinymce.each(dom.select('span.Apple-style-span,font.Apple-style-span', blockElm), function(span) {\r
1113                                         var bm = selection.getBookmark();\r
1114 \r
1115                                         if (clonedSpan) {\r
1116                                                 dom.replace(clonedSpan.cloneNode(false), span, true);\r
1117                                         } else {\r
1118                                                 dom.remove(span, true);\r
1119                                         }\r
1120 \r
1121                                         // Restore the selection\r
1122                                         selection.moveToBookmark(bm);\r
1123                                 });\r
1124                         }\r
1125                 });\r
1126         };\r
1127 \r
1128         function emptyEditorWhenDeleting(ed) {\r
1129                 function serializeRng(rng) {\r
1130                         var body = ed.dom.create("body");\r
1131                         var contents = rng.cloneContents();\r
1132                         body.appendChild(contents);\r
1133                         return ed.selection.serializer.serialize(body, {format: 'html'});\r
1134                 }\r
1135 \r
1136                 function allContentsSelected(rng) {\r
1137                         var selection = serializeRng(rng);\r
1138 \r
1139                         var allRng = ed.dom.createRng();\r
1140                         allRng.selectNode(ed.getBody());\r
1141 \r
1142                         var allSelection = serializeRng(allRng);\r
1143                         return selection === allSelection;\r
1144                 }\r
1145 \r
1146                 ed.onKeyDown.addToTop(function(ed, e) {\r
1147                         var keyCode = e.keyCode;\r
1148                         if (keyCode == DELETE || keyCode == BACKSPACE) {\r
1149                                 var rng = ed.selection.getRng(true);\r
1150                                 if (!rng.collapsed && allContentsSelected(rng)) {\r
1151                                         ed.setContent('', {format : 'raw'});\r
1152                                         ed.nodeChanged();\r
1153                                         e.preventDefault();\r
1154                                 }\r
1155                         }\r
1156                 });\r
1157         };\r
1158 \r
1159         function inputMethodFocus(ed) {\r
1160                 ed.dom.bind(ed.getDoc(), 'focusin', function() {\r
1161                         ed.selection.setRng(ed.selection.getRng());\r
1162                 });\r
1163         };\r
1164 \r
1165         function removeHrOnBackspace(ed) {\r
1166                 ed.onKeyDown.add(function(ed, e) {\r
1167                         if (e.keyCode === BACKSPACE) {\r
1168                                 if (ed.selection.isCollapsed() && ed.selection.getRng(true).startOffset === 0) {\r
1169                                         var node = ed.selection.getNode();\r
1170                                         var previousSibling = node.previousSibling;\r
1171                                         if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") {\r
1172                                                 ed.dom.remove(previousSibling);\r
1173                                                 tinymce.dom.Event.cancel(e);\r
1174                                         }\r
1175                                 }\r
1176                         }\r
1177                 })\r
1178         }\r
1179 \r
1180         function focusBody(ed) {\r
1181                 // Fix for a focus bug in FF 3.x where the body element\r
1182                 // wouldn't get proper focus if the user clicked on the HTML element\r
1183                 if (!Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4\r
1184                         ed.onMouseDown.add(function(ed, e) {\r
1185                                 if (e.target.nodeName === "HTML") {\r
1186                                         var body = ed.getBody();\r
1187 \r
1188                                         // Blur the body it's focused but not correctly focused\r
1189                                         body.blur();\r
1190 \r
1191                                         // Refocus the body after a little while\r
1192                                         setTimeout(function() {\r
1193                                                 body.focus();\r
1194                                         }, 0);\r
1195                                 }\r
1196                         });\r
1197                 }\r
1198         };\r
1199 \r
1200         function selectControlElements(ed) {\r
1201                 ed.onClick.add(function(ed, e) {\r
1202                         e = e.target;\r
1203 \r
1204                         // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250\r
1205                         // WebKit can't even do simple things like selecting an image\r
1206                         // Needs tobe the setBaseAndExtend or it will fail to select floated images\r
1207                         if (/^(IMG|HR)$/.test(e.nodeName))\r
1208                                 ed.selection.getSel().setBaseAndExtent(e, 0, e, 1);\r
1209 \r
1210                         if (e.nodeName == 'A' && ed.dom.hasClass(e, 'mceItemAnchor'))\r
1211                                 ed.selection.select(e);\r
1212 \r
1213                         ed.nodeChanged();\r
1214                 });\r
1215         };\r
1216 \r
1217         function removeStylesWhenDeletingAccrossBlockElements(ed) {\r
1218                 var selection = ed.selection, dom = ed.dom;\r
1219 \r
1220                 function getAttributeApplyFunction() {\r
1221                         var template = dom.getAttribs(selection.getStart().cloneNode(false));\r
1222 \r
1223                         return function() {\r
1224                                 var target = selection.getStart();\r
1225 \r
1226                                 if (target !== ed.getBody()) {\r
1227                                         dom.setAttrib(target, "style", null);\r
1228 \r
1229                                 tinymce.each(template, function(attr) {\r
1230                                         target.setAttributeNode(attr.cloneNode(true));\r
1231                                 });\r
1232                                 }\r
1233                         };\r
1234                 }\r
1235 \r
1236                 function isSelectionAcrossElements() {\r
1237                         return !selection.isCollapsed() && selection.getStart() != selection.getEnd();\r
1238                 }\r
1239 \r
1240                 function blockEvent(ed, e) {\r
1241                         e.preventDefault();\r
1242                         return false;\r
1243                 }\r
1244 \r
1245                 ed.onKeyPress.add(function(ed, e) {\r
1246                         var applyAttributes;\r
1247 \r
1248                         if ((e.keyCode == 8 || e.keyCode == 46) && isSelectionAcrossElements()) {\r
1249                                 applyAttributes = getAttributeApplyFunction();\r
1250                                 ed.getDoc().execCommand('delete', false, null);\r
1251                                 applyAttributes();\r
1252                                 e.preventDefault();\r
1253                                 return false;\r
1254                         }\r
1255                 });\r
1256 \r
1257                 dom.bind(ed.getDoc(), 'cut', function(e) {\r
1258                         var applyAttributes;\r
1259 \r
1260                         if (isSelectionAcrossElements()) {\r
1261                                 applyAttributes = getAttributeApplyFunction();\r
1262                                 ed.onKeyUp.addToTop(blockEvent);\r
1263 \r
1264                                 setTimeout(function() {\r
1265                                         applyAttributes();\r
1266                                         ed.onKeyUp.remove(blockEvent);\r
1267                                 }, 0);\r
1268                         }\r
1269                 });\r
1270         }\r
1271         \r
1272         /*\r
1273         function removeStylesOnPTagsInheritedFromHeadingTag(ed) {\r
1274                 ed.onKeyDown.add(function(ed, event) {\r
1275                         function checkInHeadingTag(ed) {\r
1276                                 var currentNode = ed.selection.getNode();\r
1277                                 var headingTags = 'h1,h2,h3,h4,h5,h6';\r
1278                                 return ed.dom.is(currentNode, headingTags) || ed.dom.getParent(currentNode, headingTags) !== null;\r
1279                         }\r
1280 \r
1281                         if (event.keyCode === VK.ENTER && !VK.modifierPressed(event) && checkInHeadingTag(ed)) {\r
1282                                 setTimeout(function() {\r
1283                                         var currentNode = ed.selection.getNode();\r
1284                                         if (ed.dom.is(currentNode, 'p')) {\r
1285                                                 ed.dom.setAttrib(currentNode, 'style', null);\r
1286                                                 // While tiny's content is correct after this method call, the content shown is not representative of it and needs to be 'repainted'\r
1287                                                 ed.execCommand('mceCleanup');\r
1288                                         }\r
1289                                 }, 0);\r
1290                         }\r
1291                 });\r
1292         }\r
1293         */\r
1294 \r
1295         function selectionChangeNodeChanged(ed) {\r
1296                 var lastRng, selectionTimer;\r
1297 \r
1298                 ed.dom.bind(ed.getDoc(), 'selectionchange', function() {\r
1299                         if (selectionTimer) {\r
1300                                 clearTimeout(selectionTimer);\r
1301                                 selectionTimer = 0;\r
1302                         }\r
1303 \r
1304                         selectionTimer = window.setTimeout(function() {\r
1305                                 var rng = ed.selection.getRng();\r
1306 \r
1307                                 // Compare the ranges to see if it was a real change or not\r
1308                                 if (!lastRng || !tinymce.dom.RangeUtils.compareRanges(rng, lastRng)) {\r
1309                                         ed.nodeChanged();\r
1310                                         lastRng = rng;\r
1311                                 }\r
1312                         }, 50);\r
1313                 });\r
1314         }\r
1315 \r
1316         function ensureBodyHasRoleApplication(ed) {\r
1317                 document.body.setAttribute("role", "application");\r
1318         }\r
1319         \r
1320         function disableBackspaceIntoATable(ed) {\r
1321                 ed.onKeyDown.add(function(ed, e) {\r
1322                         if (e.keyCode === BACKSPACE) {\r
1323                                 if (ed.selection.isCollapsed() && ed.selection.getRng(true).startOffset === 0) {\r
1324                                         var previousSibling = ed.selection.getNode().previousSibling;\r
1325                                         if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "table") {\r
1326                                                 return tinymce.dom.Event.cancel(e);\r
1327                                         }\r
1328                                 }\r
1329                         }\r
1330                 })\r
1331         }\r
1332 \r
1333         function addNewLinesBeforeBrInPre(editor) {\r
1334                 var documentMode = editor.getDoc().documentMode;\r
1335 \r
1336                 // IE8+ rendering mode does the right thing with BR in PRE\r
1337                 if (documentMode && documentMode > 7) {\r
1338                         return;\r
1339                 }\r
1340 \r
1341                  // Enable display: none in area and add a specific class that hides all BR elements in PRE to\r
1342                  // avoid the caret from getting stuck at the BR elements while pressing the right arrow key\r
1343                 setEditorCommandState(editor, 'RespectVisibilityInDesign', true);\r
1344                 editor.dom.addClass(editor.getBody(), 'mceHideBrInPre');\r
1345 \r
1346                 // Adds a \n before all BR elements in PRE to get them visual\r
1347                 editor.parser.addNodeFilter('pre', function(nodes, name) {\r
1348                         var i = nodes.length, brNodes, j, brElm, sibling;\r
1349 \r
1350                         while (i--) {\r
1351                                 brNodes = nodes[i].getAll('br');\r
1352                                 j = brNodes.length;\r
1353                                 while (j--) {\r
1354                                         brElm = brNodes[j];\r
1355 \r
1356                                         // Add \n before BR in PRE elements on older IE:s so the new lines get rendered\r
1357                                         sibling = brElm.prev;\r
1358                                         if (sibling && sibling.type === 3 && sibling.value.charAt(sibling.value - 1) != '\n') {\r
1359                                                 sibling.value += '\n';\r
1360                                         } else {\r
1361                                                 brElm.parent.insert(new tinymce.html.Node('#text', 3), brElm, true).value = '\n';\r
1362                                         }\r
1363                                 }\r
1364                         }\r
1365                 });\r
1366 \r
1367                 // Removes any \n before BR elements in PRE since other browsers and in contentEditable=false mode they will be visible\r
1368                 editor.serializer.addNodeFilter('pre', function(nodes, name) {\r
1369                         var i = nodes.length, brNodes, j, brElm, sibling;\r
1370 \r
1371                         while (i--) {\r
1372                                 brNodes = nodes[i].getAll('br');\r
1373                                 j = brNodes.length;\r
1374                                 while (j--) {\r
1375                                         brElm = brNodes[j];\r
1376                                         sibling = brElm.prev;\r
1377                                         if (sibling && sibling.type == 3) {\r
1378                                                 sibling.value = sibling.value.replace(/\r?\n$/, '');\r
1379                                         }\r
1380                                 }\r
1381                         }\r
1382                 });\r
1383         }\r
1384 \r
1385         tinymce.create('tinymce.util.Quirks', {\r
1386                 Quirks: function(ed) {\r
1387                         // All browsers\r
1388                         disableBackspaceIntoATable(ed);\r
1389 \r
1390                         // WebKit\r
1391                         if (tinymce.isWebKit) {\r
1392                                 cleanupStylesWhenDeleting(ed);\r
1393                                 emptyEditorWhenDeleting(ed);\r
1394                                 inputMethodFocus(ed);\r
1395                                 selectControlElements(ed);\r
1396 \r
1397                                 // iOS\r
1398                                 if (tinymce.isIDevice) {\r
1399                                         selectionChangeNodeChanged(ed);\r
1400                                 }\r
1401                         }\r
1402 \r
1403                         // IE\r
1404                         if (tinymce.isIE) {\r
1405                                 removeHrOnBackspace(ed);\r
1406                                 emptyEditorWhenDeleting(ed);\r
1407                                 ensureBodyHasRoleApplication(ed);\r
1408                                 //removeStylesOnPTagsInheritedFromHeadingTag(ed)\r
1409                                 addNewLinesBeforeBrInPre(ed);\r
1410                         }\r
1411 \r
1412                         // Gecko\r
1413                         if (tinymce.isGecko) {\r
1414                                 removeHrOnBackspace(ed);\r
1415                                 focusBody(ed);\r
1416                                 removeStylesWhenDeletingAccrossBlockElements(ed);\r
1417                         }\r
1418                 }\r
1419         });\r
1420 })(tinymce);\r
1421 \r
1422 (function(tinymce) {\r
1423         var namedEntities, baseEntities, reverseEntities,\r
1424                 attrsCharsRegExp = /[&<>\"\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,\r
1425                 textCharsRegExp = /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,\r
1426                 rawCharsRegExp = /[<>&\"\']/g,\r
1427                 entityRegExp = /&(#x|#)?([\w]+);/g,\r
1428                 asciiMap = {\r
1429                                 128 : "\u20AC", 130 : "\u201A", 131 : "\u0192", 132 : "\u201E", 133 : "\u2026", 134 : "\u2020",\r
1430                                 135 : "\u2021", 136 : "\u02C6", 137 : "\u2030", 138 : "\u0160", 139 : "\u2039", 140 : "\u0152",\r
1431                                 142 : "\u017D", 145 : "\u2018", 146 : "\u2019", 147 : "\u201C", 148 : "\u201D", 149 : "\u2022",\r
1432                                 150 : "\u2013", 151 : "\u2014", 152 : "\u02DC", 153 : "\u2122", 154 : "\u0161", 155 : "\u203A",\r
1433                                 156 : "\u0153", 158 : "\u017E", 159 : "\u0178"\r
1434                 };\r
1435 \r
1436         // Raw entities\r
1437         baseEntities = {\r
1438                 '\"' : '&quot;', // Needs to be escaped since the YUI compressor would otherwise break the code\r
1439                 "'" : '&#39;',\r
1440                 '<' : '&lt;',\r
1441                 '>' : '&gt;',\r
1442                 '&' : '&amp;'\r
1443         };\r
1444 \r
1445         // Reverse lookup table for raw entities\r
1446         reverseEntities = {\r
1447                 '&lt;' : '<',\r
1448                 '&gt;' : '>',\r
1449                 '&amp;' : '&',\r
1450                 '&quot;' : '"',\r
1451                 '&apos;' : "'"\r
1452         };\r
1453 \r
1454         // Decodes text by using the browser\r
1455         function nativeDecode(text) {\r
1456                 var elm;\r
1457 \r
1458                 elm = document.createElement("div");\r
1459                 elm.innerHTML = text;\r
1460 \r
1461                 return elm.textContent || elm.innerText || text;\r
1462         };\r
1463 \r
1464         // Build a two way lookup table for the entities\r
1465         function buildEntitiesLookup(items, radix) {\r
1466                 var i, chr, entity, lookup = {};\r
1467 \r
1468                 if (items) {\r
1469                         items = items.split(',');\r
1470                         radix = radix || 10;\r
1471 \r
1472                         // Build entities lookup table\r
1473                         for (i = 0; i < items.length; i += 2) {\r
1474                                 chr = String.fromCharCode(parseInt(items[i], radix));\r
1475 \r
1476                                 // Only add non base entities\r
1477                                 if (!baseEntities[chr]) {\r
1478                                         entity = '&' + items[i + 1] + ';';\r
1479                                         lookup[chr] = entity;\r
1480                                         lookup[entity] = chr;\r
1481                                 }\r
1482                         }\r
1483 \r
1484                         return lookup;\r
1485                 }\r
1486         };\r
1487 \r
1488         // Unpack entities lookup where the numbers are in radix 32 to reduce the size\r
1489         namedEntities = buildEntitiesLookup(\r
1490                 '50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' +\r
1491                 '5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' +\r
1492                 '5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' +\r
1493                 '5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' +\r
1494                 '68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' +\r
1495                 '6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' +\r
1496                 '6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' +\r
1497                 '75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' +\r
1498                 '7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' +\r
1499                 '7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' +\r
1500                 'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' +\r
1501                 'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' +\r
1502                 't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' +\r
1503                 'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' +\r
1504                 'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' +\r
1505                 '81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' +\r
1506                 '8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' +\r
1507                 '8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' +\r
1508                 '8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' +\r
1509                 '8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' +\r
1510                 'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' +\r
1511                 'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' +\r
1512                 'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' +\r
1513                 '80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' +\r
1514                 '811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro'\r
1515         , 32);\r
1516 \r
1517         tinymce.html = tinymce.html || {};\r
1518 \r
1519         tinymce.html.Entities = {\r
1520                 encodeRaw : function(text, attr) {\r
1521                         return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {\r
1522                                 return baseEntities[chr] || chr;\r
1523                         });\r
1524                 },\r
1525 \r
1526                 encodeAllRaw : function(text) {\r
1527                         return ('' + text).replace(rawCharsRegExp, function(chr) {\r
1528                                 return baseEntities[chr] || chr;\r
1529                         });\r
1530                 },\r
1531 \r
1532                 encodeNumeric : function(text, attr) {\r
1533                         return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {\r
1534                                 // Multi byte sequence convert it to a single entity\r
1535                                 if (chr.length > 1)\r
1536                                         return '&#' + (((chr.charCodeAt(0) - 0xD800) * 0x400) + (chr.charCodeAt(1) - 0xDC00) + 0x10000) + ';';\r
1537 \r
1538                                 return baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';\r
1539                         });\r
1540                 },\r
1541 \r
1542                 encodeNamed : function(text, attr, entities) {\r
1543                         entities = entities || namedEntities;\r
1544 \r
1545                         return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {\r
1546                                 return baseEntities[chr] || entities[chr] || chr;\r
1547                         });\r
1548                 },\r
1549 \r
1550                 getEncodeFunc : function(name, entities) {\r
1551                         var Entities = tinymce.html.Entities;\r
1552 \r
1553                         entities = buildEntitiesLookup(entities) || namedEntities;\r
1554 \r
1555                         function encodeNamedAndNumeric(text, attr) {\r
1556                                 return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {\r
1557                                         return baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;\r
1558                                 });\r
1559                         };\r
1560 \r
1561                         function encodeCustomNamed(text, attr) {\r
1562                                 return Entities.encodeNamed(text, attr, entities);\r
1563                         };\r
1564 \r
1565                         // Replace + with , to be compatible with previous TinyMCE versions\r
1566                         name = tinymce.makeMap(name.replace(/\+/g, ','));\r
1567 \r
1568                         // Named and numeric encoder\r
1569                         if (name.named && name.numeric)\r
1570                                 return encodeNamedAndNumeric;\r
1571 \r
1572                         // Named encoder\r
1573                         if (name.named) {\r
1574                                 // Custom names\r
1575                                 if (entities)\r
1576                                         return encodeCustomNamed;\r
1577 \r
1578                                 return Entities.encodeNamed;\r
1579                         }\r
1580 \r
1581                         // Numeric\r
1582                         if (name.numeric)\r
1583                                 return Entities.encodeNumeric;\r
1584 \r
1585                         // Raw encoder\r
1586                         return Entities.encodeRaw;\r
1587                 },\r
1588 \r
1589                 decode : function(text) {\r
1590                         return text.replace(entityRegExp, function(all, numeric, value) {\r
1591                                 if (numeric) {\r
1592                                         value = parseInt(value, numeric.length === 2 ? 16 : 10);\r
1593 \r
1594                                         // Support upper UTF\r
1595                                         if (value > 0xFFFF) {\r
1596                                                 value -= 0x10000;\r
1597 \r
1598                                                 return String.fromCharCode(0xD800 + (value >> 10), 0xDC00 + (value & 0x3FF));\r
1599                                         } else\r
1600                                                 return asciiMap[value] || String.fromCharCode(value);\r
1601                                 }\r
1602 \r
1603                                 return reverseEntities[all] || namedEntities[all] || nativeDecode(all);\r
1604                         });\r
1605                 }\r
1606         };\r
1607 })(tinymce);\r
1608 \r
1609 tinymce.html.Styles = function(settings, schema) {\r
1610         var rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,\r
1611                 urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,\r
1612                 styleRegExp = /\s*([^:]+):\s*([^;]+);?/g,\r
1613                 trimRightRegExp = /\s+$/,\r
1614                 urlColorRegExp = /rgb/,\r
1615                 undef, i, encodingLookup = {}, encodingItems;\r
1616 \r
1617         settings = settings || {};\r
1618 \r
1619         encodingItems = '\\" \\\' \\; \\: ; : \uFEFF'.split(' ');\r
1620         for (i = 0; i < encodingItems.length; i++) {\r
1621                 encodingLookup[encodingItems[i]] = '\uFEFF' + i;\r
1622                 encodingLookup['\uFEFF' + i] = encodingItems[i];\r
1623         }\r
1624 \r
1625         function toHex(match, r, g, b) {\r
1626                 function hex(val) {\r
1627                         val = parseInt(val).toString(16);\r
1628 \r
1629                         return val.length > 1 ? val : '0' + val; // 0 -> 00\r
1630                 };\r
1631 \r
1632                 return '#' + hex(r) + hex(g) + hex(b);\r
1633         };\r
1634 \r
1635         return {\r
1636                 toHex : function(color) {\r
1637                         return color.replace(rgbRegExp, toHex);\r
1638                 },\r
1639 \r
1640                 parse : function(css) {\r
1641                         var styles = {}, matches, name, value, isEncoded, urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope || this;\r
1642 \r
1643                         function compress(prefix, suffix) {\r
1644                                 var top, right, bottom, left;\r
1645 \r
1646                                 // Get values and check it it needs compressing\r
1647                                 top = styles[prefix + '-top' + suffix];\r
1648                                 if (!top)\r
1649                                         return;\r
1650 \r
1651                                 right = styles[prefix + '-right' + suffix];\r
1652                                 if (top != right)\r
1653                                         return;\r
1654 \r
1655                                 bottom = styles[prefix + '-bottom' + suffix];\r
1656                                 if (right != bottom)\r
1657                                         return;\r
1658 \r
1659                                 left = styles[prefix + '-left' + suffix];\r
1660                                 if (bottom != left)\r
1661                                         return;\r
1662 \r
1663                                 // Compress\r
1664                                 styles[prefix + suffix] = left;\r
1665                                 delete styles[prefix + '-top' + suffix];\r
1666                                 delete styles[prefix + '-right' + suffix];\r
1667                                 delete styles[prefix + '-bottom' + suffix];\r
1668                                 delete styles[prefix + '-left' + suffix];\r
1669                         };\r
1670 \r
1671                         function canCompress(key) {\r
1672                                 var value = styles[key], i;\r
1673 \r
1674                                 if (!value || value.indexOf(' ') < 0)\r
1675                                         return;\r
1676 \r
1677                                 value = value.split(' ');\r
1678                                 i = value.length;\r
1679                                 while (i--) {\r
1680                                         if (value[i] !== value[0])\r
1681                                                 return false;\r
1682                                 }\r
1683 \r
1684                                 styles[key] = value[0];\r
1685 \r
1686                                 return true;\r
1687                         };\r
1688 \r
1689                         function compress2(target, a, b, c) {\r
1690                                 if (!canCompress(a))\r
1691                                         return;\r
1692 \r
1693                                 if (!canCompress(b))\r
1694                                         return;\r
1695 \r
1696                                 if (!canCompress(c))\r
1697                                         return;\r
1698 \r
1699                                 // Compress\r
1700                                 styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c];\r
1701                                 delete styles[a];\r
1702                                 delete styles[b];\r
1703                                 delete styles[c];\r
1704                         };\r
1705 \r
1706                         // Encodes the specified string by replacing all \" \' ; : with _<num>\r
1707                         function encode(str) {\r
1708                                 isEncoded = true;\r
1709 \r
1710                                 return encodingLookup[str];\r
1711                         };\r
1712 \r
1713                         // Decodes the specified string by replacing all _<num> with it's original value \" \' etc\r
1714                         // It will also decode the \" \' if keep_slashes is set to fale or omitted\r
1715                         function decode(str, keep_slashes) {\r
1716                                 if (isEncoded) {\r
1717                                         str = str.replace(/\uFEFF[0-9]/g, function(str) {\r
1718                                                 return encodingLookup[str];\r
1719                                         });\r
1720                                 }\r
1721 \r
1722                                 if (!keep_slashes)\r
1723                                         str = str.replace(/\\([\'\";:])/g, "$1");\r
1724 \r
1725                                 return str;\r
1726                         }\r
1727 \r
1728                         if (css) {\r
1729                                 // Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing\r
1730                                 css = css.replace(/\\[\"\';:\uFEFF]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, function(str) {\r
1731                                         return str.replace(/[;:]/g, encode);\r
1732                                 });\r
1733 \r
1734                                 // Parse styles\r
1735                                 while (matches = styleRegExp.exec(css)) {\r
1736                                         name = matches[1].replace(trimRightRegExp, '').toLowerCase();\r
1737                                         value = matches[2].replace(trimRightRegExp, '');\r
1738 \r
1739                                         if (name && value.length > 0) {\r
1740                                                 // Opera will produce 700 instead of bold in their style values\r
1741                                                 if (name === 'font-weight' && value === '700')\r
1742                                                         value = 'bold';\r
1743                                                 else if (name === 'color' || name === 'background-color') // Lowercase colors like RED\r
1744                                                         value = value.toLowerCase();            \r
1745 \r
1746                                                 // Convert RGB colors to HEX\r
1747                                                 value = value.replace(rgbRegExp, toHex);\r
1748 \r
1749                                                 // Convert URLs and force them into url('value') format\r
1750                                                 value = value.replace(urlOrStrRegExp, function(match, url, url2, url3, str, str2) {\r
1751                                                         str = str || str2;\r
1752 \r
1753                                                         if (str) {\r
1754                                                                 str = decode(str);\r
1755 \r
1756                                                                 // Force strings into single quote format\r
1757                                                                 return "'" + str.replace(/\'/g, "\\'") + "'";\r
1758                                                         }\r
1759 \r
1760                                                         url = decode(url || url2 || url3);\r
1761 \r
1762                                                         // Convert the URL to relative/absolute depending on config\r
1763                                                         if (urlConverter)\r
1764                                                                 url = urlConverter.call(urlConverterScope, url, 'style');\r
1765 \r
1766                                                         // Output new URL format\r
1767                                                         return "url('" + url.replace(/\'/g, "\\'") + "')";\r
1768                                                 });\r
1769 \r
1770                                                 styles[name] = isEncoded ? decode(value, true) : value;\r
1771                                         }\r
1772 \r
1773                                         styleRegExp.lastIndex = matches.index + matches[0].length;\r
1774                                 }\r
1775 \r
1776                                 // Compress the styles to reduce it's size for example IE will expand styles\r
1777                                 compress("border", "");\r
1778                                 compress("border", "-width");\r
1779                                 compress("border", "-color");\r
1780                                 compress("border", "-style");\r
1781                                 compress("padding", "");\r
1782                                 compress("margin", "");\r
1783                                 compress2('border', 'border-width', 'border-style', 'border-color');\r
1784 \r
1785                                 // Remove pointless border, IE produces these\r
1786                                 if (styles.border === 'medium none')\r
1787                                         delete styles.border;\r
1788                         }\r
1789 \r
1790                         return styles;\r
1791                 },\r
1792 \r
1793                 serialize : function(styles, element_name) {\r
1794                         var css = '', name, value;\r
1795 \r
1796                         function serializeStyles(name) {\r
1797                                 var styleList, i, l, value;\r
1798 \r
1799                                 styleList = schema.styles[name];\r
1800                                 if (styleList) {\r
1801                                         for (i = 0, l = styleList.length; i < l; i++) {\r
1802                                                 name = styleList[i];\r
1803                                                 value = styles[name];\r
1804 \r
1805                                                 if (value !== undef && value.length > 0)\r
1806                                                         css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';\r
1807                                         }\r
1808                                 }\r
1809                         };\r
1810 \r
1811                         // Serialize styles according to schema\r
1812                         if (element_name && schema && schema.styles) {\r
1813                                 // Serialize global styles and element specific styles\r
1814                                 serializeStyles('*');\r
1815                                 serializeStyles(element_name);\r
1816                         } else {\r
1817                                 // Output the styles in the order they are inside the object\r
1818                                 for (name in styles) {\r
1819                                         value = styles[name];\r
1820 \r
1821                                         if (value !== undef && value.length > 0)\r
1822                                                 css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';\r
1823                                 }\r
1824                         }\r
1825 \r
1826                         return css;\r
1827                 }\r
1828         };\r
1829 };\r
1830 \r
1831 (function(tinymce) {\r
1832         var mapCache = {}, makeMap = tinymce.makeMap, each = tinymce.each;\r
1833 \r
1834         function split(str, delim) {\r
1835                 return str.split(delim || ',');\r
1836         };\r
1837 \r
1838         function unpack(lookup, data) {\r
1839                 var key, elements = {};\r
1840 \r
1841                 function replace(value) {\r
1842                         return value.replace(/[A-Z]+/g, function(key) {\r
1843                                 return replace(lookup[key]);\r
1844                         });\r
1845                 };\r
1846 \r
1847                 // Unpack lookup\r
1848                 for (key in lookup) {\r
1849                         if (lookup.hasOwnProperty(key))\r
1850                                 lookup[key] = replace(lookup[key]);\r
1851                 }\r
1852 \r
1853                 // Unpack and parse data into object map\r
1854                 replace(data).replace(/#/g, '#text').replace(/(\w+)\[([^\]]+)\]\[([^\]]*)\]/g, function(str, name, attributes, children) {\r
1855                         attributes = split(attributes, '|');\r
1856 \r
1857                         elements[name] = {\r
1858                                 attributes : makeMap(attributes),\r
1859                                 attributesOrder : attributes,\r
1860                                 children : makeMap(children, '|', {'#comment' : {}})\r
1861                         }\r
1862                 });\r
1863 \r
1864                 return elements;\r
1865         };\r
1866 \r
1867         function getHTML5() {\r
1868                 var html5 = mapCache.html5;\r
1869 \r
1870                 if (!html5) {\r
1871                         html5 = mapCache.html5 = unpack({\r
1872                                         A : 'id|accesskey|class|dir|draggable|item|hidden|itemprop|role|spellcheck|style|subject|title',\r
1873                                         B : '#|a|abbr|area|audio|b|bdo|br|button|canvas|cite|code|command|datalist|del|dfn|em|embed|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|meta|meter|noscript|object|output|progress|q|ruby|samp|script|select|small|span|strong|sub|sup|svg|textarea|time|var|video',\r
1874                                         C : '#|a|abbr|area|address|article|aside|audio|b|bdo|blockquote|br|button|canvas|cite|code|command|datalist|del|details|dfn|dialog|div|dl|em|embed|fieldset|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|hr|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|menu|meta|meter|nav|noscript|ol|object|output|p|pre|progress|q|ruby|samp|script|section|select|small|span|strong|style|sub|sup|svg|table|textarea|time|ul|var|video'\r
1875                                 }, 'html[A|manifest][body|head]' +\r
1876                                         'head[A][base|command|link|meta|noscript|script|style|title]' +\r
1877                                         'title[A][#]' +\r
1878                                         'base[A|href|target][]' +\r
1879                                         'link[A|href|rel|media|type|sizes][]' +\r
1880                                         'meta[A|http-equiv|name|content|charset][]' +\r
1881                                         'style[A|type|media|scoped][#]' +\r
1882                                         'script[A|charset|type|src|defer|async][#]' +\r
1883                                         'noscript[A][C]' +\r
1884                                         'body[A][C]' +\r
1885                                         'section[A][C]' +\r
1886                                         'nav[A][C]' +\r
1887                                         'article[A][C]' +\r
1888                                         'aside[A][C]' +\r
1889                                         'h1[A][B]' +\r
1890                                         'h2[A][B]' +\r
1891                                         'h3[A][B]' +\r
1892                                         'h4[A][B]' +\r
1893                                         'h5[A][B]' +\r
1894                                         'h6[A][B]' +\r
1895                                         'hgroup[A][h1|h2|h3|h4|h5|h6]' +\r
1896                                         'header[A][C]' +\r
1897                                         'footer[A][C]' +\r
1898                                         'address[A][C]' +\r
1899                                         'p[A][B]' +\r
1900                                         'br[A][]' +\r
1901                                         'pre[A][B]' +\r
1902                                         'dialog[A][dd|dt]' +\r
1903                                         'blockquote[A|cite][C]' +\r
1904                                         'ol[A|start|reversed][li]' +\r
1905                                         'ul[A][li]' +\r
1906                                         'li[A|value][C]' +\r
1907                                         'dl[A][dd|dt]' +\r
1908                                         'dt[A][B]' +\r
1909                                         'dd[A][C]' +\r
1910                                         'a[A|href|target|ping|rel|media|type][C]' +\r
1911                                         'em[A][B]' +\r
1912                                         'strong[A][B]' +\r
1913                                         'small[A][B]' +\r
1914                                         'cite[A][B]' +\r
1915                                         'q[A|cite][B]' +\r
1916                                         'dfn[A][B]' +\r
1917                                         'abbr[A][B]' +\r
1918                                         'code[A][B]' +\r
1919                                         'var[A][B]' +\r
1920                                         'samp[A][B]' +\r
1921                                         'kbd[A][B]' +\r
1922                                         'sub[A][B]' +\r
1923                                         'sup[A][B]' +\r
1924                                         'i[A][B]' +\r
1925                                         'b[A][B]' +\r
1926                                         'mark[A][B]' +\r
1927                                         'progress[A|value|max][B]' +\r
1928                                         'meter[A|value|min|max|low|high|optimum][B]' +\r
1929                                         'time[A|datetime][B]' +\r
1930                                         'ruby[A][B|rt|rp]' +\r
1931                                         'rt[A][B]' +\r
1932                                         'rp[A][B]' +\r
1933                                         'bdo[A][B]' +\r
1934                                         'span[A][B]' +\r
1935                                         'ins[A|cite|datetime][B]' +\r
1936                                         'del[A|cite|datetime][B]' +\r
1937                                         'figure[A][C|legend]' +\r
1938                                         'img[A|alt|src|height|width|usemap|ismap][]' +\r
1939                                         'iframe[A|name|src|height|width|sandbox|seamless][]' +\r
1940                                         'embed[A|src|height|width|type][]' +\r
1941                                         'object[A|data|type|height|width|usemap|name|form|classid][param]' +\r
1942                                         'param[A|name|value][]' +\r
1943                                         'details[A|open][C|legend]' +\r
1944                                         'command[A|type|label|icon|disabled|checked|radiogroup][]' +\r
1945                                         'menu[A|type|label][C|li]' +\r
1946                                         'legend[A][C|B]' +\r
1947                                         'div[A][C]' +\r
1948                                         'source[A|src|type|media][]' +\r
1949                                         'audio[A|src|autobuffer|autoplay|loop|controls][source]' +\r
1950                                         'video[A|src|autobuffer|autoplay|loop|controls|width|height|poster][source]' +\r
1951                                         'hr[A][]' +\r
1952                                         'form[A|accept-charset|action|autocomplete|enctype|method|name|novalidate|target][C]' +\r
1953                                         'fieldset[A|disabled|form|name][C|legend]' +\r
1954                                         'label[A|form|for][B]' +\r
1955                                         'input[A|type|accept|alt|autocomplete|checked|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|height|list|max|maxlength|min|multiple|pattern|placeholder|readonly|required|size|src|step|width|files|value][]' +\r
1956                                         'button[A|autofocus|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|name|value|type][B]' +\r
1957                                         'select[A|autofocus|disabled|form|multiple|name|size][option|optgroup]' +\r
1958                                         'datalist[A][B|option]' +\r
1959                                         'optgroup[A|disabled|label][option]' +\r
1960                                         'option[A|disabled|selected|label|value][]' +\r
1961                                         'textarea[A|autofocus|disabled|form|maxlength|name|placeholder|readonly|required|rows|cols|wrap][]' +\r
1962                                         'keygen[A|autofocus|challenge|disabled|form|keytype|name][]' +\r
1963                                         'output[A|for|form|name][B]' +\r
1964                                         'canvas[A|width|height][]' +\r
1965                                         'map[A|name][B|C]' +\r
1966                                         'area[A|shape|coords|href|alt|target|media|rel|ping|type][]' +\r
1967                                         'mathml[A][]' +\r
1968                                         'svg[A][]' +\r
1969                                         'table[A|summary][caption|colgroup|thead|tfoot|tbody|tr]' +\r
1970                                         'caption[A][C]' +\r
1971                                         'colgroup[A|span][col]' +\r
1972                                         'col[A|span][]' +\r
1973                                         'thead[A][tr]' +\r
1974                                         'tfoot[A][tr]' +\r
1975                                         'tbody[A][tr]' +\r
1976                                         'tr[A][th|td]' +\r
1977                                         'th[A|headers|rowspan|colspan|scope][B]' +\r
1978                                         'td[A|headers|rowspan|colspan][C]'\r
1979                         );\r
1980                 }\r
1981 \r
1982                 return html5;\r
1983         };\r
1984 \r
1985         function getHTML4() {\r
1986                 var html4 = mapCache.html4;\r
1987 \r
1988                 if (!html4) {\r
1989                         // This is the XHTML 1.0 transitional elements with it's attributes and children packed to reduce it's size\r
1990                         html4 = mapCache.html4 = unpack({\r
1991                                 Z : 'H|K|N|O|P',\r
1992                                 Y : 'X|form|R|Q',\r
1993                                 ZG : 'E|span|width|align|char|charoff|valign',\r
1994                                 X : 'p|T|div|U|W|isindex|fieldset|table',\r
1995                                 ZF : 'E|align|char|charoff|valign',\r
1996                                 W : 'pre|hr|blockquote|address|center|noframes',\r
1997                                 ZE : 'abbr|axis|headers|scope|rowspan|colspan|align|char|charoff|valign|nowrap|bgcolor|width|height',\r
1998                                 ZD : '[E][S]',\r
1999                                 U : 'ul|ol|dl|menu|dir',\r
2000                                 ZC : 'p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q',\r
2001                                 T : 'h1|h2|h3|h4|h5|h6',\r
2002                                 ZB : 'X|S|Q',\r
2003                                 S : 'R|P',\r
2004                                 ZA : 'a|G|J|M|O|P',\r
2005                                 R : 'a|H|K|N|O',\r
2006                                 Q : 'noscript|P',\r
2007                                 P : 'ins|del|script',\r
2008                                 O : 'input|select|textarea|label|button',\r
2009                                 N : 'M|L',\r
2010                                 M : 'em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym',\r
2011                                 L : 'sub|sup',\r
2012                                 K : 'J|I',\r
2013                                 J : 'tt|i|b|u|s|strike',\r
2014                                 I : 'big|small|font|basefont',\r
2015                                 H : 'G|F',\r
2016                                 G : 'br|span|bdo',\r
2017                                 F : 'object|applet|img|map|iframe',\r
2018                                 E : 'A|B|C',\r
2019                                 D : 'accesskey|tabindex|onfocus|onblur',\r
2020                                 C : 'onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup',\r
2021                                 B : 'lang|xml:lang|dir',\r
2022                                 A : 'id|class|style|title'\r
2023                         }, 'script[id|charset|type|language|src|defer|xml:space][]' + \r
2024                                 'style[B|id|type|media|title|xml:space][]' + \r
2025                                 'object[E|declare|classid|codebase|data|type|codetype|archive|standby|width|height|usemap|name|tabindex|align|border|hspace|vspace][#|param|Y]' + \r
2026                                 'param[id|name|value|valuetype|type][]' + \r
2027                                 'p[E|align][#|S]' + \r
2028                                 'a[E|D|charset|type|name|href|hreflang|rel|rev|shape|coords|target][#|Z]' + \r
2029                                 'br[A|clear][]' + \r
2030                                 'span[E][#|S]' + \r
2031                                 'bdo[A|C|B][#|S]' + \r
2032                                 'applet[A|codebase|archive|code|object|alt|name|width|height|align|hspace|vspace][#|param|Y]' + \r
2033                                 'h1[E|align][#|S]' + \r
2034                                 'img[E|src|alt|name|longdesc|width|height|usemap|ismap|align|border|hspace|vspace][]' + \r
2035                                 'map[B|C|A|name][X|form|Q|area]' + \r
2036                                 'h2[E|align][#|S]' + \r
2037                                 'iframe[A|longdesc|name|src|frameborder|marginwidth|marginheight|scrolling|align|width|height][#|Y]' + \r
2038                                 'h3[E|align][#|S]' + \r
2039                                 'tt[E][#|S]' + \r
2040                                 'i[E][#|S]' + \r
2041                                 'b[E][#|S]' + \r
2042                                 'u[E][#|S]' + \r
2043                                 's[E][#|S]' + \r
2044                                 'strike[E][#|S]' + \r
2045                                 'big[E][#|S]' + \r
2046                                 'small[E][#|S]' + \r
2047                                 'font[A|B|size|color|face][#|S]' + \r
2048                                 'basefont[id|size|color|face][]' + \r
2049                                 'em[E][#|S]' + \r
2050                                 'strong[E][#|S]' + \r
2051                                 'dfn[E][#|S]' + \r
2052                                 'code[E][#|S]' + \r
2053                                 'q[E|cite][#|S]' + \r
2054                                 'samp[E][#|S]' + \r
2055                                 'kbd[E][#|S]' + \r
2056                                 'var[E][#|S]' + \r
2057                                 'cite[E][#|S]' + \r
2058                                 'abbr[E][#|S]' + \r
2059                                 'acronym[E][#|S]' + \r
2060                                 'sub[E][#|S]' + \r
2061                                 'sup[E][#|S]' + \r
2062                                 'input[E|D|type|name|value|checked|disabled|readonly|size|maxlength|src|alt|usemap|onselect|onchange|accept|align][]' + \r
2063                                 'select[E|name|size|multiple|disabled|tabindex|onfocus|onblur|onchange][optgroup|option]' + \r
2064                                 'optgroup[E|disabled|label][option]' + \r
2065                                 'option[E|selected|disabled|label|value][]' + \r
2066                                 'textarea[E|D|name|rows|cols|disabled|readonly|onselect|onchange][]' + \r
2067                                 'label[E|for|accesskey|onfocus|onblur][#|S]' + \r
2068                                 'button[E|D|name|value|type|disabled][#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]' + \r
2069                                 'h4[E|align][#|S]' + \r
2070                                 'ins[E|cite|datetime][#|Y]' + \r
2071                                 'h5[E|align][#|S]' + \r
2072                                 'del[E|cite|datetime][#|Y]' + \r
2073                                 'h6[E|align][#|S]' + \r
2074                                 'div[E|align][#|Y]' + \r
2075                                 'ul[E|type|compact][li]' + \r
2076                                 'li[E|type|value][#|Y]' + \r
2077                                 'ol[E|type|compact|start][li]' + \r
2078                                 'dl[E|compact][dt|dd]' + \r
2079                                 'dt[E][#|S]' + \r
2080                                 'dd[E][#|Y]' + \r
2081                                 'menu[E|compact][li]' + \r
2082                                 'dir[E|compact][li]' + \r
2083                                 'pre[E|width|xml:space][#|ZA]' + \r
2084                                 'hr[E|align|noshade|size|width][]' + \r
2085                                 'blockquote[E|cite][#|Y]' + \r
2086                                 'address[E][#|S|p]' + \r
2087                                 'center[E][#|Y]' + \r
2088                                 'noframes[E][#|Y]' + \r
2089                                 'isindex[A|B|prompt][]' + \r
2090                                 'fieldset[E][#|legend|Y]' + \r
2091                                 'legend[E|accesskey|align][#|S]' + \r
2092                                 'table[E|summary|width|border|frame|rules|cellspacing|cellpadding|align|bgcolor][caption|col|colgroup|thead|tfoot|tbody|tr]' + \r
2093                                 'caption[E|align][#|S]' + \r
2094                                 'col[ZG][]' + \r
2095                                 'colgroup[ZG][col]' + \r
2096                                 'thead[ZF][tr]' + \r
2097                                 'tr[ZF|bgcolor][th|td]' + \r
2098                                 'th[E|ZE][#|Y]' + \r
2099                                 'form[E|action|method|name|enctype|onsubmit|onreset|accept|accept-charset|target][#|X|R|Q]' + \r
2100                                 'noscript[E][#|Y]' + \r
2101                                 'td[E|ZE][#|Y]' + \r
2102                                 'tfoot[ZF][tr]' + \r
2103                                 'tbody[ZF][tr]' + \r
2104                                 'area[E|D|shape|coords|href|nohref|alt|target][]' + \r
2105                                 'base[id|href|target][]' + \r
2106                                 'body[E|onload|onunload|background|bgcolor|text|link|vlink|alink][#|Y]'\r
2107                         );\r
2108                 }\r
2109 \r
2110                 return html4;\r
2111         };\r
2112 \r
2113         tinymce.html.Schema = function(settings) {\r
2114                 var self = this, elements = {}, children = {}, patternElements = [], validStyles, schemaItems;\r
2115                 var whiteSpaceElementsMap, selfClosingElementsMap, shortEndedElementsMap, boolAttrMap, blockElementsMap, nonEmptyElementsMap, customElementsMap = {};\r
2116 \r
2117                 // Creates an lookup table map object for the specified option or the default value\r
2118                 function createLookupTable(option, default_value, extend) {\r
2119                         var value = settings[option];\r
2120 \r
2121                         if (!value) {\r
2122                                 // Get cached default map or make it if needed\r
2123                                 value = mapCache[option];\r
2124 \r
2125                                 if (!value) {\r
2126                                         value = makeMap(default_value, ' ', makeMap(default_value.toUpperCase(), ' '));\r
2127                                         value = tinymce.extend(value, extend);\r
2128 \r
2129                                         mapCache[option] = value;\r
2130                                 }\r
2131                         } else {\r
2132                                 // Create custom map\r
2133                                 value = makeMap(value, ',', makeMap(value.toUpperCase(), ' '));\r
2134                         }\r
2135 \r
2136                         return value;\r
2137                 };\r
2138 \r
2139                 settings = settings || {};\r
2140                 schemaItems = settings.schema == "html5" ? getHTML5() : getHTML4();\r
2141 \r
2142                 // Allow all elements and attributes if verify_html is set to false\r
2143                 if (settings.verify_html === false)\r
2144                         settings.valid_elements = '*[*]';\r
2145 \r
2146                 // Build styles list\r
2147                 if (settings.valid_styles) {\r
2148                         validStyles = {};\r
2149 \r
2150                         // Convert styles into a rule list\r
2151                         each(settings.valid_styles, function(value, key) {\r
2152                                 validStyles[key] = tinymce.explode(value);\r
2153                         });\r
2154                 }\r
2155 \r
2156                 // Setup map objects\r
2157                 whiteSpaceElementsMap = createLookupTable('whitespace_elements', 'pre script style textarea');\r
2158                 selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li options p td tfoot th thead tr');\r
2159                 shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link meta param embed source');\r
2160                 boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected autoplay loop controls');\r
2161                 nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object', shortEndedElementsMap);\r
2162                 blockElementsMap = createLookupTable('block_elements', 'h1 h2 h3 h4 h5 h6 hr p div address pre form table tbody thead tfoot ' + \r
2163                                                 'th tr td li ol ul caption blockquote center dl dt dd dir fieldset ' + \r
2164                                                 'noscript menu isindex samp header footer article section hgroup aside nav');\r
2165 \r
2166                 // Converts a wildcard expression string to a regexp for example *a will become /.*a/.\r
2167                 function patternToRegExp(str) {\r
2168                         return new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$');\r
2169                 };\r
2170 \r
2171                 // Parses the specified valid_elements string and adds to the current rules\r
2172                 // This function is a bit hard to read since it's heavily optimized for speed\r
2173                 function addValidElements(valid_elements) {\r
2174                         var ei, el, ai, al, yl, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder,\r
2175                                 prefix, outputName, globalAttributes, globalAttributesOrder, transElement, key, childKey, value,\r
2176                                 elementRuleRegExp = /^([#+-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,\r
2177                                 attrRuleRegExp = /^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,\r
2178                                 hasPatternsRegExp = /[*?+]/;\r
2179 \r
2180                         if (valid_elements) {\r
2181                                 // Split valid elements into an array with rules\r
2182                                 valid_elements = split(valid_elements);\r
2183 \r
2184                                 if (elements['@']) {\r
2185                                         globalAttributes = elements['@'].attributes;\r
2186                                         globalAttributesOrder = elements['@'].attributesOrder;\r
2187                                 }\r
2188 \r
2189                                 // Loop all rules\r
2190                                 for (ei = 0, el = valid_elements.length; ei < el; ei++) {\r
2191                                         // Parse element rule\r
2192                                         matches = elementRuleRegExp.exec(valid_elements[ei]);\r
2193                                         if (matches) {\r
2194                                                 // Setup local names for matches\r
2195                                                 prefix = matches[1];\r
2196                                                 elementName = matches[2];\r
2197                                                 outputName = matches[3];\r
2198                                                 attrData = matches[4];\r
2199 \r
2200                                                 // Create new attributes and attributesOrder\r
2201                                                 attributes = {};\r
2202                                                 attributesOrder = [];\r
2203 \r
2204                                                 // Create the new element\r
2205                                                 element = {\r
2206                                                         attributes : attributes,\r
2207                                                         attributesOrder : attributesOrder\r
2208                                                 };\r
2209 \r
2210                                                 // Padd empty elements prefix\r
2211                                                 if (prefix === '#')\r
2212                                                         element.paddEmpty = true;\r
2213 \r
2214                                                 // Remove empty elements prefix\r
2215                                                 if (prefix === '-')\r
2216                                                         element.removeEmpty = true;\r
2217 \r
2218                                                 // Copy attributes from global rule into current rule\r
2219                                                 if (globalAttributes) {\r
2220                                                         for (key in globalAttributes)\r
2221                                                                 attributes[key] = globalAttributes[key];\r
2222 \r
2223                                                         attributesOrder.push.apply(attributesOrder, globalAttributesOrder);\r
2224                                                 }\r
2225 \r
2226                                                 // Attributes defined\r
2227                                                 if (attrData) {\r
2228                                                         attrData = split(attrData, '|');\r
2229                                                         for (ai = 0, al = attrData.length; ai < al; ai++) {\r
2230                                                                 matches = attrRuleRegExp.exec(attrData[ai]);\r
2231                                                                 if (matches) {\r
2232                                                                         attr = {};\r
2233                                                                         attrType = matches[1];\r
2234                                                                         attrName = matches[2].replace(/::/g, ':');\r
2235                                                                         prefix = matches[3];\r
2236                                                                         value = matches[4];\r
2237 \r
2238                                                                         // Required\r
2239                                                                         if (attrType === '!') {\r
2240                                                                                 element.attributesRequired = element.attributesRequired || [];\r
2241                                                                                 element.attributesRequired.push(attrName);\r
2242                                                                                 attr.required = true;\r
2243                                                                         }\r
2244 \r
2245                                                                         // Denied from global\r
2246                                                                         if (attrType === '-') {\r
2247                                                                                 delete attributes[attrName];\r
2248                                                                                 attributesOrder.splice(tinymce.inArray(attributesOrder, attrName), 1);\r
2249                                                                                 continue;\r
2250                                                                         }\r
2251 \r
2252                                                                         // Default value\r
2253                                                                         if (prefix) {\r
2254                                                                                 // Default value\r
2255                                                                                 if (prefix === '=') {\r
2256                                                                                         element.attributesDefault = element.attributesDefault || [];\r
2257                                                                                         element.attributesDefault.push({name: attrName, value: value});\r
2258                                                                                         attr.defaultValue = value;\r
2259                                                                                 }\r
2260 \r
2261                                                                                 // Forced value\r
2262                                                                                 if (prefix === ':') {\r
2263                                                                                         element.attributesForced = element.attributesForced || [];\r
2264                                                                                         element.attributesForced.push({name: attrName, value: value});\r
2265                                                                                         attr.forcedValue = value;\r
2266                                                                                 }\r
2267 \r
2268                                                                                 // Required values\r
2269                                                                                 if (prefix === '<')\r
2270                                                                                         attr.validValues = makeMap(value, '?');\r
2271                                                                         }\r
2272 \r
2273                                                                         // Check for attribute patterns\r
2274                                                                         if (hasPatternsRegExp.test(attrName)) {\r
2275                                                                                 element.attributePatterns = element.attributePatterns || [];\r
2276                                                                                 attr.pattern = patternToRegExp(attrName);\r
2277                                                                                 element.attributePatterns.push(attr);\r
2278                                                                         } else {\r
2279                                                                                 // Add attribute to order list if it doesn't already exist\r
2280                                                                                 if (!attributes[attrName])\r
2281                                                                                         attributesOrder.push(attrName);\r
2282 \r
2283                                                                                 attributes[attrName] = attr;\r
2284                                                                         }\r
2285                                                                 }\r
2286                                                         }\r
2287                                                 }\r
2288 \r
2289                                                 // Global rule, store away these for later usage\r
2290                                                 if (!globalAttributes && elementName == '@') {\r
2291                                                         globalAttributes = attributes;\r
2292                                                         globalAttributesOrder = attributesOrder;\r
2293                                                 }\r
2294 \r
2295                                                 // Handle substitute elements such as b/strong\r
2296                                                 if (outputName) {\r
2297                                                         element.outputName = elementName;\r
2298                                                         elements[outputName] = element;\r
2299                                                 }\r
2300 \r
2301                                                 // Add pattern or exact element\r
2302                                                 if (hasPatternsRegExp.test(elementName)) {\r
2303                                                         element.pattern = patternToRegExp(elementName);\r
2304                                                         patternElements.push(element);\r
2305                                                 } else\r
2306                                                         elements[elementName] = element;\r
2307                                         }\r
2308                                 }\r
2309                         }\r
2310                 };\r
2311 \r
2312                 function setValidElements(valid_elements) {\r
2313                         elements = {};\r
2314                         patternElements = [];\r
2315 \r
2316                         addValidElements(valid_elements);\r
2317 \r
2318                         each(schemaItems, function(element, name) {\r
2319                                 children[name] = element.children;\r
2320                         });\r
2321                 };\r
2322 \r
2323                 // Adds custom non HTML elements to the schema\r
2324                 function addCustomElements(custom_elements) {\r
2325                         var customElementRegExp = /^(~)?(.+)$/;\r
2326 \r
2327                         if (custom_elements) {\r
2328                                 each(split(custom_elements), function(rule) {\r
2329                                         var matches = customElementRegExp.exec(rule),\r
2330                                                 inline = matches[1] === '~',\r
2331                                                 cloneName = inline ? 'span' : 'div',\r
2332                                                 name = matches[2];\r
2333 \r
2334                                         children[name] = children[cloneName];\r
2335                                         customElementsMap[name] = cloneName;\r
2336 \r
2337                                         // If it's not marked as inline then add it to valid block elements\r
2338                                         if (!inline)\r
2339                                                 blockElementsMap[name] = {};\r
2340 \r
2341                                         // Add custom elements at span/div positions\r
2342                                         each(children, function(element, child) {\r
2343                                                 if (element[cloneName])\r
2344                                                         element[name] = element[cloneName];\r
2345                                         });\r
2346                                 });\r
2347                         }\r
2348                 };\r
2349 \r
2350                 // Adds valid children to the schema object\r
2351                 function addValidChildren(valid_children) {\r
2352                         var childRuleRegExp = /^([+\-]?)(\w+)\[([^\]]+)\]$/;\r
2353 \r
2354                         if (valid_children) {\r
2355                                 each(split(valid_children), function(rule) {\r
2356                                         var matches = childRuleRegExp.exec(rule), parent, prefix;\r
2357 \r
2358                                         if (matches) {\r
2359                                                 prefix = matches[1];\r
2360 \r
2361                                                 // Add/remove items from default\r
2362                                                 if (prefix)\r
2363                                                         parent = children[matches[2]];\r
2364                                                 else\r
2365                                                         parent = children[matches[2]] = {'#comment' : {}};\r
2366 \r
2367                                                 parent = children[matches[2]];\r
2368 \r
2369                                                 each(split(matches[3], '|'), function(child) {\r
2370                                                         if (prefix === '-')\r
2371                                                                 delete parent[child];\r
2372                                                         else\r
2373                                                                 parent[child] = {};\r
2374                                                 });\r
2375                                         }\r
2376                                 });\r
2377                         }\r
2378                 };\r
2379 \r
2380                 function getElementRule(name) {\r
2381                         var element = elements[name], i;\r
2382 \r
2383                         // Exact match found\r
2384                         if (element)\r
2385                                 return element;\r
2386 \r
2387                         // No exact match then try the patterns\r
2388                         i = patternElements.length;\r
2389                         while (i--) {\r
2390                                 element = patternElements[i];\r
2391 \r
2392                                 if (element.pattern.test(name))\r
2393                                         return element;\r
2394                         }\r
2395                 };\r
2396 \r
2397                 if (!settings.valid_elements) {\r
2398                         // No valid elements defined then clone the elements from the schema spec\r
2399                         each(schemaItems, function(element, name) {\r
2400                                 elements[name] = {\r
2401                                         attributes : element.attributes,\r
2402                                         attributesOrder : element.attributesOrder\r
2403                                 };\r
2404 \r
2405                                 children[name] = element.children;\r
2406                         });\r
2407 \r
2408                         // Switch these on HTML4\r
2409                         if (settings.schema != "html5") {\r
2410                                 each(split('strong/b,em/i'), function(item) {\r
2411                                         item = split(item, '/');\r
2412                                         elements[item[1]].outputName = item[0];\r
2413                                 });\r
2414                         }\r
2415 \r
2416                         // Add default alt attribute for images\r
2417                         elements.img.attributesDefault = [{name: 'alt', value: ''}];\r
2418 \r
2419                         // Remove these if they are empty by default\r
2420                         each(split('ol,ul,sub,sup,blockquote,span,font,a,table,tbody,tr,strong,em,b,i'), function(name) {\r
2421                                 if (elements[name]) {\r
2422                                         elements[name].removeEmpty = true;\r
2423                                 }\r
2424                         });\r
2425 \r
2426                         // Padd these by default\r
2427                         each(split('p,h1,h2,h3,h4,h5,h6,th,td,pre,div,address,caption'), function(name) {\r
2428                                 elements[name].paddEmpty = true;\r
2429                         });\r
2430                 } else\r
2431                         setValidElements(settings.valid_elements);\r
2432 \r
2433                 addCustomElements(settings.custom_elements);\r
2434                 addValidChildren(settings.valid_children);\r
2435                 addValidElements(settings.extended_valid_elements);\r
2436 \r
2437                 // Todo: Remove this when we fix list handling to be valid\r
2438                 addValidChildren('+ol[ul|ol],+ul[ul|ol]');\r
2439 \r
2440                 // Delete invalid elements\r
2441                 if (settings.invalid_elements) {\r
2442                         tinymce.each(tinymce.explode(settings.invalid_elements), function(item) {\r
2443                                 if (elements[item])\r
2444                                         delete elements[item];\r
2445                         });\r
2446                 }\r
2447 \r
2448                 // If the user didn't allow span only allow internal spans\r
2449                 if (!getElementRule('span'))\r
2450                         addValidElements('span[!data-mce-type|*]');\r
2451 \r
2452                 self.children = children;\r
2453 \r
2454                 self.styles = validStyles;\r
2455 \r
2456                 self.getBoolAttrs = function() {\r
2457                         return boolAttrMap;\r
2458                 };\r
2459 \r
2460                 self.getBlockElements = function() {\r
2461                         return blockElementsMap;\r
2462                 };\r
2463 \r
2464                 self.getShortEndedElements = function() {\r
2465                         return shortEndedElementsMap;\r
2466                 };\r
2467 \r
2468                 self.getSelfClosingElements = function() {\r
2469                         return selfClosingElementsMap;\r
2470                 };\r
2471 \r
2472                 self.getNonEmptyElements = function() {\r
2473                         return nonEmptyElementsMap;\r
2474                 };\r
2475 \r
2476                 self.getWhiteSpaceElements = function() {\r
2477                         return whiteSpaceElementsMap;\r
2478                 };\r
2479 \r
2480                 self.isValidChild = function(name, child) {\r
2481                         var parent = children[name];\r
2482 \r
2483                         return !!(parent && parent[child]);\r
2484                 };\r
2485 \r
2486                 self.getElementRule = getElementRule;\r
2487 \r
2488                 self.getCustomElements = function() {\r
2489                         return customElementsMap;\r
2490                 };\r
2491 \r
2492                 self.addValidElements = addValidElements;\r
2493 \r
2494                 self.setValidElements = setValidElements;\r
2495 \r
2496                 self.addCustomElements = addCustomElements;\r
2497 \r
2498                 self.addValidChildren = addValidChildren;\r
2499         };\r
2500 })(tinymce);\r
2501 \r
2502 (function(tinymce) {\r
2503         tinymce.html.SaxParser = function(settings, schema) {\r
2504                 var self = this, noop = function() {};\r
2505 \r
2506                 settings = settings || {};\r
2507                 self.schema = schema = schema || new tinymce.html.Schema();\r
2508 \r
2509                 if (settings.fix_self_closing !== false)\r
2510                         settings.fix_self_closing = true;\r
2511 \r
2512                 // Add handler functions from settings and setup default handlers\r
2513                 tinymce.each('comment cdata text start end pi doctype'.split(' '), function(name) {\r
2514                         if (name)\r
2515                                 self[name] = settings[name] || noop;\r
2516                 });\r
2517 \r
2518                 self.parse = function(html) {\r
2519                         var self = this, matches, index = 0, value, endRegExp, stack = [], attrList, i, text, name, isInternalElement, removeInternalElements,\r
2520                                 shortEndedElements, fillAttrsMap, isShortEnded, validate, elementRule, isValidElement, attr, attribsValue, invalidPrefixRegExp,\r
2521                                 validAttributesMap, validAttributePatterns, attributesRequired, attributesDefault, attributesForced, selfClosing,\r
2522                                 tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0, decode = tinymce.html.Entities.decode, fixSelfClosing, isIE;\r
2523 \r
2524                         function processEndTag(name) {\r
2525                                 var pos, i;\r
2526 \r
2527                                 // Find position of parent of the same type\r
2528                                 pos = stack.length;\r
2529                                 while (pos--) {\r
2530                                         if (stack[pos].name === name)\r
2531                                                 break;                                          \r
2532                                 }\r
2533 \r
2534                                 // Found parent\r
2535                                 if (pos >= 0) {\r
2536                                         // Close all the open elements\r
2537                                         for (i = stack.length - 1; i >= pos; i--) {\r
2538                                                 name = stack[i];\r
2539 \r
2540                                                 if (name.valid)\r
2541                                                         self.end(name.name);\r
2542                                         }\r
2543 \r
2544                                         // Remove the open elements from the stack\r
2545                                         stack.length = pos;\r
2546                                 }\r
2547                         };\r
2548 \r
2549                         // Precompile RegExps and map objects\r
2550                         tokenRegExp = new RegExp('<(?:' +\r
2551                                 '(?:!--([\\w\\W]*?)-->)|' + // Comment\r
2552                                 '(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|' + // CDATA\r
2553                                 '(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE\r
2554                                 '(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI\r
2555                                 '(?:\\/([^>]+)>)|' + // End element\r
2556                                 '(?:([A-Za-z0-9\-\:]+)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/|\\s+)>)' + // Start element\r
2557                         ')', 'g');\r
2558 \r
2559                         attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:\\.|[^\"])*)\")|(?:\'((?:\\.|[^\'])*)\')|([^>\s]+)))?/g;\r
2560                         specialElements = {\r
2561                                 'script' : /<\/script[^>]*>/gi,\r
2562                                 'style' : /<\/style[^>]*>/gi,\r
2563                                 'noscript' : /<\/noscript[^>]*>/gi\r
2564                         };\r
2565 \r
2566                         // Setup lookup tables for empty elements and boolean attributes\r
2567                         shortEndedElements = schema.getShortEndedElements();\r
2568                         selfClosing = schema.getSelfClosingElements();\r
2569                         fillAttrsMap = schema.getBoolAttrs();\r
2570                         validate = settings.validate;\r
2571                         removeInternalElements = settings.remove_internals;\r
2572                         fixSelfClosing = settings.fix_self_closing;\r
2573                         isIE = tinymce.isIE;\r
2574                         invalidPrefixRegExp = /^:/;\r
2575 \r
2576                         while (matches = tokenRegExp.exec(html)) {\r
2577                                 // Text\r
2578                                 if (index < matches.index)\r
2579                                         self.text(decode(html.substr(index, matches.index - index)));\r
2580 \r
2581                                 if (value = matches[6]) { // End element\r
2582                                         value = value.toLowerCase();\r
2583 \r
2584                                         // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements\r
2585                                         if (isIE && invalidPrefixRegExp.test(value))\r
2586                                                 value = value.substr(1);\r
2587 \r
2588                                         processEndTag(value);\r
2589                                 } else if (value = matches[7]) { // Start element\r
2590                                         value = value.toLowerCase();\r
2591 \r
2592                                         // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements\r
2593                                         if (isIE && invalidPrefixRegExp.test(value))\r
2594                                                 value = value.substr(1);\r
2595 \r
2596                                         isShortEnded = value in shortEndedElements;\r
2597 \r
2598                                         // Is self closing tag for example an <li> after an open <li>\r
2599                                         if (fixSelfClosing && selfClosing[value] && stack.length > 0 && stack[stack.length - 1].name === value)\r
2600                                                 processEndTag(value);\r
2601 \r
2602                                         // Validate element\r
2603                                         if (!validate || (elementRule = schema.getElementRule(value))) {\r
2604                                                 isValidElement = true;\r
2605 \r
2606                                                 // Grab attributes map and patters when validation is enabled\r
2607                                                 if (validate) {\r
2608                                                         validAttributesMap = elementRule.attributes;\r
2609                                                         validAttributePatterns = elementRule.attributePatterns;\r
2610                                                 }\r
2611 \r
2612                                                 // Parse attributes\r
2613                                                 if (attribsValue = matches[8]) {\r
2614                                                         isInternalElement = attribsValue.indexOf('data-mce-type') !== -1; // Check if the element is an internal element\r
2615 \r
2616                                                         // If the element has internal attributes then remove it if we are told to do so\r
2617                                                         if (isInternalElement && removeInternalElements)\r
2618                                                                 isValidElement = false;\r
2619 \r
2620                                                         attrList = [];\r
2621                                                         attrList.map = {};\r
2622 \r
2623                                                         attribsValue.replace(attrRegExp, function(match, name, value, val2, val3) {\r
2624                                                                 var attrRule, i;\r
2625 \r
2626                                                                 name = name.toLowerCase();\r
2627                                                                 value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute\r
2628 \r
2629                                                                 // Validate name and value\r
2630                                                                 if (validate && !isInternalElement && name.indexOf('data-') !== 0) {\r
2631                                                                         attrRule = validAttributesMap[name];\r
2632 \r
2633                                                                         // Find rule by pattern matching\r
2634                                                                         if (!attrRule && validAttributePatterns) {\r
2635                                                                                 i = validAttributePatterns.length;\r
2636                                                                                 while (i--) {\r
2637                                                                                         attrRule = validAttributePatterns[i];\r
2638                                                                                         if (attrRule.pattern.test(name))\r
2639                                                                                                 break;\r
2640                                                                                 }\r
2641 \r
2642                                                                                 // No rule matched\r
2643                                                                                 if (i === -1)\r
2644                                                                                         attrRule = null;\r
2645                                                                         }\r
2646 \r
2647                                                                         // No attribute rule found\r
2648                                                                         if (!attrRule)\r
2649                                                                                 return;\r
2650 \r
2651                                                                         // Validate value\r
2652                                                                         if (attrRule.validValues && !(value in attrRule.validValues))\r
2653                                                                                 return;\r
2654                                                                 }\r
2655 \r
2656                                                                 // Add attribute to list and map\r
2657                                                                 attrList.map[name] = value;\r
2658                                                                 attrList.push({\r
2659                                                                         name: name,\r
2660                                                                         value: value\r
2661                                                                 });\r
2662                                                         });\r
2663                                                 } else {\r
2664                                                         attrList = [];\r
2665                                                         attrList.map = {};\r
2666                                                 }\r
2667 \r
2668                                                 // Process attributes if validation is enabled\r
2669                                                 if (validate && !isInternalElement) {\r
2670                                                         attributesRequired = elementRule.attributesRequired;\r
2671                                                         attributesDefault = elementRule.attributesDefault;\r
2672                                                         attributesForced = elementRule.attributesForced;\r
2673 \r
2674                                                         // Handle forced attributes\r
2675                                                         if (attributesForced) {\r
2676                                                                 i = attributesForced.length;\r
2677                                                                 while (i--) {\r
2678                                                                         attr = attributesForced[i];\r
2679                                                                         name = attr.name;\r
2680                                                                         attrValue = attr.value;\r
2681 \r
2682                                                                         if (attrValue === '{$uid}')\r
2683                                                                                 attrValue = 'mce_' + idCount++;\r
2684 \r
2685                                                                         attrList.map[name] = attrValue;\r
2686                                                                         attrList.push({name: name, value: attrValue});\r
2687                                                                 }\r
2688                                                         }\r
2689 \r
2690                                                         // Handle default attributes\r
2691                                                         if (attributesDefault) {\r
2692                                                                 i = attributesDefault.length;\r
2693                                                                 while (i--) {\r
2694                                                                         attr = attributesDefault[i];\r
2695                                                                         name = attr.name;\r
2696 \r
2697                                                                         if (!(name in attrList.map)) {\r
2698                                                                                 attrValue = attr.value;\r
2699 \r
2700                                                                                 if (attrValue === '{$uid}')\r
2701                                                                                         attrValue = 'mce_' + idCount++;\r
2702 \r
2703                                                                                 attrList.map[name] = attrValue;\r
2704                                                                                 attrList.push({name: name, value: attrValue});\r
2705                                                                         }\r
2706                                                                 }\r
2707                                                         }\r
2708 \r
2709                                                         // Handle required attributes\r
2710                                                         if (attributesRequired) {\r
2711                                                                 i = attributesRequired.length;\r
2712                                                                 while (i--) {\r
2713                                                                         if (attributesRequired[i] in attrList.map)\r
2714                                                                                 break;\r
2715                                                                 }\r
2716 \r
2717                                                                 // None of the required attributes where found\r
2718                                                                 if (i === -1)\r
2719                                                                         isValidElement = false;\r
2720                                                         }\r
2721 \r
2722                                                         // Invalidate element if it's marked as bogus\r
2723                                                         if (attrList.map['data-mce-bogus'])\r
2724                                                                 isValidElement = false;\r
2725                                                 }\r
2726 \r
2727                                                 if (isValidElement)\r
2728                                                         self.start(value, attrList, isShortEnded);\r
2729                                         } else\r
2730                                                 isValidElement = false;\r
2731 \r
2732                                         // Treat script, noscript and style a bit different since they may include code that looks like elements\r
2733                                         if (endRegExp = specialElements[value]) {\r
2734                                                 endRegExp.lastIndex = index = matches.index + matches[0].length;\r
2735 \r
2736                                                 if (matches = endRegExp.exec(html)) {\r
2737                                                         if (isValidElement)\r
2738                                                                 text = html.substr(index, matches.index - index);\r
2739 \r
2740                                                         index = matches.index + matches[0].length;\r
2741                                                 } else {\r
2742                                                         text = html.substr(index);\r
2743                                                         index = html.length;\r
2744                                                 }\r
2745 \r
2746                                                 if (isValidElement && text.length > 0)\r
2747                                                         self.text(text, true);\r
2748 \r
2749                                                 if (isValidElement)\r
2750                                                         self.end(value);\r
2751 \r
2752                                                 tokenRegExp.lastIndex = index;\r
2753                                                 continue;\r
2754                                         }\r
2755 \r
2756                                         // Push value on to stack\r
2757                                         if (!isShortEnded) {\r
2758                                                 if (!attribsValue || attribsValue.indexOf('/') != attribsValue.length - 1)\r
2759                                                         stack.push({name: value, valid: isValidElement});\r
2760                                                 else if (isValidElement)\r
2761                                                         self.end(value);\r
2762                                         }\r
2763                                 } else if (value = matches[1]) { // Comment\r
2764                                         self.comment(value);\r
2765                                 } else if (value = matches[2]) { // CDATA\r
2766                                         self.cdata(value);\r
2767                                 } else if (value = matches[3]) { // DOCTYPE\r
2768                                         self.doctype(value);\r
2769                                 } else if (value = matches[4]) { // PI\r
2770                                         self.pi(value, matches[5]);\r
2771                                 }\r
2772 \r
2773                                 index = matches.index + matches[0].length;\r
2774                         }\r
2775 \r
2776                         // Text\r
2777                         if (index < html.length)\r
2778                                 self.text(decode(html.substr(index)));\r
2779 \r
2780                         // Close any open elements\r
2781                         for (i = stack.length - 1; i >= 0; i--) {\r
2782                                 value = stack[i];\r
2783 \r
2784                                 if (value.valid)\r
2785                                         self.end(value.name);\r
2786                         }\r
2787                 };\r
2788         }\r
2789 })(tinymce);\r
2790 \r
2791 (function(tinymce) {\r
2792         var whiteSpaceRegExp = /^[ \t\r\n]*$/, typeLookup = {\r
2793                 '#text' : 3,\r
2794                 '#comment' : 8,\r
2795                 '#cdata' : 4,\r
2796                 '#pi' : 7,\r
2797                 '#doctype' : 10,\r
2798                 '#document-fragment' : 11\r
2799         };\r
2800 \r
2801         // Walks the tree left/right\r
2802         function walk(node, root_node, prev) {\r
2803                 var sibling, parent, startName = prev ? 'lastChild' : 'firstChild', siblingName = prev ? 'prev' : 'next';\r
2804 \r
2805                 // Walk into nodes if it has a start\r
2806                 if (node[startName])\r
2807                         return node[startName];\r
2808 \r
2809                 // Return the sibling if it has one\r
2810                 if (node !== root_node) {\r
2811                         sibling = node[siblingName];\r
2812 \r
2813                         if (sibling)\r
2814                                 return sibling;\r
2815 \r
2816                         // Walk up the parents to look for siblings\r
2817                         for (parent = node.parent; parent && parent !== root_node; parent = parent.parent) {\r
2818                                 sibling = parent[siblingName];\r
2819 \r
2820                                 if (sibling)\r
2821                                         return sibling;\r
2822                         }\r
2823                 }\r
2824         };\r
2825 \r
2826         function Node(name, type) {\r
2827                 this.name = name;\r
2828                 this.type = type;\r
2829 \r
2830                 if (type === 1) {\r
2831                         this.attributes = [];\r
2832                         this.attributes.map = {};\r
2833                 }\r
2834         }\r
2835 \r
2836         tinymce.extend(Node.prototype, {\r
2837                 replace : function(node) {\r
2838                         var self = this;\r
2839 \r
2840                         if (node.parent)\r
2841                                 node.remove();\r
2842 \r
2843                         self.insert(node, self);\r
2844                         self.remove();\r
2845 \r
2846                         return self;\r
2847                 },\r
2848 \r
2849                 attr : function(name, value) {\r
2850                         var self = this, attrs, i, undef;\r
2851 \r
2852                         if (typeof name !== "string") {\r
2853                                 for (i in name)\r
2854                                         self.attr(i, name[i]);\r
2855 \r
2856                                 return self;\r
2857                         }\r
2858 \r
2859                         if (attrs = self.attributes) {\r
2860                                 if (value !== undef) {\r
2861                                         // Remove attribute\r
2862                                         if (value === null) {\r
2863                                                 if (name in attrs.map) {\r
2864                                                         delete attrs.map[name];\r
2865 \r
2866                                                         i = attrs.length;\r
2867                                                         while (i--) {\r
2868                                                                 if (attrs[i].name === name) {\r
2869                                                                         attrs = attrs.splice(i, 1);\r
2870                                                                         return self;\r
2871                                                                 }\r
2872                                                         }\r
2873                                                 }\r
2874 \r
2875                                                 return self;\r
2876                                         }\r
2877 \r
2878                                         // Set attribute\r
2879                                         if (name in attrs.map) {\r
2880                                                 // Set attribute\r
2881                                                 i = attrs.length;\r
2882                                                 while (i--) {\r
2883                                                         if (attrs[i].name === name) {\r
2884                                                                 attrs[i].value = value;\r
2885                                                                 break;\r
2886                                                         }\r
2887                                                 }\r
2888                                         } else\r
2889                                                 attrs.push({name: name, value: value});\r
2890 \r
2891                                         attrs.map[name] = value;\r
2892 \r
2893                                         return self;\r
2894                                 } else {\r
2895                                         return attrs.map[name];\r
2896                                 }\r
2897                         }\r
2898                 },\r
2899 \r
2900                 clone : function() {\r
2901                         var self = this, clone = new Node(self.name, self.type), i, l, selfAttrs, selfAttr, cloneAttrs;\r
2902 \r
2903                         // Clone element attributes\r
2904                         if (selfAttrs = self.attributes) {\r
2905                                 cloneAttrs = [];\r
2906                                 cloneAttrs.map = {};\r
2907 \r
2908                                 for (i = 0, l = selfAttrs.length; i < l; i++) {\r
2909                                         selfAttr = selfAttrs[i];\r
2910 \r
2911                                         // Clone everything except id\r
2912                                         if (selfAttr.name !== 'id') {\r
2913                                                 cloneAttrs[cloneAttrs.length] = {name: selfAttr.name, value: selfAttr.value};\r
2914                                                 cloneAttrs.map[selfAttr.name] = selfAttr.value;\r
2915                                         }\r
2916                                 }\r
2917 \r
2918                                 clone.attributes = cloneAttrs;\r
2919                         }\r
2920 \r
2921                         clone.value = self.value;\r
2922                         clone.shortEnded = self.shortEnded;\r
2923 \r
2924                         return clone;\r
2925                 },\r
2926 \r
2927                 wrap : function(wrapper) {\r
2928                         var self = this;\r
2929 \r
2930                         self.parent.insert(wrapper, self);\r
2931                         wrapper.append(self);\r
2932 \r
2933                         return self;\r
2934                 },\r
2935 \r
2936                 unwrap : function() {\r
2937                         var self = this, node, next;\r
2938 \r
2939                         for (node = self.firstChild; node; ) {\r
2940                                 next = node.next;\r
2941                                 self.insert(node, self, true);\r
2942                                 node = next;\r
2943                         }\r
2944 \r
2945                         self.remove();\r
2946                 },\r
2947 \r
2948                 remove : function() {\r
2949                         var self = this, parent = self.parent, next = self.next, prev = self.prev;\r
2950 \r
2951                         if (parent) {\r
2952                                 if (parent.firstChild === self) {\r
2953                                         parent.firstChild = next;\r
2954 \r
2955                                         if (next)\r
2956                                                 next.prev = null;\r
2957                                 } else {\r
2958                                         prev.next = next;\r
2959                                 }\r
2960 \r
2961                                 if (parent.lastChild === self) {\r
2962                                         parent.lastChild = prev;\r
2963 \r
2964                                         if (prev)\r
2965                                                 prev.next = null;\r
2966                                 } else {\r
2967                                         next.prev = prev;\r
2968                                 }\r
2969 \r
2970                                 self.parent = self.next = self.prev = null;\r
2971                         }\r
2972 \r
2973                         return self;\r
2974                 },\r
2975 \r
2976                 append : function(node) {\r
2977                         var self = this, last;\r
2978 \r
2979                         if (node.parent)\r
2980                                 node.remove();\r
2981 \r
2982                         last = self.lastChild;\r
2983                         if (last) {\r
2984                                 last.next = node;\r
2985                                 node.prev = last;\r
2986                                 self.lastChild = node;\r
2987                         } else\r
2988                                 self.lastChild = self.firstChild = node;\r
2989 \r
2990                         node.parent = self;\r
2991 \r
2992                         return node;\r
2993                 },\r
2994 \r
2995                 insert : function(node, ref_node, before) {\r
2996                         var parent;\r
2997 \r
2998                         if (node.parent)\r
2999                                 node.remove();\r
3000 \r
3001                         parent = ref_node.parent || this;\r
3002 \r
3003                         if (before) {\r
3004                                 if (ref_node === parent.firstChild)\r
3005                                         parent.firstChild = node;\r
3006                                 else\r
3007                                         ref_node.prev.next = node;\r
3008 \r
3009                                 node.prev = ref_node.prev;\r
3010                                 node.next = ref_node;\r
3011                                 ref_node.prev = node;\r
3012                         } else {\r
3013                                 if (ref_node === parent.lastChild)\r
3014                                         parent.lastChild = node;\r
3015                                 else\r
3016                                         ref_node.next.prev = node;\r
3017 \r
3018                                 node.next = ref_node.next;\r
3019                                 node.prev = ref_node;\r
3020                                 ref_node.next = node;\r
3021                         }\r
3022 \r
3023                         node.parent = parent;\r
3024 \r
3025                         return node;\r
3026                 },\r
3027 \r
3028                 getAll : function(name) {\r
3029                         var self = this, node, collection = [];\r
3030 \r
3031                         for (node = self.firstChild; node; node = walk(node, self)) {\r
3032                                 if (node.name === name)\r
3033                                         collection.push(node);\r
3034                         }\r
3035 \r
3036                         return collection;\r
3037                 },\r
3038 \r
3039                 empty : function() {\r
3040                         var self = this, nodes, i, node;\r
3041 \r
3042                         // Remove all children\r
3043                         if (self.firstChild) {\r
3044                                 nodes = [];\r
3045 \r
3046                                 // Collect the children\r
3047                                 for (node = self.firstChild; node; node = walk(node, self))\r
3048                                         nodes.push(node);\r
3049 \r
3050                                 // Remove the children\r
3051                                 i = nodes.length;\r
3052                                 while (i--) {\r
3053                                         node = nodes[i];\r
3054                                         node.parent = node.firstChild = node.lastChild = node.next = node.prev = null;\r
3055                                 }\r
3056                         }\r
3057 \r
3058                         self.firstChild = self.lastChild = null;\r
3059 \r
3060                         return self;\r
3061                 },\r
3062 \r
3063                 isEmpty : function(elements) {\r
3064                         var self = this, node = self.firstChild, i, name;\r
3065 \r
3066                         if (node) {\r
3067                                 do {\r
3068                                         if (node.type === 1) {\r
3069                                                 // Ignore bogus elements\r
3070                                                 if (node.attributes.map['data-mce-bogus'])\r
3071                                                         continue;\r
3072 \r
3073                                                 // Keep empty elements like <img />\r
3074                                                 if (elements[node.name])\r
3075                                                         return false;\r
3076 \r
3077                                                 // Keep elements with data attributes or name attribute like <a name="1"></a>\r
3078                                                 i = node.attributes.length;\r
3079                                                 while (i--) {\r
3080                                                         name = node.attributes[i].name;\r
3081                                                         if (name === "name" || name.indexOf('data-') === 0)\r
3082                                                                 return false;\r
3083                                                 }\r
3084                                         }\r
3085 \r
3086                                         // Keep comments\r
3087                                         if (node.type === 8)\r
3088                                                 return false;\r
3089                                         \r
3090                                         // Keep non whitespace text nodes\r
3091                                         if ((node.type === 3 && !whiteSpaceRegExp.test(node.value)))\r
3092                                                 return false;\r
3093                                 } while (node = walk(node, self));\r
3094                         }\r
3095 \r
3096                         return true;\r
3097                 },\r
3098 \r
3099                 walk : function(prev) {\r
3100                         return walk(this, null, prev);\r
3101                 }\r
3102         });\r
3103 \r
3104         tinymce.extend(Node, {\r
3105                 create : function(name, attrs) {\r
3106                         var node, attrName;\r
3107 \r
3108                         // Create node\r
3109                         node = new Node(name, typeLookup[name] || 1);\r
3110 \r
3111                         // Add attributes if needed\r
3112                         if (attrs) {\r
3113                                 for (attrName in attrs)\r
3114                                         node.attr(attrName, attrs[attrName]);\r
3115                         }\r
3116 \r
3117                         return node;\r
3118                 }\r
3119         });\r
3120 \r
3121         tinymce.html.Node = Node;\r
3122 })(tinymce);\r
3123 \r
3124 (function(tinymce) {\r
3125         var Node = tinymce.html.Node;\r
3126 \r
3127         tinymce.html.DomParser = function(settings, schema) {\r
3128                 var self = this, nodeFilters = {}, attributeFilters = [], matchedNodes = {}, matchedAttributes = {};\r
3129 \r
3130                 settings = settings || {};\r
3131                 settings.validate = "validate" in settings ? settings.validate : true;\r
3132                 settings.root_name = settings.root_name || 'body';\r
3133                 self.schema = schema = schema || new tinymce.html.Schema();\r
3134 \r
3135                 function fixInvalidChildren(nodes) {\r
3136                         var ni, node, parent, parents, newParent, currentNode, tempNode, childNode, i,\r
3137                                 childClone, nonEmptyElements, nonSplitableElements, sibling, nextNode;\r
3138 \r
3139                         nonSplitableElements = tinymce.makeMap('tr,td,th,tbody,thead,tfoot,table');\r
3140                         nonEmptyElements = schema.getNonEmptyElements();\r
3141 \r
3142                         for (ni = 0; ni < nodes.length; ni++) {\r
3143                                 node = nodes[ni];\r
3144 \r
3145                                 // Already removed\r
3146                                 if (!node.parent)\r
3147                                         continue;\r
3148 \r
3149                                 // Get list of all parent nodes until we find a valid parent to stick the child into\r
3150                                 parents = [node];\r
3151                                 for (parent = node.parent; parent && !schema.isValidChild(parent.name, node.name) && !nonSplitableElements[parent.name]; parent = parent.parent)\r
3152                                         parents.push(parent);\r
3153 \r
3154                                 // Found a suitable parent\r
3155                                 if (parent && parents.length > 1) {\r
3156                                         // Reverse the array since it makes looping easier\r
3157                                         parents.reverse();\r
3158 \r
3159                                         // Clone the related parent and insert that after the moved node\r
3160                                         newParent = currentNode = self.filterNode(parents[0].clone());\r
3161 \r
3162                                         // Start cloning and moving children on the left side of the target node\r
3163                                         for (i = 0; i < parents.length - 1; i++) {\r
3164                                                 if (schema.isValidChild(currentNode.name, parents[i].name)) {\r
3165                                                         tempNode = self.filterNode(parents[i].clone());\r
3166                                                         currentNode.append(tempNode);\r
3167                                                 } else\r
3168                                                         tempNode = currentNode;\r
3169 \r
3170                                                 for (childNode = parents[i].firstChild; childNode && childNode != parents[i + 1]; ) {\r
3171                                                         nextNode = childNode.next;\r
3172                                                         tempNode.append(childNode);\r
3173                                                         childNode = nextNode;\r
3174                                                 }\r
3175 \r
3176                                                 currentNode = tempNode;\r
3177                                         }\r
3178 \r
3179                                         if (!newParent.isEmpty(nonEmptyElements)) {\r
3180                                                 parent.insert(newParent, parents[0], true);\r
3181                                                 parent.insert(node, newParent);\r
3182                                         } else {\r
3183                                                 parent.insert(node, parents[0], true);\r
3184                                         }\r
3185 \r
3186                                         // Check if the element is empty by looking through it's contents and special treatment for <p><br /></p>\r
3187                                         parent = parents[0];\r
3188                                         if (parent.isEmpty(nonEmptyElements) || parent.firstChild === parent.lastChild && parent.firstChild.name === 'br') {\r
3189                                                 parent.empty().remove();\r
3190                                         }\r
3191                                 } else if (node.parent) {\r
3192                                         // If it's an LI try to find a UL/OL for it or wrap it\r
3193                                         if (node.name === 'li') {\r
3194                                                 sibling = node.prev;\r
3195                                                 if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {\r
3196                                                         sibling.append(node);\r
3197                                                         continue;\r
3198                                                 }\r
3199 \r
3200                                                 sibling = node.next;\r
3201                                                 if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {\r
3202                                                         sibling.insert(node, sibling.firstChild, true);\r
3203                                                         continue;\r
3204                                                 }\r
3205 \r
3206                                                 node.wrap(self.filterNode(new Node('ul', 1)));\r
3207                                                 continue;\r
3208                                         }\r
3209 \r
3210                                         // Try wrapping the element in a DIV\r
3211                                         if (schema.isValidChild(node.parent.name, 'div') && schema.isValidChild('div', node.name)) {\r
3212                                                 node.wrap(self.filterNode(new Node('div', 1)));\r
3213                                         } else {\r
3214                                                 // We failed wrapping it, then remove or unwrap it\r
3215                                                 if (node.name === 'style' || node.name === 'script')\r
3216                                                         node.empty().remove();\r
3217                                                 else\r
3218                                                         node.unwrap();\r
3219                                         }\r
3220                                 }\r
3221                         }\r
3222                 };\r
3223 \r
3224                 self.filterNode = function(node) {\r
3225                         var i, name, list;\r
3226 \r
3227                         // Run element filters\r
3228                         if (name in nodeFilters) {\r
3229                                 list = matchedNodes[name];\r
3230 \r
3231                                 if (list)\r
3232                                         list.push(node);\r
3233                                 else\r
3234                                         matchedNodes[name] = [node];\r
3235                         }\r
3236 \r
3237                         // Run attribute filters\r
3238                         i = attributeFilters.length;\r
3239                         while (i--) {\r
3240                                 name = attributeFilters[i].name;\r
3241 \r
3242                                 if (name in node.attributes.map) {\r
3243                                         list = matchedAttributes[name];\r
3244 \r
3245                                         if (list)\r
3246                                                 list.push(node);\r
3247                                         else\r
3248                                                 matchedAttributes[name] = [node];\r
3249                                 }\r
3250                         }\r
3251 \r
3252                         return node;\r
3253                 };\r
3254 \r
3255                 self.addNodeFilter = function(name, callback) {\r
3256                         tinymce.each(tinymce.explode(name), function(name) {\r
3257                                 var list = nodeFilters[name];\r
3258 \r
3259                                 if (!list)\r
3260                                         nodeFilters[name] = list = [];\r
3261 \r
3262                                 list.push(callback);\r
3263                         });\r
3264                 };\r
3265 \r
3266                 self.addAttributeFilter = function(name, callback) {\r
3267                         tinymce.each(tinymce.explode(name), function(name) {\r
3268                                 var i;\r
3269 \r
3270                                 for (i = 0; i < attributeFilters.length; i++) {\r
3271                                         if (attributeFilters[i].name === name) {\r
3272                                                 attributeFilters[i].callbacks.push(callback);\r
3273                                                 return;\r
3274                                         }\r
3275                                 }\r
3276 \r
3277                                 attributeFilters.push({name: name, callbacks: [callback]});\r
3278                         });\r
3279                 };\r
3280 \r
3281                 self.parse = function(html, args) {\r
3282                         var parser, rootNode, node, nodes, i, l, fi, fl, list, name, validate,\r
3283                                 blockElements, startWhiteSpaceRegExp, invalidChildren = [], isInWhiteSpacePreservedElement,\r
3284                                 endWhiteSpaceRegExp, allWhiteSpaceRegExp, whiteSpaceElements, children, nonEmptyElements, rootBlockName;\r
3285 \r
3286                         args = args || {};\r
3287                         matchedNodes = {};\r
3288                         matchedAttributes = {};\r
3289                         blockElements = tinymce.extend(tinymce.makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements());\r
3290                         nonEmptyElements = schema.getNonEmptyElements();\r
3291                         children = schema.children;\r
3292                         validate = settings.validate;\r
3293                         rootBlockName = "forced_root_block" in args ? args.forced_root_block : settings.forced_root_block;\r
3294 \r
3295                         whiteSpaceElements = schema.getWhiteSpaceElements();\r
3296                         startWhiteSpaceRegExp = /^[ \t\r\n]+/;\r
3297                         endWhiteSpaceRegExp = /[ \t\r\n]+$/;\r
3298                         allWhiteSpaceRegExp = /[ \t\r\n]+/g;\r
3299 \r
3300                         function addRootBlocks() {\r
3301                                 var node = rootNode.firstChild, next, rootBlockNode;\r
3302 \r
3303                                 while (node) {\r
3304                                         next = node.next;\r
3305 \r
3306                                         if (node.type == 3 || (node.type == 1 && node.name !== 'p' && !blockElements[node.name] && !node.attr('data-mce-type'))) {\r
3307                                                 if (!rootBlockNode) {\r
3308                                                         // Create a new root block element\r
3309                                                         rootBlockNode = createNode(rootBlockName, 1);\r
3310                                                         rootNode.insert(rootBlockNode, node);\r
3311                                                         rootBlockNode.append(node);\r
3312                                                 } else\r
3313                                                         rootBlockNode.append(node);\r
3314                                         } else {\r
3315                                                 rootBlockNode = null;\r
3316                                         }\r
3317 \r
3318                                         node = next;\r
3319                                 };\r
3320                         };\r
3321 \r
3322                         function createNode(name, type) {\r
3323                                 var node = new Node(name, type), list;\r
3324 \r
3325                                 if (name in nodeFilters) {\r
3326                                         list = matchedNodes[name];\r
3327 \r
3328                                         if (list)\r
3329                                                 list.push(node);\r
3330                                         else\r
3331                                                 matchedNodes[name] = [node];\r
3332                                 }\r
3333 \r
3334                                 return node;\r
3335                         };\r
3336 \r
3337                         function removeWhitespaceBefore(node) {\r
3338                                 var textNode, textVal, sibling;\r
3339 \r
3340                                 for (textNode = node.prev; textNode && textNode.type === 3; ) {\r
3341                                         textVal = textNode.value.replace(endWhiteSpaceRegExp, '');\r
3342 \r
3343                                         if (textVal.length > 0) {\r
3344                                                 textNode.value = textVal;\r
3345                                                 textNode = textNode.prev;\r
3346                                         } else {\r
3347                                                 sibling = textNode.prev;\r
3348                                                 textNode.remove();\r
3349                                                 textNode = sibling;\r
3350                                         }\r
3351                                 }\r
3352                         };\r
3353 \r
3354                         parser = new tinymce.html.SaxParser({\r
3355                                 validate : validate,\r
3356                                 fix_self_closing : !validate, // Let the DOM parser handle <li> in <li> or <p> in <p> for better results\r
3357 \r
3358                                 cdata: function(text) {\r
3359                                         node.append(createNode('#cdata', 4)).value = text;\r
3360                                 },\r
3361 \r
3362                                 text: function(text, raw) {\r
3363                                         var textNode;\r
3364 \r
3365                                         // Trim all redundant whitespace on non white space elements\r
3366                                         if (!isInWhiteSpacePreservedElement) {\r
3367                                                 text = text.replace(allWhiteSpaceRegExp, ' ');\r
3368 \r
3369                                                 if (node.lastChild && blockElements[node.lastChild.name])\r
3370                                                         text = text.replace(startWhiteSpaceRegExp, '');\r
3371                                         }\r
3372 \r
3373                                         // Do we need to create the node\r
3374                                         if (text.length !== 0) {\r
3375                                                 textNode = createNode('#text', 3);\r
3376                                                 textNode.raw = !!raw;\r
3377                                                 node.append(textNode).value = text;\r
3378                                         }\r
3379                                 },\r
3380 \r
3381                                 comment: function(text) {\r
3382                                         node.append(createNode('#comment', 8)).value = text;\r
3383                                 },\r
3384 \r
3385                                 pi: function(name, text) {\r
3386                                         node.append(createNode(name, 7)).value = text;\r
3387                                         removeWhitespaceBefore(node);\r
3388                                 },\r
3389 \r
3390                                 doctype: function(text) {\r
3391                                         var newNode;\r
3392                 \r
3393                                         newNode = node.append(createNode('#doctype', 10));\r
3394                                         newNode.value = text;\r
3395                                         removeWhitespaceBefore(node);\r
3396                                 },\r
3397 \r
3398                                 start: function(name, attrs, empty) {\r
3399                                         var newNode, attrFiltersLen, elementRule, textNode, attrName, text, sibling, parent;\r
3400 \r
3401                                         elementRule = validate ? schema.getElementRule(name) : {};\r
3402                                         if (elementRule) {\r
3403                                                 newNode = createNode(elementRule.outputName || name, 1);\r
3404                                                 newNode.attributes = attrs;\r
3405                                                 newNode.shortEnded = empty;\r
3406 \r
3407                                                 node.append(newNode);\r
3408 \r
3409                                                 // Check if node is valid child of the parent node is the child is\r
3410                                                 // unknown we don't collect it since it's probably a custom element\r
3411                                                 parent = children[node.name];\r
3412                                                 if (parent && children[newNode.name] && !parent[newNode.name])\r
3413                                                         invalidChildren.push(newNode);\r
3414 \r
3415                                                 attrFiltersLen = attributeFilters.length;\r
3416                                                 while (attrFiltersLen--) {\r
3417                                                         attrName = attributeFilters[attrFiltersLen].name;\r
3418 \r
3419                                                         if (attrName in attrs.map) {\r
3420                                                                 list = matchedAttributes[attrName];\r
3421 \r
3422                                                                 if (list)\r
3423                                                                         list.push(newNode);\r
3424                                                                 else\r
3425                                                                         matchedAttributes[attrName] = [newNode];\r
3426                                                         }\r
3427                                                 }\r
3428 \r
3429                                                 // Trim whitespace before block\r
3430                                                 if (blockElements[name])\r
3431                                                         removeWhitespaceBefore(newNode);\r
3432 \r
3433                                                 // Change current node if the element wasn't empty i.e not <br /> or <img />\r
3434                                                 if (!empty)\r
3435                                                         node = newNode;\r
3436 \r
3437                                                 // Check if we are inside a whitespace preserved element\r
3438                                                 if (!isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {\r
3439                                                         isInWhiteSpacePreservedElement = true;\r
3440                                                 }\r
3441                                         }\r
3442                                 },\r
3443 \r
3444                                 end: function(name) {\r
3445                                         var textNode, elementRule, text, sibling, tempNode;\r
3446 \r
3447                                         elementRule = validate ? schema.getElementRule(name) : {};\r
3448                                         if (elementRule) {\r
3449                                                 if (blockElements[name]) {\r
3450                                                         if (!isInWhiteSpacePreservedElement) {\r
3451                                                                 // Trim whitespace at beginning of block\r
3452                                                                 for (textNode = node.firstChild; textNode && textNode.type === 3; ) {\r
3453                                                                         text = textNode.value.replace(startWhiteSpaceRegExp, '');\r
3454 \r
3455                                                                         if (text.length > 0) {\r
3456                                                                                 textNode.value = text;\r
3457                                                                                 textNode = textNode.next;\r
3458                                                                         } else {\r
3459                                                                                 sibling = textNode.next;\r
3460                                                                                 textNode.remove();\r
3461                                                                                 textNode = sibling;\r
3462                                                                         }\r
3463                                                                 }\r
3464 \r
3465                                                                 // Trim whitespace at end of block\r
3466                                                                 for (textNode = node.lastChild; textNode && textNode.type === 3; ) {\r
3467                                                                         text = textNode.value.replace(endWhiteSpaceRegExp, '');\r
3468 \r
3469                                                                         if (text.length > 0) {\r
3470                                                                                 textNode.value = text;\r
3471                                                                                 textNode = textNode.prev;\r
3472                                                                         } else {\r
3473                                                                                 sibling = textNode.prev;\r
3474                                                                                 textNode.remove();\r
3475                                                                                 textNode = sibling;\r
3476                                                                         }\r
3477                                                                 }\r
3478                                                         }\r
3479 \r
3480                                                         // Trim start white space\r
3481                                                         textNode = node.prev;\r
3482                                                         if (textNode && textNode.type === 3) {\r
3483                                                                 text = textNode.value.replace(startWhiteSpaceRegExp, '');\r
3484 \r
3485                                                                 if (text.length > 0)\r
3486                                                                         textNode.value = text;\r
3487                                                                 else\r
3488                                                                         textNode.remove();\r
3489                                                         }\r
3490                                                 }\r
3491 \r
3492                                                 // Check if we exited a whitespace preserved element\r
3493                                                 if (isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {\r
3494                                                         isInWhiteSpacePreservedElement = false;\r
3495                                                 }\r
3496 \r
3497                                                 // Handle empty nodes\r
3498                                                 if (elementRule.removeEmpty || elementRule.paddEmpty) {\r
3499                                                         if (node.isEmpty(nonEmptyElements)) {\r
3500                                                                 if (elementRule.paddEmpty)\r
3501                                                                         node.empty().append(new Node('#text', '3')).value = '\u00a0';\r
3502                                                                 else {\r
3503                                                                         // Leave nodes that have a name like <a name="name">\r
3504                                                                         if (!node.attributes.map.name) {\r
3505                                                                                 tempNode = node.parent;\r
3506                                                                                 node.empty().remove();\r
3507                                                                                 node = tempNode;\r
3508                                                                                 return;\r
3509                                                                         }\r
3510                                                                 }\r
3511                                                         }\r
3512                                                 }\r
3513 \r
3514                                                 node = node.parent;\r
3515                                         }\r
3516                                 }\r
3517                         }, schema);\r
3518 \r
3519                         rootNode = node = new Node(args.context || settings.root_name, 11);\r
3520 \r
3521                         parser.parse(html);\r
3522 \r
3523                         // Fix invalid children or report invalid children in a contextual parsing\r
3524                         if (validate && invalidChildren.length) {\r
3525                                 if (!args.context)\r
3526                                         fixInvalidChildren(invalidChildren);\r
3527                                 else\r
3528                                         args.invalid = true;\r
3529                         }\r
3530 \r
3531                         // Wrap nodes in the root into block elements if the root is body\r
3532                         if (rootBlockName && rootNode.name == 'body')\r
3533                                 addRootBlocks();\r
3534 \r
3535                         // Run filters only when the contents is valid\r
3536                         if (!args.invalid) {\r
3537                                 // Run node filters\r
3538                                 for (name in matchedNodes) {\r
3539                                         list = nodeFilters[name];\r
3540                                         nodes = matchedNodes[name];\r
3541 \r
3542                                         // Remove already removed children\r
3543                                         fi = nodes.length;\r
3544                                         while (fi--) {\r
3545                                                 if (!nodes[fi].parent)\r
3546                                                         nodes.splice(fi, 1);\r
3547                                         }\r
3548 \r
3549                                         for (i = 0, l = list.length; i < l; i++)\r
3550                                                 list[i](nodes, name, args);\r
3551                                 }\r
3552 \r
3553                                 // Run attribute filters\r
3554                                 for (i = 0, l = attributeFilters.length; i < l; i++) {\r
3555                                         list = attributeFilters[i];\r
3556 \r
3557                                         if (list.name in matchedAttributes) {\r
3558                                                 nodes = matchedAttributes[list.name];\r
3559 \r
3560                                                 // Remove already removed children\r
3561                                                 fi = nodes.length;\r
3562                                                 while (fi--) {\r
3563                                                         if (!nodes[fi].parent)\r
3564                                                                 nodes.splice(fi, 1);\r
3565                                                 }\r
3566 \r
3567                                                 for (fi = 0, fl = list.callbacks.length; fi < fl; fi++)\r
3568                                                         list.callbacks[fi](nodes, list.name, args);\r
3569                                         }\r
3570                                 }\r
3571                         }\r
3572 \r
3573                         return rootNode;\r
3574                 };\r
3575 \r
3576                 // Remove <br> at end of block elements Gecko and WebKit injects BR elements to\r
3577                 // make it possible to place the caret inside empty blocks. This logic tries to remove\r
3578                 // these elements and keep br elements that where intended to be there intact\r
3579                 if (settings.remove_trailing_brs) {\r
3580                         self.addNodeFilter('br', function(nodes, name) {\r
3581                                 var i, l = nodes.length, node, blockElements = schema.getBlockElements(),\r
3582                                         nonEmptyElements = schema.getNonEmptyElements(), parent, prev, prevName;\r
3583 \r
3584                                 // Remove brs from body element as well\r
3585                                 blockElements.body = 1;\r
3586 \r
3587                                 // Must loop forwards since it will otherwise remove all brs in <p>a<br><br><br></p>\r
3588                                 for (i = 0; i < l; i++) {\r
3589                                         node = nodes[i];\r
3590                                         parent = node.parent;\r
3591 \r
3592                                         if (blockElements[node.parent.name] && node === parent.lastChild) {\r
3593                                                 // Loop all nodes to the right of the current node and check for other BR elements\r
3594                                                 // excluding bookmarks since they are invisible\r
3595                                                 prev = node.prev;\r
3596                                                 while (prev) {\r
3597                                                         prevName = prev.name;\r
3598 \r
3599                                                         // Ignore bookmarks\r
3600                                                         if (prevName !== "span" || prev.attr('data-mce-type') !== 'bookmark') {\r
3601                                                                 // Found a non BR element\r
3602                                                                 if (prevName !== "br")\r
3603                                                                         break;\r
3604         \r
3605                                                                 // Found another br it's a <br><br> structure then don't remove anything\r
3606                                                                 if (prevName === 'br') {\r
3607                                                                         node = null;\r
3608                                                                         break;\r
3609                                                                 }\r
3610                                                         }\r
3611 \r
3612                                                         prev = prev.prev;\r
3613                                                 }\r
3614 \r
3615                                                 if (node) {\r
3616                                                         node.remove();\r
3617 \r
3618                                                         // Is the parent to be considered empty after we removed the BR\r
3619                                                         if (parent.isEmpty(nonEmptyElements)) {\r
3620                                                                 elementRule = schema.getElementRule(parent.name);\r
3621 \r
3622                                                                 // Remove or padd the element depending on schema rule\r
3623                                                                 if (elementRule) {\r
3624                                                                         if (elementRule.removeEmpty)\r
3625                                                                                 parent.remove();\r
3626                                                                         else if (elementRule.paddEmpty)\r
3627                                                                                 parent.empty().append(new tinymce.html.Node('#text', 3)).value = '\u00a0';\r
3628                                                                 }\r
3629                                                         }\r
3630                                                 }\r
3631                                         }\r
3632                                 }\r
3633                         });\r
3634                 }\r
3635         }\r
3636 })(tinymce);\r
3637 \r
3638 tinymce.html.Writer = function(settings) {\r
3639         var html = [], indent, indentBefore, indentAfter, encode, htmlOutput;\r
3640 \r
3641         settings = settings || {};\r
3642         indent = settings.indent;\r
3643         indentBefore = tinymce.makeMap(settings.indent_before || '');\r
3644         indentAfter = tinymce.makeMap(settings.indent_after || '');\r
3645         encode = tinymce.html.Entities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);\r
3646         htmlOutput = settings.element_format == "html";\r
3647 \r
3648         return {\r
3649                 start: function(name, attrs, empty) {\r
3650                         var i, l, attr, value;\r
3651 \r
3652                         if (indent && indentBefore[name] && html.length > 0) {\r
3653                                 value = html[html.length - 1];\r
3654 \r
3655                                 if (value.length > 0 && value !== '\n')\r
3656                                         html.push('\n');\r
3657                         }\r
3658 \r
3659                         html.push('<', name);\r
3660 \r
3661                         if (attrs) {\r
3662                                 for (i = 0, l = attrs.length; i < l; i++) {\r
3663                                         attr = attrs[i];\r
3664                                         html.push(' ', attr.name, '="', encode(attr.value, true), '"');\r
3665                                 }\r
3666                         }\r
3667 \r
3668                         if (!empty || htmlOutput)\r
3669                                 html[html.length] = '>';\r
3670                         else\r
3671                                 html[html.length] = ' />';\r
3672 \r
3673                         if (empty && indent && indentAfter[name] && html.length > 0) {\r
3674                                 value = html[html.length - 1];\r
3675 \r
3676                                 if (value.length > 0 && value !== '\n')\r
3677                                         html.push('\n');\r
3678                         }\r
3679                 },\r
3680 \r
3681                 end: function(name) {\r
3682                         var value;\r
3683 \r
3684                         /*if (indent && indentBefore[name] && html.length > 0) {\r
3685                                 value = html[html.length - 1];\r
3686 \r
3687                                 if (value.length > 0 && value !== '\n')\r
3688                                         html.push('\n');\r
3689                         }*/\r
3690 \r
3691                         html.push('</', name, '>');\r
3692 \r
3693                         if (indent && indentAfter[name] && html.length > 0) {\r
3694                                 value = html[html.length - 1];\r
3695 \r
3696                                 if (value.length > 0 && value !== '\n')\r
3697                                         html.push('\n');\r
3698                         }\r
3699                 },\r
3700 \r
3701                 text: function(text, raw) {\r
3702                         if (text.length > 0)\r
3703                                 html[html.length] = raw ? text : encode(text);\r
3704                 },\r
3705 \r
3706                 cdata: function(text) {\r
3707                         html.push('<![CDATA[', text, ']]>');\r
3708                 },\r
3709 \r
3710                 comment: function(text) {\r
3711                         html.push('<!--', text, '-->');\r
3712                 },\r
3713 \r
3714                 pi: function(name, text) {\r
3715                         if (text)\r
3716                                 html.push('<?', name, ' ', text, '?>');\r
3717                         else\r
3718                                 html.push('<?', name, '?>');\r
3719 \r
3720                         if (indent)\r
3721                                 html.push('\n');\r
3722                 },\r
3723 \r
3724                 doctype: function(text) {\r
3725                         html.push('<!DOCTYPE', text, '>', indent ? '\n' : '');\r
3726                 },\r
3727 \r
3728                 reset: function() {\r
3729                         html.length = 0;\r
3730                 },\r
3731 \r
3732                 getContent: function() {\r
3733                         return html.join('').replace(/\n$/, '');\r
3734                 }\r
3735         };\r
3736 };\r
3737 \r
3738 (function(tinymce) {\r
3739         tinymce.html.Serializer = function(settings, schema) {\r
3740                 var self = this, writer = new tinymce.html.Writer(settings);\r
3741 \r
3742                 settings = settings || {};\r
3743                 settings.validate = "validate" in settings ? settings.validate : true;\r
3744 \r
3745                 self.schema = schema = schema || new tinymce.html.Schema();\r
3746                 self.writer = writer;\r
3747 \r
3748                 self.serialize = function(node) {\r
3749                         var handlers, validate;\r
3750 \r
3751                         validate = settings.validate;\r
3752 \r
3753                         handlers = {\r
3754                                 // #text\r
3755                                 3: function(node, raw) {\r
3756                                         writer.text(node.value, node.raw);\r
3757                                 },\r
3758 \r
3759                                 // #comment\r
3760                                 8: function(node) {\r
3761                                         writer.comment(node.value);\r
3762                                 },\r
3763 \r
3764                                 // Processing instruction\r
3765                                 7: function(node) {\r
3766                                         writer.pi(node.name, node.value);\r
3767                                 },\r
3768 \r
3769                                 // Doctype\r
3770                                 10: function(node) {\r
3771                                         writer.doctype(node.value);\r
3772                                 },\r
3773 \r
3774                                 // CDATA\r
3775                                 4: function(node) {\r
3776                                         writer.cdata(node.value);\r
3777                                 },\r
3778 \r
3779                                 // Document fragment\r
3780                                 11: function(node) {\r
3781                                         if ((node = node.firstChild)) {\r
3782                                                 do {\r
3783                                                         walk(node);\r
3784                                                 } while (node = node.next);\r
3785                                         }\r
3786                                 }\r
3787                         };\r
3788 \r
3789                         writer.reset();\r
3790 \r
3791                         function walk(node) {\r
3792                                 var handler = handlers[node.type], name, isEmpty, attrs, attrName, attrValue, sortedAttrs, i, l, elementRule;\r
3793 \r
3794                                 if (!handler) {\r
3795                                         name = node.name;\r
3796                                         isEmpty = node.shortEnded;\r
3797                                         attrs = node.attributes;\r
3798 \r
3799                                         // Sort attributes\r
3800                                         if (validate && attrs && attrs.length > 1) {\r
3801                                                 sortedAttrs = [];\r
3802                                                 sortedAttrs.map = {};\r
3803 \r
3804                                                 elementRule = schema.getElementRule(node.name);\r
3805                                                 for (i = 0, l = elementRule.attributesOrder.length; i < l; i++) {\r
3806                                                         attrName = elementRule.attributesOrder[i];\r
3807 \r
3808                                                         if (attrName in attrs.map) {\r
3809                                                                 attrValue = attrs.map[attrName];\r
3810                                                                 sortedAttrs.map[attrName] = attrValue;\r
3811                                                                 sortedAttrs.push({name: attrName, value: attrValue});\r
3812                                                         }\r
3813                                                 }\r
3814 \r
3815                                                 for (i = 0, l = attrs.length; i < l; i++) {\r
3816                                                         attrName = attrs[i].name;\r
3817 \r
3818                                                         if (!(attrName in sortedAttrs.map)) {\r
3819                                                                 attrValue = attrs.map[attrName];\r
3820                                                                 sortedAttrs.map[attrName] = attrValue;\r
3821                                                                 sortedAttrs.push({name: attrName, value: attrValue});\r
3822                                                         }\r
3823                                                 }\r
3824 \r
3825                                                 attrs = sortedAttrs;\r
3826                                         }\r
3827 \r
3828                                         writer.start(node.name, attrs, isEmpty);\r
3829 \r
3830                                         if (!isEmpty) {\r
3831                                                 if ((node = node.firstChild)) {\r
3832                                                         do {\r
3833                                                                 walk(node);\r
3834                                                         } while (node = node.next);\r
3835                                                 }\r
3836 \r
3837                                                 writer.end(name);\r
3838                                         }\r
3839                                 } else\r
3840                                         handler(node);\r
3841                         }\r
3842 \r
3843                         // Serialize element and treat all non elements as fragments\r
3844                         if (node.type == 1 && !settings.inner)\r
3845                                 walk(node);\r
3846                         else\r
3847                                 handlers[11](node);\r
3848 \r
3849                         return writer.getContent();\r
3850                 };\r
3851         }\r
3852 })(tinymce);\r
3853 \r
3854 // JSLint defined globals\r
3855 /*global tinymce:false, window:false */\r
3856 \r
3857 tinymce.dom = {};\r
3858 \r
3859 (function(namespace, expando) {\r
3860         var w3cEventModel = !!document.addEventListener;\r
3861 \r
3862         function addEvent(target, name, callback, capture) {\r
3863                 if (target.addEventListener) {\r
3864                         target.addEventListener(name, callback, capture || false);\r
3865                 } else if (target.attachEvent) {\r
3866                         target.attachEvent('on' + name, callback);\r
3867                 }\r
3868         }\r
3869 \r
3870         function removeEvent(target, name, callback, capture) {\r
3871                 if (target.removeEventListener) {\r
3872                         target.removeEventListener(name, callback, capture || false);\r
3873                 } else if (target.detachEvent) {\r
3874                         target.detachEvent('on' + name, callback);\r
3875                 }\r
3876         }\r
3877 \r
3878         function fix(original_event, data) {\r
3879                 var name, event = data || {};\r
3880 \r
3881                 // Dummy function that gets replaced on the delegation state functions\r
3882                 function returnFalse() {\r
3883                         return false;\r
3884                 }\r
3885 \r
3886                 // Dummy function that gets replaced on the delegation state functions\r
3887                 function returnTrue() {\r
3888                         return true;\r
3889                 }\r
3890 \r
3891                 // Copy all properties from the original event\r
3892                 for (name in original_event) {\r
3893                         // layerX/layerY is deprecated in Chrome and produces a warning\r
3894                         if (name !== "layerX" && name !== "layerY") {\r
3895                                 event[name] = original_event[name];\r
3896                         }\r
3897                 }\r
3898 \r
3899                 // Normalize target IE uses srcElement\r
3900                 if (!event.target) {\r
3901                         event.target = event.srcElement || document;\r
3902                 }\r
3903 \r
3904                 // Add preventDefault method\r
3905                 event.preventDefault = function() {\r
3906                         event.isDefaultPrevented = returnTrue;\r
3907 \r
3908                         // Execute preventDefault on the original event object\r
3909                         if (original_event) {\r
3910                                 if (original_event.preventDefault) {\r
3911                                         original_event.preventDefault();\r
3912                                 } else {\r
3913                                         original_event.returnValue = false; // IE\r
3914                                 }\r
3915                         }\r
3916                 };\r
3917 \r
3918                 // Add stopPropagation\r
3919                 event.stopPropagation = function() {\r
3920                         event.isPropagationStopped = returnTrue;\r
3921 \r
3922                         // Execute stopPropagation on the original event object\r
3923                         if (original_event) {\r
3924                                 if (original_event.stopPropagation) {\r
3925                                         original_event.stopPropagation();\r
3926                                 } else {\r
3927                                         original_event.cancelBubble = true; // IE\r
3928                                 }\r
3929                          }\r
3930                 };\r
3931 \r
3932                 // Add stopImmediatePropagation\r
3933                 event.stopImmediatePropagation = function() {\r
3934                         event.isImmediatePropagationStopped = returnTrue;\r
3935                         event.stopPropagation();\r
3936                 };\r
3937 \r
3938                 // Add event delegation states\r
3939                 if (!event.isDefaultPrevented) {\r
3940                         event.isDefaultPrevented = returnFalse;\r
3941                         event.isPropagationStopped = returnFalse;\r
3942                         event.isImmediatePropagationStopped = returnFalse;\r
3943                 }\r
3944 \r
3945                 return event;\r
3946         }\r
3947 \r
3948         function bindOnReady(win, callback, event_utils) {\r
3949                 var doc = win.document, event = {type: 'ready'};\r
3950 \r
3951                 // Gets called when the DOM is ready\r
3952                 function readyHandler() {\r
3953                         if (!event_utils.domLoaded) {\r
3954                                 event_utils.domLoaded = true;\r
3955                                 callback(event);\r
3956                         }\r
3957                 }\r
3958 \r
3959                 // Use W3C method\r
3960                 if (w3cEventModel) {\r
3961                         addEvent(win, 'DOMContentLoaded', readyHandler);\r
3962                 } else {\r
3963                         // Use IE method\r
3964                         addEvent(doc, "readystatechange", function() {\r
3965                                 if (doc.readyState === "complete") {\r
3966                                         removeEvent(doc, "readystatechange", arguments.callee);\r
3967                                         readyHandler();\r
3968                                 }\r
3969                         });\r
3970 \r
3971                         // Wait until we can scroll, when we can the DOM is initialized\r
3972                         if (doc.documentElement.doScroll && win === win.top) {\r
3973                                 (function() {\r
3974                                         try {\r
3975                                                 // If IE is used, use the trick by Diego Perini licensed under MIT by request to the author.\r
3976                                                 // http://javascript.nwbox.com/IEContentLoaded/\r
3977                                                 doc.documentElement.doScroll("left");\r
3978                                         } catch (ex) {\r
3979                                                 setTimeout(arguments.callee, 0);\r
3980                                                 return;\r
3981                                         }\r
3982 \r
3983                                         readyHandler();\r
3984                                 })();\r
3985                         }\r
3986                 }\r
3987 \r
3988                 // Fallback if any of the above methods should fail for some odd reason\r
3989                 addEvent(win, 'load', readyHandler);\r
3990         }\r
3991 \r
3992         function EventUtils(proxy) {\r
3993                 var self = this, events = {}, count, isFocusBlurBound, hasFocusIn, hasMouseEnterLeave, mouseEnterLeave;\r
3994 \r
3995                 hasMouseEnterLeave = "onmouseenter" in document.documentElement;\r
3996                 hasFocusIn = "onfocusin" in document.documentElement;\r
3997                 mouseEnterLeave = {mouseenter: 'mouseover', mouseleave: 'mouseout'};\r
3998                 count = 1;\r
3999 \r
4000                 // State if the DOMContentLoaded was executed or not\r
4001                 self.domLoaded = false;\r
4002                 self.events = events;\r
4003 \r
4004                 function executeHandlers(evt, id) {\r
4005                         var callbackList, i, l, callback;\r
4006 \r
4007                         callbackList = events[id][evt.type];\r
4008                         if (callbackList) {\r
4009                                 for (i = 0, l = callbackList.length; i < l; i++) {\r
4010                                         callback = callbackList[i];\r
4011                                         \r
4012                                         // Check if callback exists might be removed if a unbind is called inside the callback\r
4013                                         if (callback && callback.func.call(callback.scope, evt) === false) {\r
4014                                                 evt.preventDefault();\r
4015                                         }\r
4016 \r
4017                                         // Should we stop propagation to immediate listeners\r
4018                                         if (evt.isImmediatePropagationStopped()) {\r
4019                                                 return;\r
4020                                         }\r
4021                                 }\r
4022                         }\r
4023                 }\r
4024 \r
4025                 self.bind = function(target, names, callback, scope) {\r
4026                         var id, callbackList, i, name, fakeName, nativeHandler, capture, win = window;\r
4027 \r
4028                         // Native event handler function patches the event and executes the callbacks for the expando\r
4029                         function defaultNativeHandler(evt) {\r
4030                                 executeHandlers(fix(evt || win.event), id);\r
4031                         }\r
4032 \r
4033                         // Don't bind to text nodes or comments\r
4034                         if (!target || target.nodeType === 3 || target.nodeType === 8) {\r
4035                                 return;\r
4036                         }\r
4037 \r
4038                         // Create or get events id for the target\r
4039                         if (!target[expando]) {\r
4040                                 id = count++;\r
4041                                 target[expando] = id;\r
4042                                 events[id] = {};\r
4043                         } else {\r
4044                                 id = target[expando];\r
4045 \r
4046                                 if (!events[id]) {\r
4047                                         events[id] = {};\r
4048                                 }\r
4049                         }\r
4050 \r
4051                         // Setup the specified scope or use the target as a default\r
4052                         scope = scope || target;\r
4053 \r
4054                         // Split names and bind each event, enables you to bind multiple events with one call\r
4055                         names = names.split(' ');\r
4056                         i = names.length;\r
4057                         while (i--) {\r
4058                                 name = names[i];\r
4059                                 nativeHandler = defaultNativeHandler;\r
4060                                 fakeName = capture = false;\r
4061 \r
4062                                 // Use ready instead of DOMContentLoaded\r
4063                                 if (name === "DOMContentLoaded") {\r
4064                                         name = "ready";\r
4065                                 }\r
4066 \r
4067                                 // DOM is already ready\r
4068                                 if ((self.domLoaded || target.readyState == 'complete') && name === "ready") {\r
4069                                         self.domLoaded = true;\r
4070                                         callback.call(scope, fix({type: name}));\r
4071                                         continue;\r
4072                                 }\r
4073 \r
4074                                 // Handle mouseenter/mouseleaver\r
4075                                 if (!hasMouseEnterLeave) {\r
4076                                         fakeName = mouseEnterLeave[name];\r
4077 \r
4078                                         if (fakeName) {\r
4079                                                 nativeHandler = function(evt) {\r
4080                                                         var current, related;\r
4081 \r
4082                                                         current = evt.currentTarget;\r
4083                                                         related = evt.relatedTarget;\r
4084 \r
4085                                                         // Check if related is inside the current target if it's not then the event should be ignored since it's a mouseover/mouseout inside the element\r
4086                                                         if (related && current.contains) {\r
4087                                                                 // Use contains for performance\r
4088                                                                 related = current.contains(related);\r
4089                                                         } else {\r
4090                                                                 while (related && related !== current) {\r
4091                                                                         related = related.parentNode;\r
4092                                                                 }\r
4093                                                         }\r
4094 \r
4095                                                         // Fire fake event\r
4096                                                         if (!related) {\r
4097                                                                 evt = fix(evt || win.event);\r
4098                                                                 evt.type = evt.type === 'mouseout' ? 'mouseleave' : 'mouseenter';\r
4099                                                                 evt.target = current;\r
4100                                                                 executeHandlers(evt, id);\r
4101                                                         }\r
4102                                                 };\r
4103                                         }\r
4104                                 }\r
4105 \r
4106                                 // Fake bubbeling of focusin/focusout\r
4107                                 if (!hasFocusIn && (name === "focusin" || name === "focusout")) {\r
4108                                         capture = true;\r
4109                                         fakeName = name === "focusin" ? "focus" : "blur";\r
4110                                         nativeHandler = function(evt) {\r
4111                                                 evt = fix(evt || win.event);\r
4112                                                 evt.type = evt.type === 'focus' ? 'focusin' : 'focusout';\r
4113                                                 executeHandlers(evt, id);\r
4114                                         };\r
4115                                 }\r
4116 \r
4117                                 // Setup callback list and bind native event\r
4118                                 callbackList = events[id][name];\r
4119                                 if (!callbackList) {\r
4120                                         events[id][name] = callbackList = [{func: callback, scope: scope}];\r
4121                                         callbackList.fakeName = fakeName;\r
4122                                         callbackList.capture = capture;\r
4123 \r
4124                                         // Add the nativeHandler to the callback list so that we can later unbind it\r
4125                                         callbackList.nativeHandler = nativeHandler;\r
4126                                         if (!w3cEventModel) {\r
4127                                                 callbackList.proxyHandler = proxy(id);\r
4128                                         }\r
4129 \r
4130                                         // Check if the target has native events support\r
4131                                         if (name === "ready") {\r
4132                                                 bindOnReady(target, nativeHandler, self);\r
4133                                         } else {\r
4134                                                 addEvent(target, fakeName || name, w3cEventModel ? nativeHandler : callbackList.proxyHandler, capture);\r
4135                                         }\r
4136                                 } else {\r
4137                                         // If it already has an native handler then just push the callback\r
4138                                         callbackList.push({func: callback, scope: scope});\r
4139                                 }\r
4140                         }\r
4141 \r
4142                         target = callbackList = 0; // Clean memory for IE\r
4143 \r
4144                         return callback;\r
4145                 };\r
4146 \r
4147                 self.unbind = function(target, names, callback) {\r
4148                         var id, callbackList, i, ci, name, eventMap;\r
4149 \r
4150                         // Don't bind to text nodes or comments\r
4151                         if (!target || target.nodeType === 3 || target.nodeType === 8) {\r
4152                                 return self;\r
4153                         }\r
4154 \r
4155                         // Unbind event or events if the target has the expando\r
4156                         id = target[expando];\r
4157                         if (id) {\r
4158                                 eventMap = events[id];\r
4159 \r
4160                                 // Specific callback\r
4161                                 if (names) {\r
4162                                         names = names.split(' ');\r
4163                                         i = names.length;\r
4164                                         while (i--) {\r
4165                                                 name = names[i];\r
4166                                                 callbackList = eventMap[name];\r
4167 \r
4168                                                 // Unbind the event if it exists in the map\r
4169                                                 if (callbackList) {\r
4170                                                         // Remove specified callback\r
4171                                                         if (callback) {\r
4172                                                                 ci = callbackList.length;\r
4173                                                                 while (ci--) {\r
4174                                                                         if (callbackList[ci].func === callback) {\r
4175                                                                                 callbackList.splice(ci, 1);\r
4176                                                                         }\r
4177                                                                 }\r
4178                                                         }\r
4179 \r
4180                                                         // Remove all callbacks if there isn't a specified callback or there is no callbacks left\r
4181                                                         if (!callback || callbackList.length === 0) {\r
4182                                                                 delete eventMap[name];\r
4183                                                                 removeEvent(target, callbackList.fakeName || name, w3cEventModel ? callbackList.nativeHandler : callbackList.proxyHandler, callbackList.capture);\r
4184                                                         }\r
4185                                                 }\r
4186                                         }\r
4187                                 } else {\r
4188                                         // All events for a specific element\r
4189                                         for (name in eventMap) {\r
4190                                                 callbackList = eventMap[name];\r
4191                                                 removeEvent(target, callbackList.fakeName || name, w3cEventModel ? callbackList.nativeHandler : callbackList.proxyHandler, callbackList.capture);\r
4192                                         }\r
4193 \r
4194                                         eventMap = {};\r
4195                                 }\r
4196 \r
4197                                 // Check if object is empty, if it isn't then we won't remove the expando map\r
4198                                 for (name in eventMap) {\r
4199                                         return self;\r
4200                                 }\r
4201 \r
4202                                 // Delete event object\r
4203                                 delete events[id];\r
4204 \r
4205                                 // Remove expando from target\r
4206                                 try {\r
4207                                         // IE will fail here since it can't delete properties from window\r
4208                                         delete target[expando];\r
4209                                 } catch (ex) {\r
4210                                         // IE will set it to null\r
4211                                         target[expando] = null;\r
4212                                 }\r
4213                         }\r
4214 \r
4215                         return self;\r
4216                 };\r
4217 \r
4218                 self.fire = function(target, name, args) {\r
4219                         var id, event;\r
4220 \r
4221                         // Don't bind to text nodes or comments\r
4222                         if (!target || target.nodeType === 3 || target.nodeType === 8) {\r
4223                                 return self;\r
4224                         }\r
4225 \r
4226                         // Build event object by patching the args\r
4227                         event = fix(null, args);\r
4228                         event.type = name;\r
4229 \r
4230                         do {\r
4231                                 // Found an expando that means there is listeners to execute\r
4232                                 id = target[expando];\r
4233                                 if (id) {\r
4234                                         executeHandlers(event, id);\r
4235                                 }\r
4236 \r
4237                                 // Walk up the DOM\r
4238                                 target = target.parentNode || target.ownerDocument || target.defaultView || target.parentWindow;\r
4239                         } while (target && !event.isPropagationStopped());\r
4240 \r
4241                         return self;\r
4242                 };\r
4243 \r
4244                 self.clean = function(target) {\r
4245                         var i, children, unbind = self.unbind;\r
4246         \r
4247                         // Don't bind to text nodes or comments\r
4248                         if (!target || target.nodeType === 3 || target.nodeType === 8) {\r
4249                                 return self;\r
4250                         }\r
4251 \r
4252                         // Unbind any element on the specificed target\r
4253                         if (target[expando]) {\r
4254                                 unbind(target);\r
4255                         }\r
4256 \r
4257                         // Target doesn't have getElementsByTagName it's probably a window object then use it's document to find the children\r
4258                         if (!target.getElementsByTagName) {\r
4259                                 target = target.document;\r
4260                         }\r
4261 \r
4262                         // Remove events from each child element\r
4263                         if (target && target.getElementsByTagName) {\r
4264                                 unbind(target);\r
4265 \r
4266                                 children = target.getElementsByTagName('*');\r
4267                                 i = children.length;\r
4268                                 while (i--) {\r
4269                                         target = children[i];\r
4270 \r
4271                                         if (target[expando]) {\r
4272                                                 unbind(target);\r
4273                                         }\r
4274                                 }\r
4275                         }\r
4276 \r
4277                         return self;\r
4278                 };\r
4279 \r
4280                 self.callNativeHandler = function(id, evt) {\r
4281                         if (events) {\r
4282                                 events[id][evt.type].nativeHandler(evt);\r
4283                         }\r
4284                 };\r
4285 \r
4286                 self.destory = function() {\r
4287                         events = {};\r
4288                 };\r
4289 \r
4290                 // Legacy function calls\r
4291 \r
4292                 self.add = function(target, events, func, scope) {\r
4293                         // Old API supported direct ID assignment\r
4294                         if (typeof(target) === "string") {\r
4295                                 target = document.getElementById(target);\r
4296                         }\r
4297 \r
4298                         // Old API supported multiple targets\r
4299                         if (target && target instanceof Array) {\r
4300                                 var i = target;\r
4301 \r
4302                                 while (i--) {\r
4303                                         self.add(target[i], events, func, scope);\r
4304                                 }\r
4305 \r
4306                                 return;\r
4307                         }\r
4308 \r
4309                         // Old API called ready init\r
4310                         if (events === "init") {\r
4311                                 events = "ready";\r
4312                         }\r
4313 \r
4314                         return self.bind(target, events instanceof Array ? events.join(' ') : events, func, scope);\r
4315                 };\r
4316 \r
4317                 self.remove = function(target, events, func) {\r
4318                         // Old API supported direct ID assignment\r
4319                         if (typeof(target) === "string") {\r
4320                                 target = document.getElementById(target);\r
4321                         }\r
4322 \r
4323                         // Old API supported multiple targets\r
4324                         if (target instanceof Array) {\r
4325                                 var i = target;\r
4326 \r
4327                                 while (i--) {\r
4328                                         self.remove(target[i], events, func, scope);\r
4329                                 }\r
4330 \r
4331                                 return self;\r
4332                         }\r
4333 \r
4334                         return self.unbind(target, events instanceof Array ? events.join(' ') : events, func);\r
4335                 };\r
4336 \r
4337                 self.clear = function(target) {\r
4338                         // Old API supported direct ID assignment\r
4339                         if (typeof(target) === "string") {\r
4340                                 target = document.getElementById(target);\r
4341                         }\r
4342 \r
4343                         return self.clean(target);\r
4344                 };\r
4345 \r
4346                 self.cancel = function(e) {\r
4347                         if (e) {\r
4348                                 self.prevent(e);\r
4349                                 self.stop(e);\r
4350                         }\r
4351 \r
4352                         return false;\r
4353                 };\r
4354 \r
4355                 self.prevent = function(e) {\r
4356                         e.preventDefault();\r
4357 \r
4358                         return false;\r
4359                 };\r
4360 \r
4361                 self.stop = function(e) {\r
4362                         e.stopPropagation();\r
4363 \r
4364                         return false;\r
4365                 };\r
4366         }\r
4367 \r
4368         namespace.EventUtils = EventUtils;\r
4369 \r
4370         namespace.Event = new EventUtils(function(id) {\r
4371                 return function(evt) {\r
4372                         tinymce.dom.Event.callNativeHandler(id, evt);\r
4373                 };\r
4374         });\r
4375 \r
4376         // Bind ready event when tinymce script is loaded\r
4377         namespace.Event.bind(window, 'ready', function() {});\r
4378 \r
4379         namespace = 0;\r
4380 })(tinymce.dom, 'data-mce-expando'); // Namespace and expando\r
4381 \r
4382 (function(tinymce) {\r
4383         // Shorten names\r
4384         var each = tinymce.each,\r
4385                 is = tinymce.is,\r
4386                 isWebKit = tinymce.isWebKit,\r
4387                 isIE = tinymce.isIE,\r
4388                 Entities = tinymce.html.Entities,\r
4389                 simpleSelectorRe = /^([a-z0-9],?)+$/i,\r
4390                 whiteSpaceRegExp = /^[ \t\r\n]*$/;\r
4391 \r
4392         tinymce.create('tinymce.dom.DOMUtils', {\r
4393                 doc : null,\r
4394                 root : null,\r
4395                 files : null,\r
4396                 pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,\r
4397                 props : {\r
4398                         "for" : "htmlFor",\r
4399                         "class" : "className",\r
4400                         className : "className",\r
4401                         checked : "checked",\r
4402                         disabled : "disabled",\r
4403                         maxlength : "maxLength",\r
4404                         readonly : "readOnly",\r
4405                         selected : "selected",\r
4406                         value : "value",\r
4407                         id : "id",\r
4408                         name : "name",\r
4409                         type : "type"\r
4410                 },\r
4411 \r
4412                 DOMUtils : function(d, s) {\r
4413                         var t = this, globalStyle, name, blockElementsMap;\r
4414 \r
4415                         t.doc = d;\r
4416                         t.win = window;\r
4417                         t.files = {};\r
4418                         t.cssFlicker = false;\r
4419                         t.counter = 0;\r
4420                         t.stdMode = !tinymce.isIE || d.documentMode >= 8;\r
4421                         t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat" || t.stdMode;\r
4422                         t.hasOuterHTML = "outerHTML" in d.createElement("a");\r
4423 \r
4424                         t.settings = s = tinymce.extend({\r
4425                                 keep_values : false,\r
4426                                 hex_colors : 1\r
4427                         }, s);\r
4428                         \r
4429                         t.schema = s.schema;\r
4430                         t.styles = new tinymce.html.Styles({\r
4431                                 url_converter : s.url_converter,\r
4432                                 url_converter_scope : s.url_converter_scope\r
4433                         }, s.schema);\r
4434 \r
4435                         // Fix IE6SP2 flicker and check it failed for pre SP2\r
4436                         if (tinymce.isIE6) {\r
4437                                 try {\r
4438                                         d.execCommand('BackgroundImageCache', false, true);\r
4439                                 } catch (e) {\r
4440                                         t.cssFlicker = true;\r
4441                                 }\r
4442                         }\r
4443 \r
4444                         t.fixDoc(d);\r
4445                         t.events = s.ownEvents ? new tinymce.dom.EventUtils(s.proxy) : tinymce.dom.Event;\r
4446                         tinymce.addUnload(t.destroy, t);\r
4447                         blockElementsMap = s.schema ? s.schema.getBlockElements() : {};\r
4448 \r
4449                         t.isBlock = function(node) {\r
4450                                 // This function is called in module pattern style since it might be executed with the wrong this scope\r
4451                                 var type = node.nodeType;\r
4452 \r
4453                                 // If it's a node then check the type and use the nodeName\r
4454                                 if (type)\r
4455                                         return !!(type === 1 && blockElementsMap[node.nodeName]);\r
4456 \r
4457                                 return !!blockElementsMap[node];\r
4458                         };\r
4459                 },\r
4460 \r
4461                 fixDoc: function(doc) {\r
4462                         var settings = this.settings, name;\r
4463 \r
4464                         if (isIE && settings.schema) {\r
4465                                 // Add missing HTML 4/5 elements to IE\r
4466                                 ('abbr article aside audio canvas ' +\r
4467                                 'details figcaption figure footer ' +\r
4468                                 'header hgroup mark menu meter nav ' +\r
4469                                 'output progress section summary ' +\r
4470                                 'time video').replace(/\w+/g, function(name) {\r
4471                                         doc.createElement(name);\r
4472                                 });\r
4473 \r
4474                                 // Create all custom elements\r
4475                                 for (name in settings.schema.getCustomElements()) {\r
4476                                         doc.createElement(name);\r
4477                                 }\r
4478                         }\r
4479                 },\r
4480 \r
4481                 clone: function(node, deep) {\r
4482                         var self = this, clone, doc;\r
4483 \r
4484                         // TODO: Add feature detection here in the future\r
4485                         if (!isIE || node.nodeType !== 1 || deep) {\r
4486                                 return node.cloneNode(deep);\r
4487                         }\r
4488 \r
4489                         doc = self.doc;\r
4490 \r
4491                         // Make a HTML5 safe shallow copy\r
4492                         if (!deep) {\r
4493                                 clone = doc.createElement(node.nodeName);\r
4494 \r
4495                                 // Copy attribs\r
4496                                 each(self.getAttribs(node), function(attr) {\r
4497                                         self.setAttrib(clone, attr.nodeName, self.getAttrib(node, attr.nodeName));\r
4498                                 });\r
4499 \r
4500                                 return clone;\r
4501                         }\r
4502 /*\r
4503                         // Setup HTML5 patched document fragment\r
4504                         if (!self.frag) {\r
4505                                 self.frag = doc.createDocumentFragment();\r
4506                                 self.fixDoc(self.frag);\r
4507                         }\r
4508 \r
4509                         // Make a deep copy by adding it to the document fragment then removing it this removed the :section\r
4510                         clone = doc.createElement('div');\r
4511                         self.frag.appendChild(clone);\r
4512                         clone.innerHTML = node.outerHTML;\r
4513                         self.frag.removeChild(clone);\r
4514 */\r
4515                         return clone.firstChild;\r
4516                 },\r
4517 \r
4518                 getRoot : function() {\r
4519                         var t = this, s = t.settings;\r
4520 \r
4521                         return (s && t.get(s.root_element)) || t.doc.body;\r
4522                 },\r
4523 \r
4524                 getViewPort : function(w) {\r
4525                         var d, b;\r
4526 \r
4527                         w = !w ? this.win : w;\r
4528                         d = w.document;\r
4529                         b = this.boxModel ? d.documentElement : d.body;\r
4530 \r
4531                         // Returns viewport size excluding scrollbars\r
4532                         return {\r
4533                                 x : w.pageXOffset || b.scrollLeft,\r
4534                                 y : w.pageYOffset || b.scrollTop,\r
4535                                 w : w.innerWidth || b.clientWidth,\r
4536                                 h : w.innerHeight || b.clientHeight\r
4537                         };\r
4538                 },\r
4539 \r
4540                 getRect : function(e) {\r
4541                         var p, t = this, sr;\r
4542 \r
4543                         e = t.get(e);\r
4544                         p = t.getPos(e);\r
4545                         sr = t.getSize(e);\r
4546 \r
4547                         return {\r
4548                                 x : p.x,\r
4549                                 y : p.y,\r
4550                                 w : sr.w,\r
4551                                 h : sr.h\r
4552                         };\r
4553                 },\r
4554 \r
4555                 getSize : function(e) {\r
4556                         var t = this, w, h;\r
4557 \r
4558                         e = t.get(e);\r
4559                         w = t.getStyle(e, 'width');\r
4560                         h = t.getStyle(e, 'height');\r
4561 \r
4562                         // Non pixel value, then force offset/clientWidth\r
4563                         if (w.indexOf('px') === -1)\r
4564                                 w = 0;\r
4565 \r
4566                         // Non pixel value, then force offset/clientWidth\r
4567                         if (h.indexOf('px') === -1)\r
4568                                 h = 0;\r
4569 \r
4570                         return {\r
4571                                 w : parseInt(w) || e.offsetWidth || e.clientWidth,\r
4572                                 h : parseInt(h) || e.offsetHeight || e.clientHeight\r
4573                         };\r
4574                 },\r
4575 \r
4576                 getParent : function(n, f, r) {\r
4577                         return this.getParents(n, f, r, false);\r
4578                 },\r
4579 \r
4580                 getParents : function(n, f, r, c) {\r
4581                         var t = this, na, se = t.settings, o = [];\r
4582 \r
4583                         n = t.get(n);\r
4584                         c = c === undefined;\r
4585 \r
4586                         if (se.strict_root)\r
4587                                 r = r || t.getRoot();\r
4588 \r
4589                         // Wrap node name as func\r
4590                         if (is(f, 'string')) {\r
4591                                 na = f;\r
4592 \r
4593                                 if (f === '*') {\r
4594                                         f = function(n) {return n.nodeType == 1;};\r
4595                                 } else {\r
4596                                         f = function(n) {\r
4597                                                 return t.is(n, na);\r
4598                                         };\r
4599                                 }\r
4600                         }\r
4601 \r
4602                         while (n) {\r
4603                                 if (n == r || !n.nodeType || n.nodeType === 9)\r
4604                                         break;\r
4605 \r
4606                                 if (!f || f(n)) {\r
4607                                         if (c)\r
4608                                                 o.push(n);\r
4609                                         else\r
4610                                                 return n;\r
4611                                 }\r
4612 \r
4613                                 n = n.parentNode;\r
4614                         }\r
4615 \r
4616                         return c ? o : null;\r
4617                 },\r
4618 \r
4619                 get : function(e) {\r
4620                         var n;\r
4621 \r
4622                         if (e && this.doc && typeof(e) == 'string') {\r
4623                                 n = e;\r
4624                                 e = this.doc.getElementById(e);\r
4625 \r
4626                                 // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick\r
4627                                 if (e && e.id !== n)\r
4628                                         return this.doc.getElementsByName(n)[1];\r
4629                         }\r
4630 \r
4631                         return e;\r
4632                 },\r
4633 \r
4634                 getNext : function(node, selector) {\r
4635                         return this._findSib(node, selector, 'nextSibling');\r
4636                 },\r
4637 \r
4638                 getPrev : function(node, selector) {\r
4639                         return this._findSib(node, selector, 'previousSibling');\r
4640                 },\r
4641 \r
4642 \r
4643                 select : function(pa, s) {\r
4644                         var t = this;\r
4645 \r
4646                         return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []);\r
4647                 },\r
4648 \r
4649                 is : function(n, selector) {\r
4650                         var i;\r
4651 \r
4652                         // If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance\r
4653                         if (n.length === undefined) {\r
4654                                 // Simple all selector\r
4655                                 if (selector === '*')\r
4656                                         return n.nodeType == 1;\r
4657 \r
4658                                 // Simple selector just elements\r
4659                                 if (simpleSelectorRe.test(selector)) {\r
4660                                         selector = selector.toLowerCase().split(/,/);\r
4661                                         n = n.nodeName.toLowerCase();\r
4662 \r
4663                                         for (i = selector.length - 1; i >= 0; i--) {\r
4664                                                 if (selector[i] == n)\r
4665                                                         return true;\r
4666                                         }\r
4667 \r
4668                                         return false;\r
4669                                 }\r
4670                         }\r
4671 \r
4672                         return tinymce.dom.Sizzle.matches(selector, n.nodeType ? [n] : n).length > 0;\r
4673                 },\r
4674 \r
4675 \r
4676                 add : function(p, n, a, h, c) {\r
4677                         var t = this;\r
4678 \r
4679                         return this.run(p, function(p) {\r
4680                                 var e, k;\r
4681 \r
4682                                 e = is(n, 'string') ? t.doc.createElement(n) : n;\r
4683                                 t.setAttribs(e, a);\r
4684 \r
4685                                 if (h) {\r
4686                                         if (h.nodeType)\r
4687                                                 e.appendChild(h);\r
4688                                         else\r
4689                                                 t.setHTML(e, h);\r
4690                                 }\r
4691 \r
4692                                 return !c ? p.appendChild(e) : e;\r
4693                         });\r
4694                 },\r
4695 \r
4696                 create : function(n, a, h) {\r
4697                         return this.add(this.doc.createElement(n), n, a, h, 1);\r
4698                 },\r
4699 \r
4700                 createHTML : function(n, a, h) {\r
4701                         var o = '', t = this, k;\r
4702 \r
4703                         o += '<' + n;\r
4704 \r
4705                         for (k in a) {\r
4706                                 if (a.hasOwnProperty(k))\r
4707                                         o += ' ' + k + '="' + t.encode(a[k]) + '"';\r
4708                         }\r
4709 \r
4710                         // A call to tinymce.is doesn't work for some odd reason on IE9 possible bug inside their JS runtime\r
4711                         if (typeof(h) != "undefined")\r
4712                                 return o + '>' + h + '</' + n + '>';\r
4713 \r
4714                         return o + ' />';\r
4715                 },\r
4716 \r
4717                 remove : function(node, keep_children) {\r
4718                         return this.run(node, function(node) {\r
4719                                 var child, parent = node.parentNode;\r
4720 \r
4721                                 if (!parent)\r
4722                                         return null;\r
4723 \r
4724                                 if (keep_children) {\r
4725                                         while (child = node.firstChild) {\r
4726                                                 // IE 8 will crash if you don't remove completely empty text nodes\r
4727                                                 if (!tinymce.isIE || child.nodeType !== 3 || child.nodeValue)\r
4728                                                         parent.insertBefore(child, node);\r
4729                                                 else\r
4730                                                         node.removeChild(child);\r
4731                                         }\r
4732                                 }\r
4733 \r
4734                                 return parent.removeChild(node);\r
4735                         });\r
4736                 },\r
4737 \r
4738                 setStyle : function(n, na, v) {\r
4739                         var t = this;\r
4740 \r
4741                         return t.run(n, function(e) {\r
4742                                 var s, i;\r
4743 \r
4744                                 s = e.style;\r
4745 \r
4746                                 // Camelcase it, if needed\r
4747                                 na = na.replace(/-(\D)/g, function(a, b){\r
4748                                         return b.toUpperCase();\r
4749                                 });\r
4750 \r
4751                                 // Default px suffix on these\r
4752                                 if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v)))\r
4753                                         v += 'px';\r
4754 \r
4755                                 switch (na) {\r
4756                                         case 'opacity':\r
4757                                                 // IE specific opacity\r
4758                                                 if (isIE) {\r
4759                                                         s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")";\r
4760 \r
4761                                                         if (!n.currentStyle || !n.currentStyle.hasLayout)\r
4762                                                                 s.display = 'inline-block';\r
4763                                                 }\r
4764 \r
4765                                                 // Fix for older browsers\r
4766                                                 s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || '';\r
4767                                                 break;\r
4768 \r
4769                                         case 'float':\r
4770                                                 isIE ? s.styleFloat = v : s.cssFloat = v;\r
4771                                                 break;\r
4772                                         \r
4773                                         default:\r
4774                                                 s[na] = v || '';\r
4775                                 }\r
4776 \r
4777                                 // Force update of the style data\r
4778                                 if (t.settings.update_styles)\r
4779                                         t.setAttrib(e, 'data-mce-style');\r
4780                         });\r
4781                 },\r
4782 \r
4783                 getStyle : function(n, na, c) {\r
4784                         n = this.get(n);\r
4785 \r
4786                         if (!n)\r
4787                                 return;\r
4788 \r
4789                         // Gecko\r
4790                         if (this.doc.defaultView && c) {\r
4791                                 // Remove camelcase\r
4792                                 na = na.replace(/[A-Z]/g, function(a){\r
4793                                         return '-' + a;\r
4794                                 });\r
4795 \r
4796                                 try {\r
4797                                         return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);\r
4798                                 } catch (ex) {\r
4799                                         // Old safari might fail\r
4800                                         return null;\r
4801                                 }\r
4802                         }\r
4803 \r
4804                         // Camelcase it, if needed\r
4805                         na = na.replace(/-(\D)/g, function(a, b){\r
4806                                 return b.toUpperCase();\r
4807                         });\r
4808 \r
4809                         if (na == 'float')\r
4810                                 na = isIE ? 'styleFloat' : 'cssFloat';\r
4811 \r
4812                         // IE & Opera\r
4813                         if (n.currentStyle && c)\r
4814                                 return n.currentStyle[na];\r
4815 \r
4816                         return n.style ? n.style[na] : undefined;\r
4817                 },\r
4818 \r
4819                 setStyles : function(e, o) {\r
4820                         var t = this, s = t.settings, ol;\r
4821 \r
4822                         ol = s.update_styles;\r
4823                         s.update_styles = 0;\r
4824 \r
4825                         each(o, function(v, n) {\r
4826                                 t.setStyle(e, n, v);\r
4827                         });\r
4828 \r
4829                         // Update style info\r
4830                         s.update_styles = ol;\r
4831                         if (s.update_styles)\r
4832                                 t.setAttrib(e, s.cssText);\r
4833                 },\r
4834 \r
4835                 removeAllAttribs: function(e) {\r
4836                         return this.run(e, function(e) {\r
4837                                 var i, attrs = e.attributes;\r
4838                                 for (i = attrs.length - 1; i >= 0; i--) {\r
4839                                         e.removeAttributeNode(attrs.item(i));\r
4840                                 }\r
4841                         });\r
4842                 },\r
4843 \r
4844                 setAttrib : function(e, n, v) {\r
4845                         var t = this;\r
4846 \r
4847                         // Whats the point\r
4848                         if (!e || !n)\r
4849                                 return;\r
4850 \r
4851                         // Strict XML mode\r
4852                         if (t.settings.strict)\r
4853                                 n = n.toLowerCase();\r
4854 \r
4855                         return this.run(e, function(e) {\r
4856                                 var s = t.settings;\r
4857                                 var originalValue = e.getAttribute(n);\r
4858                                 if (v !== null) {\r
4859                                         switch (n) {\r
4860                                                 case "style":\r
4861                                                         if (!is(v, 'string')) {\r
4862                                                                 each(v, function(v, n) {\r
4863                                                                         t.setStyle(e, n, v);\r
4864                                                                 });\r
4865 \r
4866                                                                 return;\r
4867                                                         }\r
4868 \r
4869                                                         // No mce_style for elements with these since they might get resized by the user\r
4870                                                         if (s.keep_values) {\r
4871                                                                 if (v && !t._isRes(v))\r
4872                                                                         e.setAttribute('data-mce-style', v, 2);\r
4873                                                                 else\r
4874                                                                         e.removeAttribute('data-mce-style', 2);\r
4875                                                         }\r
4876 \r
4877                                                         e.style.cssText = v;\r
4878                                                         break;\r
4879 \r
4880                                                 case "class":\r
4881                                                         e.className = v || ''; // Fix IE null bug\r
4882                                                         break;\r
4883 \r
4884                                                 case "src":\r
4885                                                 case "href":\r
4886                                                         if (s.keep_values) {\r
4887                                                                 if (s.url_converter)\r
4888                                                                         v = s.url_converter.call(s.url_converter_scope || t, v, n, e);\r
4889 \r
4890                                                                 t.setAttrib(e, 'data-mce-' + n, v, 2);\r
4891                                                         }\r
4892 \r
4893                                                         break;\r
4894 \r
4895                                                 case "shape":\r
4896                                                         e.setAttribute('data-mce-style', v);\r
4897                                                         break;\r
4898                                         }\r
4899                                 }\r
4900                                 if (is(v) && v !== null && v.length !== 0)\r
4901                                         e.setAttribute(n, '' + v, 2);\r
4902                                 else\r
4903                                         e.removeAttribute(n, 2);\r
4904 \r
4905                                 // fire onChangeAttrib event for attributes that have changed\r
4906                                 if (tinyMCE.activeEditor && originalValue != v) {\r
4907                                         var ed = tinyMCE.activeEditor;\r
4908                                         ed.onSetAttrib.dispatch(ed, e, n, v);\r
4909                                 }\r
4910                         });\r
4911                 },\r
4912 \r
4913                 setAttribs : function(e, o) {\r
4914                         var t = this;\r
4915 \r
4916                         return this.run(e, function(e) {\r
4917                                 each(o, function(v, n) {\r
4918                                         t.setAttrib(e, n, v);\r
4919                                 });\r
4920                         });\r
4921                 },\r
4922 \r
4923                 getAttrib : function(e, n, dv) {\r
4924                         var v, t = this, undef;\r
4925 \r
4926                         e = t.get(e);\r
4927 \r
4928                         if (!e || e.nodeType !== 1)\r
4929                                 return dv === undef ? false : dv;\r
4930 \r
4931                         if (!is(dv))\r
4932                                 dv = '';\r
4933 \r
4934                         // Try the mce variant for these\r
4935                         if (/^(src|href|style|coords|shape)$/.test(n)) {\r
4936                                 v = e.getAttribute("data-mce-" + n);\r
4937 \r
4938                                 if (v)\r
4939                                         return v;\r
4940                         }\r
4941 \r
4942                         if (isIE && t.props[n]) {\r
4943                                 v = e[t.props[n]];\r
4944                                 v = v && v.nodeValue ? v.nodeValue : v;\r
4945                         }\r
4946 \r
4947                         if (!v)\r
4948                                 v = e.getAttribute(n, 2);\r
4949 \r
4950                         // Check boolean attribs\r
4951                         if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(n)) {\r
4952                                 if (e[t.props[n]] === true && v === '')\r
4953                                         return n;\r
4954 \r
4955                                 return v ? n : '';\r
4956                         }\r
4957 \r
4958                         // Inner input elements will override attributes on form elements\r
4959                         if (e.nodeName === "FORM" && e.getAttributeNode(n))\r
4960                                 return e.getAttributeNode(n).nodeValue;\r
4961 \r
4962                         if (n === 'style') {\r
4963                                 v = v || e.style.cssText;\r
4964 \r
4965                                 if (v) {\r
4966                                         v = t.serializeStyle(t.parseStyle(v), e.nodeName);\r
4967 \r
4968                                         if (t.settings.keep_values && !t._isRes(v))\r
4969                                                 e.setAttribute('data-mce-style', v);\r
4970                                 }\r
4971                         }\r
4972 \r
4973                         // Remove Apple and WebKit stuff\r
4974                         if (isWebKit && n === "class" && v)\r
4975                                 v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, '');\r
4976 \r
4977                         // Handle IE issues\r
4978                         if (isIE) {\r
4979                                 switch (n) {\r
4980                                         case 'rowspan':\r
4981                                         case 'colspan':\r
4982                                                 // IE returns 1 as default value\r
4983                                                 if (v === 1)\r
4984                                                         v = '';\r
4985 \r
4986                                                 break;\r
4987 \r
4988                                         case 'size':\r
4989                                                 // IE returns +0 as default value for size\r
4990                                                 if (v === '+0' || v === 20 || v === 0)\r
4991                                                         v = '';\r
4992 \r
4993                                                 break;\r
4994 \r
4995                                         case 'width':\r
4996                                         case 'height':\r
4997                                         case 'vspace':\r
4998                                         case 'checked':\r
4999                                         case 'disabled':\r
5000                                         case 'readonly':\r
5001                                                 if (v === 0)\r
5002                                                         v = '';\r
5003 \r
5004                                                 break;\r
5005 \r
5006                                         case 'hspace':\r
5007                                                 // IE returns -1 as default value\r
5008                                                 if (v === -1)\r
5009                                                         v = '';\r
5010 \r
5011                                                 break;\r
5012 \r
5013                                         case 'maxlength':\r
5014                                         case 'tabindex':\r
5015                                                 // IE returns default value\r
5016                                                 if (v === 32768 || v === 2147483647 || v === '32768')\r
5017                                                         v = '';\r
5018 \r
5019                                                 break;\r
5020 \r
5021                                         case 'multiple':\r
5022                                         case 'compact':\r
5023                                         case 'noshade':\r
5024                                         case 'nowrap':\r
5025                                                 if (v === 65535)\r
5026                                                         return n;\r
5027 \r
5028                                                 return dv;\r
5029 \r
5030                                         case 'shape':\r
5031                                                 v = v.toLowerCase();\r
5032                                                 break;\r
5033 \r
5034                                         default:\r
5035                                                 // IE has odd anonymous function for event attributes\r
5036                                                 if (n.indexOf('on') === 0 && v)\r
5037                                                         v = tinymce._replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1', '' + v);\r
5038                                 }\r
5039                         }\r
5040 \r
5041                         return (v !== undef && v !== null && v !== '') ? '' + v : dv;\r
5042                 },\r
5043 \r
5044                 getPos : function(n, ro) {\r
5045                         var t = this, x = 0, y = 0, e, d = t.doc, r;\r
5046 \r
5047                         n = t.get(n);\r
5048                         ro = ro || d.body;\r
5049 \r
5050                         if (n) {\r
5051                                 // Use getBoundingClientRect if it exists since it's faster than looping offset nodes\r
5052                                 if (n.getBoundingClientRect) {\r
5053                                         n = n.getBoundingClientRect();\r
5054                                         e = t.boxModel ? d.documentElement : d.body;\r
5055 \r
5056                                         // Add scroll offsets from documentElement or body since IE with the wrong box model will use d.body and so do WebKit\r
5057                                         // Also remove the body/documentelement clientTop/clientLeft on IE 6, 7 since they offset the position\r
5058                                         x = n.left + (d.documentElement.scrollLeft || d.body.scrollLeft) - e.clientTop;\r
5059                                         y = n.top + (d.documentElement.scrollTop || d.body.scrollTop) - e.clientLeft;\r
5060 \r
5061                                         return {x : x, y : y};\r
5062                                 }\r
5063 \r
5064                                 r = n;\r
5065                                 while (r && r != ro && r.nodeType) {\r
5066                                         x += r.offsetLeft || 0;\r
5067                                         y += r.offsetTop || 0;\r
5068                                         r = r.offsetParent;\r
5069                                 }\r
5070 \r
5071                                 r = n.parentNode;\r
5072                                 while (r && r != ro && r.nodeType) {\r
5073                                         x -= r.scrollLeft || 0;\r
5074                                         y -= r.scrollTop || 0;\r
5075                                         r = r.parentNode;\r
5076                                 }\r
5077                         }\r
5078 \r
5079                         return {x : x, y : y};\r
5080                 },\r
5081 \r
5082                 parseStyle : function(st) {\r
5083                         return this.styles.parse(st);\r
5084                 },\r
5085 \r
5086                 serializeStyle : function(o, name) {\r
5087                         return this.styles.serialize(o, name);\r
5088                 },\r
5089 \r
5090                 loadCSS : function(u) {\r
5091                         var t = this, d = t.doc, head;\r
5092 \r
5093                         if (!u)\r
5094                                 u = '';\r
5095 \r
5096                         head = t.select('head')[0];\r
5097 \r
5098                         each(u.split(','), function(u) {\r
5099                                 var link;\r
5100 \r
5101                                 if (t.files[u])\r
5102                                         return;\r
5103 \r
5104                                 t.files[u] = true;\r
5105                                 link = t.create('link', {rel : 'stylesheet', href : tinymce._addVer(u)});\r
5106 \r
5107                                 // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug\r
5108                                 // This fix seems to resolve that issue by realcing the document ones a stylesheet finishes loading\r
5109                                 // It's ugly but it seems to work fine.\r
5110                                 if (isIE && d.documentMode && d.recalc) {\r
5111                                         link.onload = function() {\r
5112                                                 if (d.recalc)\r
5113                                                         d.recalc();\r
5114 \r
5115                                                 link.onload = null;\r
5116                                         };\r
5117                                 }\r
5118 \r
5119                                 head.appendChild(link);\r
5120                         });\r
5121                 },\r
5122 \r
5123                 addClass : function(e, c) {\r
5124                         return this.run(e, function(e) {\r
5125                                 var o;\r
5126 \r
5127                                 if (!c)\r
5128                                         return 0;\r
5129 \r
5130                                 if (this.hasClass(e, c))\r
5131                                         return e.className;\r
5132 \r
5133                                 o = this.removeClass(e, c);\r
5134 \r
5135                                 return e.className = (o != '' ? (o + ' ') : '') + c;\r
5136                         });\r
5137                 },\r
5138 \r
5139                 removeClass : function(e, c) {\r
5140                         var t = this, re;\r
5141 \r
5142                         return t.run(e, function(e) {\r
5143                                 var v;\r
5144 \r
5145                                 if (t.hasClass(e, c)) {\r
5146                                         if (!re)\r
5147                                                 re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g");\r
5148 \r
5149                                         v = e.className.replace(re, ' ');\r
5150                                         v = tinymce.trim(v != ' ' ? v : '');\r
5151 \r
5152                                         e.className = v;\r
5153 \r
5154                                         // Empty class attr\r
5155                                         if (!v) {\r
5156                                                 e.removeAttribute('class');\r
5157                                                 e.removeAttribute('className');\r
5158                                         }\r
5159 \r
5160                                         return v;\r
5161                                 }\r
5162 \r
5163                                 return e.className;\r
5164                         });\r
5165                 },\r
5166 \r
5167                 hasClass : function(n, c) {\r
5168                         n = this.get(n);\r
5169 \r
5170                         if (!n || !c)\r
5171                                 return false;\r
5172 \r
5173                         return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1;\r
5174                 },\r
5175 \r
5176                 show : function(e) {\r
5177                         return this.setStyle(e, 'display', 'block');\r
5178                 },\r
5179 \r
5180                 hide : function(e) {\r
5181                         return this.setStyle(e, 'display', 'none');\r
5182                 },\r
5183 \r
5184                 isHidden : function(e) {\r
5185                         e = this.get(e);\r
5186 \r
5187                         return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none';\r
5188                 },\r
5189 \r
5190                 uniqueId : function(p) {\r
5191                         return (!p ? 'mce_' : p) + (this.counter++);\r
5192                 },\r
5193 \r
5194                 setHTML : function(element, html) {\r
5195                         var self = this;\r
5196 \r
5197                         return self.run(element, function(element) {\r
5198                                 if (isIE) {\r
5199                                         // Remove all child nodes, IE keeps empty text nodes in DOM\r
5200                                         while (element.firstChild)\r
5201                                                 element.removeChild(element.firstChild);\r
5202 \r
5203                                         try {\r
5204                                                 // IE will remove comments from the beginning\r
5205                                                 // unless you padd the contents with something\r
5206                                                 element.innerHTML = '<br />' + html;\r
5207                                                 element.removeChild(element.firstChild);\r
5208                                         } catch (ex) {\r
5209                                                 // 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
5210                                                 // This seems to fix this problem\r
5211 \r
5212                                                 // Create new div with HTML contents and a BR infront to keep comments\r
5213                                                 element = self.create('div');\r
5214                                                 element.innerHTML = '<br />' + html;\r
5215 \r
5216                                                 // Add all children from div to target\r
5217                                                 each (element.childNodes, function(node, i) {\r
5218                                                         // Skip br element\r
5219                                                         if (i)\r
5220                                                                 element.appendChild(node);\r
5221                                                 });\r
5222                                         }\r
5223                                 } else\r
5224                                         element.innerHTML = html;\r
5225 \r
5226                                 return html;\r
5227                         });\r
5228                 },\r
5229 \r
5230                 getOuterHTML : function(elm) {\r
5231                         var doc, self = this;\r
5232 \r
5233                         elm = self.get(elm);\r
5234 \r
5235                         if (!elm)\r
5236                                 return null;\r
5237 \r
5238                         if (elm.nodeType === 1 && self.hasOuterHTML)\r
5239                                 return elm.outerHTML;\r
5240 \r
5241                         doc = (elm.ownerDocument || self.doc).createElement("body");\r
5242                         doc.appendChild(elm.cloneNode(true));\r
5243 \r
5244                         return doc.innerHTML;\r
5245                 },\r
5246 \r
5247                 setOuterHTML : function(e, h, d) {\r
5248                         var t = this;\r
5249 \r
5250                         function setHTML(e, h, d) {\r
5251                                 var n, tp;\r
5252 \r
5253                                 tp = d.createElement("body");\r
5254                                 tp.innerHTML = h;\r
5255 \r
5256                                 n = tp.lastChild;\r
5257                                 while (n) {\r
5258                                         t.insertAfter(n.cloneNode(true), e);\r
5259                                         n = n.previousSibling;\r
5260                                 }\r
5261 \r
5262                                 t.remove(e);\r
5263                         };\r
5264 \r
5265                         return this.run(e, function(e) {\r
5266                                 e = t.get(e);\r
5267 \r
5268                                 // Only set HTML on elements\r
5269                                 if (e.nodeType == 1) {\r
5270                                         d = d || e.ownerDocument || t.doc;\r
5271 \r
5272                                         if (isIE) {\r
5273                                                 try {\r
5274                                                         // Try outerHTML for IE it sometimes produces an unknown runtime error\r
5275                                                         if (isIE && e.nodeType == 1)\r
5276                                                                 e.outerHTML = h;\r
5277                                                         else\r
5278                                                                 setHTML(e, h, d);\r
5279                                                 } catch (ex) {\r
5280                                                         // Fix for unknown runtime error\r
5281                                                         setHTML(e, h, d);\r
5282                                                 }\r
5283                                         } else\r
5284                                                 setHTML(e, h, d);\r
5285                                 }\r
5286                         });\r
5287                 },\r
5288 \r
5289                 decode : Entities.decode,\r
5290 \r
5291                 encode : Entities.encodeAllRaw,\r
5292 \r
5293                 insertAfter : function(node, reference_node) {\r
5294                         reference_node = this.get(reference_node);\r
5295 \r
5296                         return this.run(node, function(node) {\r
5297                                 var parent, nextSibling;\r
5298 \r
5299                                 parent = reference_node.parentNode;\r
5300                                 nextSibling = reference_node.nextSibling;\r
5301 \r
5302                                 if (nextSibling)\r
5303                                         parent.insertBefore(node, nextSibling);\r
5304                                 else\r
5305                                         parent.appendChild(node);\r
5306 \r
5307                                 return node;\r
5308                         });\r
5309                 },\r
5310 \r
5311                 replace : function(n, o, k) {\r
5312                         var t = this;\r
5313 \r
5314                         if (is(o, 'array'))\r
5315                                 n = n.cloneNode(true);\r
5316 \r
5317                         return t.run(o, function(o) {\r
5318                                 if (k) {\r
5319                                         each(tinymce.grep(o.childNodes), function(c) {\r
5320                                                 n.appendChild(c);\r
5321                                         });\r
5322                                 }\r
5323 \r
5324                                 return o.parentNode.replaceChild(n, o);\r
5325                         });\r
5326                 },\r
5327 \r
5328                 rename : function(elm, name) {\r
5329                         var t = this, newElm;\r
5330 \r
5331                         if (elm.nodeName != name.toUpperCase()) {\r
5332                                 // Rename block element\r
5333                                 newElm = t.create(name);\r
5334 \r
5335                                 // Copy attribs to new block\r
5336                                 each(t.getAttribs(elm), function(attr_node) {\r
5337                                         t.setAttrib(newElm, attr_node.nodeName, t.getAttrib(elm, attr_node.nodeName));\r
5338                                 });\r
5339 \r
5340                                 // Replace block\r
5341                                 t.replace(newElm, elm, 1);\r
5342                         }\r
5343 \r
5344                         return newElm || elm;\r
5345                 },\r
5346 \r
5347                 findCommonAncestor : function(a, b) {\r
5348                         var ps = a, pe;\r
5349 \r
5350                         while (ps) {\r
5351                                 pe = b;\r
5352 \r
5353                                 while (pe && ps != pe)\r
5354                                         pe = pe.parentNode;\r
5355 \r
5356                                 if (ps == pe)\r
5357                                         break;\r
5358 \r
5359                                 ps = ps.parentNode;\r
5360                         }\r
5361 \r
5362                         if (!ps && a.ownerDocument)\r
5363                                 return a.ownerDocument.documentElement;\r
5364 \r
5365                         return ps;\r
5366                 },\r
5367 \r
5368                 toHex : function(s) {\r
5369                         var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);\r
5370 \r
5371                         function hex(s) {\r
5372                                 s = parseInt(s).toString(16);\r
5373 \r
5374                                 return s.length > 1 ? s : '0' + s; // 0 -> 00\r
5375                         };\r
5376 \r
5377                         if (c) {\r
5378                                 s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);\r
5379 \r
5380                                 return s;\r
5381                         }\r
5382 \r
5383                         return s;\r
5384                 },\r
5385 \r
5386                 getClasses : function() {\r
5387                         var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;\r
5388 \r
5389                         if (t.classes)\r
5390                                 return t.classes;\r
5391 \r
5392                         function addClasses(s) {\r
5393                                 // IE style imports\r
5394                                 each(s.imports, function(r) {\r
5395                                         addClasses(r);\r
5396                                 });\r
5397 \r
5398                                 each(s.cssRules || s.rules, function(r) {\r
5399                                         // Real type or fake it on IE\r
5400                                         switch (r.type || 1) {\r
5401                                                 // Rule\r
5402                                                 case 1:\r
5403                                                         if (r.selectorText) {\r
5404                                                                 each(r.selectorText.split(','), function(v) {\r
5405                                                                         v = v.replace(/^\s*|\s*$|^\s\./g, "");\r
5406 \r
5407                                                                         // Is internal or it doesn't contain a class\r
5408                                                                         if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v))\r
5409                                                                                 return;\r
5410 \r
5411                                                                         // Remove everything but class name\r
5412                                                                         ov = v;\r
5413                                                                         v = tinymce._replace(/.*\.([a-z0-9_\-]+).*/i, '$1', v);\r
5414 \r
5415                                                                         // Filter classes\r
5416                                                                         if (f && !(v = f(v, ov)))\r
5417                                                                                 return;\r
5418 \r
5419                                                                         if (!lo[v]) {\r
5420                                                                                 cl.push({'class' : v});\r
5421                                                                                 lo[v] = 1;\r
5422                                                                         }\r
5423                                                                 });\r
5424                                                         }\r
5425                                                         break;\r
5426 \r
5427                                                 // Import\r
5428                                                 case 3:\r
5429                                                         addClasses(r.styleSheet);\r
5430                                                         break;\r
5431                                         }\r
5432                                 });\r
5433                         };\r
5434 \r
5435                         try {\r
5436                                 each(t.doc.styleSheets, addClasses);\r
5437                         } catch (ex) {\r
5438                                 // Ignore\r
5439                         }\r
5440 \r
5441                         if (cl.length > 0)\r
5442                                 t.classes = cl;\r
5443 \r
5444                         return cl;\r
5445                 },\r
5446 \r
5447                 run : function(e, f, s) {\r
5448                         var t = this, o;\r
5449 \r
5450                         if (t.doc && typeof(e) === 'string')\r
5451                                 e = t.get(e);\r
5452 \r
5453                         if (!e)\r
5454                                 return false;\r
5455 \r
5456                         s = s || this;\r
5457                         if (!e.nodeType && (e.length || e.length === 0)) {\r
5458                                 o = [];\r
5459 \r
5460                                 each(e, function(e, i) {\r
5461                                         if (e) {\r
5462                                                 if (typeof(e) == 'string')\r
5463                                                         e = t.doc.getElementById(e);\r
5464 \r
5465                                                 o.push(f.call(s, e, i));\r
5466                                         }\r
5467                                 });\r
5468 \r
5469                                 return o;\r
5470                         }\r
5471 \r
5472                         return f.call(s, e);\r
5473                 },\r
5474 \r
5475                 getAttribs : function(n) {\r
5476                         var o;\r
5477 \r
5478                         n = this.get(n);\r
5479 \r
5480                         if (!n)\r
5481                                 return [];\r
5482 \r
5483                         if (isIE) {\r
5484                                 o = [];\r
5485 \r
5486                                 // Object will throw exception in IE\r
5487                                 if (n.nodeName == 'OBJECT')\r
5488                                         return n.attributes;\r
5489 \r
5490                                 // IE doesn't keep the selected attribute if you clone option elements\r
5491                                 if (n.nodeName === 'OPTION' && this.getAttrib(n, 'selected'))\r
5492                                         o.push({specified : 1, nodeName : 'selected'});\r
5493 \r
5494                                 // It's crazy that this is faster in IE but it's because it returns all attributes all the time\r
5495                                 n.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi, '').replace(/[\w:\-]+/gi, function(a) {\r
5496                                         o.push({specified : 1, nodeName : a});\r
5497                                 });\r
5498 \r
5499                                 return o;\r
5500                         }\r
5501 \r
5502                         return n.attributes;\r
5503                 },\r
5504 \r
5505                 isEmpty : function(node, elements) {\r
5506                         var self = this, i, attributes, type, walker, name, brCount = 0;\r
5507 \r
5508                         node = node.firstChild;\r
5509                         if (node) {\r
5510                                 walker = new tinymce.dom.TreeWalker(node, node.parentNode);\r
5511                                 elements = elements || self.schema ? self.schema.getNonEmptyElements() : null;\r
5512 \r
5513                                 do {\r
5514                                         type = node.nodeType;\r
5515 \r
5516                                         if (type === 1) {\r
5517                                                 // Ignore bogus elements\r
5518                                                 if (node.getAttribute('data-mce-bogus'))\r
5519                                                         continue;\r
5520 \r
5521                                                 // Keep empty elements like <img />\r
5522                                                 name = node.nodeName.toLowerCase();\r
5523                                                 if (elements && elements[name]) {\r
5524                                                         // Ignore single BR elements in blocks like <p><br /></p> or <p><span><br /></span></p>\r
5525                                                         if (name === 'br') {\r
5526                                                                 brCount++;\r
5527                                                                 continue;\r
5528                                                         }\r
5529 \r
5530                                                         return false;\r
5531                                                 }\r
5532 \r
5533                                                 // Keep elements with data-bookmark attributes or name attribute like <a name="1"></a>\r
5534                                                 attributes = self.getAttribs(node);\r
5535                                                 i = node.attributes.length;\r
5536                                                 while (i--) {\r
5537                                                         name = node.attributes[i].nodeName;\r
5538                                                         if (name === "name" || name === 'data-mce-bookmark')\r
5539                                                                 return false;\r
5540                                                 }\r
5541                                         }\r
5542 \r
5543                                         // Keep comment nodes\r
5544                                         if (type == 8)\r
5545                                                 return false;\r
5546 \r
5547                                         // Keep non whitespace text nodes\r
5548                                         if ((type === 3 && !whiteSpaceRegExp.test(node.nodeValue)))\r
5549                                                 return false;\r
5550                                 } while (node = walker.next());\r
5551                         }\r
5552 \r
5553                         return brCount <= 1;\r
5554                 },\r
5555 \r
5556                 destroy : function(s) {\r
5557                         var t = this;\r
5558 \r
5559                         t.win = t.doc = t.root = t.events = t.frag = null;\r
5560 \r
5561                         // Manual destroy then remove unload handler\r
5562                         if (!s)\r
5563                                 tinymce.removeUnload(t.destroy);\r
5564                 },\r
5565 \r
5566                 createRng : function() {\r
5567                         var d = this.doc;\r
5568 \r
5569                         return d.createRange ? d.createRange() : new tinymce.dom.Range(this);\r
5570                 },\r
5571 \r
5572                 nodeIndex : function(node, normalized) {\r
5573                         var idx = 0, lastNodeType, lastNode, nodeType;\r
5574 \r
5575                         if (node) {\r
5576                                 for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {\r
5577                                         nodeType = node.nodeType;\r
5578 \r
5579                                         // Normalize text nodes\r
5580                                         if (normalized && nodeType == 3) {\r
5581                                                 if (nodeType == lastNodeType || !node.nodeValue.length)\r
5582                                                         continue;\r
5583                                         }\r
5584                                         idx++;\r
5585                                         lastNodeType = nodeType;\r
5586                                 }\r
5587                         }\r
5588 \r
5589                         return idx;\r
5590                 },\r
5591 \r
5592                 split : function(pe, e, re) {\r
5593                         var t = this, r = t.createRng(), bef, aft, pa;\r
5594 \r
5595                         // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sense\r
5596                         // but we don't want that in our code since it serves no purpose for the end user\r
5597                         // For example if this is chopped:\r
5598                         //   <p>text 1<span><b>CHOP</b></span>text 2</p>\r
5599                         // would produce:\r
5600                         //   <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>\r
5601                         // this function will then trim of empty edges and produce:\r
5602                         //   <p>text 1</p><b>CHOP</b><p>text 2</p>\r
5603                         function trim(node) {\r
5604                                 var i, children = node.childNodes, type = node.nodeType;\r
5605 \r
5606                                 function surroundedBySpans(node) {\r
5607                                         var previousIsSpan = node.previousSibling && node.previousSibling.nodeName == 'SPAN';\r
5608                                         var nextIsSpan = node.nextSibling && node.nextSibling.nodeName == 'SPAN';\r
5609                                         return previousIsSpan && nextIsSpan;\r
5610                                 }\r
5611 \r
5612                                 if (type == 1 && node.getAttribute('data-mce-type') == 'bookmark')\r
5613                                         return;\r
5614 \r
5615                                 for (i = children.length - 1; i >= 0; i--)\r
5616                                         trim(children[i]);\r
5617 \r
5618                                 if (type != 9) {\r
5619                                         // Keep non whitespace text nodes\r
5620                                         if (type == 3 && node.nodeValue.length > 0) {\r
5621                                                 // If parent element isn't a block or there isn't any useful contents for example "<p>   </p>"\r
5622                                                 // Also keep text nodes with only spaces if surrounded by spans.\r
5623                                                 // eg. "<p><span>a</span> <span>b</span></p>" should keep space between a and b\r
5624                                                 var trimmedLength = tinymce.trim(node.nodeValue).length;\r
5625                                                 if (!t.isBlock(node.parentNode) || trimmedLength > 0 || trimmedLength == 0 && surroundedBySpans(node))\r
5626                                                         return;\r
5627                                         } else if (type == 1) {\r
5628                                                 // If the only child is a bookmark then move it up\r
5629                                                 children = node.childNodes;\r
5630                                                 if (children.length == 1 && children[0] && children[0].nodeType == 1 && children[0].getAttribute('data-mce-type') == 'bookmark')\r
5631                                                         node.parentNode.insertBefore(children[0], node);\r
5632 \r
5633                                                 // Keep non empty elements or img, hr etc\r
5634                                                 if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName))\r
5635                                                         return;\r
5636                                         }\r
5637 \r
5638                                         t.remove(node);\r
5639                                 }\r
5640 \r
5641                                 return node;\r
5642                         };\r
5643 \r
5644                         if (pe && e) {\r
5645                                 // Get before chunk\r
5646                                 r.setStart(pe.parentNode, t.nodeIndex(pe));\r
5647                                 r.setEnd(e.parentNode, t.nodeIndex(e));\r
5648                                 bef = r.extractContents();\r
5649 \r
5650                                 // Get after chunk\r
5651                                 r = t.createRng();\r
5652                                 r.setStart(e.parentNode, t.nodeIndex(e) + 1);\r
5653                                 r.setEnd(pe.parentNode, t.nodeIndex(pe) + 1);\r
5654                                 aft = r.extractContents();\r
5655 \r
5656                                 // Insert before chunk\r
5657                                 pa = pe.parentNode;\r
5658                                 pa.insertBefore(trim(bef), pe);\r
5659 \r
5660                                 // Insert middle chunk\r
5661                                 if (re)\r
5662                                 pa.replaceChild(re, e);\r
5663                         else\r
5664                                 pa.insertBefore(e, pe);\r
5665 \r
5666                                 // Insert after chunk\r
5667                                 pa.insertBefore(trim(aft), pe);\r
5668                                 t.remove(pe);\r
5669 \r
5670                                 return re || e;\r
5671                         }\r
5672                 },\r
5673 \r
5674                 bind : function(target, name, func, scope) {\r
5675                         return this.events.add(target, name, func, scope || this);\r
5676                 },\r
5677 \r
5678                 unbind : function(target, name, func) {\r
5679                         return this.events.remove(target, name, func);\r
5680                 },\r
5681 \r
5682                 fire : function(target, name, evt) {\r
5683                         return this.events.fire(target, name, evt);\r
5684                 },\r
5685 \r
5686 \r
5687                 _findSib : function(node, selector, name) {\r
5688                         var t = this, f = selector;\r
5689 \r
5690                         if (node) {\r
5691                                 // If expression make a function of it using is\r
5692                                 if (is(f, 'string')) {\r
5693                                         f = function(node) {\r
5694                                                 return t.is(node, selector);\r
5695                                         };\r
5696                                 }\r
5697 \r
5698                                 // Loop all siblings\r
5699                                 for (node = node[name]; node; node = node[name]) {\r
5700                                         if (f(node))\r
5701                                                 return node;\r
5702                                 }\r
5703                         }\r
5704 \r
5705                         return null;\r
5706                 },\r
5707 \r
5708                 _isRes : function(c) {\r
5709                         // Is live resizble element\r
5710                         return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c);\r
5711                 }\r
5712 \r
5713                 /*\r
5714                 walk : function(n, f, s) {\r
5715                         var d = this.doc, w;\r
5716 \r
5717                         if (d.createTreeWalker) {\r
5718                                 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);\r
5719 \r
5720                                 while ((n = w.nextNode()) != null)\r
5721                                         f.call(s || this, n);\r
5722                         } else\r
5723                                 tinymce.walk(n, f, 'childNodes', s);\r
5724                 }\r
5725                 */\r
5726 \r
5727                 /*\r
5728                 toRGB : function(s) {\r
5729                         var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);\r
5730 \r
5731                         if (c) {\r
5732                                 // #FFF -> #FFFFFF\r
5733                                 if (!is(c[3]))\r
5734                                         c[3] = c[2] = c[1];\r
5735 \r
5736                                 return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";\r
5737                         }\r
5738 \r
5739                         return s;\r
5740                 }\r
5741                 */\r
5742         });\r
5743 \r
5744         tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});\r
5745 })(tinymce);\r
5746 \r
5747 (function(ns) {\r
5748         // Range constructor\r
5749         function Range(dom) {\r
5750                 var t = this,\r
5751                         doc = dom.doc,\r
5752                         EXTRACT = 0,\r
5753                         CLONE = 1,\r
5754                         DELETE = 2,\r
5755                         TRUE = true,\r
5756                         FALSE = false,\r
5757                         START_OFFSET = 'startOffset',\r
5758                         START_CONTAINER = 'startContainer',\r
5759                         END_CONTAINER = 'endContainer',\r
5760                         END_OFFSET = 'endOffset',\r
5761                         extend = tinymce.extend,\r
5762                         nodeIndex = dom.nodeIndex;\r
5763 \r
5764                 extend(t, {\r
5765                         // Inital states\r
5766                         startContainer : doc,\r
5767                         startOffset : 0,\r
5768                         endContainer : doc,\r
5769                         endOffset : 0,\r
5770                         collapsed : TRUE,\r
5771                         commonAncestorContainer : doc,\r
5772 \r
5773                         // Range constants\r
5774                         START_TO_START : 0,\r
5775                         START_TO_END : 1,\r
5776                         END_TO_END : 2,\r
5777                         END_TO_START : 3,\r
5778 \r
5779                         // Public methods\r
5780                         setStart : setStart,\r
5781                         setEnd : setEnd,\r
5782                         setStartBefore : setStartBefore,\r
5783                         setStartAfter : setStartAfter,\r
5784                         setEndBefore : setEndBefore,\r
5785                         setEndAfter : setEndAfter,\r
5786                         collapse : collapse,\r
5787                         selectNode : selectNode,\r
5788                         selectNodeContents : selectNodeContents,\r
5789                         compareBoundaryPoints : compareBoundaryPoints,\r
5790                         deleteContents : deleteContents,\r
5791                         extractContents : extractContents,\r
5792                         cloneContents : cloneContents,\r
5793                         insertNode : insertNode,\r
5794                         surroundContents : surroundContents,\r
5795                         cloneRange : cloneRange\r
5796                 });\r
5797 \r
5798                 function createDocumentFragment() {\r
5799                         return doc.createDocumentFragment();\r
5800                 };\r
5801 \r
5802                 function setStart(n, o) {\r
5803                         _setEndPoint(TRUE, n, o);\r
5804                 };\r
5805 \r
5806                 function setEnd(n, o) {\r
5807                         _setEndPoint(FALSE, n, o);\r
5808                 };\r
5809 \r
5810                 function setStartBefore(n) {\r
5811                         setStart(n.parentNode, nodeIndex(n));\r
5812                 };\r
5813 \r
5814                 function setStartAfter(n) {\r
5815                         setStart(n.parentNode, nodeIndex(n) + 1);\r
5816                 };\r
5817 \r
5818                 function setEndBefore(n) {\r
5819                         setEnd(n.parentNode, nodeIndex(n));\r
5820                 };\r
5821 \r
5822                 function setEndAfter(n) {\r
5823                         setEnd(n.parentNode, nodeIndex(n) + 1);\r
5824                 };\r
5825 \r
5826                 function collapse(ts) {\r
5827                         if (ts) {\r
5828                                 t[END_CONTAINER] = t[START_CONTAINER];\r
5829                                 t[END_OFFSET] = t[START_OFFSET];\r
5830                         } else {\r
5831                                 t[START_CONTAINER] = t[END_CONTAINER];\r
5832                                 t[START_OFFSET] = t[END_OFFSET];\r
5833                         }\r
5834 \r
5835                         t.collapsed = TRUE;\r
5836                 };\r
5837 \r
5838                 function selectNode(n) {\r
5839                         setStartBefore(n);\r
5840                         setEndAfter(n);\r
5841                 };\r
5842 \r
5843                 function selectNodeContents(n) {\r
5844                         setStart(n, 0);\r
5845                         setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);\r
5846                 };\r
5847 \r
5848                 function compareBoundaryPoints(h, r) {\r
5849                         var sc = t[START_CONTAINER], so = t[START_OFFSET], ec = t[END_CONTAINER], eo = t[END_OFFSET],\r
5850                         rsc = r.startContainer, rso = r.startOffset, rec = r.endContainer, reo = r.endOffset;\r
5851 \r
5852                         // Check START_TO_START\r
5853                         if (h === 0)\r
5854                                 return _compareBoundaryPoints(sc, so, rsc, rso);\r
5855         \r
5856                         // Check START_TO_END\r
5857                         if (h === 1)\r
5858                                 return _compareBoundaryPoints(ec, eo, rsc, rso);\r
5859         \r
5860                         // Check END_TO_END\r
5861                         if (h === 2)\r
5862                                 return _compareBoundaryPoints(ec, eo, rec, reo);\r
5863         \r
5864                         // Check END_TO_START\r
5865                         if (h === 3) \r
5866                                 return _compareBoundaryPoints(sc, so, rec, reo);\r
5867                 };\r
5868 \r
5869                 function deleteContents() {\r
5870                         _traverse(DELETE);\r
5871                 };\r
5872 \r
5873                 function extractContents() {\r
5874                         return _traverse(EXTRACT);\r
5875                 };\r
5876 \r
5877                 function cloneContents() {\r
5878                         return _traverse(CLONE);\r
5879                 };\r
5880 \r
5881                 function insertNode(n) {\r
5882                         var startContainer = this[START_CONTAINER],\r
5883                                 startOffset = this[START_OFFSET], nn, o;\r
5884 \r
5885                         // Node is TEXT_NODE or CDATA\r
5886                         if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {\r
5887                                 if (!startOffset) {\r
5888                                         // At the start of text\r
5889                                         startContainer.parentNode.insertBefore(n, startContainer);\r
5890                                 } else if (startOffset >= startContainer.nodeValue.length) {\r
5891                                         // At the end of text\r
5892                                         dom.insertAfter(n, startContainer);\r
5893                                 } else {\r
5894                                         // Middle, need to split\r
5895                                         nn = startContainer.splitText(startOffset);\r
5896                                         startContainer.parentNode.insertBefore(n, nn);\r
5897                                 }\r
5898                         } else {\r
5899                                 // Insert element node\r
5900                                 if (startContainer.childNodes.length > 0)\r
5901                                         o = startContainer.childNodes[startOffset];\r
5902 \r
5903                                 if (o)\r
5904                                         startContainer.insertBefore(n, o);\r
5905                                 else\r
5906                                         startContainer.appendChild(n);\r
5907                         }\r
5908                 };\r
5909 \r
5910                 function surroundContents(n) {\r
5911                         var f = t.extractContents();\r
5912 \r
5913                         t.insertNode(n);\r
5914                         n.appendChild(f);\r
5915                         t.selectNode(n);\r
5916                 };\r
5917 \r
5918                 function cloneRange() {\r
5919                         return extend(new Range(dom), {\r
5920                                 startContainer : t[START_CONTAINER],\r
5921                                 startOffset : t[START_OFFSET],\r
5922                                 endContainer : t[END_CONTAINER],\r
5923                                 endOffset : t[END_OFFSET],\r
5924                                 collapsed : t.collapsed,\r
5925                                 commonAncestorContainer : t.commonAncestorContainer\r
5926                         });\r
5927                 };\r
5928 \r
5929                 // Private methods\r
5930 \r
5931                 function _getSelectedNode(container, offset) {\r
5932                         var child;\r
5933 \r
5934                         if (container.nodeType == 3 /* TEXT_NODE */)\r
5935                                 return container;\r
5936 \r
5937                         if (offset < 0)\r
5938                                 return container;\r
5939 \r
5940                         child = container.firstChild;\r
5941                         while (child && offset > 0) {\r
5942                                 --offset;\r
5943                                 child = child.nextSibling;\r
5944                         }\r
5945 \r
5946                         if (child)\r
5947                                 return child;\r
5948 \r
5949                         return container;\r
5950                 };\r
5951 \r
5952                 function _isCollapsed() {\r
5953                         return (t[START_CONTAINER] == t[END_CONTAINER] && t[START_OFFSET] == t[END_OFFSET]);\r
5954                 };\r
5955 \r
5956                 function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {\r
5957                         var c, offsetC, n, cmnRoot, childA, childB;\r
5958                         \r
5959                         // In the first case the boundary-points have the same container. A is before B\r
5960                         // if its offset is less than the offset of B, A is equal to B if its offset is\r
5961                         // equal to the offset of B, and A is after B if its offset is greater than the\r
5962                         // offset of B.\r
5963                         if (containerA == containerB) {\r
5964                                 if (offsetA == offsetB)\r
5965                                         return 0; // equal\r
5966 \r
5967                                 if (offsetA < offsetB)\r
5968                                         return -1; // before\r
5969 \r
5970                                 return 1; // after\r
5971                         }\r
5972 \r
5973                         // In the second case a child node C of the container of A is an ancestor\r
5974                         // container of B. In this case, A is before B if the offset of A is less than or\r
5975                         // equal to the index of the child node C and A is after B otherwise.\r
5976                         c = containerB;\r
5977                         while (c && c.parentNode != containerA)\r
5978                                 c = c.parentNode;\r
5979 \r
5980                         if (c) {\r
5981                                 offsetC = 0;\r
5982                                 n = containerA.firstChild;\r
5983 \r
5984                                 while (n != c && offsetC < offsetA) {\r
5985                                         offsetC++;\r
5986                                         n = n.nextSibling;\r
5987                                 }\r
5988 \r
5989                                 if (offsetA <= offsetC)\r
5990                                         return -1; // before\r
5991 \r
5992                                 return 1; // after\r
5993                         }\r
5994 \r
5995                         // In the third case a child node C of the container of B is an ancestor container\r
5996                         // of A. In this case, A is before B if the index of the child node C is less than\r
5997                         // the offset of B and A is after B otherwise.\r
5998                         c = containerA;\r
5999                         while (c && c.parentNode != containerB) {\r
6000                                 c = c.parentNode;\r
6001                         }\r
6002 \r
6003                         if (c) {\r
6004                                 offsetC = 0;\r
6005                                 n = containerB.firstChild;\r
6006 \r
6007                                 while (n != c && offsetC < offsetB) {\r
6008                                         offsetC++;\r
6009                                         n = n.nextSibling;\r
6010                                 }\r
6011 \r
6012                                 if (offsetC < offsetB)\r
6013                                         return -1; // before\r
6014 \r
6015                                 return 1; // after\r
6016                         }\r
6017 \r
6018                         // In the fourth case, none of three other cases hold: the containers of A and B\r
6019                         // are siblings or descendants of sibling nodes. In this case, A is before B if\r
6020                         // the container of A is before the container of B in a pre-order traversal of the\r
6021                         // Ranges' context tree and A is after B otherwise.\r
6022                         cmnRoot = dom.findCommonAncestor(containerA, containerB);\r
6023                         childA = containerA;\r
6024 \r
6025                         while (childA && childA.parentNode != cmnRoot)\r
6026                                 childA = childA.parentNode;\r
6027 \r
6028                         if (!childA)\r
6029                                 childA = cmnRoot;\r
6030 \r
6031                         childB = containerB;\r
6032                         while (childB && childB.parentNode != cmnRoot)\r
6033                                 childB = childB.parentNode;\r
6034 \r
6035                         if (!childB)\r
6036                                 childB = cmnRoot;\r
6037 \r
6038                         if (childA == childB)\r
6039                                 return 0; // equal\r
6040 \r
6041                         n = cmnRoot.firstChild;\r
6042                         while (n) {\r
6043                                 if (n == childA)\r
6044                                         return -1; // before\r
6045 \r
6046                                 if (n == childB)\r
6047                                         return 1; // after\r
6048 \r
6049                                 n = n.nextSibling;\r
6050                         }\r
6051                 };\r
6052 \r
6053                 function _setEndPoint(st, n, o) {\r
6054                         var ec, sc;\r
6055 \r
6056                         if (st) {\r
6057                                 t[START_CONTAINER] = n;\r
6058                                 t[START_OFFSET] = o;\r
6059                         } else {\r
6060                                 t[END_CONTAINER] = n;\r
6061                                 t[END_OFFSET] = o;\r
6062                         }\r
6063 \r
6064                         // If one boundary-point of a Range is set to have a root container\r
6065                         // other than the current one for the Range, the Range is collapsed to\r
6066                         // the new position. This enforces the restriction that both boundary-\r
6067                         // points of a Range must have the same root container.\r
6068                         ec = t[END_CONTAINER];\r
6069                         while (ec.parentNode)\r
6070                                 ec = ec.parentNode;\r
6071 \r
6072                         sc = t[START_CONTAINER];\r
6073                         while (sc.parentNode)\r
6074                                 sc = sc.parentNode;\r
6075 \r
6076                         if (sc == ec) {\r
6077                                 // The start position of a Range is guaranteed to never be after the\r
6078                                 // end position. To enforce this restriction, if the start is set to\r
6079                                 // be at a position after the end, the Range is collapsed to that\r
6080                                 // position.\r
6081                                 if (_compareBoundaryPoints(t[START_CONTAINER], t[START_OFFSET], t[END_CONTAINER], t[END_OFFSET]) > 0)\r
6082                                         t.collapse(st);\r
6083                         } else\r
6084                                 t.collapse(st);\r
6085 \r
6086                         t.collapsed = _isCollapsed();\r
6087                         t.commonAncestorContainer = dom.findCommonAncestor(t[START_CONTAINER], t[END_CONTAINER]);\r
6088                 };\r
6089 \r
6090                 function _traverse(how) {\r
6091                         var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;\r
6092 \r
6093                         if (t[START_CONTAINER] == t[END_CONTAINER])\r
6094                                 return _traverseSameContainer(how);\r
6095 \r
6096                         for (c = t[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
6097                                 if (p == t[START_CONTAINER])\r
6098                                         return _traverseCommonStartContainer(c, how);\r
6099 \r
6100                                 ++endContainerDepth;\r
6101                         }\r
6102 \r
6103                         for (c = t[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
6104                                 if (p == t[END_CONTAINER])\r
6105                                         return _traverseCommonEndContainer(c, how);\r
6106 \r
6107                                 ++startContainerDepth;\r
6108                         }\r
6109 \r
6110                         depthDiff = startContainerDepth - endContainerDepth;\r
6111 \r
6112                         startNode = t[START_CONTAINER];\r
6113                         while (depthDiff > 0) {\r
6114                                 startNode = startNode.parentNode;\r
6115                                 depthDiff--;\r
6116                         }\r
6117 \r
6118                         endNode = t[END_CONTAINER];\r
6119                         while (depthDiff < 0) {\r
6120                                 endNode = endNode.parentNode;\r
6121                                 depthDiff++;\r
6122                         }\r
6123 \r
6124                         // ascend the ancestor hierarchy until we have a common parent.\r
6125                         for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {\r
6126                                 startNode = sp;\r
6127                                 endNode = ep;\r
6128                         }\r
6129 \r
6130                         return _traverseCommonAncestors(startNode, endNode, how);\r
6131                 };\r
6132 \r
6133                  function _traverseSameContainer(how) {\r
6134                         var frag, s, sub, n, cnt, sibling, xferNode, start, len;\r
6135 \r
6136                         if (how != DELETE)\r
6137                                 frag = createDocumentFragment();\r
6138 \r
6139                         // If selection is empty, just return the fragment\r
6140                         if (t[START_OFFSET] == t[END_OFFSET])\r
6141                                 return frag;\r
6142 \r
6143                         // Text node needs special case handling\r
6144                         if (t[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {\r
6145                                 // get the substring\r
6146                                 s = t[START_CONTAINER].nodeValue;\r
6147                                 sub = s.substring(t[START_OFFSET], t[END_OFFSET]);\r
6148 \r
6149                                 // set the original text node to its new value\r
6150                                 if (how != CLONE) {\r
6151                                         n = t[START_CONTAINER];\r
6152                                         start = t[START_OFFSET];\r
6153                                         len = t[END_OFFSET] - t[START_OFFSET];\r
6154 \r
6155                                         if (start === 0 && len >= n.nodeValue.length - 1) {\r
6156                                                 n.parentNode.removeChild(n);\r
6157                                         } else {\r
6158                                                 n.deleteData(start, len);\r
6159                                         }\r
6160 \r
6161                                         // Nothing is partially selected, so collapse to start point\r
6162                                         t.collapse(TRUE);\r
6163                                 }\r
6164 \r
6165                                 if (how == DELETE)\r
6166                                         return;\r
6167 \r
6168                                 if (sub.length > 0) {\r
6169                                         frag.appendChild(doc.createTextNode(sub));\r
6170                                 }\r
6171 \r
6172                                 return frag;\r
6173                         }\r
6174 \r
6175                         // Copy nodes between the start/end offsets.\r
6176                         n = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]);\r
6177                         cnt = t[END_OFFSET] - t[START_OFFSET];\r
6178 \r
6179                         while (n && cnt > 0) {\r
6180                                 sibling = n.nextSibling;\r
6181                                 xferNode = _traverseFullySelected(n, how);\r
6182 \r
6183                                 if (frag)\r
6184                                         frag.appendChild( xferNode );\r
6185 \r
6186                                 --cnt;\r
6187                                 n = sibling;\r
6188                         }\r
6189 \r
6190                         // Nothing is partially selected, so collapse to start point\r
6191                         if (how != CLONE)\r
6192                                 t.collapse(TRUE);\r
6193 \r
6194                         return frag;\r
6195                 };\r
6196 \r
6197                 function _traverseCommonStartContainer(endAncestor, how) {\r
6198                         var frag, n, endIdx, cnt, sibling, xferNode;\r
6199 \r
6200                         if (how != DELETE)\r
6201                                 frag = createDocumentFragment();\r
6202 \r
6203                         n = _traverseRightBoundary(endAncestor, how);\r
6204 \r
6205                         if (frag)\r
6206                                 frag.appendChild(n);\r
6207 \r
6208                         endIdx = nodeIndex(endAncestor);\r
6209                         cnt = endIdx - t[START_OFFSET];\r
6210 \r
6211                         if (cnt <= 0) {\r
6212                                 // Collapse to just before the endAncestor, which\r
6213                                 // is partially selected.\r
6214                                 if (how != CLONE) {\r
6215                                         t.setEndBefore(endAncestor);\r
6216                                         t.collapse(FALSE);\r
6217                                 }\r
6218 \r
6219                                 return frag;\r
6220                         }\r
6221 \r
6222                         n = endAncestor.previousSibling;\r
6223                         while (cnt > 0) {\r
6224                                 sibling = n.previousSibling;\r
6225                                 xferNode = _traverseFullySelected(n, how);\r
6226 \r
6227                                 if (frag)\r
6228                                         frag.insertBefore(xferNode, frag.firstChild);\r
6229 \r
6230                                 --cnt;\r
6231                                 n = sibling;\r
6232                         }\r
6233 \r
6234                         // Collapse to just before the endAncestor, which\r
6235                         // is partially selected.\r
6236                         if (how != CLONE) {\r
6237                                 t.setEndBefore(endAncestor);\r
6238                                 t.collapse(FALSE);\r
6239                         }\r
6240 \r
6241                         return frag;\r
6242                 };\r
6243 \r
6244                 function _traverseCommonEndContainer(startAncestor, how) {\r
6245                         var frag, startIdx, n, cnt, sibling, xferNode;\r
6246 \r
6247                         if (how != DELETE)\r
6248                                 frag = createDocumentFragment();\r
6249 \r
6250                         n = _traverseLeftBoundary(startAncestor, how);\r
6251                         if (frag)\r
6252                                 frag.appendChild(n);\r
6253 \r
6254                         startIdx = nodeIndex(startAncestor);\r
6255                         ++startIdx; // Because we already traversed it\r
6256 \r
6257                         cnt = t[END_OFFSET] - startIdx;\r
6258                         n = startAncestor.nextSibling;\r
6259                         while (n && cnt > 0) {\r
6260                                 sibling = n.nextSibling;\r
6261                                 xferNode = _traverseFullySelected(n, how);\r
6262 \r
6263                                 if (frag)\r
6264                                         frag.appendChild(xferNode);\r
6265 \r
6266                                 --cnt;\r
6267                                 n = sibling;\r
6268                         }\r
6269 \r
6270                         if (how != CLONE) {\r
6271                                 t.setStartAfter(startAncestor);\r
6272                                 t.collapse(TRUE);\r
6273                         }\r
6274 \r
6275                         return frag;\r
6276                 };\r
6277 \r
6278                 function _traverseCommonAncestors(startAncestor, endAncestor, how) {\r
6279                         var n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;\r
6280 \r
6281                         if (how != DELETE)\r
6282                                 frag = createDocumentFragment();\r
6283 \r
6284                         n = _traverseLeftBoundary(startAncestor, how);\r
6285                         if (frag)\r
6286                                 frag.appendChild(n);\r
6287 \r
6288                         commonParent = startAncestor.parentNode;\r
6289                         startOffset = nodeIndex(startAncestor);\r
6290                         endOffset = nodeIndex(endAncestor);\r
6291                         ++startOffset;\r
6292 \r
6293                         cnt = endOffset - startOffset;\r
6294                         sibling = startAncestor.nextSibling;\r
6295 \r
6296                         while (cnt > 0) {\r
6297                                 nextSibling = sibling.nextSibling;\r
6298                                 n = _traverseFullySelected(sibling, how);\r
6299 \r
6300                                 if (frag)\r
6301                                         frag.appendChild(n);\r
6302 \r
6303                                 sibling = nextSibling;\r
6304                                 --cnt;\r
6305                         }\r
6306 \r
6307                         n = _traverseRightBoundary(endAncestor, how);\r
6308 \r
6309                         if (frag)\r
6310                                 frag.appendChild(n);\r
6311 \r
6312                         if (how != CLONE) {\r
6313                                 t.setStartAfter(startAncestor);\r
6314                                 t.collapse(TRUE);\r
6315                         }\r
6316 \r
6317                         return frag;\r
6318                 };\r
6319 \r
6320                 function _traverseRightBoundary(root, how) {\r
6321                         var next = _getSelectedNode(t[END_CONTAINER], t[END_OFFSET] - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != t[END_CONTAINER];\r
6322 \r
6323                         if (next == root)\r
6324                                 return _traverseNode(next, isFullySelected, FALSE, how);\r
6325 \r
6326                         parent = next.parentNode;\r
6327                         clonedParent = _traverseNode(parent, FALSE, FALSE, how);\r
6328 \r
6329                         while (parent) {\r
6330                                 while (next) {\r
6331                                         prevSibling = next.previousSibling;\r
6332                                         clonedChild = _traverseNode(next, isFullySelected, FALSE, how);\r
6333 \r
6334                                         if (how != DELETE)\r
6335                                                 clonedParent.insertBefore(clonedChild, clonedParent.firstChild);\r
6336 \r
6337                                         isFullySelected = TRUE;\r
6338                                         next = prevSibling;\r
6339                                 }\r
6340 \r
6341                                 if (parent == root)\r
6342                                         return clonedParent;\r
6343 \r
6344                                 next = parent.previousSibling;\r
6345                                 parent = parent.parentNode;\r
6346 \r
6347                                 clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);\r
6348 \r
6349                                 if (how != DELETE)\r
6350                                         clonedGrandParent.appendChild(clonedParent);\r
6351 \r
6352                                 clonedParent = clonedGrandParent;\r
6353                         }\r
6354                 };\r
6355 \r
6356                 function _traverseLeftBoundary(root, how) {\r
6357                         var next = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]), isFullySelected = next != t[START_CONTAINER], parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;\r
6358 \r
6359                         if (next == root)\r
6360                                 return _traverseNode(next, isFullySelected, TRUE, how);\r
6361 \r
6362                         parent = next.parentNode;\r
6363                         clonedParent = _traverseNode(parent, FALSE, TRUE, how);\r
6364 \r
6365                         while (parent) {\r
6366                                 while (next) {\r
6367                                         nextSibling = next.nextSibling;\r
6368                                         clonedChild = _traverseNode(next, isFullySelected, TRUE, how);\r
6369 \r
6370                                         if (how != DELETE)\r
6371                                                 clonedParent.appendChild(clonedChild);\r
6372 \r
6373                                         isFullySelected = TRUE;\r
6374                                         next = nextSibling;\r
6375                                 }\r
6376 \r
6377                                 if (parent == root)\r
6378                                         return clonedParent;\r
6379 \r
6380                                 next = parent.nextSibling;\r
6381                                 parent = parent.parentNode;\r
6382 \r
6383                                 clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);\r
6384 \r
6385                                 if (how != DELETE)\r
6386                                         clonedGrandParent.appendChild(clonedParent);\r
6387 \r
6388                                 clonedParent = clonedGrandParent;\r
6389                         }\r
6390                 };\r
6391 \r
6392                 function _traverseNode(n, isFullySelected, isLeft, how) {\r
6393                         var txtValue, newNodeValue, oldNodeValue, offset, newNode;\r
6394 \r
6395                         if (isFullySelected)\r
6396                                 return _traverseFullySelected(n, how);\r
6397 \r
6398                         if (n.nodeType == 3 /* TEXT_NODE */) {\r
6399                                 txtValue = n.nodeValue;\r
6400 \r
6401                                 if (isLeft) {\r
6402                                         offset = t[START_OFFSET];\r
6403                                         newNodeValue = txtValue.substring(offset);\r
6404                                         oldNodeValue = txtValue.substring(0, offset);\r
6405                                 } else {\r
6406                                         offset = t[END_OFFSET];\r
6407                                         newNodeValue = txtValue.substring(0, offset);\r
6408                                         oldNodeValue = txtValue.substring(offset);\r
6409                                 }\r
6410 \r
6411                                 if (how != CLONE)\r
6412                                         n.nodeValue = oldNodeValue;\r
6413 \r
6414                                 if (how == DELETE)\r
6415                                         return;\r
6416 \r
6417                                 newNode = dom.clone(n, FALSE);\r
6418                                 newNode.nodeValue = newNodeValue;\r
6419 \r
6420                                 return newNode;\r
6421                         }\r
6422 \r
6423                         if (how == DELETE)\r
6424                                 return;\r
6425 \r
6426                         return dom.clone(n, FALSE);\r
6427                 };\r
6428 \r
6429                 function _traverseFullySelected(n, how) {\r
6430                         if (how != DELETE)\r
6431                                 return how == CLONE ? dom.clone(n, TRUE) : n;\r
6432 \r
6433                         n.parentNode.removeChild(n);\r
6434                 };\r
6435         };\r
6436 \r
6437         ns.Range = Range;\r
6438 })(tinymce.dom);\r
6439 \r
6440 (function() {\r
6441         function Selection(selection) {\r
6442                 var self = this, dom = selection.dom, TRUE = true, FALSE = false;\r
6443 \r
6444                 function getPosition(rng, start) {\r
6445                         var checkRng, startIndex = 0, endIndex, inside,\r
6446                                 children, child, offset, index, position = -1, parent;\r
6447 \r
6448                         // Setup test range, collapse it and get the parent\r
6449                         checkRng = rng.duplicate();\r
6450                         checkRng.collapse(start);\r
6451                         parent = checkRng.parentElement();\r
6452 \r
6453                         // Check if the selection is within the right document\r
6454                         if (parent.ownerDocument !== selection.dom.doc)\r
6455                                 return;\r
6456 \r
6457                         // IE will report non editable elements as it's parent so look for an editable one\r
6458                         while (parent.contentEditable === "false") {\r
6459                                 parent = parent.parentNode;\r
6460                         }\r
6461 \r
6462                         // If parent doesn't have any children then return that we are inside the element\r
6463                         if (!parent.hasChildNodes()) {\r
6464                                 return {node : parent, inside : 1};\r
6465                         }\r
6466 \r
6467                         // Setup node list and endIndex\r
6468                         children = parent.children;\r
6469                         endIndex = children.length - 1;\r
6470 \r
6471                         // Perform a binary search for the position\r
6472                         while (startIndex <= endIndex) {\r
6473                                 index = Math.floor((startIndex + endIndex) / 2);\r
6474 \r
6475                                 // Move selection to node and compare the ranges\r
6476                                 child = children[index];\r
6477                                 checkRng.moveToElementText(child);\r
6478                                 position = checkRng.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', rng);\r
6479 \r
6480                                 // Before/after or an exact match\r
6481                                 if (position > 0) {\r
6482                                         endIndex = index - 1;\r
6483                                 } else if (position < 0) {\r
6484                                         startIndex = index + 1;\r
6485                                 } else {\r
6486                                         return {node : child};\r
6487                                 }\r
6488                         }\r
6489 \r
6490                         // Check if child position is before or we didn't find a position\r
6491                         if (position < 0) {\r
6492                                 // No element child was found use the parent element and the offset inside that\r
6493                                 if (!child) {\r
6494                                         checkRng.moveToElementText(parent);\r
6495                                         checkRng.collapse(true);\r
6496                                         child = parent;\r
6497                                         inside = true;\r
6498                                 } else\r
6499                                         checkRng.collapse(false);\r
6500 \r
6501                                 // Walk character by character in text node until we hit the selected range endpoint, hit the end of document or parent isn't the right one\r
6502                                 // We need to walk char by char since rng.text or rng.htmlText will trim line endings\r
6503                                 offset = 0;\r
6504                                 while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {\r
6505                                         if (checkRng.move('character', 1) == 0 || parent != checkRng.parentElement()) {\r
6506                                                 break;\r
6507                                         }\r
6508 \r
6509                                         offset++;\r
6510                                 }\r
6511                         } else {\r
6512                                 // Child position is after the selection endpoint\r
6513                                 checkRng.collapse(true);\r
6514 \r
6515                                 // Walk character by character in text node until we hit the selected range endpoint, hit the end of document or parent isn't the right one\r
6516                                 offset = 0;\r
6517                                 while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {\r
6518                                         if (checkRng.move('character', -1) == 0 || parent != checkRng.parentElement()) {\r
6519                                                 break;\r
6520                                         }\r
6521 \r
6522                                         offset++;\r
6523                                 }\r
6524                         }\r
6525 \r
6526                         return {node : child, position : position, offset : offset, inside : inside};\r
6527                 };\r
6528 \r
6529                 // Returns a W3C DOM compatible range object by using the IE Range API\r
6530                 function getRange() {\r
6531                         var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed, tmpRange, element2, bookmark, fail;\r
6532 \r
6533                         // If selection is outside the current document just return an empty range\r
6534                         element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();\r
6535                         if (element.ownerDocument != dom.doc)\r
6536                                 return domRange;\r
6537 \r
6538                         collapsed = selection.isCollapsed();\r
6539 \r
6540                         // Handle control selection\r
6541                         if (ieRange.item) {\r
6542                                 domRange.setStart(element.parentNode, dom.nodeIndex(element));\r
6543                                 domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);\r
6544 \r
6545                                 return domRange;\r
6546                         }\r
6547 \r
6548                         function findEndPoint(start) {\r
6549                                 var endPoint = getPosition(ieRange, start), container, offset, textNodeOffset = 0, sibling, undef, nodeValue;\r
6550 \r
6551                                 container = endPoint.node;\r
6552                                 offset = endPoint.offset;\r
6553 \r
6554                                 if (endPoint.inside && !container.hasChildNodes()) {\r
6555                                         domRange[start ? 'setStart' : 'setEnd'](container, 0);\r
6556                                         return;\r
6557                                 }\r
6558 \r
6559                                 if (offset === undef) {\r
6560                                         domRange[start ? 'setStartBefore' : 'setEndAfter'](container);\r
6561                                         return;\r
6562                                 }\r
6563 \r
6564                                 if (endPoint.position < 0) {\r
6565                                         sibling = endPoint.inside ? container.firstChild : container.nextSibling;\r
6566 \r
6567                                         if (!sibling) {\r
6568                                                 domRange[start ? 'setStartAfter' : 'setEndAfter'](container);\r
6569                                                 return;\r
6570                                         }\r
6571 \r
6572                                         if (!offset) {\r
6573                                                 if (sibling.nodeType == 3)\r
6574                                                         domRange[start ? 'setStart' : 'setEnd'](sibling, 0);\r
6575                                                 else\r
6576                                                         domRange[start ? 'setStartBefore' : 'setEndBefore'](sibling);\r
6577 \r
6578                                                 return;\r
6579                                         }\r
6580 \r
6581                                         // Find the text node and offset\r
6582                                         while (sibling) {\r
6583                                                 nodeValue = sibling.nodeValue;\r
6584                                                 textNodeOffset += nodeValue.length;\r
6585 \r
6586                                                 // We are at or passed the position we where looking for\r
6587                                                 if (textNodeOffset >= offset) {\r
6588                                                         container = sibling;\r
6589                                                         textNodeOffset -= offset;\r
6590                                                         textNodeOffset = nodeValue.length - textNodeOffset;\r
6591                                                         break;\r
6592                                                 }\r
6593 \r
6594                                                 sibling = sibling.nextSibling;\r
6595                                         }\r
6596                                 } else {\r
6597                                         // Find the text node and offset\r
6598                                         sibling = container.previousSibling;\r
6599 \r
6600                                         if (!sibling)\r
6601                                                 return domRange[start ? 'setStartBefore' : 'setEndBefore'](container);\r
6602 \r
6603                                         // If there isn't any text to loop then use the first position\r
6604                                         if (!offset) {\r
6605                                                 if (container.nodeType == 3)\r
6606                                                         domRange[start ? 'setStart' : 'setEnd'](sibling, container.nodeValue.length);\r
6607                                                 else\r
6608                                                         domRange[start ? 'setStartAfter' : 'setEndAfter'](sibling);\r
6609 \r
6610                                                 return;\r
6611                                         }\r
6612 \r
6613                                         while (sibling) {\r
6614                                                 textNodeOffset += sibling.nodeValue.length;\r
6615 \r
6616                                                 // We are at or passed the position we where looking for\r
6617                                                 if (textNodeOffset >= offset) {\r
6618                                                         container = sibling;\r
6619                                                         textNodeOffset -= offset;\r
6620                                                         break;\r
6621                                                 }\r
6622 \r
6623                                                 sibling = sibling.previousSibling;\r
6624                                         }\r
6625                                 }\r
6626 \r
6627                                 domRange[start ? 'setStart' : 'setEnd'](container, textNodeOffset);\r
6628                         };\r
6629 \r
6630                         try {\r
6631                                 // Find start point\r
6632                                 findEndPoint(true);\r
6633 \r
6634                                 // Find end point if needed\r
6635                                 if (!collapsed)\r
6636                                         findEndPoint();\r
6637                         } catch (ex) {\r
6638                                 // IE has a nasty bug where text nodes might throw "invalid argument" when you\r
6639                                 // access the nodeValue or other properties of text nodes. This seems to happend when\r
6640                                 // text nodes are split into two nodes by a delete/backspace call. So lets detect it and try to fix it.\r
6641                                 if (ex.number == -2147024809) {\r
6642                                         // Get the current selection\r
6643                                         bookmark = self.getBookmark(2);\r
6644 \r
6645                                         // Get start element\r
6646                                         tmpRange = ieRange.duplicate();\r
6647                                         tmpRange.collapse(true);\r
6648                                         element = tmpRange.parentElement();\r
6649 \r
6650                                         // Get end element\r
6651                                         if (!collapsed) {\r
6652                                                 tmpRange = ieRange.duplicate();\r
6653                                                 tmpRange.collapse(false);\r
6654                                                 element2 = tmpRange.parentElement();\r
6655                                                 element2.innerHTML = element2.innerHTML;\r
6656                                         }\r
6657 \r
6658                                         // Remove the broken elements\r
6659                                         element.innerHTML = element.innerHTML;\r
6660 \r
6661                                         // Restore the selection\r
6662                                         self.moveToBookmark(bookmark);\r
6663 \r
6664                                         // Since the range has moved we need to re-get it\r
6665                                         ieRange = selection.getRng();\r
6666 \r
6667                                         // Find start point\r
6668                                         findEndPoint(true);\r
6669 \r
6670                                         // Find end point if needed\r
6671                                         if (!collapsed)\r
6672                                                 findEndPoint();\r
6673                                 } else\r
6674                                         throw ex; // Throw other errors\r
6675                         }\r
6676 \r
6677                         return domRange;\r
6678                 };\r
6679 \r
6680                 this.getBookmark = function(type) {\r
6681                         var rng = selection.getRng(), start, end, bookmark = {};\r
6682 \r
6683                         function getIndexes(node) {\r
6684                                 var node, parent, root, children, i, indexes = [];\r
6685 \r
6686                                 parent = node.parentNode;\r
6687                                 root = dom.getRoot().parentNode;\r
6688 \r
6689                                 while (parent != root && parent.nodeType !== 9) {\r
6690                                         children = parent.children;\r
6691 \r
6692                                         i = children.length;\r
6693                                         while (i--) {\r
6694                                                 if (node === children[i]) {\r
6695                                                         indexes.push(i);\r
6696                                                         break;\r
6697                                                 }\r
6698                                         }\r
6699 \r
6700                                         node = parent;\r
6701                                         parent = parent.parentNode;\r
6702                                 }\r
6703 \r
6704                                 return indexes;\r
6705                         };\r
6706 \r
6707                         function getBookmarkEndPoint(start) {\r
6708                                 var position;\r
6709 \r
6710                                 position = getPosition(rng, start);\r
6711                                 if (position) {\r
6712                                         return {\r
6713                                                 position : position.position,\r
6714                                                 offset : position.offset,\r
6715                                                 indexes : getIndexes(position.node),\r
6716                                                 inside : position.inside\r
6717                                         };\r
6718                                 }\r
6719                         };\r
6720 \r
6721                         // Non ubstructive bookmark\r
6722                         if (type === 2) {\r
6723                                 // Handle text selection\r
6724                                 if (!rng.item) {\r
6725                                         bookmark.start = getBookmarkEndPoint(true);\r
6726 \r
6727                                         if (!selection.isCollapsed())\r
6728                                                 bookmark.end = getBookmarkEndPoint();\r
6729                                 } else\r
6730                                         bookmark.start = {ctrl : true, indexes : getIndexes(rng.item(0))};\r
6731                         }\r
6732 \r
6733                         return bookmark;\r
6734                 };\r
6735 \r
6736                 this.moveToBookmark = function(bookmark) {\r
6737                         var rng, body = dom.doc.body;\r
6738 \r
6739                         function resolveIndexes(indexes) {\r
6740                                 var node, i, idx, children;\r
6741 \r
6742                                 node = dom.getRoot();\r
6743                                 for (i = indexes.length - 1; i >= 0; i--) {\r
6744                                         children = node.children;\r
6745                                         idx = indexes[i];\r
6746 \r
6747                                         if (idx <= children.length - 1) {\r
6748                                                 node = children[idx];\r
6749                                         }\r
6750                                 }\r
6751 \r
6752                                 return node;\r
6753                         };\r
6754                         \r
6755                         function setBookmarkEndPoint(start) {\r
6756                                 var endPoint = bookmark[start ? 'start' : 'end'], moveLeft, moveRng, undef;\r
6757 \r
6758                                 if (endPoint) {\r
6759                                         moveLeft = endPoint.position > 0;\r
6760 \r
6761                                         moveRng = body.createTextRange();\r
6762                                         moveRng.moveToElementText(resolveIndexes(endPoint.indexes));\r
6763 \r
6764                                         offset = endPoint.offset;\r
6765                                         if (offset !== undef) {\r
6766                                                 moveRng.collapse(endPoint.inside || moveLeft);\r
6767                                                 moveRng.moveStart('character', moveLeft ? -offset : offset);\r
6768                                         } else\r
6769                                                 moveRng.collapse(start);\r
6770 \r
6771                                         rng.setEndPoint(start ? 'StartToStart' : 'EndToStart', moveRng);\r
6772 \r
6773                                         if (start)\r
6774                                                 rng.collapse(true);\r
6775                                 }\r
6776                         };\r
6777 \r
6778                         if (bookmark.start) {\r
6779                                 if (bookmark.start.ctrl) {\r
6780                                         rng = body.createControlRange();\r
6781                                         rng.addElement(resolveIndexes(bookmark.start.indexes));\r
6782                                         rng.select();\r
6783                                 } else {\r
6784                                         rng = body.createTextRange();\r
6785                                         setBookmarkEndPoint(true);\r
6786                                         setBookmarkEndPoint();\r
6787                                         rng.select();\r
6788                                 }\r
6789                         }\r
6790                 };\r
6791 \r
6792                 this.addRange = function(rng) {\r
6793                         var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, doc = selection.dom.doc, body = doc.body;\r
6794 \r
6795                         function setEndPoint(start) {\r
6796                                 var container, offset, marker, tmpRng, nodes;\r
6797 \r
6798                                 marker = dom.create('a');\r
6799                                 container = start ? startContainer : endContainer;\r
6800                                 offset = start ? startOffset : endOffset;\r
6801                                 tmpRng = ieRng.duplicate();\r
6802 \r
6803                                 if (container == doc || container == doc.documentElement) {\r
6804                                         container = body;\r
6805                                         offset = 0;\r
6806                                 }\r
6807 \r
6808                                 if (container.nodeType == 3) {\r
6809                                         container.parentNode.insertBefore(marker, container);\r
6810                                         tmpRng.moveToElementText(marker);\r
6811                                         tmpRng.moveStart('character', offset);\r
6812                                         dom.remove(marker);\r
6813                                         ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);\r
6814                                 } else {\r
6815                                         nodes = container.childNodes;\r
6816 \r
6817                                         if (nodes.length) {\r
6818                                                 if (offset >= nodes.length) {\r
6819                                                         dom.insertAfter(marker, nodes[nodes.length - 1]);\r
6820                                                 } else {\r
6821                                                         container.insertBefore(marker, nodes[offset]);\r
6822                                                 }\r
6823 \r
6824                                                 tmpRng.moveToElementText(marker);\r
6825                                         } else if (container.canHaveHTML) {\r
6826                                                 // Empty node selection for example <div>|</div>\r
6827                                                 // Setting innerHTML with a span marker then remove that marker seems to keep empty block elements open\r
6828                                                 container.innerHTML = '<span>\uFEFF</span>';\r
6829                                                 marker = container.firstChild;\r
6830                                                 tmpRng.moveToElementText(marker);\r
6831                                                 tmpRng.collapse(FALSE); // Collapse false works better than true for some odd reason\r
6832                                         }\r
6833 \r
6834                                         ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);\r
6835                                         dom.remove(marker);\r
6836                                 }\r
6837                         }\r
6838 \r
6839                         // Setup some shorter versions\r
6840                         startContainer = rng.startContainer;\r
6841                         startOffset = rng.startOffset;\r
6842                         endContainer = rng.endContainer;\r
6843                         endOffset = rng.endOffset;\r
6844                         ieRng = body.createTextRange();\r
6845 \r
6846                         // If single element selection then try making a control selection out of it\r
6847                         if (startContainer == endContainer && startContainer.nodeType == 1) {\r
6848                                 // Trick to place the caret inside an empty block element like <p></p>\r
6849                                 if (!startContainer.hasChildNodes()) {\r
6850                                         startContainer.innerHTML = '<span>\uFEFF</span><span>\uFEFF</span>';\r
6851                                         ieRng.moveToElementText(startContainer.lastChild);\r
6852                                         ieRng.select();\r
6853                                         dom.doc.selection.clear();\r
6854                                         startContainer.innerHTML = '';\r
6855                                         return;\r
6856                                 }\r
6857 \r
6858                                 if (startOffset == endOffset - 1) {\r
6859                                         try {\r
6860                                                 ctrlRng = body.createControlRange();\r
6861                                                 ctrlRng.addElement(startContainer.childNodes[startOffset]);\r
6862                                                 ctrlRng.select();\r
6863                                                 return;\r
6864                                         } catch (ex) {\r
6865                                                 // Ignore\r
6866                                         }\r
6867                                 }\r
6868                         }\r
6869 \r
6870                         // Set start/end point of selection\r
6871                         setEndPoint(true);\r
6872                         setEndPoint();\r
6873 \r
6874                         // Select the new range and scroll it into view\r
6875                         ieRng.select();\r
6876                 };\r
6877 \r
6878                 // Expose range method\r
6879                 this.getRangeAt = getRange;\r
6880         };\r
6881 \r
6882         // Expose the selection object\r
6883         tinymce.dom.TridentSelection = Selection;\r
6884 })();\r
6885 \r
6886 \r
6887 /*\r
6888  * Sizzle CSS Selector Engine - v1.0\r
6889  *  Copyright 2009, The Dojo Foundation\r
6890  *  Released under the MIT, BSD, and GPL Licenses.\r
6891  *  More information: http://sizzlejs.com/\r
6892  */\r
6893 (function(){\r
6894 \r
6895 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,\r
6896         done = 0,\r
6897         toString = Object.prototype.toString,\r
6898         hasDuplicate = false,\r
6899         baseHasDuplicate = true;\r
6900 \r
6901 // Here we check if the JavaScript engine is using some sort of\r
6902 // optimization where it does not always call our comparision\r
6903 // function. If that is the case, discard the hasDuplicate value.\r
6904 //   Thus far that includes Google Chrome.\r
6905 [0, 0].sort(function(){\r
6906         baseHasDuplicate = false;\r
6907         return 0;\r
6908 });\r
6909 \r
6910 var Sizzle = function(selector, context, results, seed) {\r
6911         results = results || [];\r
6912         context = context || document;\r
6913 \r
6914         var origContext = context;\r
6915 \r
6916         if ( context.nodeType !== 1 && context.nodeType !== 9 ) {\r
6917                 return [];\r
6918         }\r
6919         \r
6920         if ( !selector || typeof selector !== "string" ) {\r
6921                 return results;\r
6922         }\r
6923 \r
6924         var parts = [], m, set, checkSet, extra, prune = true, contextXML = Sizzle.isXML(context),\r
6925                 soFar = selector, ret, cur, pop, i;\r
6926         \r
6927         // Reset the position of the chunker regexp (start from head)\r
6928         do {\r
6929                 chunker.exec("");\r
6930                 m = chunker.exec(soFar);\r
6931 \r
6932                 if ( m ) {\r
6933                         soFar = m[3];\r
6934                 \r
6935                         parts.push( m[1] );\r
6936                 \r
6937                         if ( m[2] ) {\r
6938                                 extra = m[3];\r
6939                                 break;\r
6940                         }\r
6941                 }\r
6942         } while ( m );\r
6943 \r
6944         if ( parts.length > 1 && origPOS.exec( selector ) ) {\r
6945                 if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {\r
6946                         set = posProcess( parts[0] + parts[1], context );\r
6947                 } else {\r
6948                         set = Expr.relative[ parts[0] ] ?\r
6949                                 [ context ] :\r
6950                                 Sizzle( parts.shift(), context );\r
6951 \r
6952                         while ( parts.length ) {\r
6953                                 selector = parts.shift();\r
6954 \r
6955                                 if ( Expr.relative[ selector ] ) {\r
6956                                         selector += parts.shift();\r
6957                                 }\r
6958                                 \r
6959                                 set = posProcess( selector, set );\r
6960                         }\r
6961                 }\r
6962         } else {\r
6963                 // Take a shortcut and set the context if the root selector is an ID\r
6964                 // (but not if it'll be faster if the inner selector is an ID)\r
6965                 if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&\r
6966                                 Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {\r
6967                         ret = Sizzle.find( parts.shift(), context, contextXML );\r
6968                         context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];\r
6969                 }\r
6970 \r
6971                 if ( context ) {\r
6972                         ret = seed ?\r
6973                                 { expr: parts.pop(), set: makeArray(seed) } :\r
6974                                 Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );\r
6975                         set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;\r
6976 \r
6977                         if ( parts.length > 0 ) {\r
6978                                 checkSet = makeArray(set);\r
6979                         } else {\r
6980                                 prune = false;\r
6981                         }\r
6982 \r
6983                         while ( parts.length ) {\r
6984                                 cur = parts.pop();\r
6985                                 pop = cur;\r
6986 \r
6987                                 if ( !Expr.relative[ cur ] ) {\r
6988                                         cur = "";\r
6989                                 } else {\r
6990                                         pop = parts.pop();\r
6991                                 }\r
6992 \r
6993                                 if ( pop == null ) {\r
6994                                         pop = context;\r
6995                                 }\r
6996 \r
6997                                 Expr.relative[ cur ]( checkSet, pop, contextXML );\r
6998                         }\r
6999                 } else {\r
7000                         checkSet = parts = [];\r
7001                 }\r
7002         }\r
7003 \r
7004         if ( !checkSet ) {\r
7005                 checkSet = set;\r
7006         }\r
7007 \r
7008         if ( !checkSet ) {\r
7009                 Sizzle.error( cur || selector );\r
7010         }\r
7011 \r
7012         if ( toString.call(checkSet) === "[object Array]" ) {\r
7013                 if ( !prune ) {\r
7014                         results.push.apply( results, checkSet );\r
7015                 } else if ( context && context.nodeType === 1 ) {\r
7016                         for ( i = 0; checkSet[i] != null; i++ ) {\r
7017                                 if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {\r
7018                                         results.push( set[i] );\r
7019                                 }\r
7020                         }\r
7021                 } else {\r
7022                         for ( i = 0; checkSet[i] != null; i++ ) {\r
7023                                 if ( checkSet[i] && checkSet[i].nodeType === 1 ) {\r
7024                                         results.push( set[i] );\r
7025                                 }\r
7026                         }\r
7027                 }\r
7028         } else {\r
7029                 makeArray( checkSet, results );\r
7030         }\r
7031 \r
7032         if ( extra ) {\r
7033                 Sizzle( extra, origContext, results, seed );\r
7034                 Sizzle.uniqueSort( results );\r
7035         }\r
7036 \r
7037         return results;\r
7038 };\r
7039 \r
7040 Sizzle.uniqueSort = function(results){\r
7041         if ( sortOrder ) {\r
7042                 hasDuplicate = baseHasDuplicate;\r
7043                 results.sort(sortOrder);\r
7044 \r
7045                 if ( hasDuplicate ) {\r
7046                         for ( var i = 1; i < results.length; i++ ) {\r
7047                                 if ( results[i] === results[i-1] ) {\r
7048                                         results.splice(i--, 1);\r
7049                                 }\r
7050                         }\r
7051                 }\r
7052         }\r
7053 \r
7054         return results;\r
7055 };\r
7056 \r
7057 Sizzle.matches = function(expr, set){\r
7058         return Sizzle(expr, null, null, set);\r
7059 };\r
7060 \r
7061 Sizzle.find = function(expr, context, isXML){\r
7062         var set;\r
7063 \r
7064         if ( !expr ) {\r
7065                 return [];\r
7066         }\r
7067 \r
7068         for ( var i = 0, l = Expr.order.length; i < l; i++ ) {\r
7069                 var type = Expr.order[i], match;\r
7070                 \r
7071                 if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {\r
7072                         var left = match[1];\r
7073                         match.splice(1,1);\r
7074 \r
7075                         if ( left.substr( left.length - 1 ) !== "\\" ) {\r
7076                                 match[1] = (match[1] || "").replace(/\\/g, "");\r
7077                                 set = Expr.find[ type ]( match, context, isXML );\r
7078                                 if ( set != null ) {\r
7079                                         expr = expr.replace( Expr.match[ type ], "" );\r
7080                                         break;\r
7081                                 }\r
7082                         }\r
7083                 }\r
7084         }\r
7085 \r
7086         if ( !set ) {\r
7087                 set = context.getElementsByTagName("*");\r
7088         }\r
7089 \r
7090         return {set: set, expr: expr};\r
7091 };\r
7092 \r
7093 Sizzle.filter = function(expr, set, inplace, not){\r
7094         var old = expr, result = [], curLoop = set, match, anyFound,\r
7095                 isXMLFilter = set && set[0] && Sizzle.isXML(set[0]);\r
7096 \r
7097         while ( expr && set.length ) {\r
7098                 for ( var type in Expr.filter ) {\r
7099                         if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {\r
7100                                 var filter = Expr.filter[ type ], found, item, left = match[1];\r
7101                                 anyFound = false;\r
7102 \r
7103                                 match.splice(1,1);\r
7104 \r
7105                                 if ( left.substr( left.length - 1 ) === "\\" ) {\r
7106                                         continue;\r
7107                                 }\r
7108 \r
7109                                 if ( curLoop === result ) {\r
7110                                         result = [];\r
7111                                 }\r
7112 \r
7113                                 if ( Expr.preFilter[ type ] ) {\r
7114                                         match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );\r
7115 \r
7116                                         if ( !match ) {\r
7117                                                 anyFound = found = true;\r
7118                                         } else if ( match === true ) {\r
7119                                                 continue;\r
7120                                         }\r
7121                                 }\r
7122 \r
7123                                 if ( match ) {\r
7124                                         for ( var i = 0; (item = curLoop[i]) != null; i++ ) {\r
7125                                                 if ( item ) {\r
7126                                                         found = filter( item, match, i, curLoop );\r
7127                                                         var pass = not ^ !!found;\r
7128 \r
7129                                                         if ( inplace && found != null ) {\r
7130                                                                 if ( pass ) {\r
7131                                                                         anyFound = true;\r
7132                                                                 } else {\r
7133                                                                         curLoop[i] = false;\r
7134                                                                 }\r
7135                                                         } else if ( pass ) {\r
7136                                                                 result.push( item );\r
7137                                                                 anyFound = true;\r
7138                                                         }\r
7139                                                 }\r
7140                                         }\r
7141                                 }\r
7142 \r
7143                                 if ( found !== undefined ) {\r
7144                                         if ( !inplace ) {\r
7145                                                 curLoop = result;\r
7146                                         }\r
7147 \r
7148                                         expr = expr.replace( Expr.match[ type ], "" );\r
7149 \r
7150                                         if ( !anyFound ) {\r
7151                                                 return [];\r
7152                                         }\r
7153 \r
7154                                         break;\r
7155                                 }\r
7156                         }\r
7157                 }\r
7158 \r
7159                 // Improper expression\r
7160                 if ( expr === old ) {\r
7161                         if ( anyFound == null ) {\r
7162                                 Sizzle.error( expr );\r
7163                         } else {\r
7164                                 break;\r
7165                         }\r
7166                 }\r
7167 \r
7168                 old = expr;\r
7169         }\r
7170 \r
7171         return curLoop;\r
7172 };\r
7173 \r
7174 Sizzle.error = function( msg ) {\r
7175         throw "Syntax error, unrecognized expression: " + msg;\r
7176 };\r
7177 \r
7178 var Expr = Sizzle.selectors = {\r
7179         order: [ "ID", "NAME", "TAG" ],\r
7180         match: {\r
7181                 ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,\r
7182                 CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,\r
7183                 NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,\r
7184                 ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,\r
7185                 TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,\r
7186                 CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,\r
7187                 POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,\r
7188                 PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/\r
7189         },\r
7190         leftMatch: {},\r
7191         attrMap: {\r
7192                 "class": "className",\r
7193                 "for": "htmlFor"\r
7194         },\r
7195         attrHandle: {\r
7196                 href: function(elem){\r
7197                         return elem.getAttribute("href");\r
7198                 }\r
7199         },\r
7200         relative: {\r
7201                 "+": function(checkSet, part){\r
7202                         var isPartStr = typeof part === "string",\r
7203                                 isTag = isPartStr && !/\W/.test(part),\r
7204                                 isPartStrNotTag = isPartStr && !isTag;\r
7205 \r
7206                         if ( isTag ) {\r
7207                                 part = part.toLowerCase();\r
7208                         }\r
7209 \r
7210                         for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {\r
7211                                 if ( (elem = checkSet[i]) ) {\r
7212                                         while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}\r
7213 \r
7214                                         checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?\r
7215                                                 elem || false :\r
7216                                                 elem === part;\r
7217                                 }\r
7218                         }\r
7219 \r
7220                         if ( isPartStrNotTag ) {\r
7221                                 Sizzle.filter( part, checkSet, true );\r
7222                         }\r
7223                 },\r
7224                 ">": function(checkSet, part){\r
7225                         var isPartStr = typeof part === "string",\r
7226                                 elem, i = 0, l = checkSet.length;\r
7227 \r
7228                         if ( isPartStr && !/\W/.test(part) ) {\r
7229                                 part = part.toLowerCase();\r
7230 \r
7231                                 for ( ; i < l; i++ ) {\r
7232                                         elem = checkSet[i];\r
7233                                         if ( elem ) {\r
7234                                                 var parent = elem.parentNode;\r
7235                                                 checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;\r
7236                                         }\r
7237                                 }\r
7238                         } else {\r
7239                                 for ( ; i < l; i++ ) {\r
7240                                         elem = checkSet[i];\r
7241                                         if ( elem ) {\r
7242                                                 checkSet[i] = isPartStr ?\r
7243                                                         elem.parentNode :\r
7244                                                         elem.parentNode === part;\r
7245                                         }\r
7246                                 }\r
7247 \r
7248                                 if ( isPartStr ) {\r
7249                                         Sizzle.filter( part, checkSet, true );\r
7250                                 }\r
7251                         }\r
7252                 },\r
7253                 "": function(checkSet, part, isXML){\r
7254                         var doneName = done++, checkFn = dirCheck, nodeCheck;\r
7255 \r
7256                         if ( typeof part === "string" && !/\W/.test(part) ) {\r
7257                                 part = part.toLowerCase();\r
7258                                 nodeCheck = part;\r
7259                                 checkFn = dirNodeCheck;\r
7260                         }\r
7261 \r
7262                         checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);\r
7263                 },\r
7264                 "~": function(checkSet, part, isXML){\r
7265                         var doneName = done++, checkFn = dirCheck, nodeCheck;\r
7266 \r
7267                         if ( typeof part === "string" && !/\W/.test(part) ) {\r
7268                                 part = part.toLowerCase();\r
7269                                 nodeCheck = part;\r
7270                                 checkFn = dirNodeCheck;\r
7271                         }\r
7272 \r
7273                         checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);\r
7274                 }\r
7275         },\r
7276         find: {\r
7277                 ID: function(match, context, isXML){\r
7278                         if ( typeof context.getElementById !== "undefined" && !isXML ) {\r
7279                                 var m = context.getElementById(match[1]);\r
7280                                 return m ? [m] : [];\r
7281                         }\r
7282                 },\r
7283                 NAME: function(match, context){\r
7284                         if ( typeof context.getElementsByName !== "undefined" ) {\r
7285                                 var ret = [], results = context.getElementsByName(match[1]);\r
7286 \r
7287                                 for ( var i = 0, l = results.length; i < l; i++ ) {\r
7288                                         if ( results[i].getAttribute("name") === match[1] ) {\r
7289                                                 ret.push( results[i] );\r
7290                                         }\r
7291                                 }\r
7292 \r
7293                                 return ret.length === 0 ? null : ret;\r
7294                         }\r
7295                 },\r
7296                 TAG: function(match, context){\r
7297                         return context.getElementsByTagName(match[1]);\r
7298                 }\r
7299         },\r
7300         preFilter: {\r
7301                 CLASS: function(match, curLoop, inplace, result, not, isXML){\r
7302                         match = " " + match[1].replace(/\\/g, "") + " ";\r
7303 \r
7304                         if ( isXML ) {\r
7305                                 return match;\r
7306                         }\r
7307 \r
7308                         for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {\r
7309                                 if ( elem ) {\r
7310                                         if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {\r
7311                                                 if ( !inplace ) {\r
7312                                                         result.push( elem );\r
7313                                                 }\r
7314                                         } else if ( inplace ) {\r
7315                                                 curLoop[i] = false;\r
7316                                         }\r
7317                                 }\r
7318                         }\r
7319 \r
7320                         return false;\r
7321                 },\r
7322                 ID: function(match){\r
7323                         return match[1].replace(/\\/g, "");\r
7324                 },\r
7325                 TAG: function(match, curLoop){\r
7326                         return match[1].toLowerCase();\r
7327                 },\r
7328                 CHILD: function(match){\r
7329                         if ( match[1] === "nth" ) {\r
7330                                 // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'\r
7331                                 var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(\r
7332                                         match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||\r
7333                                         !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);\r
7334 \r
7335                                 // calculate the numbers (first)n+(last) including if they are negative\r
7336                                 match[2] = (test[1] + (test[2] || 1)) - 0;\r
7337                                 match[3] = test[3] - 0;\r
7338                         }\r
7339 \r
7340                         // TODO: Move to normal caching system\r
7341                         match[0] = done++;\r
7342 \r
7343                         return match;\r
7344                 },\r
7345                 ATTR: function(match, curLoop, inplace, result, not, isXML){\r
7346                         var name = match[1].replace(/\\/g, "");\r
7347                         \r
7348                         if ( !isXML && Expr.attrMap[name] ) {\r
7349                                 match[1] = Expr.attrMap[name];\r
7350                         }\r
7351 \r
7352                         if ( match[2] === "~=" ) {\r
7353                                 match[4] = " " + match[4] + " ";\r
7354                         }\r
7355 \r
7356                         return match;\r
7357                 },\r
7358                 PSEUDO: function(match, curLoop, inplace, result, not){\r
7359                         if ( match[1] === "not" ) {\r
7360                                 // If we're dealing with a complex expression, or a simple one\r
7361                                 if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {\r
7362                                         match[3] = Sizzle(match[3], null, null, curLoop);\r
7363                                 } else {\r
7364                                         var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);\r
7365                                         if ( !inplace ) {\r
7366                                                 result.push.apply( result, ret );\r
7367                                         }\r
7368                                         return false;\r
7369                                 }\r
7370                         } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {\r
7371                                 return true;\r
7372                         }\r
7373                         \r
7374                         return match;\r
7375                 },\r
7376                 POS: function(match){\r
7377                         match.unshift( true );\r
7378                         return match;\r
7379                 }\r
7380         },\r
7381         filters: {\r
7382                 enabled: function(elem){\r
7383                         return elem.disabled === false && elem.type !== "hidden";\r
7384                 },\r
7385                 disabled: function(elem){\r
7386                         return elem.disabled === true;\r
7387                 },\r
7388                 checked: function(elem){\r
7389                         return elem.checked === true;\r
7390                 },\r
7391                 selected: function(elem){\r
7392                         // Accessing this property makes selected-by-default\r
7393                         // options in Safari work properly\r
7394                         elem.parentNode.selectedIndex;\r
7395                         return elem.selected === true;\r
7396                 },\r
7397                 parent: function(elem){\r
7398                         return !!elem.firstChild;\r
7399                 },\r
7400                 empty: function(elem){\r
7401                         return !elem.firstChild;\r
7402                 },\r
7403                 has: function(elem, i, match){\r
7404                         return !!Sizzle( match[3], elem ).length;\r
7405                 },\r
7406                 header: function(elem){\r
7407                         return (/h\d/i).test( elem.nodeName );\r
7408                 },\r
7409                 text: function(elem){\r
7410                         return "text" === elem.type;\r
7411                 },\r
7412                 radio: function(elem){\r
7413                         return "radio" === elem.type;\r
7414                 },\r
7415                 checkbox: function(elem){\r
7416                         return "checkbox" === elem.type;\r
7417                 },\r
7418                 file: function(elem){\r
7419                         return "file" === elem.type;\r
7420                 },\r
7421                 password: function(elem){\r
7422                         return "password" === elem.type;\r
7423                 },\r
7424                 submit: function(elem){\r
7425                         return "submit" === elem.type;\r
7426                 },\r
7427                 image: function(elem){\r
7428                         return "image" === elem.type;\r
7429                 },\r
7430                 reset: function(elem){\r
7431                         return "reset" === elem.type;\r
7432                 },\r
7433                 button: function(elem){\r
7434                         return "button" === elem.type || elem.nodeName.toLowerCase() === "button";\r
7435                 },\r
7436                 input: function(elem){\r
7437                         return (/input|select|textarea|button/i).test(elem.nodeName);\r
7438                 }\r
7439         },\r
7440         setFilters: {\r
7441                 first: function(elem, i){\r
7442                         return i === 0;\r
7443                 },\r
7444                 last: function(elem, i, match, array){\r
7445                         return i === array.length - 1;\r
7446                 },\r
7447                 even: function(elem, i){\r
7448                         return i % 2 === 0;\r
7449                 },\r
7450                 odd: function(elem, i){\r
7451                         return i % 2 === 1;\r
7452                 },\r
7453                 lt: function(elem, i, match){\r
7454                         return i < match[3] - 0;\r
7455                 },\r
7456                 gt: function(elem, i, match){\r
7457                         return i > match[3] - 0;\r
7458                 },\r
7459                 nth: function(elem, i, match){\r
7460                         return match[3] - 0 === i;\r
7461                 },\r
7462                 eq: function(elem, i, match){\r
7463                         return match[3] - 0 === i;\r
7464                 }\r
7465         },\r
7466         filter: {\r
7467                 PSEUDO: function(elem, match, i, array){\r
7468                         var name = match[1], filter = Expr.filters[ name ];\r
7469 \r
7470                         if ( filter ) {\r
7471                                 return filter( elem, i, match, array );\r
7472                         } else if ( name === "contains" ) {\r
7473                                 return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;\r
7474                         } else if ( name === "not" ) {\r
7475                                 var not = match[3];\r
7476 \r
7477                                 for ( var j = 0, l = not.length; j < l; j++ ) {\r
7478                                         if ( not[j] === elem ) {\r
7479                                                 return false;\r
7480                                         }\r
7481                                 }\r
7482 \r
7483                                 return true;\r
7484                         } else {\r
7485                                 Sizzle.error( "Syntax error, unrecognized expression: " + name );\r
7486                         }\r
7487                 },\r
7488                 CHILD: function(elem, match){\r
7489                         var type = match[1], node = elem;\r
7490                         switch (type) {\r
7491                                 case 'only':\r
7492                                 case 'first':\r
7493                                         while ( (node = node.previousSibling) )  {\r
7494                                                 if ( node.nodeType === 1 ) { \r
7495                                                         return false; \r
7496                                                 }\r
7497                                         }\r
7498                                         if ( type === "first" ) { \r
7499                                                 return true; \r
7500                                         }\r
7501                                         node = elem;\r
7502                                 case 'last':\r
7503                                         while ( (node = node.nextSibling) )      {\r
7504                                                 if ( node.nodeType === 1 ) { \r
7505                                                         return false; \r
7506                                                 }\r
7507                                         }\r
7508                                         return true;\r
7509                                 case 'nth':\r
7510                                         var first = match[2], last = match[3];\r
7511 \r
7512                                         if ( first === 1 && last === 0 ) {\r
7513                                                 return true;\r
7514                                         }\r
7515                                         \r
7516                                         var doneName = match[0],\r
7517                                                 parent = elem.parentNode;\r
7518         \r
7519                                         if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {\r
7520                                                 var count = 0;\r
7521                                                 for ( node = parent.firstChild; node; node = node.nextSibling ) {\r
7522                                                         if ( node.nodeType === 1 ) {\r
7523                                                                 node.nodeIndex = ++count;\r
7524                                                         }\r
7525                                                 } \r
7526                                                 parent.sizcache = doneName;\r
7527                                         }\r
7528                                         \r
7529                                         var diff = elem.nodeIndex - last;\r
7530                                         if ( first === 0 ) {\r
7531                                                 return diff === 0;\r
7532                                         } else {\r
7533                                                 return ( diff % first === 0 && diff / first >= 0 );\r
7534                                         }\r
7535                         }\r
7536                 },\r
7537                 ID: function(elem, match){\r
7538                         return elem.nodeType === 1 && elem.getAttribute("id") === match;\r
7539                 },\r
7540                 TAG: function(elem, match){\r
7541                         return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;\r
7542                 },\r
7543                 CLASS: function(elem, match){\r
7544                         return (" " + (elem.className || elem.getAttribute("class")) + " ")\r
7545                                 .indexOf( match ) > -1;\r
7546                 },\r
7547                 ATTR: function(elem, match){\r
7548                         var name = match[1],\r
7549                                 result = Expr.attrHandle[ name ] ?\r
7550                                         Expr.attrHandle[ name ]( elem ) :\r
7551                                         elem[ name ] != null ?\r
7552                                                 elem[ name ] :\r
7553                                                 elem.getAttribute( name ),\r
7554                                 value = result + "",\r
7555                                 type = match[2],\r
7556                                 check = match[4];\r
7557 \r
7558                         return result == null ?\r
7559                                 type === "!=" :\r
7560                                 type === "=" ?\r
7561                                 value === check :\r
7562                                 type === "*=" ?\r
7563                                 value.indexOf(check) >= 0 :\r
7564                                 type === "~=" ?\r
7565                                 (" " + value + " ").indexOf(check) >= 0 :\r
7566                                 !check ?\r
7567                                 value && result !== false :\r
7568                                 type === "!=" ?\r
7569                                 value !== check :\r
7570                                 type === "^=" ?\r
7571                                 value.indexOf(check) === 0 :\r
7572                                 type === "$=" ?\r
7573                                 value.substr(value.length - check.length) === check :\r
7574                                 type === "|=" ?\r
7575                                 value === check || value.substr(0, check.length + 1) === check + "-" :\r
7576                                 false;\r
7577                 },\r
7578                 POS: function(elem, match, i, array){\r
7579                         var name = match[2], filter = Expr.setFilters[ name ];\r
7580 \r
7581                         if ( filter ) {\r
7582                                 return filter( elem, i, match, array );\r
7583                         }\r
7584                 }\r
7585         }\r
7586 };\r
7587 \r
7588 var origPOS = Expr.match.POS,\r
7589         fescape = function(all, num){\r
7590                 return "\\" + (num - 0 + 1);\r
7591         };\r
7592 \r
7593 for ( var type in Expr.match ) {\r
7594         Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );\r
7595         Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );\r
7596 }\r
7597 \r
7598 var makeArray = function(array, results) {\r
7599         array = Array.prototype.slice.call( array, 0 );\r
7600 \r
7601         if ( results ) {\r
7602                 results.push.apply( results, array );\r
7603                 return results;\r
7604         }\r
7605         \r
7606         return array;\r
7607 };\r
7608 \r
7609 // Perform a simple check to determine if the browser is capable of\r
7610 // converting a NodeList to an array using builtin methods.\r
7611 // Also verifies that the returned array holds DOM nodes\r
7612 // (which is not the case in the Blackberry browser)\r
7613 try {\r
7614         Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;\r
7615 \r
7616 // Provide a fallback method if it does not work\r
7617 } catch(e){\r
7618         makeArray = function(array, results) {\r
7619                 var ret = results || [], i = 0;\r
7620 \r
7621                 if ( toString.call(array) === "[object Array]" ) {\r
7622                         Array.prototype.push.apply( ret, array );\r
7623                 } else {\r
7624                         if ( typeof array.length === "number" ) {\r
7625                                 for ( var l = array.length; i < l; i++ ) {\r
7626                                         ret.push( array[i] );\r
7627                                 }\r
7628                         } else {\r
7629                                 for ( ; array[i]; i++ ) {\r
7630                                         ret.push( array[i] );\r
7631                                 }\r
7632                         }\r
7633                 }\r
7634 \r
7635                 return ret;\r
7636         };\r
7637 }\r
7638 \r
7639 var sortOrder;\r
7640 \r
7641 if ( document.documentElement.compareDocumentPosition ) {\r
7642         sortOrder = function( a, b ) {\r
7643                 if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {\r
7644                         if ( a == b ) {\r
7645                                 hasDuplicate = true;\r
7646                         }\r
7647                         return a.compareDocumentPosition ? -1 : 1;\r
7648                 }\r
7649 \r
7650                 var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;\r
7651                 if ( ret === 0 ) {\r
7652                         hasDuplicate = true;\r
7653                 }\r
7654                 return ret;\r
7655         };\r
7656 } else if ( "sourceIndex" in document.documentElement ) {\r
7657         sortOrder = function( a, b ) {\r
7658                 if ( !a.sourceIndex || !b.sourceIndex ) {\r
7659                         if ( a == b ) {\r
7660                                 hasDuplicate = true;\r
7661                         }\r
7662                         return a.sourceIndex ? -1 : 1;\r
7663                 }\r
7664 \r
7665                 var ret = a.sourceIndex - b.sourceIndex;\r
7666                 if ( ret === 0 ) {\r
7667                         hasDuplicate = true;\r
7668                 }\r
7669                 return ret;\r
7670         };\r
7671 } else if ( document.createRange ) {\r
7672         sortOrder = function( a, b ) {\r
7673                 if ( !a.ownerDocument || !b.ownerDocument ) {\r
7674                         if ( a == b ) {\r
7675                                 hasDuplicate = true;\r
7676                         }\r
7677                         return a.ownerDocument ? -1 : 1;\r
7678                 }\r
7679 \r
7680                 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();\r
7681                 aRange.setStart(a, 0);\r
7682                 aRange.setEnd(a, 0);\r
7683                 bRange.setStart(b, 0);\r
7684                 bRange.setEnd(b, 0);\r
7685                 var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);\r
7686                 if ( ret === 0 ) {\r
7687                         hasDuplicate = true;\r
7688                 }\r
7689                 return ret;\r
7690         };\r
7691 }\r
7692 \r
7693 // Utility function for retreiving the text value of an array of DOM nodes\r
7694 Sizzle.getText = function( elems ) {\r
7695         var ret = "", elem;\r
7696 \r
7697         for ( var i = 0; elems[i]; i++ ) {\r
7698                 elem = elems[i];\r
7699 \r
7700                 // Get the text from text nodes and CDATA nodes\r
7701                 if ( elem.nodeType === 3 || elem.nodeType === 4 ) {\r
7702                         ret += elem.nodeValue;\r
7703 \r
7704                 // Traverse everything else, except comment nodes\r
7705                 } else if ( elem.nodeType !== 8 ) {\r
7706                         ret += Sizzle.getText( elem.childNodes );\r
7707                 }\r
7708         }\r
7709 \r
7710         return ret;\r
7711 };\r
7712 \r
7713 // Check to see if the browser returns elements by name when\r
7714 // querying by getElementById (and provide a workaround)\r
7715 (function(){\r
7716         // We're going to inject a fake input element with a specified name\r
7717         var form = document.createElement("div"),\r
7718                 id = "script" + (new Date()).getTime();\r
7719         form.innerHTML = "<a name='" + id + "'/>";\r
7720 \r
7721         // Inject it into the root element, check its status, and remove it quickly\r
7722         var root = document.documentElement;\r
7723         root.insertBefore( form, root.firstChild );\r
7724 \r
7725         // The workaround has to do additional checks after a getElementById\r
7726         // Which slows things down for other browsers (hence the branching)\r
7727         if ( document.getElementById( id ) ) {\r
7728                 Expr.find.ID = function(match, context, isXML){\r
7729                         if ( typeof context.getElementById !== "undefined" && !isXML ) {\r
7730                                 var m = context.getElementById(match[1]);\r
7731                                 return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];\r
7732                         }\r
7733                 };\r
7734 \r
7735                 Expr.filter.ID = function(elem, match){\r
7736                         var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");\r
7737                         return elem.nodeType === 1 && node && node.nodeValue === match;\r
7738                 };\r
7739         }\r
7740 \r
7741         root.removeChild( form );\r
7742         root = form = null; // release memory in IE\r
7743 })();\r
7744 \r
7745 (function(){\r
7746         // Check to see if the browser returns only elements\r
7747         // when doing getElementsByTagName("*")\r
7748 \r
7749         // Create a fake element\r
7750         var div = document.createElement("div");\r
7751         div.appendChild( document.createComment("") );\r
7752 \r
7753         // Make sure no comments are found\r
7754         if ( div.getElementsByTagName("*").length > 0 ) {\r
7755                 Expr.find.TAG = function(match, context){\r
7756                         var results = context.getElementsByTagName(match[1]);\r
7757 \r
7758                         // Filter out possible comments\r
7759                         if ( match[1] === "*" ) {\r
7760                                 var tmp = [];\r
7761 \r
7762                                 for ( var i = 0; results[i]; i++ ) {\r
7763                                         if ( results[i].nodeType === 1 ) {\r
7764                                                 tmp.push( results[i] );\r
7765                                         }\r
7766                                 }\r
7767 \r
7768                                 results = tmp;\r
7769                         }\r
7770 \r
7771                         return results;\r
7772                 };\r
7773         }\r
7774 \r
7775         // Check to see if an attribute returns normalized href attributes\r
7776         div.innerHTML = "<a href='#'></a>";\r
7777         if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&\r
7778                         div.firstChild.getAttribute("href") !== "#" ) {\r
7779                 Expr.attrHandle.href = function(elem){\r
7780                         return elem.getAttribute("href", 2);\r
7781                 };\r
7782         }\r
7783 \r
7784         div = null; // release memory in IE\r
7785 })();\r
7786 \r
7787 if ( document.querySelectorAll ) {\r
7788         (function(){\r
7789                 var oldSizzle = Sizzle, div = document.createElement("div");\r
7790                 div.innerHTML = "<p class='TEST'></p>";\r
7791 \r
7792                 // Safari can't handle uppercase or unicode characters when\r
7793                 // in quirks mode.\r
7794                 if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {\r
7795                         return;\r
7796                 }\r
7797         \r
7798                 Sizzle = function(query, context, extra, seed){\r
7799                         context = context || document;\r
7800 \r
7801                         // Only use querySelectorAll on non-XML documents\r
7802                         // (ID selectors don't work in non-HTML documents)\r
7803                         if ( !seed && context.nodeType === 9 && !Sizzle.isXML(context) ) {\r
7804                                 try {\r
7805                                         return makeArray( context.querySelectorAll(query), extra );\r
7806                                 } catch(e){}\r
7807                         }\r
7808                 \r
7809                         return oldSizzle(query, context, extra, seed);\r
7810                 };\r
7811 \r
7812                 for ( var prop in oldSizzle ) {\r
7813                         Sizzle[ prop ] = oldSizzle[ prop ];\r
7814                 }\r
7815 \r
7816                 div = null; // release memory in IE\r
7817         })();\r
7818 }\r
7819 \r
7820 (function(){\r
7821         var div = document.createElement("div");\r
7822 \r
7823         div.innerHTML = "<div class='test e'></div><div class='test'></div>";\r
7824 \r
7825         // Opera can't find a second classname (in 9.6)\r
7826         // Also, make sure that getElementsByClassName actually exists\r
7827         if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {\r
7828                 return;\r
7829         }\r
7830 \r
7831         // Safari caches class attributes, doesn't catch changes (in 3.2)\r
7832         div.lastChild.className = "e";\r
7833 \r
7834         if ( div.getElementsByClassName("e").length === 1 ) {\r
7835                 return;\r
7836         }\r
7837         \r
7838         Expr.order.splice(1, 0, "CLASS");\r
7839         Expr.find.CLASS = function(match, context, isXML) {\r
7840                 if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {\r
7841                         return context.getElementsByClassName(match[1]);\r
7842                 }\r
7843         };\r
7844 \r
7845         div = null; // release memory in IE\r
7846 })();\r
7847 \r
7848 function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\r
7849         for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
7850                 var elem = checkSet[i];\r
7851                 if ( elem ) {\r
7852                         elem = elem[dir];\r
7853                         var match = false;\r
7854 \r
7855                         while ( elem ) {\r
7856                                 if ( elem.sizcache === doneName ) {\r
7857                                         match = checkSet[elem.sizset];\r
7858                                         break;\r
7859                                 }\r
7860 \r
7861                                 if ( elem.nodeType === 1 && !isXML ){\r
7862                                         elem.sizcache = doneName;\r
7863                                         elem.sizset = i;\r
7864                                 }\r
7865 \r
7866                                 if ( elem.nodeName.toLowerCase() === cur ) {\r
7867                                         match = elem;\r
7868                                         break;\r
7869                                 }\r
7870 \r
7871                                 elem = elem[dir];\r
7872                         }\r
7873 \r
7874                         checkSet[i] = match;\r
7875                 }\r
7876         }\r
7877 }\r
7878 \r
7879 function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\r
7880         for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
7881                 var elem = checkSet[i];\r
7882                 if ( elem ) {\r
7883                         elem = elem[dir];\r
7884                         var match = false;\r
7885 \r
7886                         while ( elem ) {\r
7887                                 if ( elem.sizcache === doneName ) {\r
7888                                         match = checkSet[elem.sizset];\r
7889                                         break;\r
7890                                 }\r
7891 \r
7892                                 if ( elem.nodeType === 1 ) {\r
7893                                         if ( !isXML ) {\r
7894                                                 elem.sizcache = doneName;\r
7895                                                 elem.sizset = i;\r
7896                                         }\r
7897                                         if ( typeof cur !== "string" ) {\r
7898                                                 if ( elem === cur ) {\r
7899                                                         match = true;\r
7900                                                         break;\r
7901                                                 }\r
7902 \r
7903                                         } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {\r
7904                                                 match = elem;\r
7905                                                 break;\r
7906                                         }\r
7907                                 }\r
7908 \r
7909                                 elem = elem[dir];\r
7910                         }\r
7911 \r
7912                         checkSet[i] = match;\r
7913                 }\r
7914         }\r
7915 }\r
7916 \r
7917 Sizzle.contains = document.compareDocumentPosition ? function(a, b){\r
7918         return !!(a.compareDocumentPosition(b) & 16);\r
7919 } : function(a, b){\r
7920         return a !== b && (a.contains ? a.contains(b) : true);\r
7921 };\r
7922 \r
7923 Sizzle.isXML = function(elem){\r
7924         // documentElement is verified for cases where it doesn't yet exist\r
7925         // (such as loading iframes in IE - #4833) \r
7926         var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;\r
7927         return documentElement ? documentElement.nodeName !== "HTML" : false;\r
7928 };\r
7929 \r
7930 var posProcess = function(selector, context){\r
7931         var tmpSet = [], later = "", match,\r
7932                 root = context.nodeType ? [context] : context;\r
7933 \r
7934         // Position selectors must be done after the filter\r
7935         // And so must :not(positional) so we move all PSEUDOs to the end\r
7936         while ( (match = Expr.match.PSEUDO.exec( selector )) ) {\r
7937                 later += match[0];\r
7938                 selector = selector.replace( Expr.match.PSEUDO, "" );\r
7939         }\r
7940 \r
7941         selector = Expr.relative[selector] ? selector + "*" : selector;\r
7942 \r
7943         for ( var i = 0, l = root.length; i < l; i++ ) {\r
7944                 Sizzle( selector, root[i], tmpSet );\r
7945         }\r
7946 \r
7947         return Sizzle.filter( later, tmpSet );\r
7948 };\r
7949 \r
7950 // EXPOSE\r
7951 \r
7952 window.tinymce.dom.Sizzle = Sizzle;\r
7953 \r
7954 })();\r
7955 \r
7956 \r
7957 (function(tinymce) {\r
7958         tinymce.dom.Element = function(id, settings) {\r
7959                 var t = this, dom, el;\r
7960 \r
7961                 t.settings = settings = settings || {};\r
7962                 t.id = id;\r
7963                 t.dom = dom = settings.dom || tinymce.DOM;\r
7964 \r
7965                 // Only IE leaks DOM references, this is a lot faster\r
7966                 if (!tinymce.isIE)\r
7967                         el = dom.get(t.id);\r
7968 \r
7969                 tinymce.each(\r
7970                                 ('getPos,getRect,getParent,add,setStyle,getStyle,setStyles,' + \r
7971                                 'setAttrib,setAttribs,getAttrib,addClass,removeClass,' + \r
7972                                 'hasClass,getOuterHTML,setOuterHTML,remove,show,hide,' + \r
7973                                 'isHidden,setHTML,get').split(/,/)\r
7974                         , function(k) {\r
7975                                 t[k] = function() {\r
7976                                         var a = [id], i;\r
7977 \r
7978                                         for (i = 0; i < arguments.length; i++)\r
7979                                                 a.push(arguments[i]);\r
7980 \r
7981                                         a = dom[k].apply(dom, a);\r
7982                                         t.update(k);\r
7983 \r
7984                                         return a;\r
7985                                 };\r
7986                 });\r
7987 \r
7988                 tinymce.extend(t, {\r
7989                         on : function(n, f, s) {\r
7990                                 return tinymce.dom.Event.add(t.id, n, f, s);\r
7991                         },\r
7992 \r
7993                         getXY : function() {\r
7994                                 return {\r
7995                                         x : parseInt(t.getStyle('left')),\r
7996                                         y : parseInt(t.getStyle('top'))\r
7997                                 };\r
7998                         },\r
7999 \r
8000                         getSize : function() {\r
8001                                 var n = dom.get(t.id);\r
8002 \r
8003                                 return {\r
8004                                         w : parseInt(t.getStyle('width') || n.clientWidth),\r
8005                                         h : parseInt(t.getStyle('height') || n.clientHeight)\r
8006                                 };\r
8007                         },\r
8008 \r
8009                         moveTo : function(x, y) {\r
8010                                 t.setStyles({left : x, top : y});\r
8011                         },\r
8012 \r
8013                         moveBy : function(x, y) {\r
8014                                 var p = t.getXY();\r
8015 \r
8016                                 t.moveTo(p.x + x, p.y + y);\r
8017                         },\r
8018 \r
8019                         resizeTo : function(w, h) {\r
8020                                 t.setStyles({width : w, height : h});\r
8021                         },\r
8022 \r
8023                         resizeBy : function(w, h) {\r
8024                                 var s = t.getSize();\r
8025 \r
8026                                 t.resizeTo(s.w + w, s.h + h);\r
8027                         },\r
8028 \r
8029                         update : function(k) {\r
8030                                 var b;\r
8031 \r
8032                                 if (tinymce.isIE6 && settings.blocker) {\r
8033                                         k = k || '';\r
8034 \r
8035                                         // Ignore getters\r
8036                                         if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0)\r
8037                                                 return;\r
8038 \r
8039                                         // Remove blocker on remove\r
8040                                         if (k == 'remove') {\r
8041                                                 dom.remove(t.blocker);\r
8042                                                 return;\r
8043                                         }\r
8044 \r
8045                                         if (!t.blocker) {\r
8046                                                 t.blocker = dom.uniqueId();\r
8047                                                 b = dom.add(settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'});\r
8048                                                 dom.setStyle(b, 'opacity', 0);\r
8049                                         } else\r
8050                                                 b = dom.get(t.blocker);\r
8051 \r
8052                                         dom.setStyles(b, {\r
8053                                                 left : t.getStyle('left', 1),\r
8054                                                 top : t.getStyle('top', 1),\r
8055                                                 width : t.getStyle('width', 1),\r
8056                                                 height : t.getStyle('height', 1),\r
8057                                                 display : t.getStyle('display', 1),\r
8058                                                 zIndex : parseInt(t.getStyle('zIndex', 1) || 0) - 1\r
8059                                         });\r
8060                                 }\r
8061                         }\r
8062                 });\r
8063         };\r
8064 })(tinymce);\r
8065 \r
8066 (function(tinymce) {\r
8067         function trimNl(s) {\r
8068                 return s.replace(/[\n\r]+/g, '');\r
8069         };\r
8070 \r
8071         // Shorten names\r
8072         var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each;\r
8073 \r
8074         tinymce.create('tinymce.dom.Selection', {\r
8075                 Selection : function(dom, win, serializer) {\r
8076                         var t = this;\r
8077 \r
8078                         t.dom = dom;\r
8079                         t.win = win;\r
8080                         t.serializer = serializer;\r
8081 \r
8082                         // Add events\r
8083                         each([\r
8084                                 'onBeforeSetContent',\r
8085 \r
8086                                 'onBeforeGetContent',\r
8087 \r
8088                                 'onSetContent',\r
8089 \r
8090                                 'onGetContent'\r
8091                         ], function(e) {\r
8092                                 t[e] = new tinymce.util.Dispatcher(t);\r
8093                         });\r
8094 \r
8095                         // No W3C Range support\r
8096                         if (!t.win.getSelection)\r
8097                                 t.tridentSel = new tinymce.dom.TridentSelection(t);\r
8098 \r
8099                         if (tinymce.isIE && dom.boxModel)\r
8100                                 this._fixIESelection();\r
8101 \r
8102                         // Prevent leaks\r
8103                         tinymce.addUnload(t.destroy, t);\r
8104                 },\r
8105 \r
8106                 setCursorLocation: function(node, offset) {\r
8107                         var t = this; var r = t.dom.createRng();\r
8108                         r.setStart(node, offset);\r
8109                         r.setEnd(node, offset);\r
8110                         t.setRng(r);\r
8111                         t.collapse(false);\r
8112                 },\r
8113                 getContent : function(s) {\r
8114                         var t = this, r = t.getRng(), e = t.dom.create("body"), se = t.getSel(), wb, wa, n;\r
8115 \r
8116                         s = s || {};\r
8117                         wb = wa = '';\r
8118                         s.get = true;\r
8119                         s.format = s.format || 'html';\r
8120                         s.forced_root_block = '';\r
8121                         t.onBeforeGetContent.dispatch(t, s);\r
8122 \r
8123                         if (s.format == 'text')\r
8124                                 return t.isCollapsed() ? '' : (r.text || (se.toString ? se.toString() : ''));\r
8125 \r
8126                         if (r.cloneContents) {\r
8127                                 n = r.cloneContents();\r
8128 \r
8129                                 if (n)\r
8130                                         e.appendChild(n);\r
8131                         } else if (is(r.item) || is(r.htmlText)) {\r
8132                                 // IE will produce invalid markup if elements are present that\r
8133                                 // it doesn't understand like custom elements or HTML5 elements.\r
8134                                 // Adding a BR in front of the contents and then remoiving it seems to fix it though.\r
8135                                 e.innerHTML = '<br>' + (r.item ? r.item(0).outerHTML : r.htmlText);\r
8136                                 e.removeChild(e.firstChild);\r
8137                         } else\r
8138                                 e.innerHTML = r.toString();\r
8139 \r
8140                         // Keep whitespace before and after\r
8141                         if (/^\s/.test(e.innerHTML))\r
8142                                 wb = ' ';\r
8143 \r
8144                         if (/\s+$/.test(e.innerHTML))\r
8145                                 wa = ' ';\r
8146 \r
8147                         s.getInner = true;\r
8148 \r
8149                         s.content = t.isCollapsed() ? '' : wb + t.serializer.serialize(e, s) + wa;\r
8150                         t.onGetContent.dispatch(t, s);\r
8151 \r
8152                         return s.content;\r
8153                 },\r
8154 \r
8155                 setContent : function(content, args) {\r
8156                         var self = this, rng = self.getRng(), caretNode, doc = self.win.document, frag, temp;\r
8157 \r
8158                         args = args || {format : 'html'};\r
8159                         args.set = true;\r
8160                         content = args.content = content;\r
8161 \r
8162                         // Dispatch before set content event\r
8163                         if (!args.no_events)\r
8164                                 self.onBeforeSetContent.dispatch(self, args);\r
8165 \r
8166                         content = args.content;\r
8167 \r
8168                         if (rng.insertNode) {\r
8169                                 // Make caret marker since insertNode places the caret in the beginning of text after insert\r
8170                                 content += '<span id="__caret">_</span>';\r
8171 \r
8172                                 // Delete and insert new node\r
8173                                 if (rng.startContainer == doc && rng.endContainer == doc) {\r
8174                                         // WebKit will fail if the body is empty since the range is then invalid and it can't insert contents\r
8175                                         doc.body.innerHTML = content;\r
8176                                 } else {\r
8177                                         rng.deleteContents();\r
8178 \r
8179                                         if (doc.body.childNodes.length == 0) {\r
8180                                                 doc.body.innerHTML = content;\r
8181                                         } else {\r
8182                                                 // createContextualFragment doesn't exists in IE 9 DOMRanges\r
8183                                                 if (rng.createContextualFragment) {\r
8184                                                         rng.insertNode(rng.createContextualFragment(content));\r
8185                                                 } else {\r
8186                                                         // Fake createContextualFragment call in IE 9\r
8187                                                         frag = doc.createDocumentFragment();\r
8188                                                         temp = doc.createElement('div');\r
8189 \r
8190                                                         frag.appendChild(temp);\r
8191                                                         temp.outerHTML = content;\r
8192 \r
8193                                                         rng.insertNode(frag);\r
8194                                                 }\r
8195                                         }\r
8196                                 }\r
8197 \r
8198                                 // Move to caret marker\r
8199                                 caretNode = self.dom.get('__caret');\r
8200 \r
8201                                 // Make sure we wrap it compleatly, Opera fails with a simple select call\r
8202                                 rng = doc.createRange();\r
8203                                 rng.setStartBefore(caretNode);\r
8204                                 rng.setEndBefore(caretNode);\r
8205                                 self.setRng(rng);\r
8206 \r
8207                                 // Remove the caret position\r
8208                                 self.dom.remove('__caret');\r
8209 \r
8210                                 try {\r
8211                                         self.setRng(rng);\r
8212                                 } catch (ex) {\r
8213                                         // Might fail on Opera for some odd reason\r
8214                                 }\r
8215                         } else {\r
8216                                 if (rng.item) {\r
8217                                         // Delete content and get caret text selection\r
8218                                         doc.execCommand('Delete', false, null);\r
8219                                         rng = self.getRng();\r
8220                                 }\r
8221 \r
8222                                 // Explorer removes spaces from the beginning of pasted contents\r
8223                                 if (/^\s+/.test(content)) {\r
8224                                         rng.pasteHTML('<span id="__mce_tmp">_</span>' + content);\r
8225                                         self.dom.remove('__mce_tmp');\r
8226                                 } else\r
8227                                         rng.pasteHTML(content);\r
8228                         }\r
8229 \r
8230                         // Dispatch set content event\r
8231                         if (!args.no_events)\r
8232                                 self.onSetContent.dispatch(self, args);\r
8233                 },\r
8234 \r
8235                 getStart : function() {\r
8236                         var rng = this.getRng(), startElement, parentElement, checkRng, node;\r
8237 \r
8238                         if (rng.duplicate || rng.item) {\r
8239                                 // Control selection, return first item\r
8240                                 if (rng.item)\r
8241                                         return rng.item(0);\r
8242 \r
8243                                 // Get start element\r
8244                                 checkRng = rng.duplicate();\r
8245                                 checkRng.collapse(1);\r
8246                                 startElement = checkRng.parentElement();\r
8247 \r
8248                                 // Check if range parent is inside the start element, then return the inner parent element\r
8249                                 // This will fix issues when a single element is selected, IE would otherwise return the wrong start element\r
8250                                 parentElement = node = rng.parentElement();\r
8251                                 while (node = node.parentNode) {\r
8252                                         if (node == startElement) {\r
8253                                                 startElement = parentElement;\r
8254                                                 break;\r
8255                                         }\r
8256                                 }\r
8257 \r
8258                                 return startElement;\r
8259                         } else {\r
8260                                 startElement = rng.startContainer;\r
8261 \r
8262                                 if (startElement.nodeType == 1 && startElement.hasChildNodes())\r
8263                                         startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];\r
8264 \r
8265                                 if (startElement && startElement.nodeType == 3)\r
8266                                         return startElement.parentNode;\r
8267 \r
8268                                 return startElement;\r
8269                         }\r
8270                 },\r
8271 \r
8272                 getEnd : function() {\r
8273                         var t = this, r = t.getRng(), e, eo;\r
8274 \r
8275                         if (r.duplicate || r.item) {\r
8276                                 if (r.item)\r
8277                                         return r.item(0);\r
8278 \r
8279                                 r = r.duplicate();\r
8280                                 r.collapse(0);\r
8281                                 e = r.parentElement();\r
8282 \r
8283                                 if (e && e.nodeName == 'BODY')\r
8284                                         return e.lastChild || e;\r
8285 \r
8286                                 return e;\r
8287                         } else {\r
8288                                 e = r.endContainer;\r
8289                                 eo = r.endOffset;\r
8290 \r
8291                                 if (e.nodeType == 1 && e.hasChildNodes())\r
8292                                         e = e.childNodes[eo > 0 ? eo - 1 : eo];\r
8293 \r
8294                                 if (e && e.nodeType == 3)\r
8295                                         return e.parentNode;\r
8296 \r
8297                                 return e;\r
8298                         }\r
8299                 },\r
8300 \r
8301                 getBookmark : function(type, normalized) {\r
8302                         var t = this, dom = t.dom, rng, rng2, id, collapsed, name, element, index, chr = '\uFEFF', styles;\r
8303 \r
8304                         function findIndex(name, element) {\r
8305                                 var index = 0;\r
8306 \r
8307                                 each(dom.select(name), function(node, i) {\r
8308                                         if (node == element)\r
8309                                                 index = i;\r
8310                                 });\r
8311 \r
8312                                 return index;\r
8313                         };\r
8314 \r
8315                         if (type == 2) {\r
8316                                 function getLocation() {\r
8317                                         var rng = t.getRng(true), root = dom.getRoot(), bookmark = {};\r
8318 \r
8319                                         function getPoint(rng, start) {\r
8320                                                 var container = rng[start ? 'startContainer' : 'endContainer'],\r
8321                                                         offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;\r
8322 \r
8323                                                 if (container.nodeType == 3) {\r
8324                                                         if (normalized) {\r
8325                                                                 for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling)\r
8326                                                                         offset += node.nodeValue.length;\r
8327                                                         }\r
8328 \r
8329                                                         point.push(offset);\r
8330                                                 } else {\r
8331                                                         childNodes = container.childNodes;\r
8332 \r
8333                                                         if (offset >= childNodes.length && childNodes.length) {\r
8334                                                                 after = 1;\r
8335                                                                 offset = Math.max(0, childNodes.length - 1);\r
8336                                                         }\r
8337 \r
8338                                                         point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after);\r
8339                                                 }\r
8340 \r
8341                                                 for (; container && container != root; container = container.parentNode)\r
8342                                                         point.push(t.dom.nodeIndex(container, normalized));\r
8343 \r
8344                                                 return point;\r
8345                                         };\r
8346 \r
8347                                         bookmark.start = getPoint(rng, true);\r
8348 \r
8349                                         if (!t.isCollapsed())\r
8350                                                 bookmark.end = getPoint(rng);\r
8351 \r
8352                                         return bookmark;\r
8353                                 };\r
8354 \r
8355                                 if (t.tridentSel)\r
8356                                         return t.tridentSel.getBookmark(type);\r
8357 \r
8358                                 return getLocation();\r
8359                         }\r
8360 \r
8361                         // Handle simple range\r
8362                         if (type)\r
8363                                 return {rng : t.getRng()};\r
8364 \r
8365                         rng = t.getRng();\r
8366                         id = dom.uniqueId();\r
8367                         collapsed = tinyMCE.activeEditor.selection.isCollapsed();\r
8368                         styles = 'overflow:hidden;line-height:0px';\r
8369 \r
8370                         // Explorer method\r
8371                         if (rng.duplicate || rng.item) {\r
8372                                 // Text selection\r
8373                                 if (!rng.item) {\r
8374                                         rng2 = rng.duplicate();\r
8375 \r
8376                                         try {\r
8377                                                 // Insert start marker\r
8378                                                 rng.collapse();\r
8379                                                 rng.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');\r
8380 \r
8381                                                 // Insert end marker\r
8382                                                 if (!collapsed) {\r
8383                                                         rng2.collapse(false);\r
8384 \r
8385                                                         // Detect the empty space after block elements in IE and move the end back one character <p></p>] becomes <p>]</p>\r
8386                                                         rng.moveToElementText(rng2.parentElement());\r
8387                                                         if (rng.compareEndPoints('StartToEnd', rng2) == 0)\r
8388                                                                 rng2.move('character', -1);\r
8389 \r
8390                                                         rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');\r
8391                                                 }\r
8392                                         } catch (ex) {\r
8393                                                 // IE might throw unspecified error so lets ignore it\r
8394                                                 return null;\r
8395                                         }\r
8396                                 } else {\r
8397                                         // Control selection\r
8398                                         element = rng.item(0);\r
8399                                         name = element.nodeName;\r
8400 \r
8401                                         return {name : name, index : findIndex(name, element)};\r
8402                                 }\r
8403                         } else {\r
8404                                 element = t.getNode();\r
8405                                 name = element.nodeName;\r
8406                                 if (name == 'IMG')\r
8407                                         return {name : name, index : findIndex(name, element)};\r
8408 \r
8409                                 // Can't insert a node into the root of document WebKit defaults to document\r
8410                                 if (rng.startContainer.nodeType == 9) {\r
8411                                         return;\r
8412                                 }\r
8413 \r
8414                                 // W3C method\r
8415                                 rng2 = rng.cloneRange();\r
8416 \r
8417                                 // Insert end marker\r
8418                                 if (!collapsed) {\r
8419                                         rng2.collapse(false);\r
8420                                         rng2.insertNode(dom.create('span', {'data-mce-type' : "bookmark", id : id + '_end', style : styles}, chr));\r
8421                                 }\r
8422 \r
8423                                 rng.collapse(true);\r
8424                                 rng.insertNode(dom.create('span', {'data-mce-type' : "bookmark", id : id + '_start', style : styles}, chr));\r
8425                         }\r
8426 \r
8427                         t.moveToBookmark({id : id, keep : 1});\r
8428 \r
8429                         return {id : id};\r
8430                 },\r
8431 \r
8432                 moveToBookmark : function(bookmark) {\r
8433                         var t = this, dom = t.dom, marker1, marker2, rng, root, startContainer, endContainer, startOffset, endOffset;\r
8434 \r
8435                         if (bookmark) {\r
8436                                 if (bookmark.start) {\r
8437                                         rng = dom.createRng();\r
8438                                         root = dom.getRoot();\r
8439 \r
8440                                         function setEndPoint(start) {\r
8441                                                 var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;\r
8442 \r
8443                                                 if (point) {\r
8444                                                         offset = point[0];\r
8445 \r
8446                                                         // Find container node\r
8447                                                         for (node = root, i = point.length - 1; i >= 1; i--) {\r
8448                                                                 children = node.childNodes;\r
8449 \r
8450                                                                 if (point[i] > children.length - 1)\r
8451                                                                         return;\r
8452 \r
8453                                                                 node = children[point[i]];\r
8454                                                         }\r
8455 \r
8456                                                         // Move text offset to best suitable location\r
8457                                                         if (node.nodeType === 3)\r
8458                                                                 offset = Math.min(point[0], node.nodeValue.length);\r
8459 \r
8460                                                         // Move element offset to best suitable location\r
8461                                                         if (node.nodeType === 1)\r
8462                                                                 offset = Math.min(point[0], node.childNodes.length);\r
8463 \r
8464                                                         // Set offset within container node\r
8465                                                         if (start)\r
8466                                                                 rng.setStart(node, offset);\r
8467                                                         else\r
8468                                                                 rng.setEnd(node, offset);\r
8469                                                 }\r
8470 \r
8471                                                 return true;\r
8472                                         };\r
8473 \r
8474                                         if (t.tridentSel)\r
8475                                                 return t.tridentSel.moveToBookmark(bookmark);\r
8476 \r
8477                                         if (setEndPoint(true) && setEndPoint()) {\r
8478                                                 t.setRng(rng);\r
8479                                         }\r
8480                                 } else if (bookmark.id) {\r
8481                                         function restoreEndPoint(suffix) {\r
8482                                                 var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;\r
8483 \r
8484                                                 if (marker) {\r
8485                                                         node = marker.parentNode;\r
8486 \r
8487                                                         if (suffix == 'start') {\r
8488                                                                 if (!keep) {\r
8489                                                                         idx = dom.nodeIndex(marker);\r
8490                                                                 } else {\r
8491                                                                         node = marker.firstChild;\r
8492                                                                         idx = 1;\r
8493                                                                 }\r
8494 \r
8495                                                                 startContainer = endContainer = node;\r
8496                                                                 startOffset = endOffset = idx;\r
8497                                                         } else {\r
8498                                                                 if (!keep) {\r
8499                                                                         idx = dom.nodeIndex(marker);\r
8500                                                                 } else {\r
8501                                                                         node = marker.firstChild;\r
8502                                                                         idx = 1;\r
8503                                                                 }\r
8504 \r
8505                                                                 endContainer = node;\r
8506                                                                 endOffset = idx;\r
8507                                                         }\r
8508 \r
8509                                                         if (!keep) {\r
8510                                                                 prev = marker.previousSibling;\r
8511                                                                 next = marker.nextSibling;\r
8512 \r
8513                                                                 // Remove all marker text nodes\r
8514                                                                 each(tinymce.grep(marker.childNodes), function(node) {\r
8515                                                                         if (node.nodeType == 3)\r
8516                                                                                 node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');\r
8517                                                                 });\r
8518 \r
8519                                                                 // Remove marker but keep children if for example contents where inserted into the marker\r
8520                                                                 // Also remove duplicated instances of the marker for example by a split operation or by WebKit auto split on paste feature\r
8521                                                                 while (marker = dom.get(bookmark.id + '_' + suffix))\r
8522                                                                         dom.remove(marker, 1);\r
8523 \r
8524                                                                 // If siblings are text nodes then merge them unless it's Opera since it some how removes the node\r
8525                                                                 // 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
8526                                                                 if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !tinymce.isOpera) {\r
8527                                                                         idx = prev.nodeValue.length;\r
8528                                                                         prev.appendData(next.nodeValue);\r
8529                                                                         dom.remove(next);\r
8530 \r
8531                                                                         if (suffix == 'start') {\r
8532                                                                                 startContainer = endContainer = prev;\r
8533                                                                                 startOffset = endOffset = idx;\r
8534                                                                         } else {\r
8535                                                                                 endContainer = prev;\r
8536                                                                                 endOffset = idx;\r
8537                                                                         }\r
8538                                                                 }\r
8539                                                         }\r
8540                                                 }\r
8541                                         };\r
8542 \r
8543                                         function addBogus(node) {\r
8544                                                 // Adds a bogus BR element for empty block elements or just a space on IE since it renders BR elements incorrectly\r
8545                                                 if (dom.isBlock(node) && !node.innerHTML)\r
8546                                                         node.innerHTML = !isIE ? '<br data-mce-bogus="1" />' : ' ';\r
8547 \r
8548                                                 return node;\r
8549                                         };\r
8550 \r
8551                                         // Restore start/end points\r
8552                                         restoreEndPoint('start');\r
8553                                         restoreEndPoint('end');\r
8554 \r
8555                                         if (startContainer) {\r
8556                                                 rng = dom.createRng();\r
8557                                                 rng.setStart(addBogus(startContainer), startOffset);\r
8558                                                 rng.setEnd(addBogus(endContainer), endOffset);\r
8559                                                 t.setRng(rng);\r
8560                                         }\r
8561                                 } else if (bookmark.name) {\r
8562                                         t.select(dom.select(bookmark.name)[bookmark.index]);\r
8563                                 } else if (bookmark.rng)\r
8564                                         t.setRng(bookmark.rng);\r
8565                         }\r
8566                 },\r
8567 \r
8568                 select : function(node, content) {\r
8569                         var t = this, dom = t.dom, rng = dom.createRng(), idx;\r
8570 \r
8571                         if (node) {\r
8572                                 idx = dom.nodeIndex(node);\r
8573                                 rng.setStart(node.parentNode, idx);\r
8574                                 rng.setEnd(node.parentNode, idx + 1);\r
8575 \r
8576                                 // Find first/last text node or BR element\r
8577                                 if (content) {\r
8578                                         function setPoint(node, start) {\r
8579                                                 var walker = new tinymce.dom.TreeWalker(node, node);\r
8580 \r
8581                                                 do {\r
8582                                                         // Text node\r
8583                                                         if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length != 0) {\r
8584                                                                 if (start)\r
8585                                                                         rng.setStart(node, 0);\r
8586                                                                 else\r
8587                                                                         rng.setEnd(node, node.nodeValue.length);\r
8588 \r
8589                                                                 return;\r
8590                                                         }\r
8591 \r
8592                                                         // BR element\r
8593                                                         if (node.nodeName == 'BR') {\r
8594                                                                 if (start)\r
8595                                                                         rng.setStartBefore(node);\r
8596                                                                 else\r
8597                                                                         rng.setEndBefore(node);\r
8598 \r
8599                                                                 return;\r
8600                                                         }\r
8601                                                 } while (node = (start ? walker.next() : walker.prev()));\r
8602                                         };\r
8603 \r
8604                                         setPoint(node, 1);\r
8605                                         setPoint(node);\r
8606                                 }\r
8607 \r
8608                                 t.setRng(rng);\r
8609                         }\r
8610 \r
8611                         return node;\r
8612                 },\r
8613 \r
8614                 isCollapsed : function() {\r
8615                         var t = this, r = t.getRng(), s = t.getSel();\r
8616 \r
8617                         if (!r || r.item)\r
8618                                 return false;\r
8619 \r
8620                         if (r.compareEndPoints)\r
8621                                 return r.compareEndPoints('StartToEnd', r) === 0;\r
8622 \r
8623                         return !s || r.collapsed;\r
8624                 },\r
8625 \r
8626                 collapse : function(to_start) {\r
8627                         var self = this, rng = self.getRng(), node;\r
8628 \r
8629                         // Control range on IE\r
8630                         if (rng.item) {\r
8631                                 node = rng.item(0);\r
8632                                 rng = self.win.document.body.createTextRange();\r
8633                                 rng.moveToElementText(node);\r
8634                         }\r
8635 \r
8636                         rng.collapse(!!to_start);\r
8637                         self.setRng(rng);\r
8638                 },\r
8639 \r
8640                 getSel : function() {\r
8641                         var t = this, w = this.win;\r
8642 \r
8643                         return w.getSelection ? w.getSelection() : w.document.selection;\r
8644                 },\r
8645 \r
8646                 getRng : function(w3c) {\r
8647                         var t = this, s, r, elm, doc = t.win.document;\r
8648 \r
8649                         // Found tridentSel object then we need to use that one\r
8650                         if (w3c && t.tridentSel)\r
8651                                 return t.tridentSel.getRangeAt(0);\r
8652 \r
8653                         try {\r
8654                                 if (s = t.getSel())\r
8655                                         r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : doc.createRange());\r
8656                         } catch (ex) {\r
8657                                 // IE throws unspecified error here if TinyMCE is placed in a frame/iframe\r
8658                         }\r
8659 \r
8660                         // We have W3C ranges and it's IE then fake control selection since IE9 doesn't handle that correctly yet\r
8661                         if (tinymce.isIE && r && r.setStart && doc.selection.createRange().item) {\r
8662                                 elm = doc.selection.createRange().item(0);\r
8663                                 r = doc.createRange();\r
8664                                 r.setStartBefore(elm);\r
8665                                 r.setEndAfter(elm);\r
8666                         }\r
8667 \r
8668                         // No range found then create an empty one\r
8669                         // This can occur when the editor is placed in a hidden container element on Gecko\r
8670                         // Or on IE when there was an exception\r
8671                         if (!r)\r
8672                                 r = doc.createRange ? doc.createRange() : doc.body.createTextRange();\r
8673 \r
8674                         if (t.selectedRange && t.explicitRange) {\r
8675                                 if (r.compareBoundaryPoints(r.START_TO_START, t.selectedRange) === 0 && r.compareBoundaryPoints(r.END_TO_END, t.selectedRange) === 0) {\r
8676                                         // Safari, Opera and Chrome only ever select text which causes the range to change.\r
8677                                         // This lets us use the originally set range if the selection hasn't been changed by the user.\r
8678                                         r = t.explicitRange;\r
8679                                 } else {\r
8680                                         t.selectedRange = null;\r
8681                                         t.explicitRange = null;\r
8682                                 }\r
8683                         }\r
8684 \r
8685                         return r;\r
8686                 },\r
8687 \r
8688                 setRng : function(r) {\r
8689                         var s, t = this;\r
8690 \r
8691                         if (!t.tridentSel) {\r
8692                                 s = t.getSel();\r
8693 \r
8694                                 if (s) {\r
8695                                         t.explicitRange = r;\r
8696 \r
8697                                         try {\r
8698                                                 s.removeAllRanges();\r
8699                                         } catch (ex) {\r
8700                                                 // IE9 might throw errors here don't know why\r
8701                                         }\r
8702 \r
8703                                         s.addRange(r);\r
8704                                         // adding range isn't always successful so we need to check range count otherwise an exception can occur\r
8705                                         t.selectedRange = s.rangeCount > 0 ? s.getRangeAt(0) : null;\r
8706                                 }\r
8707                         } else {\r
8708                                 // Is W3C Range\r
8709                                 if (r.cloneRange) {\r
8710                                         try {\r
8711                                                 t.tridentSel.addRange(r);\r
8712                                                 return;\r
8713                                         } catch (ex) {\r
8714                                                 //IE9 throws an error here if called before selection is placed in the editor\r
8715                                         }\r
8716                                 }\r
8717 \r
8718                                 // Is IE specific range\r
8719                                 try {\r
8720                                         r.select();\r
8721                                 } catch (ex) {\r
8722                                         // Needed for some odd IE bug #1843306\r
8723                                 }\r
8724                         }\r
8725                 },\r
8726 \r
8727                 setNode : function(n) {\r
8728                         var t = this;\r
8729 \r
8730                         t.setContent(t.dom.getOuterHTML(n));\r
8731 \r
8732                         return n;\r
8733                 },\r
8734 \r
8735                 getNode : function() {\r
8736                         var t = this, rng = t.getRng(), sel = t.getSel(), elm, start = rng.startContainer, end = rng.endContainer;\r
8737 \r
8738                         // Range maybe lost after the editor is made visible again\r
8739                         if (!rng)\r
8740                                 return t.dom.getRoot();\r
8741 \r
8742                         if (rng.setStart) {\r
8743                                 elm = rng.commonAncestorContainer;\r
8744 \r
8745                                 // Handle selection a image or other control like element such as anchors\r
8746                                 if (!rng.collapsed) {\r
8747                                         if (rng.startContainer == rng.endContainer) {\r
8748                                                 if (rng.endOffset - rng.startOffset < 2) {\r
8749                                                         if (rng.startContainer.hasChildNodes())\r
8750                                                                 elm = rng.startContainer.childNodes[rng.startOffset];\r
8751                                                 }\r
8752                                         }\r
8753 \r
8754                                         // If the anchor node is a element instead of a text node then return this element\r
8755                                         //if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1)\r
8756                                         //      return sel.anchorNode.childNodes[sel.anchorOffset];\r
8757 \r
8758                                         // Handle cases where the selection is immediately wrapped around a node and return that node instead of it's parent.\r
8759                                         // This happens when you double click an underlined word in FireFox.\r
8760                                         if (start.nodeType === 3 && end.nodeType === 3) {\r
8761                                                 function skipEmptyTextNodes(n, forwards) {\r
8762                                                         var orig = n;\r
8763                                                         while (n && n.nodeType === 3 && n.length === 0) {\r
8764                                                                 n = forwards ? n.nextSibling : n.previousSibling;\r
8765                                                         }\r
8766                                                         return n || orig;\r
8767                                                 }\r
8768                                                 if (start.length === rng.startOffset) {\r
8769                                                         start = skipEmptyTextNodes(start.nextSibling, true);\r
8770                                                 } else {\r
8771                                                         start = start.parentNode;\r
8772                                                 }\r
8773                                                 if (rng.endOffset === 0) {\r
8774                                                         end = skipEmptyTextNodes(end.previousSibling, false);\r
8775                                                 } else {\r
8776                                                         end = end.parentNode;\r
8777                                                 }\r
8778 \r
8779                                                 if (start && start === end)\r
8780                                                         return start;\r
8781                                         }\r
8782                                 }\r
8783 \r
8784                                 if (elm && elm.nodeType == 3)\r
8785                                         return elm.parentNode;\r
8786 \r
8787                                 return elm;\r
8788                         }\r
8789 \r
8790                         return rng.item ? rng.item(0) : rng.parentElement();\r
8791                 },\r
8792 \r
8793                 getSelectedBlocks : function(st, en) {\r
8794                         var t = this, dom = t.dom, sb, eb, n, bl = [];\r
8795 \r
8796                         sb = dom.getParent(st || t.getStart(), dom.isBlock);\r
8797                         eb = dom.getParent(en || t.getEnd(), dom.isBlock);\r
8798 \r
8799                         if (sb)\r
8800                                 bl.push(sb);\r
8801 \r
8802                         if (sb && eb && sb != eb) {\r
8803                                 n = sb;\r
8804 \r
8805                                 var walker = new tinymce.dom.TreeWalker(sb, dom.getRoot());\r
8806                                 while ((n = walker.next()) && n != eb) {\r
8807                                         if (dom.isBlock(n))\r
8808                                                 bl.push(n);\r
8809                                 }\r
8810                         }\r
8811 \r
8812                         if (eb && sb != eb)\r
8813                                 bl.push(eb);\r
8814 \r
8815                         return bl;\r
8816                 },\r
8817 \r
8818                 normalize : function() {\r
8819                         var self = this, rng, normalized;\r
8820 \r
8821                         // TODO:\r
8822                         // Retain selection direction.\r
8823                         // Lean left/right on Gecko for inline elements.\r
8824                         // Run this on mouse up/key up when the user manually moves the selection\r
8825 \r
8826                         // Normalize only on non IE browsers for now\r
8827                         if (tinymce.isIE)\r
8828                                 return;\r
8829 \r
8830                         function normalizeEndPoint(start) {\r
8831                                 var container, offset, walker, dom = self.dom, body = dom.getRoot(), node;\r
8832 \r
8833                                 container = rng[(start ? 'start' : 'end') + 'Container'];\r
8834                                 offset = rng[(start ? 'start' : 'end') + 'Offset'];\r
8835 \r
8836                                 // If the container is a document move it to the body element\r
8837                                 if (container.nodeType === 9) {\r
8838                                         container = container.body;\r
8839                                         offset = 0;\r
8840                                 }\r
8841 \r
8842                                 // If the container is body try move it into the closest text node or position\r
8843                                 // TODO: Add more logic here to handle element selection cases\r
8844                                 if (container === body) {\r
8845                                         // Resolve the index\r
8846                                         if (container.hasChildNodes()) {\r
8847                                                 container = container.childNodes[Math.min(!start && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1)];\r
8848                                                 offset = 0;\r
8849 \r
8850                                                 // Don't walk into elements that doesn't have any child nodes like a IMG\r
8851                                                 if (container.hasChildNodes()) {\r
8852                                                         // Walk the DOM to find a text node to place the caret at or a BR\r
8853                                                         node = container;\r
8854                                                         walker = new tinymce.dom.TreeWalker(container, body);\r
8855                                                         do {\r
8856                                                                 // Found a text node use that position\r
8857                                                                 if (node.nodeType === 3) {\r
8858                                                                         offset = start ? 0 : node.nodeValue.length - 1;\r
8859                                                                         container = node;\r
8860                                                                         normalized = true;\r
8861                                                                         break;\r
8862                                                                 }\r
8863 \r
8864                                                                 // Found a BR/IMG element that we can place the caret before\r
8865                                                                 if (/^(BR|IMG)$/.test(node.nodeName)) {\r
8866                                                                         offset = dom.nodeIndex(node);\r
8867                                                                         container = node.parentNode;\r
8868 \r
8869                                                                         // Put caret after image when moving the end point\r
8870                                                                         if (node.nodeName ==  "IMG" && !start) {\r
8871                                                                                 offset++;\r
8872                                                                         }\r
8873 \r
8874                                                                         normalized = true;\r
8875                                                                         break;\r
8876                                                                 }\r
8877                                                         } while (node = (start ? walker.next() : walker.prev()));\r
8878                                                 }\r
8879                                         }\r
8880                                 }\r
8881 \r
8882                                 // Set endpoint if it was normalized\r
8883                                 if (normalized)\r
8884                                         rng['set' + (start ? 'Start' : 'End')](container, offset);\r
8885                         };\r
8886 \r
8887                         rng = self.getRng();\r
8888 \r
8889                         // Normalize the end points\r
8890                         normalizeEndPoint(true);\r
8891 \r
8892                         if (!rng.collapsed)\r
8893                                 normalizeEndPoint();\r
8894 \r
8895                         // Set the selection if it was normalized\r
8896                         if (normalized) {\r
8897                                 //console.log(self.dom.dumpRng(rng));\r
8898                                 self.setRng(rng);\r
8899                         }\r
8900                 },\r
8901 \r
8902                 destroy : function(s) {\r
8903                         var t = this;\r
8904 \r
8905                         t.win = null;\r
8906 \r
8907                         // Manual destroy then remove unload handler\r
8908                         if (!s)\r
8909                                 tinymce.removeUnload(t.destroy);\r
8910                 },\r
8911 \r
8912                 // 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
8913                 _fixIESelection : function() {\r
8914                         var dom = this.dom, doc = dom.doc, body = doc.body, started, startRng, htmlElm;\r
8915 \r
8916                         // Make HTML element unselectable since we are going to handle selection by hand\r
8917                         doc.documentElement.unselectable = true;\r
8918 \r
8919                         // Return range from point or null if it failed\r
8920                         function rngFromPoint(x, y) {\r
8921                                 var rng = body.createTextRange();\r
8922 \r
8923                                 try {\r
8924                                         rng.moveToPoint(x, y);\r
8925                                 } catch (ex) {\r
8926                                         // IE sometimes throws and exception, so lets just ignore it\r
8927                                         rng = null;\r
8928                                 }\r
8929 \r
8930                                 return rng;\r
8931                         };\r
8932 \r
8933                         // Fires while the selection is changing\r
8934                         function selectionChange(e) {\r
8935                                 var pointRng;\r
8936 \r
8937                                 // Check if the button is down or not\r
8938                                 if (e.button) {\r
8939                                         // Create range from mouse position\r
8940                                         pointRng = rngFromPoint(e.x, e.y);\r
8941 \r
8942                                         if (pointRng) {\r
8943                                                 // Check if pointRange is before/after selection then change the endPoint\r
8944                                                 if (pointRng.compareEndPoints('StartToStart', startRng) > 0)\r
8945                                                         pointRng.setEndPoint('StartToStart', startRng);\r
8946                                                 else\r
8947                                                         pointRng.setEndPoint('EndToEnd', startRng);\r
8948 \r
8949                                                 pointRng.select();\r
8950                                         }\r
8951                                 } else\r
8952                                         endSelection();\r
8953                         }\r
8954 \r
8955                         // Removes listeners\r
8956                         function endSelection() {\r
8957                                 var rng = doc.selection.createRange();\r
8958 \r
8959                                 // If the range is collapsed then use the last start range\r
8960                                 if (startRng && !rng.item && rng.compareEndPoints('StartToEnd', rng) === 0)\r
8961                                         startRng.select();\r
8962 \r
8963                                 dom.unbind(doc, 'mouseup', endSelection);\r
8964                                 dom.unbind(doc, 'mousemove', selectionChange);\r
8965                                 startRng = started = 0;\r
8966                         };\r
8967 \r
8968                         // Detect when user selects outside BODY\r
8969                         dom.bind(doc, ['mousedown', 'contextmenu'], function(e) {\r
8970                                 if (e.target.nodeName === 'HTML') {\r
8971                                         if (started)\r
8972                                                 endSelection();\r
8973 \r
8974                                         // Detect vertical scrollbar, since IE will fire a mousedown on the scrollbar and have target set as HTML\r
8975                                         htmlElm = doc.documentElement;\r
8976                                         if (htmlElm.scrollHeight > htmlElm.clientHeight)\r
8977                                                 return;\r
8978 \r
8979                                         started = 1;\r
8980                                         // Setup start position\r
8981                                         startRng = rngFromPoint(e.x, e.y);\r
8982                                         if (startRng) {\r
8983                                                 // Listen for selection change events\r
8984                                                 dom.bind(doc, 'mouseup', endSelection);\r
8985                                                 dom.bind(doc, 'mousemove', selectionChange);\r
8986 \r
8987                                                 dom.win.focus();\r
8988                                                 startRng.select();\r
8989                                         }\r
8990                                 }\r
8991                         });\r
8992                 }\r
8993         });\r
8994 })(tinymce);\r
8995 \r
8996 (function(tinymce) {\r
8997         tinymce.dom.Serializer = function(settings, dom, schema) {\r
8998                 var onPreProcess, onPostProcess, isIE = tinymce.isIE, each = tinymce.each, htmlParser;\r
8999 \r
9000                 // Support the old apply_source_formatting option\r
9001                 if (!settings.apply_source_formatting)\r
9002                         settings.indent = false;\r
9003 \r
9004                 // Default DOM and Schema if they are undefined\r
9005                 dom = dom || tinymce.DOM;\r
9006                 schema = schema || new tinymce.html.Schema(settings);\r
9007                 settings.entity_encoding = settings.entity_encoding || 'named';\r
9008                 settings.remove_trailing_brs = "remove_trailing_brs" in settings ? settings.remove_trailing_brs : true;\r
9009 \r
9010                 onPreProcess = new tinymce.util.Dispatcher(self);\r
9011 \r
9012                 onPostProcess = new tinymce.util.Dispatcher(self);\r
9013 \r
9014                 htmlParser = new tinymce.html.DomParser(settings, schema);\r
9015 \r
9016                 // Convert move data-mce-src, data-mce-href and data-mce-style into nodes or process them if needed\r
9017                 htmlParser.addAttributeFilter('src,href,style', function(nodes, name) {\r
9018                         var i = nodes.length, node, value, internalName = 'data-mce-' + name, urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope, undef;\r
9019 \r
9020                         while (i--) {\r
9021                                 node = nodes[i];\r
9022 \r
9023                                 value = node.attributes.map[internalName];\r
9024                                 if (value !== undef) {\r
9025                                         // Set external name to internal value and remove internal\r
9026                                         node.attr(name, value.length > 0 ? value : null);\r
9027                                         node.attr(internalName, null);\r
9028                                 } else {\r
9029                                         // No internal attribute found then convert the value we have in the DOM\r
9030                                         value = node.attributes.map[name];\r
9031 \r
9032                                         if (name === "style")\r
9033                                                 value = dom.serializeStyle(dom.parseStyle(value), node.name);\r
9034                                         else if (urlConverter)\r
9035                                                 value = urlConverter.call(urlConverterScope, value, name, node.name);\r
9036 \r
9037                                         node.attr(name, value.length > 0 ? value : null);\r
9038                                 }\r
9039                         }\r
9040                 });\r
9041 \r
9042                 // Remove internal classes mceItem<..>\r
9043                 htmlParser.addAttributeFilter('class', function(nodes, name) {\r
9044                         var i = nodes.length, node, value;\r
9045 \r
9046                         while (i--) {\r
9047                                 node = nodes[i];\r
9048                                 value = node.attr('class').replace(/\s*mce(Item\w+|Selected)\s*/g, '');\r
9049                                 node.attr('class', value.length > 0 ? value : null);\r
9050                         }\r
9051                 });\r
9052 \r
9053                 // Remove bookmark elements\r
9054                 htmlParser.addAttributeFilter('data-mce-type', function(nodes, name, args) {\r
9055                         var i = nodes.length, node;\r
9056 \r
9057                         while (i--) {\r
9058                                 node = nodes[i];\r
9059 \r
9060                                 if (node.attributes.map['data-mce-type'] === 'bookmark' && !args.cleanup)\r
9061                                         node.remove();\r
9062                         }\r
9063                 });\r
9064 \r
9065                 // Remove expando attributes\r
9066                 htmlParser.addAttributeFilter('data-mce-expando', function(nodes, name, args) {\r
9067                         var i = nodes.length;\r
9068 \r
9069                         while (i--) {\r
9070                                 nodes[i].attr(name, null);\r
9071                         }\r
9072                 });\r
9073 \r
9074                 // Force script into CDATA sections and remove the mce- prefix also add comments around styles\r
9075                 htmlParser.addNodeFilter('script,style', function(nodes, name) {\r
9076                         var i = nodes.length, node, value;\r
9077 \r
9078                         function trim(value) {\r
9079                                 return value.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n')\r
9080                                                 .replace(/^[\r\n]*|[\r\n]*$/g, '')\r
9081                                                 .replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi, '')\r
9082                                                 .replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g, '');\r
9083                         };\r
9084 \r
9085                         while (i--) {\r
9086                                 node = nodes[i];\r
9087                                 value = node.firstChild ? node.firstChild.value : '';\r
9088 \r
9089                                 if (name === "script") {\r
9090                                         // Remove mce- prefix from script elements\r
9091                                         node.attr('type', (node.attr('type') || 'text/javascript').replace(/^mce\-/, ''));\r
9092 \r
9093                                         if (value.length > 0)\r
9094                                                 node.firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>';\r
9095                                 } else {\r
9096                                         if (value.length > 0)\r
9097                                                 node.firstChild.value = '<!--\n' + trim(value) + '\n-->';\r
9098                                 }\r
9099                         }\r
9100                 });\r
9101 \r
9102                 // Convert comments to cdata and handle protected comments\r
9103                 htmlParser.addNodeFilter('#comment', function(nodes, name) {\r
9104                         var i = nodes.length, node;\r
9105 \r
9106                         while (i--) {\r
9107                                 node = nodes[i];\r
9108 \r
9109                                 if (node.value.indexOf('[CDATA[') === 0) {\r
9110                                         node.name = '#cdata';\r
9111                                         node.type = 4;\r
9112                                         node.value = node.value.replace(/^\[CDATA\[|\]\]$/g, '');\r
9113                                 } else if (node.value.indexOf('mce:protected ') === 0) {\r
9114                                         node.name = "#text";\r
9115                                         node.type = 3;\r
9116                                         node.raw = true;\r
9117                                         node.value = unescape(node.value).substr(14);\r
9118                                 }\r
9119                         }\r
9120                 });\r
9121 \r
9122                 htmlParser.addNodeFilter('xml:namespace,input', function(nodes, name) {\r
9123                         var i = nodes.length, node;\r
9124 \r
9125                         while (i--) {\r
9126                                 node = nodes[i];\r
9127                                 if (node.type === 7)\r
9128                                         node.remove();\r
9129                                 else if (node.type === 1) {\r
9130                                         if (name === "input" && !("type" in node.attributes.map))\r
9131                                                 node.attr('type', 'text');\r
9132                                 }\r
9133                         }\r
9134                 });\r
9135 \r
9136                 // Fix list elements, TODO: Replace this later\r
9137                 if (settings.fix_list_elements) {\r
9138                         htmlParser.addNodeFilter('ul,ol', function(nodes, name) {\r
9139                                 var i = nodes.length, node, parentNode;\r
9140 \r
9141                                 while (i--) {\r
9142                                         node = nodes[i];\r
9143                                         parentNode = node.parent;\r
9144 \r
9145                                         if (parentNode.name === 'ul' || parentNode.name === 'ol') {\r
9146                                                 if (node.prev && node.prev.name === 'li') {\r
9147                                                         node.prev.append(node);\r
9148                                                 }\r
9149                                         }\r
9150                                 }\r
9151                         });\r
9152                 }\r
9153 \r
9154                 // Remove internal data attributes\r
9155                 htmlParser.addAttributeFilter('data-mce-src,data-mce-href,data-mce-style', function(nodes, name) {\r
9156                         var i = nodes.length;\r
9157 \r
9158                         while (i--) {\r
9159                                 nodes[i].attr(name, null);\r
9160                         }\r
9161                 });\r
9162 \r
9163                 // Return public methods\r
9164                 return {\r
9165                         schema : schema,\r
9166 \r
9167                         addNodeFilter : htmlParser.addNodeFilter,\r
9168 \r
9169                         addAttributeFilter : htmlParser.addAttributeFilter,\r
9170 \r
9171                         onPreProcess : onPreProcess,\r
9172 \r
9173                         onPostProcess : onPostProcess,\r
9174 \r
9175                         serialize : function(node, args) {\r
9176                                 var impl, doc, oldDoc, htmlSerializer, content;\r
9177 \r
9178                                 // Explorer won't clone contents of script and style and the\r
9179                                 // selected index of select elements are cleared on a clone operation.\r
9180                                 if (isIE && dom.select('script,style,select,map').length > 0) {\r
9181                                         content = node.innerHTML;\r
9182                                         node = node.cloneNode(false);\r
9183                                         dom.setHTML(node, content);\r
9184                                 } else\r
9185                                         node = node.cloneNode(true);\r
9186 \r
9187                                 // Nodes needs to be attached to something in WebKit/Opera\r
9188                                 // Older builds of Opera crashes if you attach the node to an document created dynamically\r
9189                                 // and since we can't feature detect a crash we need to sniff the acutal build number\r
9190                                 // This fix will make DOM ranges and make Sizzle happy!\r
9191                                 impl = node.ownerDocument.implementation;\r
9192                                 if (impl.createHTMLDocument) {\r
9193                                         // Create an empty HTML document\r
9194                                         doc = impl.createHTMLDocument("");\r
9195 \r
9196                                         // Add the element or it's children if it's a body element to the new document\r
9197                                         each(node.nodeName == 'BODY' ? node.childNodes : [node], function(node) {\r
9198                                                 doc.body.appendChild(doc.importNode(node, true));\r
9199                                         });\r
9200 \r
9201                                         // Grab first child or body element for serialization\r
9202                                         if (node.nodeName != 'BODY')\r
9203                                                 node = doc.body.firstChild;\r
9204                                         else\r
9205                                                 node = doc.body;\r
9206 \r
9207                                         // set the new document in DOMUtils so createElement etc works\r
9208                                         oldDoc = dom.doc;\r
9209                                         dom.doc = doc;\r
9210                                 }\r
9211 \r
9212                                 args = args || {};\r
9213                                 args.format = args.format || 'html';\r
9214 \r
9215                                 // Pre process\r
9216                                 if (!args.no_events) {\r
9217                                         args.node = node;\r
9218                                         onPreProcess.dispatch(self, args);\r
9219                                 }\r
9220 \r
9221                                 // Setup serializer\r
9222                                 htmlSerializer = new tinymce.html.Serializer(settings, schema);\r
9223 \r
9224                                 // Parse and serialize HTML\r
9225                                 args.content = htmlSerializer.serialize(\r
9226                                         htmlParser.parse(args.getInner ? node.innerHTML : tinymce.trim(dom.getOuterHTML(node), args), args)\r
9227                                 );\r
9228 \r
9229                                 // Replace all BOM characters for now until we can find a better solution\r
9230                                 if (!args.cleanup)\r
9231                                         args.content = args.content.replace(/\uFEFF|\u200B/g, '');\r
9232 \r
9233                                 // Post process\r
9234                                 if (!args.no_events)\r
9235                                         onPostProcess.dispatch(self, args);\r
9236 \r
9237                                 // Restore the old document if it was changed\r
9238                                 if (oldDoc)\r
9239                                         dom.doc = oldDoc;\r
9240 \r
9241                                 args.node = null;\r
9242 \r
9243                                 return args.content;\r
9244                         },\r
9245 \r
9246                         addRules : function(rules) {\r
9247                                 schema.addValidElements(rules);\r
9248                         },\r
9249 \r
9250                         setRules : function(rules) {\r
9251                                 schema.setValidElements(rules);\r
9252                         }\r
9253                 };\r
9254         };\r
9255 })(tinymce);\r
9256 (function(tinymce) {\r
9257         tinymce.dom.ScriptLoader = function(settings) {\r
9258                 var QUEUED = 0,\r
9259                         LOADING = 1,\r
9260                         LOADED = 2,\r
9261                         states = {},\r
9262                         queue = [],\r
9263                         scriptLoadedCallbacks = {},\r
9264                         queueLoadedCallbacks = [],\r
9265                         loading = 0,\r
9266                         undefined;\r
9267 \r
9268                 function loadScript(url, callback) {\r
9269                         var t = this, dom = tinymce.DOM, elm, uri, loc, id;\r
9270 \r
9271                         // Execute callback when script is loaded\r
9272                         function done() {\r
9273                                 dom.remove(id);\r
9274 \r
9275                                 if (elm)\r
9276                                         elm.onreadystatechange = elm.onload = elm = null;\r
9277 \r
9278                                 callback();\r
9279                         };\r
9280                         \r
9281                         function error() {\r
9282                                 // Report the error so it's easier for people to spot loading errors\r
9283                                 if (typeof(console) !== "undefined" && console.log)\r
9284                                         console.log("Failed to load: " + url);\r
9285 \r
9286                                 // We can't mark it as done if there is a load error since\r
9287                                 // A) We don't want to produce 404 errors on the server and\r
9288                                 // B) the onerror event won't fire on all browsers.\r
9289                                 // done();\r
9290                         };\r
9291 \r
9292                         id = dom.uniqueId();\r
9293 \r
9294                         if (tinymce.isIE6) {\r
9295                                 uri = new tinymce.util.URI(url);\r
9296                                 loc = location;\r
9297 \r
9298                                 // If script is from same domain and we\r
9299                                 // use IE 6 then use XHR since it's more reliable\r
9300                                 if (uri.host == loc.hostname && uri.port == loc.port && (uri.protocol + ':') == loc.protocol && uri.protocol.toLowerCase() != 'file') {\r
9301                                         tinymce.util.XHR.send({\r
9302                                                 url : tinymce._addVer(uri.getURI()),\r
9303                                                 success : function(content) {\r
9304                                                         // Create new temp script element\r
9305                                                         var script = dom.create('script', {\r
9306                                                                 type : 'text/javascript'\r
9307                                                         });\r
9308 \r
9309                                                         // Evaluate script in global scope\r
9310                                                         script.text = content;\r
9311                                                         document.getElementsByTagName('head')[0].appendChild(script);\r
9312                                                         dom.remove(script);\r
9313 \r
9314                                                         done();\r
9315                                                 },\r
9316                                                 \r
9317                                                 error : error\r
9318                                         });\r
9319 \r
9320                                         return;\r
9321                                 }\r
9322                         }\r
9323 \r
9324                         // Create new script element\r
9325                         elm = dom.create('script', {\r
9326                                 id : id,\r
9327                                 type : 'text/javascript',\r
9328                                 src : tinymce._addVer(url)\r
9329                         });\r
9330 \r
9331                         // Add onload listener for non IE browsers since IE9\r
9332                         // fires onload event before the script is parsed and executed\r
9333                         if (!tinymce.isIE)\r
9334                                 elm.onload = done;\r
9335 \r
9336                         // Add onerror event will get fired on some browsers but not all of them\r
9337                         elm.onerror = error;\r
9338 \r
9339                         // Opera 9.60 doesn't seem to fire the onreadystate event at correctly\r
9340                         if (!tinymce.isOpera) {\r
9341                                 elm.onreadystatechange = function() {\r
9342                                         var state = elm.readyState;\r
9343 \r
9344                                         // Loaded state is passed on IE 6 however there\r
9345                                         // are known issues with this method but we can't use\r
9346                                         // XHR in a cross domain loading\r
9347                                         if (state == 'complete' || state == 'loaded')\r
9348                                                 done();\r
9349                                 };\r
9350                         }\r
9351 \r
9352                         // Most browsers support this feature so we report errors\r
9353                         // for those at least to help users track their missing plugins etc\r
9354                         // todo: Removed since it produced error if the document is unloaded by navigating away, re-add it as an option\r
9355                         /*elm.onerror = function() {\r
9356                                 alert('Failed to load: ' + url);\r
9357                         };*/\r
9358 \r
9359                         // Add script to document\r
9360                         (document.getElementsByTagName('head')[0] || document.body).appendChild(elm);\r
9361                 };\r
9362 \r
9363                 this.isDone = function(url) {\r
9364                         return states[url] == LOADED;\r
9365                 };\r
9366 \r
9367                 this.markDone = function(url) {\r
9368                         states[url] = LOADED;\r
9369                 };\r
9370 \r
9371                 this.add = this.load = function(url, callback, scope) {\r
9372                         var item, state = states[url];\r
9373 \r
9374                         // Add url to load queue\r
9375                         if (state == undefined) {\r
9376                                 queue.push(url);\r
9377                                 states[url] = QUEUED;\r
9378                         }\r
9379 \r
9380                         if (callback) {\r
9381                                 // Store away callback for later execution\r
9382                                 if (!scriptLoadedCallbacks[url])\r
9383                                         scriptLoadedCallbacks[url] = [];\r
9384 \r
9385                                 scriptLoadedCallbacks[url].push({\r
9386                                         func : callback,\r
9387                                         scope : scope || this\r
9388                                 });\r
9389                         }\r
9390                 };\r
9391 \r
9392                 this.loadQueue = function(callback, scope) {\r
9393                         this.loadScripts(queue, callback, scope);\r
9394                 };\r
9395 \r
9396                 this.loadScripts = function(scripts, callback, scope) {\r
9397                         var loadScripts;\r
9398 \r
9399                         function execScriptLoadedCallbacks(url) {\r
9400                                 // Execute URL callback functions\r
9401                                 tinymce.each(scriptLoadedCallbacks[url], function(callback) {\r
9402                                         callback.func.call(callback.scope);\r
9403                                 });\r
9404 \r
9405                                 scriptLoadedCallbacks[url] = undefined;\r
9406                         };\r
9407 \r
9408                         queueLoadedCallbacks.push({\r
9409                                 func : callback,\r
9410                                 scope : scope || this\r
9411                         });\r
9412 \r
9413                         loadScripts = function() {\r
9414                                 var loadingScripts = tinymce.grep(scripts);\r
9415 \r
9416                                 // Current scripts has been handled\r
9417                                 scripts.length = 0;\r
9418 \r
9419                                 // Load scripts that needs to be loaded\r
9420                                 tinymce.each(loadingScripts, function(url) {\r
9421                                         // Script is already loaded then execute script callbacks directly\r
9422                                         if (states[url] == LOADED) {\r
9423                                                 execScriptLoadedCallbacks(url);\r
9424                                                 return;\r
9425                                         }\r
9426 \r
9427                                         // Is script not loading then start loading it\r
9428                                         if (states[url] != LOADING) {\r
9429                                                 states[url] = LOADING;\r
9430                                                 loading++;\r
9431 \r
9432                                                 loadScript(url, function() {\r
9433                                                         states[url] = LOADED;\r
9434                                                         loading--;\r
9435 \r
9436                                                         execScriptLoadedCallbacks(url);\r
9437 \r
9438                                                         // Load more scripts if they where added by the recently loaded script\r
9439                                                         loadScripts();\r
9440                                                 });\r
9441                                         }\r
9442                                 });\r
9443 \r
9444                                 // No scripts are currently loading then execute all pending queue loaded callbacks\r
9445                                 if (!loading) {\r
9446                                         tinymce.each(queueLoadedCallbacks, function(callback) {\r
9447                                                 callback.func.call(callback.scope);\r
9448                                         });\r
9449 \r
9450                                         queueLoadedCallbacks.length = 0;\r
9451                                 }\r
9452                         };\r
9453 \r
9454                         loadScripts();\r
9455                 };\r
9456         };\r
9457 \r
9458         // Global script loader\r
9459         tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();\r
9460 })(tinymce);\r
9461 \r
9462 tinymce.dom.TreeWalker = function(start_node, root_node) {\r
9463         var node = start_node;\r
9464 \r
9465         function findSibling(node, start_name, sibling_name, shallow) {\r
9466                 var sibling, parent;\r
9467 \r
9468                 if (node) {\r
9469                         // Walk into nodes if it has a start\r
9470                         if (!shallow && node[start_name])\r
9471                                 return node[start_name];\r
9472 \r
9473                         // Return the sibling if it has one\r
9474                         if (node != root_node) {\r
9475                                 sibling = node[sibling_name];\r
9476                                 if (sibling)\r
9477                                         return sibling;\r
9478 \r
9479                                 // Walk up the parents to look for siblings\r
9480                                 for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) {\r
9481                                         sibling = parent[sibling_name];\r
9482                                         if (sibling)\r
9483                                                 return sibling;\r
9484                                 }\r
9485                         }\r
9486                 }\r
9487         };\r
9488 \r
9489         this.current = function() {\r
9490                 return node;\r
9491         };\r
9492 \r
9493         this.next = function(shallow) {\r
9494                 return (node = findSibling(node, 'firstChild', 'nextSibling', shallow));\r
9495         };\r
9496 \r
9497         this.prev = function(shallow) {\r
9498                 return (node = findSibling(node, 'lastChild', 'previousSibling', shallow));\r
9499         };\r
9500 };\r
9501 \r
9502 (function(tinymce) {\r
9503         tinymce.dom.RangeUtils = function(dom) {\r
9504                 var INVISIBLE_CHAR = '\uFEFF';\r
9505 \r
9506                 this.walk = function(rng, callback) {\r
9507                         var startContainer = rng.startContainer,\r
9508                                 startOffset = rng.startOffset,\r
9509                                 endContainer = rng.endContainer,\r
9510                                 endOffset = rng.endOffset,\r
9511                                 ancestor, startPoint,\r
9512                                 endPoint, node, parent, siblings, nodes;\r
9513 \r
9514                         // Handle table cell selection the table plugin enables\r
9515                         // you to fake select table cells and perform formatting actions on them\r
9516                         nodes = dom.select('td.mceSelected,th.mceSelected');\r
9517                         if (nodes.length > 0) {\r
9518                                 tinymce.each(nodes, function(node) {\r
9519                                         callback([node]);\r
9520                                 });\r
9521 \r
9522                                 return;\r
9523                         }\r
9524 \r
9525                         function exclude(nodes) {\r
9526                                 var node;\r
9527 \r
9528                                 // First node is excluded\r
9529                                 node = nodes[0];\r
9530                                 if (node.nodeType === 3 && node === startContainer && startOffset >= node.nodeValue.length) {\r
9531                                         nodes.splice(0, 1);\r
9532                                 }\r
9533 \r
9534                                 // Last node is excluded\r
9535                                 node = nodes[nodes.length - 1];\r
9536                                 if (endOffset === 0 && nodes.length > 0 && node === endContainer && node.nodeType === 3) {\r
9537                                         nodes.splice(nodes.length - 1, 1);\r
9538                                 }\r
9539 \r
9540                                 return nodes;\r
9541                         };\r
9542 \r
9543                         function collectSiblings(node, name, end_node) {\r
9544                                 var siblings = [];\r
9545 \r
9546                                 for (; node && node != end_node; node = node[name])\r
9547                                         siblings.push(node);\r
9548 \r
9549                                 return siblings;\r
9550                         };\r
9551 \r
9552                         function findEndPoint(node, root) {\r
9553                                 do {\r
9554                                         if (node.parentNode == root)\r
9555                                                 return node;\r
9556 \r
9557                                         node = node.parentNode;\r
9558                                 } while(node);\r
9559                         };\r
9560 \r
9561                         function walkBoundary(start_node, end_node, next) {\r
9562                                 var siblingName = next ? 'nextSibling' : 'previousSibling';\r
9563 \r
9564                                 for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) {\r
9565                                         parent = node.parentNode;\r
9566                                         siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName);\r
9567 \r
9568                                         if (siblings.length) {\r
9569                                                 if (!next)\r
9570                                                         siblings.reverse();\r
9571 \r
9572                                                 callback(exclude(siblings));\r
9573                                         }\r
9574                                 }\r
9575                         };\r
9576 \r
9577                         // If index based start position then resolve it\r
9578                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes())\r
9579                                 startContainer = startContainer.childNodes[startOffset];\r
9580 \r
9581                         // If index based end position then resolve it\r
9582                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes())\r
9583                                 endContainer = endContainer.childNodes[Math.min(endOffset - 1, endContainer.childNodes.length - 1)];\r
9584 \r
9585                         // Same container\r
9586                         if (startContainer == endContainer)\r
9587                                 return callback(exclude([startContainer]));\r
9588 \r
9589                         // Find common ancestor and end points\r
9590                         ancestor = dom.findCommonAncestor(startContainer, endContainer);\r
9591                                 \r
9592                         // Process left side\r
9593                         for (node = startContainer; node; node = node.parentNode) {\r
9594                                 if (node === endContainer)\r
9595                                         return walkBoundary(startContainer, ancestor, true);\r
9596 \r
9597                                 if (node === ancestor)\r
9598                                         break;\r
9599                         }\r
9600 \r
9601                         // Process right side\r
9602                         for (node = endContainer; node; node = node.parentNode) {\r
9603                                 if (node === startContainer)\r
9604                                         return walkBoundary(endContainer, ancestor);\r
9605 \r
9606                                 if (node === ancestor)\r
9607                                         break;\r
9608                         }\r
9609 \r
9610                         // Find start/end point\r
9611                         startPoint = findEndPoint(startContainer, ancestor) || startContainer;\r
9612                         endPoint = findEndPoint(endContainer, ancestor) || endContainer;\r
9613 \r
9614                         // Walk left leaf\r
9615                         walkBoundary(startContainer, startPoint, true);\r
9616 \r
9617                         // Walk the middle from start to end point\r
9618                         siblings = collectSiblings(\r
9619                                 startPoint == startContainer ? startPoint : startPoint.nextSibling,\r
9620                                 'nextSibling',\r
9621                                 endPoint == endContainer ? endPoint.nextSibling : endPoint\r
9622                         );\r
9623 \r
9624                         if (siblings.length)\r
9625                                 callback(exclude(siblings));\r
9626 \r
9627                         // Walk right leaf\r
9628                         walkBoundary(endContainer, endPoint);\r
9629                 };\r
9630 \r
9631                 this.split = function(rng) {\r
9632                         var startContainer = rng.startContainer,\r
9633                                 startOffset = rng.startOffset,\r
9634                                 endContainer = rng.endContainer,\r
9635                                 endOffset = rng.endOffset;\r
9636 \r
9637                         function splitText(node, offset) {\r
9638                                 return node.splitText(offset);\r
9639                         };\r
9640 \r
9641                         // Handle single text node\r
9642                         if (startContainer == endContainer && startContainer.nodeType == 3) {\r
9643                                 if (startOffset > 0 && startOffset < startContainer.nodeValue.length) {\r
9644                                         endContainer = splitText(startContainer, startOffset);\r
9645                                         startContainer = endContainer.previousSibling;\r
9646 \r
9647                                         if (endOffset > startOffset) {\r
9648                                                 endOffset = endOffset - startOffset;\r
9649                                                 startContainer = endContainer = splitText(endContainer, endOffset).previousSibling;\r
9650                                                 endOffset = endContainer.nodeValue.length;\r
9651                                                 startOffset = 0;\r
9652                                         } else {\r
9653                                                 endOffset = 0;\r
9654                                         }\r
9655                                 }\r
9656                         } else {\r
9657                                 // Split startContainer text node if needed\r
9658                                 if (startContainer.nodeType == 3 && startOffset > 0 && startOffset < startContainer.nodeValue.length) {\r
9659                                         startContainer = splitText(startContainer, startOffset);\r
9660                                         startOffset = 0;\r
9661                                 }\r
9662 \r
9663                                 // Split endContainer text node if needed\r
9664                                 if (endContainer.nodeType == 3 && endOffset > 0 && endOffset < endContainer.nodeValue.length) {\r
9665                                         endContainer = splitText(endContainer, endOffset).previousSibling;\r
9666                                         endOffset = endContainer.nodeValue.length;\r
9667                                 }\r
9668                         }\r
9669 \r
9670                         return {\r
9671                                 startContainer : startContainer,\r
9672                                 startOffset : startOffset,\r
9673                                 endContainer : endContainer,\r
9674                                 endOffset : endOffset\r
9675                         };\r
9676                 };\r
9677 \r
9678         };\r
9679 \r
9680         tinymce.dom.RangeUtils.compareRanges = function(rng1, rng2) {\r
9681                 if (rng1 && rng2) {\r
9682                         // Compare native IE ranges\r
9683                         if (rng1.item || rng1.duplicate) {\r
9684                                 // Both are control ranges and the selected element matches\r
9685                                 if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))\r
9686                                         return true;\r
9687 \r
9688                                 // Both are text ranges and the range matches\r
9689                                 if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1))\r
9690                                         return true;\r
9691                         } else {\r
9692                                 // Compare w3c ranges\r
9693                                 return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;\r
9694                         }\r
9695                 }\r
9696 \r
9697                 return false;\r
9698         };\r
9699 })(tinymce);\r
9700 \r
9701 (function(tinymce) {\r
9702         var Event = tinymce.dom.Event, each = tinymce.each;\r
9703 \r
9704         tinymce.create('tinymce.ui.KeyboardNavigation', {\r
9705                 KeyboardNavigation: function(settings, dom) {\r
9706                         var t = this, root = settings.root, items = settings.items,\r
9707                                         enableUpDown = settings.enableUpDown, enableLeftRight = settings.enableLeftRight || !settings.enableUpDown,\r
9708                                         excludeFromTabOrder = settings.excludeFromTabOrder,\r
9709                                         itemFocussed, itemBlurred, rootKeydown, rootFocussed, focussedId;\r
9710 \r
9711                         dom = dom || tinymce.DOM;\r
9712 \r
9713                         itemFocussed = function(evt) {\r
9714                                 focussedId = evt.target.id;\r
9715                         };\r
9716                         \r
9717                         itemBlurred = function(evt) {\r
9718                                 dom.setAttrib(evt.target.id, 'tabindex', '-1');\r
9719                         };\r
9720                         \r
9721                         rootFocussed = function(evt) {\r
9722                                 var item = dom.get(focussedId);\r
9723                                 dom.setAttrib(item, 'tabindex', '0');\r
9724                                 item.focus();\r
9725                         };\r
9726                         \r
9727                         t.focus = function() {\r
9728                                 dom.get(focussedId).focus();\r
9729                         };\r
9730 \r
9731                         t.destroy = function() {\r
9732                                 each(items, function(item) {\r
9733                                         dom.unbind(dom.get(item.id), 'focus', itemFocussed);\r
9734                                         dom.unbind(dom.get(item.id), 'blur', itemBlurred);\r
9735                                 });\r
9736 \r
9737                                 dom.unbind(dom.get(root), 'focus', rootFocussed);\r
9738                                 dom.unbind(dom.get(root), 'keydown', rootKeydown);\r
9739 \r
9740                                 items = dom = root = t.focus = itemFocussed = itemBlurred = rootKeydown = rootFocussed = null;\r
9741                                 t.destroy = function() {};\r
9742                         };\r
9743                         \r
9744                         t.moveFocus = function(dir, evt) {\r
9745                                 var idx = -1, controls = t.controls, newFocus;\r
9746 \r
9747                                 if (!focussedId)\r
9748                                         return;\r
9749 \r
9750                                 each(items, function(item, index) {\r
9751                                         if (item.id === focussedId) {\r
9752                                                 idx = index;\r
9753                                                 return false;\r
9754                                         }\r
9755                                 });\r
9756 \r
9757                                 idx += dir;\r
9758                                 if (idx < 0) {\r
9759                                         idx = items.length - 1;\r
9760                                 } else if (idx >= items.length) {\r
9761                                         idx = 0;\r
9762                                 }\r
9763                                 \r
9764                                 newFocus = items[idx];\r
9765                                 dom.setAttrib(focussedId, 'tabindex', '-1');\r
9766                                 dom.setAttrib(newFocus.id, 'tabindex', '0');\r
9767                                 dom.get(newFocus.id).focus();\r
9768 \r
9769                                 if (settings.actOnFocus) {\r
9770                                         settings.onAction(newFocus.id);\r
9771                                 }\r
9772 \r
9773                                 if (evt)\r
9774                                         Event.cancel(evt);\r
9775                         };\r
9776                         \r
9777                         rootKeydown = function(evt) {\r
9778                                 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
9779                                 \r
9780                                 switch (evt.keyCode) {\r
9781                                         case DOM_VK_LEFT:\r
9782                                                 if (enableLeftRight) t.moveFocus(-1);\r
9783                                                 break;\r
9784         \r
9785                                         case DOM_VK_RIGHT:\r
9786                                                 if (enableLeftRight) t.moveFocus(1);\r
9787                                                 break;\r
9788         \r
9789                                         case DOM_VK_UP:\r
9790                                                 if (enableUpDown) t.moveFocus(-1);\r
9791                                                 break;\r
9792 \r
9793                                         case DOM_VK_DOWN:\r
9794                                                 if (enableUpDown) t.moveFocus(1);\r
9795                                                 break;\r
9796 \r
9797                                         case DOM_VK_ESCAPE:\r
9798                                                 if (settings.onCancel) {\r
9799                                                         settings.onCancel();\r
9800                                                         Event.cancel(evt);\r
9801                                                 }\r
9802                                                 break;\r
9803 \r
9804                                         case DOM_VK_ENTER:\r
9805                                         case DOM_VK_RETURN:\r
9806                                         case DOM_VK_SPACE:\r
9807                                                 if (settings.onAction) {\r
9808                                                         settings.onAction(focussedId);\r
9809                                                         Event.cancel(evt);\r
9810                                                 }\r
9811                                                 break;\r
9812                                 }\r
9813                         };\r
9814 \r
9815                         // Set up state and listeners for each item.\r
9816                         each(items, function(item, idx) {\r
9817                                 var tabindex;\r
9818 \r
9819                                 if (!item.id) {\r
9820                                         item.id = dom.uniqueId('_mce_item_');\r
9821                                 }\r
9822 \r
9823                                 if (excludeFromTabOrder) {\r
9824                                         dom.bind(item.id, 'blur', itemBlurred);\r
9825                                         tabindex = '-1';\r
9826                                 } else {\r
9827                                         tabindex = (idx === 0 ? '0' : '-1');\r
9828                                 }\r
9829 \r
9830                                 dom.setAttrib(item.id, 'tabindex', tabindex);\r
9831                                 dom.bind(dom.get(item.id), 'focus', itemFocussed);\r
9832                         });\r
9833                         \r
9834                         // Setup initial state for root element.\r
9835                         if (items[0]){\r
9836                                 focussedId = items[0].id;\r
9837                         }\r
9838 \r
9839                         dom.setAttrib(root, 'tabindex', '-1');\r
9840                         \r
9841                         // Setup listeners for root element.\r
9842                         dom.bind(dom.get(root), 'focus', rootFocussed);\r
9843                         dom.bind(dom.get(root), 'keydown', rootKeydown);\r
9844                 }\r
9845         });\r
9846 })(tinymce);\r
9847 \r
9848 (function(tinymce) {\r
9849         // Shorten class names\r
9850         var DOM = tinymce.DOM, is = tinymce.is;\r
9851 \r
9852         tinymce.create('tinymce.ui.Control', {\r
9853                 Control : function(id, s, editor) {\r
9854                         this.id = id;\r
9855                         this.settings = s = s || {};\r
9856                         this.rendered = false;\r
9857                         this.onRender = new tinymce.util.Dispatcher(this);\r
9858                         this.classPrefix = '';\r
9859                         this.scope = s.scope || this;\r
9860                         this.disabled = 0;\r
9861                         this.active = 0;\r
9862                         this.editor = editor;\r
9863                 },\r
9864                 \r
9865                 setAriaProperty : function(property, value) {\r
9866                         var element = DOM.get(this.id + '_aria') || DOM.get(this.id);\r
9867                         if (element) {\r
9868                                 DOM.setAttrib(element, 'aria-' + property, !!value);\r
9869                         }\r
9870                 },\r
9871                 \r
9872                 focus : function() {\r
9873                         DOM.get(this.id).focus();\r
9874                 },\r
9875 \r
9876                 setDisabled : function(s) {\r
9877                         if (s != this.disabled) {\r
9878                                 this.setAriaProperty('disabled', s);\r
9879 \r
9880                                 this.setState('Disabled', s);\r
9881                                 this.setState('Enabled', !s);\r
9882                                 this.disabled = s;\r
9883                         }\r
9884                 },\r
9885 \r
9886                 isDisabled : function() {\r
9887                         return this.disabled;\r
9888                 },\r
9889 \r
9890                 setActive : function(s) {\r
9891                         if (s != this.active) {\r
9892                                 this.setState('Active', s);\r
9893                                 this.active = s;\r
9894                                 this.setAriaProperty('pressed', s);\r
9895                         }\r
9896                 },\r
9897 \r
9898                 isActive : function() {\r
9899                         return this.active;\r
9900                 },\r
9901 \r
9902                 setState : function(c, s) {\r
9903                         var n = DOM.get(this.id);\r
9904 \r
9905                         c = this.classPrefix + c;\r
9906 \r
9907                         if (s)\r
9908                                 DOM.addClass(n, c);\r
9909                         else\r
9910                                 DOM.removeClass(n, c);\r
9911                 },\r
9912 \r
9913                 isRendered : function() {\r
9914                         return this.rendered;\r
9915                 },\r
9916 \r
9917                 renderHTML : function() {\r
9918                 },\r
9919 \r
9920                 renderTo : function(n) {\r
9921                         DOM.setHTML(n, this.renderHTML());\r
9922                 },\r
9923 \r
9924                 postRender : function() {\r
9925                         var t = this, b;\r
9926 \r
9927                         // Set pending states\r
9928                         if (is(t.disabled)) {\r
9929                                 b = t.disabled;\r
9930                                 t.disabled = -1;\r
9931                                 t.setDisabled(b);\r
9932                         }\r
9933 \r
9934                         if (is(t.active)) {\r
9935                                 b = t.active;\r
9936                                 t.active = -1;\r
9937                                 t.setActive(b);\r
9938                         }\r
9939                 },\r
9940 \r
9941                 remove : function() {\r
9942                         DOM.remove(this.id);\r
9943                         this.destroy();\r
9944                 },\r
9945 \r
9946                 destroy : function() {\r
9947                         tinymce.dom.Event.clear(this.id);\r
9948                 }\r
9949         });\r
9950 })(tinymce);\r
9951 tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {\r
9952         Container : function(id, s, editor) {\r
9953                 this.parent(id, s, editor);\r
9954 \r
9955                 this.controls = [];\r
9956 \r
9957                 this.lookup = {};\r
9958         },\r
9959 \r
9960         add : function(c) {\r
9961                 this.lookup[c.id] = c;\r
9962                 this.controls.push(c);\r
9963 \r
9964                 return c;\r
9965         },\r
9966 \r
9967         get : function(n) {\r
9968                 return this.lookup[n];\r
9969         }\r
9970 });\r
9971 \r
9972 \r
9973 tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {\r
9974         Separator : function(id, s) {\r
9975                 this.parent(id, s);\r
9976                 this.classPrefix = 'mceSeparator';\r
9977                 this.setDisabled(true);\r
9978         },\r
9979 \r
9980         renderHTML : function() {\r
9981                 return tinymce.DOM.createHTML('span', {'class' : this.classPrefix, role : 'separator', 'aria-orientation' : 'vertical', tabindex : '-1'});\r
9982         }\r
9983 });\r
9984 \r
9985 (function(tinymce) {\r
9986         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
9987 \r
9988         tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', {\r
9989                 MenuItem : function(id, s) {\r
9990                         this.parent(id, s);\r
9991                         this.classPrefix = 'mceMenuItem';\r
9992                 },\r
9993 \r
9994                 setSelected : function(s) {\r
9995                         this.setState('Selected', s);\r
9996                         this.setAriaProperty('checked', !!s);\r
9997                         this.selected = s;\r
9998                 },\r
9999 \r
10000                 isSelected : function() {\r
10001                         return this.selected;\r
10002                 },\r
10003 \r
10004                 postRender : function() {\r
10005                         var t = this;\r
10006                         \r
10007                         t.parent();\r
10008 \r
10009                         // Set pending state\r
10010                         if (is(t.selected))\r
10011                                 t.setSelected(t.selected);\r
10012                 }\r
10013         });\r
10014 })(tinymce);\r
10015 \r
10016 (function(tinymce) {\r
10017         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
10018 \r
10019         tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', {\r
10020                 Menu : function(id, s) {\r
10021                         var t = this;\r
10022 \r
10023                         t.parent(id, s);\r
10024                         t.items = {};\r
10025                         t.collapsed = false;\r
10026                         t.menuCount = 0;\r
10027                         t.onAddItem = new tinymce.util.Dispatcher(this);\r
10028                 },\r
10029 \r
10030                 expand : function(d) {\r
10031                         var t = this;\r
10032 \r
10033                         if (d) {\r
10034                                 walk(t, function(o) {\r
10035                                         if (o.expand)\r
10036                                                 o.expand();\r
10037                                 }, 'items', t);\r
10038                         }\r
10039 \r
10040                         t.collapsed = false;\r
10041                 },\r
10042 \r
10043                 collapse : function(d) {\r
10044                         var t = this;\r
10045 \r
10046                         if (d) {\r
10047                                 walk(t, function(o) {\r
10048                                         if (o.collapse)\r
10049                                                 o.collapse();\r
10050                                 }, 'items', t);\r
10051                         }\r
10052 \r
10053                         t.collapsed = true;\r
10054                 },\r
10055 \r
10056                 isCollapsed : function() {\r
10057                         return this.collapsed;\r
10058                 },\r
10059 \r
10060                 add : function(o) {\r
10061                         if (!o.settings)\r
10062                                 o = new tinymce.ui.MenuItem(o.id || DOM.uniqueId(), o);\r
10063 \r
10064                         this.onAddItem.dispatch(this, o);\r
10065 \r
10066                         return this.items[o.id] = o;\r
10067                 },\r
10068 \r
10069                 addSeparator : function() {\r
10070                         return this.add({separator : true});\r
10071                 },\r
10072 \r
10073                 addMenu : function(o) {\r
10074                         if (!o.collapse)\r
10075                                 o = this.createMenu(o);\r
10076 \r
10077                         this.menuCount++;\r
10078 \r
10079                         return this.add(o);\r
10080                 },\r
10081 \r
10082                 hasMenus : function() {\r
10083                         return this.menuCount !== 0;\r
10084                 },\r
10085 \r
10086                 remove : function(o) {\r
10087                         delete this.items[o.id];\r
10088                 },\r
10089 \r
10090                 removeAll : function() {\r
10091                         var t = this;\r
10092 \r
10093                         walk(t, function(o) {\r
10094                                 if (o.removeAll)\r
10095                                         o.removeAll();\r
10096                                 else\r
10097                                         o.remove();\r
10098 \r
10099                                 o.destroy();\r
10100                         }, 'items', t);\r
10101 \r
10102                         t.items = {};\r
10103                 },\r
10104 \r
10105                 createMenu : function(o) {\r
10106                         var m = new tinymce.ui.Menu(o.id || DOM.uniqueId(), o);\r
10107 \r
10108                         m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem);\r
10109 \r
10110                         return m;\r
10111                 }\r
10112         });\r
10113 })(tinymce);\r
10114 (function(tinymce) {\r
10115         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element;\r
10116 \r
10117         tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', {\r
10118                 DropMenu : function(id, s) {\r
10119                         s = s || {};\r
10120                         s.container = s.container || DOM.doc.body;\r
10121                         s.offset_x = s.offset_x || 0;\r
10122                         s.offset_y = s.offset_y || 0;\r
10123                         s.vp_offset_x = s.vp_offset_x || 0;\r
10124                         s.vp_offset_y = s.vp_offset_y || 0;\r
10125 \r
10126                         if (is(s.icons) && !s.icons)\r
10127                                 s['class'] += ' mceNoIcons';\r
10128 \r
10129                         this.parent(id, s);\r
10130                         this.onShowMenu = new tinymce.util.Dispatcher(this);\r
10131                         this.onHideMenu = new tinymce.util.Dispatcher(this);\r
10132                         this.classPrefix = 'mceMenu';\r
10133                 },\r
10134 \r
10135                 createMenu : function(s) {\r
10136                         var t = this, cs = t.settings, m;\r
10137 \r
10138                         s.container = s.container || cs.container;\r
10139                         s.parent = t;\r
10140                         s.constrain = s.constrain || cs.constrain;\r
10141                         s['class'] = s['class'] || cs['class'];\r
10142                         s.vp_offset_x = s.vp_offset_x || cs.vp_offset_x;\r
10143                         s.vp_offset_y = s.vp_offset_y || cs.vp_offset_y;\r
10144                         s.keyboard_focus = cs.keyboard_focus;\r
10145                         m = new tinymce.ui.DropMenu(s.id || DOM.uniqueId(), s);\r
10146 \r
10147                         m.onAddItem.add(t.onAddItem.dispatch, t.onAddItem);\r
10148 \r
10149                         return m;\r
10150                 },\r
10151                 \r
10152                 focus : function() {\r
10153                         var t = this;\r
10154                         if (t.keyboardNav) {\r
10155                                 t.keyboardNav.focus();\r
10156                         }\r
10157                 },\r
10158 \r
10159                 update : function() {\r
10160                         var t = this, s = t.settings, tb = DOM.get('menu_' + t.id + '_tbl'), co = DOM.get('menu_' + t.id + '_co'), tw, th;\r
10161 \r
10162                         tw = s.max_width ? Math.min(tb.clientWidth, s.max_width) : tb.clientWidth;\r
10163                         th = s.max_height ? Math.min(tb.clientHeight, s.max_height) : tb.clientHeight;\r
10164 \r
10165                         if (!DOM.boxModel)\r
10166                                 t.element.setStyles({width : tw + 2, height : th + 2});\r
10167                         else\r
10168                                 t.element.setStyles({width : tw, height : th});\r
10169 \r
10170                         if (s.max_width)\r
10171                                 DOM.setStyle(co, 'width', tw);\r
10172 \r
10173                         if (s.max_height) {\r
10174                                 DOM.setStyle(co, 'height', th);\r
10175 \r
10176                                 if (tb.clientHeight < s.max_height)\r
10177                                         DOM.setStyle(co, 'overflow', 'hidden');\r
10178                         }\r
10179                 },\r
10180 \r
10181                 showMenu : function(x, y, px) {\r
10182                         var t = this, s = t.settings, co, vp = DOM.getViewPort(), w, h, mx, my, ot = 2, dm, tb, cp = t.classPrefix;\r
10183 \r
10184                         t.collapse(1);\r
10185 \r
10186                         if (t.isMenuVisible)\r
10187                                 return;\r
10188 \r
10189                         if (!t.rendered) {\r
10190                                 co = DOM.add(t.settings.container, t.renderNode());\r
10191 \r
10192                                 each(t.items, function(o) {\r
10193                                         o.postRender();\r
10194                                 });\r
10195 \r
10196                                 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
10197                         } else\r
10198                                 co = DOM.get('menu_' + t.id);\r
10199 \r
10200                         // Move layer out of sight unless it's Opera since it scrolls to top of page due to an bug\r
10201                         if (!tinymce.isOpera)\r
10202                                 DOM.setStyles(co, {left : -0xFFFF , top : -0xFFFF});\r
10203 \r
10204                         DOM.show(co);\r
10205                         t.update();\r
10206 \r
10207                         x += s.offset_x || 0;\r
10208                         y += s.offset_y || 0;\r
10209                         vp.w -= 4;\r
10210                         vp.h -= 4;\r
10211 \r
10212                         // Move inside viewport if not submenu\r
10213                         if (s.constrain) {\r
10214                                 w = co.clientWidth - ot;\r
10215                                 h = co.clientHeight - ot;\r
10216                                 mx = vp.x + vp.w;\r
10217                                 my = vp.y + vp.h;\r
10218 \r
10219                                 if ((x + s.vp_offset_x + w) > mx)\r
10220                                         x = px ? px - w : Math.max(0, (mx - s.vp_offset_x) - w);\r
10221 \r
10222                                 if ((y + s.vp_offset_y + h) > my)\r
10223                                         y = Math.max(0, (my - s.vp_offset_y) - h);\r
10224                         }\r
10225 \r
10226                         DOM.setStyles(co, {left : x , top : y});\r
10227                         t.element.update();\r
10228 \r
10229                         t.isMenuVisible = 1;\r
10230                         t.mouseClickFunc = Event.add(co, 'click', function(e) {\r
10231                                 var m;\r
10232 \r
10233                                 e = e.target;\r
10234 \r
10235                                 if (e && (e = DOM.getParent(e, 'tr')) && !DOM.hasClass(e, cp + 'ItemSub')) {\r
10236                                         m = t.items[e.id];\r
10237 \r
10238                                         if (m.isDisabled())\r
10239                                                 return;\r
10240 \r
10241                                         dm = t;\r
10242 \r
10243                                         while (dm) {\r
10244                                                 if (dm.hideMenu)\r
10245                                                         dm.hideMenu();\r
10246 \r
10247                                                 dm = dm.settings.parent;\r
10248                                         }\r
10249 \r
10250                                         if (m.settings.onclick)\r
10251                                                 m.settings.onclick(e);\r
10252 \r
10253                                         return false; // Cancel to fix onbeforeunload problem\r
10254                                 }\r
10255                         });\r
10256 \r
10257                         if (t.hasMenus()) {\r
10258                                 t.mouseOverFunc = Event.add(co, 'mouseover', function(e) {\r
10259                                         var m, r, mi;\r
10260 \r
10261                                         e = e.target;\r
10262                                         if (e && (e = DOM.getParent(e, 'tr'))) {\r
10263                                                 m = t.items[e.id];\r
10264 \r
10265                                                 if (t.lastMenu)\r
10266                                                         t.lastMenu.collapse(1);\r
10267 \r
10268                                                 if (m.isDisabled())\r
10269                                                         return;\r
10270 \r
10271                                                 if (e && DOM.hasClass(e, cp + 'ItemSub')) {\r
10272                                                         //p = DOM.getPos(s.container);\r
10273                                                         r = DOM.getRect(e);\r
10274                                                         m.showMenu((r.x + r.w - ot), r.y - ot, r.x);\r
10275                                                         t.lastMenu = m;\r
10276                                                         DOM.addClass(DOM.get(m.id).firstChild, cp + 'ItemActive');\r
10277                                                 }\r
10278                                         }\r
10279                                 });\r
10280                         }\r
10281                         \r
10282                         Event.add(co, 'keydown', t._keyHandler, t);\r
10283 \r
10284                         t.onShowMenu.dispatch(t);\r
10285 \r
10286                         if (s.keyboard_focus) { \r
10287                                 t._setupKeyboardNav(); \r
10288                         }\r
10289                 },\r
10290 \r
10291                 hideMenu : function(c) {\r
10292                         var t = this, co = DOM.get('menu_' + t.id), e;\r
10293 \r
10294                         if (!t.isMenuVisible)\r
10295                                 return;\r
10296 \r
10297                         if (t.keyboardNav) t.keyboardNav.destroy();\r
10298                         Event.remove(co, 'mouseover', t.mouseOverFunc);\r
10299                         Event.remove(co, 'click', t.mouseClickFunc);\r
10300                         Event.remove(co, 'keydown', t._keyHandler);\r
10301                         DOM.hide(co);\r
10302                         t.isMenuVisible = 0;\r
10303 \r
10304                         if (!c)\r
10305                                 t.collapse(1);\r
10306 \r
10307                         if (t.element)\r
10308                                 t.element.hide();\r
10309 \r
10310                         if (e = DOM.get(t.id))\r
10311                                 DOM.removeClass(e.firstChild, t.classPrefix + 'ItemActive');\r
10312 \r
10313                         t.onHideMenu.dispatch(t);\r
10314                 },\r
10315 \r
10316                 add : function(o) {\r
10317                         var t = this, co;\r
10318 \r
10319                         o = t.parent(o);\r
10320 \r
10321                         if (t.isRendered && (co = DOM.get('menu_' + t.id)))\r
10322                                 t._add(DOM.select('tbody', co)[0], o);\r
10323 \r
10324                         return o;\r
10325                 },\r
10326 \r
10327                 collapse : function(d) {\r
10328                         this.parent(d);\r
10329                         this.hideMenu(1);\r
10330                 },\r
10331 \r
10332                 remove : function(o) {\r
10333                         DOM.remove(o.id);\r
10334                         this.destroy();\r
10335 \r
10336                         return this.parent(o);\r
10337                 },\r
10338 \r
10339                 destroy : function() {\r
10340                         var t = this, co = DOM.get('menu_' + t.id);\r
10341 \r
10342                         if (t.keyboardNav) t.keyboardNav.destroy();\r
10343                         Event.remove(co, 'mouseover', t.mouseOverFunc);\r
10344                         Event.remove(DOM.select('a', co), 'focus', t.mouseOverFunc);\r
10345                         Event.remove(co, 'click', t.mouseClickFunc);\r
10346                         Event.remove(co, 'keydown', t._keyHandler);\r
10347 \r
10348                         if (t.element)\r
10349                                 t.element.remove();\r
10350 \r
10351                         DOM.remove(co);\r
10352                 },\r
10353 \r
10354                 renderNode : function() {\r
10355                         var t = this, s = t.settings, n, tb, co, w;\r
10356 \r
10357                         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
10358                         if (t.settings.parent) {\r
10359                                 DOM.setAttrib(w, 'aria-parent', 'menu_' + t.settings.parent.id);\r
10360                         }\r
10361                         co = DOM.add(w, 'div', {role: 'presentation', id : 'menu_' + t.id + '_co', 'class' : t.classPrefix + (s['class'] ? ' ' + s['class'] : '')});\r
10362                         t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
10363 \r
10364                         if (s.menu_line)\r
10365                                 DOM.add(co, 'span', {'class' : t.classPrefix + 'Line'});\r
10366 \r
10367 //                      n = DOM.add(co, 'div', {id : 'menu_' + t.id + '_co', 'class' : 'mceMenuContainer'});\r
10368                         n = DOM.add(co, 'table', {role: 'presentation', id : 'menu_' + t.id + '_tbl', border : 0, cellPadding : 0, cellSpacing : 0});\r
10369                         tb = DOM.add(n, 'tbody');\r
10370 \r
10371                         each(t.items, function(o) {\r
10372                                 t._add(tb, o);\r
10373                         });\r
10374 \r
10375                         t.rendered = true;\r
10376 \r
10377                         return w;\r
10378                 },\r
10379 \r
10380                 // Internal functions\r
10381                 _setupKeyboardNav : function(){\r
10382                         var contextMenu, menuItems, t=this; \r
10383                         contextMenu = DOM.get('menu_' + t.id);\r
10384                         menuItems = DOM.select('a[role=option]', 'menu_' + t.id);\r
10385                         menuItems.splice(0,0,contextMenu);\r
10386                         t.keyboardNav = new tinymce.ui.KeyboardNavigation({\r
10387                                 root: 'menu_' + t.id,\r
10388                                 items: menuItems,\r
10389                                 onCancel: function() {\r
10390                                         t.hideMenu();\r
10391                                 },\r
10392                                 enableUpDown: true\r
10393                         });\r
10394                         contextMenu.focus();\r
10395                 },\r
10396 \r
10397                 _keyHandler : function(evt) {\r
10398                         var t = this, e;\r
10399                         switch (evt.keyCode) {\r
10400                                 case 37: // Left\r
10401                                         if (t.settings.parent) {\r
10402                                                 t.hideMenu();\r
10403                                                 t.settings.parent.focus();\r
10404                                                 Event.cancel(evt);\r
10405                                         }\r
10406                                         break;\r
10407                                 case 39: // Right\r
10408                                         if (t.mouseOverFunc)\r
10409                                                 t.mouseOverFunc(evt);\r
10410                                         break;\r
10411                         }\r
10412                 },\r
10413 \r
10414                 _add : function(tb, o) {\r
10415                         var n, s = o.settings, a, ro, it, cp = this.classPrefix, ic;\r
10416 \r
10417                         if (s.separator) {\r
10418                                 ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'ItemSeparator'});\r
10419                                 DOM.add(ro, 'td', {'class' : cp + 'ItemSeparator'});\r
10420 \r
10421                                 if (n = ro.previousSibling)\r
10422                                         DOM.addClass(n, 'mceLast');\r
10423 \r
10424                                 return;\r
10425                         }\r
10426 \r
10427                         n = ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'Item ' + cp + 'ItemEnabled'});\r
10428                         n = it = DOM.add(n, s.titleItem ? 'th' : 'td');\r
10429                         n = a = DOM.add(n, 'a', {id: o.id + '_aria',  role: s.titleItem ? 'presentation' : 'option', href : 'javascript:;', onclick : "return false;", onmousedown : 'return false;'});\r
10430 \r
10431                         if (s.parent) {\r
10432                                 DOM.setAttrib(a, 'aria-haspopup', 'true');\r
10433                                 DOM.setAttrib(a, 'aria-owns', 'menu_' + o.id);\r
10434                         }\r
10435 \r
10436                         DOM.addClass(it, s['class']);\r
10437 //                      n = DOM.add(n, 'span', {'class' : 'item'});\r
10438 \r
10439                         ic = DOM.add(n, 'span', {'class' : 'mceIcon' + (s.icon ? ' mce_' + s.icon : '')});\r
10440 \r
10441                         if (s.icon_src)\r
10442                                 DOM.add(ic, 'img', {src : s.icon_src});\r
10443 \r
10444                         n = DOM.add(n, s.element || 'span', {'class' : 'mceText', title : o.settings.title}, o.settings.title);\r
10445 \r
10446                         if (o.settings.style) {\r
10447                                 if (typeof o.settings.style == "function")\r
10448                                         o.settings.style = o.settings.style();\r
10449 \r
10450                                 DOM.setAttrib(n, 'style', o.settings.style);\r
10451                         }\r
10452 \r
10453                         if (tb.childNodes.length == 1)\r
10454                                 DOM.addClass(ro, 'mceFirst');\r
10455 \r
10456                         if ((n = ro.previousSibling) && DOM.hasClass(n, cp + 'ItemSeparator'))\r
10457                                 DOM.addClass(ro, 'mceFirst');\r
10458 \r
10459                         if (o.collapse)\r
10460                                 DOM.addClass(ro, cp + 'ItemSub');\r
10461 \r
10462                         if (n = ro.previousSibling)\r
10463                                 DOM.removeClass(n, 'mceLast');\r
10464 \r
10465                         DOM.addClass(ro, 'mceLast');\r
10466                 }\r
10467         });\r
10468 })(tinymce);\r
10469 (function(tinymce) {\r
10470         var DOM = tinymce.DOM;\r
10471 \r
10472         tinymce.create('tinymce.ui.Button:tinymce.ui.Control', {\r
10473                 Button : function(id, s, ed) {\r
10474                         this.parent(id, s, ed);\r
10475                         this.classPrefix = 'mceButton';\r
10476                 },\r
10477 \r
10478                 renderHTML : function() {\r
10479                         var cp = this.classPrefix, s = this.settings, h, l;\r
10480 \r
10481                         l = DOM.encode(s.label || '');\r
10482                         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
10483                         if (s.image && !(this.editor  &&this.editor.forcedHighContrastMode) )\r
10484                                 h += '<img class="mceIcon" src="' + s.image + '" alt="' + DOM.encode(s.title) + '" />' + l;\r
10485                         else\r
10486                                 h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '');\r
10487 \r
10488                         h += '<span class="mceVoiceLabel mceIconOnly" style="display: none;" id="' + this.id + '_voice">' + s.title + '</span>'; \r
10489                         h += '</a>';\r
10490                         return h;\r
10491                 },\r
10492 \r
10493                 postRender : function() {\r
10494                         var t = this, s = t.settings, imgBookmark;\r
10495 \r
10496                         // In IE a large image that occupies the entire editor area will be deselected when a button is clicked, so\r
10497                         // need to keep the selection in case the selection is lost\r
10498                         if (tinymce.isIE && t.editor) {\r
10499                                 tinymce.dom.Event.add(t.id, 'mousedown', function(e) {\r
10500                                         var nodeName = t.editor.selection.getNode().nodeName;\r
10501                                         imgBookmark = nodeName === 'IMG' ? t.editor.selection.getBookmark() : null;\r
10502                                 });\r
10503                         }\r
10504                         tinymce.dom.Event.add(t.id, 'click', function(e) {\r
10505                                 if (!t.isDisabled()) {\r
10506                                         // restore the selection in case the selection is lost in IE\r
10507                                         if (tinymce.isIE && t.editor && imgBookmark !== null) {\r
10508                                                 t.editor.selection.moveToBookmark(imgBookmark);\r
10509                                         }\r
10510                                         return s.onclick.call(s.scope, e);\r
10511                                 }\r
10512                         });\r
10513                         tinymce.dom.Event.add(t.id, 'keyup', function(e) {\r
10514                                 if (!t.isDisabled() && e.keyCode==tinymce.VK.SPACEBAR)\r
10515                                         return s.onclick.call(s.scope, e);\r
10516                         });\r
10517                 }\r
10518         });\r
10519 })(tinymce);\r
10520 \r
10521 (function(tinymce) {\r
10522         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
10523 \r
10524         tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {\r
10525                 ListBox : function(id, s, ed) {\r
10526                         var t = this;\r
10527 \r
10528                         t.parent(id, s, ed);\r
10529 \r
10530                         t.items = [];\r
10531 \r
10532                         t.onChange = new Dispatcher(t);\r
10533 \r
10534                         t.onPostRender = new Dispatcher(t);\r
10535 \r
10536                         t.onAdd = new Dispatcher(t);\r
10537 \r
10538                         t.onRenderMenu = new tinymce.util.Dispatcher(this);\r
10539 \r
10540                         t.classPrefix = 'mceListBox';\r
10541                         t.marked = {};\r
10542                 },\r
10543 \r
10544                 select : function(va) {\r
10545                         var t = this, fv, f;\r
10546 \r
10547                         t.marked = {};\r
10548 \r
10549                         if (va == undefined)\r
10550                                 return t.selectByIndex(-1);\r
10551 \r
10552                         // Is string or number make function selector\r
10553                         if (va && typeof(va)=="function")\r
10554                                 f = va;\r
10555                         else {\r
10556                                 f = function(v) {\r
10557                                         return v == va;\r
10558                                 };\r
10559                         }\r
10560 \r
10561                         // Do we need to do something?\r
10562                         if (va != t.selectedValue) {\r
10563                                 // Find item\r
10564                                 each(t.items, function(o, i) {\r
10565                                         if (f(o.value)) {\r
10566                                                 fv = 1;\r
10567                                                 t.selectByIndex(i);\r
10568                                                 return false;\r
10569                                         }\r
10570                                 });\r
10571 \r
10572                                 if (!fv)\r
10573                                         t.selectByIndex(-1);\r
10574                         }\r
10575                 },\r
10576 \r
10577                 selectByIndex : function(idx) {\r
10578                         var t = this, e, o, label;\r
10579 \r
10580                         t.marked = {};\r
10581 \r
10582                         if (idx != t.selectedIndex) {\r
10583                                 e = DOM.get(t.id + '_text');\r
10584                                 label = DOM.get(t.id + '_voiceDesc');\r
10585                                 o = t.items[idx];\r
10586 \r
10587                                 if (o) {\r
10588                                         t.selectedValue = o.value;\r
10589                                         t.selectedIndex = idx;\r
10590                                         DOM.setHTML(e, DOM.encode(o.title));\r
10591                                         DOM.setHTML(label, t.settings.title + " - " + o.title);\r
10592                                         DOM.removeClass(e, 'mceTitle');\r
10593                                         DOM.setAttrib(t.id, 'aria-valuenow', o.title);\r
10594                                 } else {\r
10595                                         DOM.setHTML(e, DOM.encode(t.settings.title));\r
10596                                         DOM.setHTML(label, DOM.encode(t.settings.title));\r
10597                                         DOM.addClass(e, 'mceTitle');\r
10598                                         t.selectedValue = t.selectedIndex = null;\r
10599                                         DOM.setAttrib(t.id, 'aria-valuenow', t.settings.title);\r
10600                                 }\r
10601                                 e = 0;\r
10602                         }\r
10603                 },\r
10604 \r
10605                 mark : function(value) {\r
10606                         this.marked[value] = true;\r
10607                 },\r
10608 \r
10609                 add : function(n, v, o) {\r
10610                         var t = this;\r
10611 \r
10612                         o = o || {};\r
10613                         o = tinymce.extend(o, {\r
10614                                 title : n,\r
10615                                 value : v\r
10616                         });\r
10617 \r
10618                         t.items.push(o);\r
10619                         t.onAdd.dispatch(t, o);\r
10620                 },\r
10621 \r
10622                 getLength : function() {\r
10623                         return this.items.length;\r
10624                 },\r
10625 \r
10626                 renderHTML : function() {\r
10627                         var h = '', t = this, s = t.settings, cp = t.classPrefix;\r
10628 \r
10629                         h = '<span role="listbox" aria-haspopup="true" aria-labelledby="' + t.id +'_voiceDesc" 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
10630                         h += '<td>' + DOM.createHTML('span', {id: t.id + '_voiceDesc', 'class': 'voiceLabel', style:'display:none;'}, t.settings.title); \r
10631                         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
10632                         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
10633                         h += '</tr></tbody></table></span>';\r
10634 \r
10635                         return h;\r
10636                 },\r
10637 \r
10638                 showMenu : function() {\r
10639                         var t = this, p2, e = DOM.get(this.id), m;\r
10640 \r
10641                         if (t.isDisabled() || t.items.length == 0)\r
10642                                 return;\r
10643 \r
10644                         if (t.menu && t.menu.isMenuVisible)\r
10645                                 return t.hideMenu();\r
10646 \r
10647                         if (!t.isMenuRendered) {\r
10648                                 t.renderMenu();\r
10649                                 t.isMenuRendered = true;\r
10650                         }\r
10651 \r
10652                         p2 = DOM.getPos(e);\r
10653 \r
10654                         m = t.menu;\r
10655                         m.settings.offset_x = p2.x;\r
10656                         m.settings.offset_y = p2.y;\r
10657                         m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus\r
10658 \r
10659                         // Select in menu\r
10660                         each(t.items, function(o) {\r
10661                                 if (m.items[o.id]) {\r
10662                                         m.items[o.id].setSelected(0);\r
10663                                 }\r
10664                         });\r
10665 \r
10666                         each(t.items, function(o) {\r
10667                                 if (m.items[o.id] && t.marked[o.value]) {\r
10668                                         m.items[o.id].setSelected(1);\r
10669                                 }\r
10670 \r
10671                                 if (o.value === t.selectedValue) {\r
10672                                         m.items[o.id].setSelected(1);\r
10673                                 }\r
10674                         });\r
10675 \r
10676                         m.showMenu(0, e.clientHeight);\r
10677 \r
10678                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
10679                         DOM.addClass(t.id, t.classPrefix + 'Selected');\r
10680 \r
10681                         //DOM.get(t.id + '_text').focus();\r
10682                 },\r
10683 \r
10684                 hideMenu : function(e) {\r
10685                         var t = this;\r
10686 \r
10687                         if (t.menu && t.menu.isMenuVisible) {\r
10688                                 DOM.removeClass(t.id, t.classPrefix + 'Selected');\r
10689 \r
10690                                 // Prevent double toogles by canceling the mouse click event to the button\r
10691                                 if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open'))\r
10692                                         return;\r
10693 \r
10694                                 if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
10695                                         DOM.removeClass(t.id, t.classPrefix + 'Selected');\r
10696                                         Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
10697                                         t.menu.hideMenu();\r
10698                                 }\r
10699                         }\r
10700                 },\r
10701 \r
10702                 renderMenu : function() {\r
10703                         var t = this, m;\r
10704 \r
10705                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
10706                                 menu_line : 1,\r
10707                                 'class' : t.classPrefix + 'Menu mceNoIcons',\r
10708                                 max_width : 150,\r
10709                                 max_height : 150\r
10710                         });\r
10711 \r
10712                         m.onHideMenu.add(function() {\r
10713                                 t.hideMenu();\r
10714                                 t.focus();\r
10715                         });\r
10716 \r
10717                         m.add({\r
10718                                 title : t.settings.title,\r
10719                                 'class' : 'mceMenuItemTitle',\r
10720                                 onclick : function() {\r
10721                                         if (t.settings.onselect('') !== false)\r
10722                                                 t.select(''); // Must be runned after\r
10723                                 }\r
10724                         });\r
10725 \r
10726                         each(t.items, function(o) {\r
10727                                 // No value then treat it as a title\r
10728                                 if (o.value === undefined) {\r
10729                                         m.add({\r
10730                                                 title : o.title,\r
10731                                                 role : "option",\r
10732                                                 'class' : 'mceMenuItemTitle',\r
10733                                                 onclick : function() {\r
10734                                                         if (t.settings.onselect('') !== false)\r
10735                                                                 t.select(''); // Must be runned after\r
10736                                                 }\r
10737                                         });\r
10738                                 } else {\r
10739                                         o.id = DOM.uniqueId();\r
10740                                         o.role= "option";\r
10741                                         o.onclick = function() {\r
10742                                                 if (t.settings.onselect(o.value) !== false)\r
10743                                                         t.select(o.value); // Must be runned after\r
10744                                         };\r
10745 \r
10746                                         m.add(o);\r
10747                                 }\r
10748                         });\r
10749 \r
10750                         t.onRenderMenu.dispatch(t, m);\r
10751                         t.menu = m;\r
10752                 },\r
10753 \r
10754                 postRender : function() {\r
10755                         var t = this, cp = t.classPrefix;\r
10756 \r
10757                         Event.add(t.id, 'click', t.showMenu, t);\r
10758                         Event.add(t.id, 'keydown', function(evt) {\r
10759                                 if (evt.keyCode == 32) { // Space\r
10760                                         t.showMenu(evt);\r
10761                                         Event.cancel(evt);\r
10762                                 }\r
10763                         });\r
10764                         Event.add(t.id, 'focus', function() {\r
10765                                 if (!t._focused) {\r
10766                                         t.keyDownHandler = Event.add(t.id, 'keydown', function(e) {\r
10767                                                 if (e.keyCode == 40) {\r
10768                                                         t.showMenu();\r
10769                                                         Event.cancel(e);\r
10770                                                 }\r
10771                                         });\r
10772                                         t.keyPressHandler = Event.add(t.id, 'keypress', function(e) {\r
10773                                                 var v;\r
10774                                                 if (e.keyCode == 13) {\r
10775                                                         // Fake select on enter\r
10776                                                         v = t.selectedValue;\r
10777                                                         t.selectedValue = null; // Needs to be null to fake change\r
10778                                                         Event.cancel(e);\r
10779                                                         t.settings.onselect(v);\r
10780                                                 }\r
10781                                         });\r
10782                                 }\r
10783 \r
10784                                 t._focused = 1;\r
10785                         });\r
10786                         Event.add(t.id, 'blur', function() {\r
10787                                 Event.remove(t.id, 'keydown', t.keyDownHandler);\r
10788                                 Event.remove(t.id, 'keypress', t.keyPressHandler);\r
10789                                 t._focused = 0;\r
10790                         });\r
10791 \r
10792                         // Old IE doesn't have hover on all elements\r
10793                         if (tinymce.isIE6 || !DOM.boxModel) {\r
10794                                 Event.add(t.id, 'mouseover', function() {\r
10795                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
10796                                                 DOM.addClass(t.id, cp + 'Hover');\r
10797                                 });\r
10798 \r
10799                                 Event.add(t.id, 'mouseout', function() {\r
10800                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
10801                                                 DOM.removeClass(t.id, cp + 'Hover');\r
10802                                 });\r
10803                         }\r
10804 \r
10805                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
10806                 },\r
10807 \r
10808                 destroy : function() {\r
10809                         this.parent();\r
10810 \r
10811                         Event.clear(this.id + '_text');\r
10812                         Event.clear(this.id + '_open');\r
10813                 }\r
10814         });\r
10815 })(tinymce);\r
10816 \r
10817 (function(tinymce) {\r
10818         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
10819 \r
10820         tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {\r
10821                 NativeListBox : function(id, s) {\r
10822                         this.parent(id, s);\r
10823                         this.classPrefix = 'mceNativeListBox';\r
10824                 },\r
10825 \r
10826                 setDisabled : function(s) {\r
10827                         DOM.get(this.id).disabled = s;\r
10828                         this.setAriaProperty('disabled', s);\r
10829                 },\r
10830 \r
10831                 isDisabled : function() {\r
10832                         return DOM.get(this.id).disabled;\r
10833                 },\r
10834 \r
10835                 select : function(va) {\r
10836                         var t = this, fv, f;\r
10837 \r
10838                         if (va == undefined)\r
10839                                 return t.selectByIndex(-1);\r
10840 \r
10841                         // Is string or number make function selector\r
10842                         if (va && typeof(va)=="function")\r
10843                                 f = va;\r
10844                         else {\r
10845                                 f = function(v) {\r
10846                                         return v == va;\r
10847                                 };\r
10848                         }\r
10849 \r
10850                         // Do we need to do something?\r
10851                         if (va != t.selectedValue) {\r
10852                                 // Find item\r
10853                                 each(t.items, function(o, i) {\r
10854                                         if (f(o.value)) {\r
10855                                                 fv = 1;\r
10856                                                 t.selectByIndex(i);\r
10857                                                 return false;\r
10858                                         }\r
10859                                 });\r
10860 \r
10861                                 if (!fv)\r
10862                                         t.selectByIndex(-1);\r
10863                         }\r
10864                 },\r
10865 \r
10866                 selectByIndex : function(idx) {\r
10867                         DOM.get(this.id).selectedIndex = idx + 1;\r
10868                         this.selectedValue = this.items[idx] ? this.items[idx].value : null;\r
10869                 },\r
10870 \r
10871                 add : function(n, v, a) {\r
10872                         var o, t = this;\r
10873 \r
10874                         a = a || {};\r
10875                         a.value = v;\r
10876 \r
10877                         if (t.isRendered())\r
10878                                 DOM.add(DOM.get(this.id), 'option', a, n);\r
10879 \r
10880                         o = {\r
10881                                 title : n,\r
10882                                 value : v,\r
10883                                 attribs : a\r
10884                         };\r
10885 \r
10886                         t.items.push(o);\r
10887                         t.onAdd.dispatch(t, o);\r
10888                 },\r
10889 \r
10890                 getLength : function() {\r
10891                         return this.items.length;\r
10892                 },\r
10893 \r
10894                 renderHTML : function() {\r
10895                         var h, t = this;\r
10896 \r
10897                         h = DOM.createHTML('option', {value : ''}, '-- ' + t.settings.title + ' --');\r
10898 \r
10899                         each(t.items, function(it) {\r
10900                                 h += DOM.createHTML('option', {value : it.value}, it.title);\r
10901                         });\r
10902 \r
10903                         h = DOM.createHTML('select', {id : t.id, 'class' : 'mceNativeListBox', 'aria-labelledby': t.id + '_aria'}, h);\r
10904                         h += DOM.createHTML('span', {id : t.id + '_aria', 'style': 'display: none'}, t.settings.title);\r
10905                         return h;\r
10906                 },\r
10907 \r
10908                 postRender : function() {\r
10909                         var t = this, ch, changeListenerAdded = true;\r
10910 \r
10911                         t.rendered = true;\r
10912 \r
10913                         function onChange(e) {\r
10914                                 var v = t.items[e.target.selectedIndex - 1];\r
10915 \r
10916                                 if (v && (v = v.value)) {\r
10917                                         t.onChange.dispatch(t, v);\r
10918 \r
10919                                         if (t.settings.onselect)\r
10920                                                 t.settings.onselect(v);\r
10921                                 }\r
10922                         };\r
10923 \r
10924                         Event.add(t.id, 'change', onChange);\r
10925 \r
10926                         // Accessibility keyhandler\r
10927                         Event.add(t.id, 'keydown', function(e) {\r
10928                                 var bf;\r
10929 \r
10930                                 Event.remove(t.id, 'change', ch);\r
10931                                 changeListenerAdded = false;\r
10932 \r
10933                                 bf = Event.add(t.id, 'blur', function() {\r
10934                                         if (changeListenerAdded) return;\r
10935                                         changeListenerAdded = true;\r
10936                                         Event.add(t.id, 'change', onChange);\r
10937                                         Event.remove(t.id, 'blur', bf);\r
10938                                 });\r
10939 \r
10940                                 //prevent default left and right keys on chrome - so that the keyboard navigation is used.\r
10941                                 if (tinymce.isWebKit && (e.keyCode==37 ||e.keyCode==39)) {\r
10942                                         return Event.prevent(e);\r
10943                                 }\r
10944                                 \r
10945                                 if (e.keyCode == 13 || e.keyCode == 32) {\r
10946                                         onChange(e);\r
10947                                         return Event.cancel(e);\r
10948                                 }\r
10949                         });\r
10950 \r
10951                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
10952                 }\r
10953         });\r
10954 })(tinymce);\r
10955 \r
10956 (function(tinymce) {\r
10957         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
10958 \r
10959         tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', {\r
10960                 MenuButton : function(id, s, ed) {\r
10961                         this.parent(id, s, ed);\r
10962 \r
10963                         this.onRenderMenu = new tinymce.util.Dispatcher(this);\r
10964 \r
10965                         s.menu_container = s.menu_container || DOM.doc.body;\r
10966                 },\r
10967 \r
10968                 showMenu : function() {\r
10969                         var t = this, p1, p2, e = DOM.get(t.id), m;\r
10970 \r
10971                         if (t.isDisabled())\r
10972                                 return;\r
10973 \r
10974                         if (!t.isMenuRendered) {\r
10975                                 t.renderMenu();\r
10976                                 t.isMenuRendered = true;\r
10977                         }\r
10978 \r
10979                         if (t.isMenuVisible)\r
10980                                 return t.hideMenu();\r
10981 \r
10982                         p1 = DOM.getPos(t.settings.menu_container);\r
10983                         p2 = DOM.getPos(e);\r
10984 \r
10985                         m = t.menu;\r
10986                         m.settings.offset_x = p2.x;\r
10987                         m.settings.offset_y = p2.y;\r
10988                         m.settings.vp_offset_x = p2.x;\r
10989                         m.settings.vp_offset_y = p2.y;\r
10990                         m.settings.keyboard_focus = t._focused;\r
10991                         m.showMenu(0, e.clientHeight);\r
10992 \r
10993                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
10994                         t.setState('Selected', 1);\r
10995 \r
10996                         t.isMenuVisible = 1;\r
10997                 },\r
10998 \r
10999                 renderMenu : function() {\r
11000                         var t = this, m;\r
11001 \r
11002                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
11003                                 menu_line : 1,\r
11004                                 'class' : this.classPrefix + 'Menu',\r
11005                                 icons : t.settings.icons\r
11006                         });\r
11007 \r
11008                         m.onHideMenu.add(function() {\r
11009                                 t.hideMenu();\r
11010                                 t.focus();\r
11011                         });\r
11012 \r
11013                         t.onRenderMenu.dispatch(t, m);\r
11014                         t.menu = m;\r
11015                 },\r
11016 \r
11017                 hideMenu : function(e) {\r
11018                         var t = this;\r
11019 \r
11020                         // Prevent double toogles by canceling the mouse click event to the button\r
11021                         if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id || e.id === t.id + '_open';}))\r
11022                                 return;\r
11023 \r
11024                         if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
11025                                 t.setState('Selected', 0);\r
11026                                 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
11027                                 if (t.menu)\r
11028                                         t.menu.hideMenu();\r
11029                         }\r
11030 \r
11031                         t.isMenuVisible = 0;\r
11032                 },\r
11033 \r
11034                 postRender : function() {\r
11035                         var t = this, s = t.settings;\r
11036 \r
11037                         Event.add(t.id, 'click', function() {\r
11038                                 if (!t.isDisabled()) {\r
11039                                         if (s.onclick)\r
11040                                                 s.onclick(t.value);\r
11041 \r
11042                                         t.showMenu();\r
11043                                 }\r
11044                         });\r
11045                 }\r
11046         });\r
11047 })(tinymce);\r
11048 \r
11049 (function(tinymce) {\r
11050         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
11051 \r
11052         tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', {\r
11053                 SplitButton : function(id, s, ed) {\r
11054                         this.parent(id, s, ed);\r
11055                         this.classPrefix = 'mceSplitButton';\r
11056                 },\r
11057 \r
11058                 renderHTML : function() {\r
11059                         var h, t = this, s = t.settings, h1;\r
11060 \r
11061                         h = '<tbody><tr>';\r
11062 \r
11063                         if (s.image)\r
11064                                 h1 = DOM.createHTML('img ', {src : s.image, role: 'presentation', 'class' : 'mceAction ' + s['class']});\r
11065                         else\r
11066                                 h1 = DOM.createHTML('span', {'class' : 'mceAction ' + s['class']}, '');\r
11067 \r
11068                         h1 += DOM.createHTML('span', {'class': 'mceVoiceLabel mceIconOnly', id: t.id + '_voice', style: 'display:none;'}, s.title);\r
11069                         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
11070         \r
11071                         h1 = DOM.createHTML('span', {'class' : 'mceOpen ' + s['class']}, '<span style="display:none;" class="mceIconOnly" aria-hidden="true">\u25BC</span>');\r
11072                         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
11073 \r
11074                         h += '</tr></tbody>';\r
11075                         h = DOM.createHTML('table', { role: 'presentation',   'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', title : s.title}, h);\r
11076                         return DOM.createHTML('div', {id : t.id, role: 'button', tabindex: '0', 'aria-labelledby': t.id + '_voice', 'aria-haspopup': 'true'}, h);\r
11077                 },\r
11078 \r
11079                 postRender : function() {\r
11080                         var t = this, s = t.settings, activate;\r
11081 \r
11082                         if (s.onclick) {\r
11083                                 activate = function(evt) {\r
11084                                         if (!t.isDisabled()) {\r
11085                                                 s.onclick(t.value);\r
11086                                                 Event.cancel(evt);\r
11087                                         }\r
11088                                 };\r
11089                                 Event.add(t.id + '_action', 'click', activate);\r
11090                                 Event.add(t.id, ['click', 'keydown'], function(evt) {\r
11091                                         var DOM_VK_SPACE = 32, DOM_VK_ENTER = 14, DOM_VK_RETURN = 13, DOM_VK_UP = 38, DOM_VK_DOWN = 40;\r
11092                                         if ((evt.keyCode === 32 || evt.keyCode === 13 || evt.keyCode === 14) && !evt.altKey && !evt.ctrlKey && !evt.metaKey) {\r
11093                                                 activate();\r
11094                                                 Event.cancel(evt);\r
11095                                         } else if (evt.type === 'click' || evt.keyCode === DOM_VK_DOWN) {\r
11096                                                 t.showMenu();\r
11097                                                 Event.cancel(evt);\r
11098                                         }\r
11099                                 });\r
11100                         }\r
11101 \r
11102                         Event.add(t.id + '_open', 'click', function (evt) {\r
11103                                 t.showMenu();\r
11104                                 Event.cancel(evt);\r
11105                         });\r
11106                         Event.add([t.id, t.id + '_open'], 'focus', function() {t._focused = 1;});\r
11107                         Event.add([t.id, t.id + '_open'], 'blur', function() {t._focused = 0;});\r
11108 \r
11109                         // Old IE doesn't have hover on all elements\r
11110                         if (tinymce.isIE6 || !DOM.boxModel) {\r
11111                                 Event.add(t.id, 'mouseover', function() {\r
11112                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
11113                                                 DOM.addClass(t.id, 'mceSplitButtonHover');\r
11114                                 });\r
11115 \r
11116                                 Event.add(t.id, 'mouseout', function() {\r
11117                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
11118                                                 DOM.removeClass(t.id, 'mceSplitButtonHover');\r
11119                                 });\r
11120                         }\r
11121                 },\r
11122 \r
11123                 destroy : function() {\r
11124                         this.parent();\r
11125 \r
11126                         Event.clear(this.id + '_action');\r
11127                         Event.clear(this.id + '_open');\r
11128                         Event.clear(this.id);\r
11129                 }\r
11130         });\r
11131 })(tinymce);\r
11132 \r
11133 (function(tinymce) {\r
11134         var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each;\r
11135 \r
11136         tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', {\r
11137                 ColorSplitButton : function(id, s, ed) {\r
11138                         var t = this;\r
11139 \r
11140                         t.parent(id, s, ed);\r
11141 \r
11142                         t.settings = s = tinymce.extend({\r
11143                                 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
11144                                 grid_width : 8,\r
11145                                 default_color : '#888888'\r
11146                         }, t.settings);\r
11147 \r
11148                         t.onShowMenu = new tinymce.util.Dispatcher(t);\r
11149 \r
11150                         t.onHideMenu = new tinymce.util.Dispatcher(t);\r
11151 \r
11152                         t.value = s.default_color;\r
11153                 },\r
11154 \r
11155                 showMenu : function() {\r
11156                         var t = this, r, p, e, p2;\r
11157 \r
11158                         if (t.isDisabled())\r
11159                                 return;\r
11160 \r
11161                         if (!t.isMenuRendered) {\r
11162                                 t.renderMenu();\r
11163                                 t.isMenuRendered = true;\r
11164                         }\r
11165 \r
11166                         if (t.isMenuVisible)\r
11167                                 return t.hideMenu();\r
11168 \r
11169                         e = DOM.get(t.id);\r
11170                         DOM.show(t.id + '_menu');\r
11171                         DOM.addClass(e, 'mceSplitButtonSelected');\r
11172                         p2 = DOM.getPos(e);\r
11173                         DOM.setStyles(t.id + '_menu', {\r
11174                                 left : p2.x,\r
11175                                 top : p2.y + e.clientHeight,\r
11176                                 zIndex : 200000\r
11177                         });\r
11178                         e = 0;\r
11179 \r
11180                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
11181                         t.onShowMenu.dispatch(t);\r
11182 \r
11183                         if (t._focused) {\r
11184                                 t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) {\r
11185                                         if (e.keyCode == 27)\r
11186                                                 t.hideMenu();\r
11187                                 });\r
11188 \r
11189                                 DOM.select('a', t.id + '_menu')[0].focus(); // Select first link\r
11190                         }\r
11191 \r
11192                         t.isMenuVisible = 1;\r
11193                 },\r
11194 \r
11195                 hideMenu : function(e) {\r
11196                         var t = this;\r
11197 \r
11198                         if (t.isMenuVisible) {\r
11199                                 // Prevent double toogles by canceling the mouse click event to the button\r
11200                                 if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';}))\r
11201                                         return;\r
11202 \r
11203                                 if (!e || !DOM.getParent(e.target, '.mceSplitButtonMenu')) {\r
11204                                         DOM.removeClass(t.id, 'mceSplitButtonSelected');\r
11205                                         Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
11206                                         Event.remove(t.id + '_menu', 'keydown', t._keyHandler);\r
11207                                         DOM.hide(t.id + '_menu');\r
11208                                 }\r
11209 \r
11210                                 t.isMenuVisible = 0;\r
11211                                 t.onHideMenu.dispatch();\r
11212                         }\r
11213                 },\r
11214 \r
11215                 renderMenu : function() {\r
11216                         var t = this, m, i = 0, s = t.settings, n, tb, tr, w, context;\r
11217 \r
11218                         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
11219                         m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'});\r
11220                         DOM.add(m, 'span', {'class' : 'mceMenuLine'});\r
11221 \r
11222                         n = DOM.add(m, 'table', {role: 'presentation', 'class' : 'mceColorSplitMenu'});\r
11223                         tb = DOM.add(n, 'tbody');\r
11224 \r
11225                         // Generate color grid\r
11226                         i = 0;\r
11227                         each(is(s.colors, 'array') ? s.colors : s.colors.split(','), function(c) {\r
11228                                 c = c.replace(/^#/, '');\r
11229 \r
11230                                 if (!i--) {\r
11231                                         tr = DOM.add(tb, 'tr');\r
11232                                         i = s.grid_width - 1;\r
11233                                 }\r
11234 \r
11235                                 n = DOM.add(tr, 'td');\r
11236                                 var settings = {\r
11237                                         href : 'javascript:;',\r
11238                                         style : {\r
11239                                                 backgroundColor : '#' + c\r
11240                                         },\r
11241                                         'title': t.editor.getLang('colors.' + c, c),\r
11242                                         'data-mce-color' : '#' + c\r
11243                                 };\r
11244 \r
11245                                 // adding a proper ARIA role = button causes JAWS to read things incorrectly on IE.\r
11246                                 if (!tinymce.isIE ) {\r
11247                                         settings['role']= 'option';\r
11248                                 }\r
11249 \r
11250                                 n = DOM.add(n, 'a', settings);\r
11251 \r
11252                                 if (t.editor.forcedHighContrastMode) {\r
11253                                         n = DOM.add(n, 'canvas', { width: 16, height: 16, 'aria-hidden': 'true' });\r
11254                                         if (n.getContext && (context = n.getContext("2d"))) {\r
11255                                                 context.fillStyle = '#' + c;\r
11256                                                 context.fillRect(0, 0, 16, 16);\r
11257                                         } else {\r
11258                                                 // No point leaving a canvas element around if it's not supported for drawing on anyway.\r
11259                                                 DOM.remove(n);\r
11260                                         }\r
11261                                 }\r
11262                         });\r
11263 \r
11264                         if (s.more_colors_func) {\r
11265                                 n = DOM.add(tb, 'tr');\r
11266                                 n = DOM.add(n, 'td', {colspan : s.grid_width, 'class' : 'mceMoreColors'});\r
11267                                 n = DOM.add(n, 'a', {role: 'option', id : t.id + '_more', href : 'javascript:;', onclick : 'return false;', 'class' : 'mceMoreColors'}, s.more_colors_title);\r
11268 \r
11269                                 Event.add(n, 'click', function(e) {\r
11270                                         s.more_colors_func.call(s.more_colors_scope || this);\r
11271                                         return Event.cancel(e); // Cancel to fix onbeforeunload problem\r
11272                                 });\r
11273                         }\r
11274 \r
11275                         DOM.addClass(m, 'mceColorSplitMenu');\r
11276                         \r
11277                         new tinymce.ui.KeyboardNavigation({\r
11278                                 root: t.id + '_menu',\r
11279                                 items: DOM.select('a', t.id + '_menu'),\r
11280                                 onCancel: function() {\r
11281                                         t.hideMenu();\r
11282                                         t.focus();\r
11283                                 }\r
11284                         });\r
11285 \r
11286                         // Prevent IE from scrolling and hindering click to occur #4019\r
11287                         Event.add(t.id + '_menu', 'mousedown', function(e) {return Event.cancel(e);});\r
11288 \r
11289                         Event.add(t.id + '_menu', 'click', function(e) {\r
11290                                 var c;\r
11291 \r
11292                                 e = DOM.getParent(e.target, 'a', tb);\r
11293 \r
11294                                 if (e && e.nodeName.toLowerCase() == 'a' && (c = e.getAttribute('data-mce-color')))\r
11295                                         t.setColor(c);\r
11296 \r
11297                                 return false; // Prevent IE auto save warning\r
11298                         });\r
11299 \r
11300                         return w;\r
11301                 },\r
11302 \r
11303                 setColor : function(c) {\r
11304                         this.displayColor(c);\r
11305                         this.hideMenu();\r
11306                         this.settings.onselect(c);\r
11307                 },\r
11308                 \r
11309                 displayColor : function(c) {\r
11310                         var t = this;\r
11311 \r
11312                         DOM.setStyle(t.id + '_preview', 'backgroundColor', c);\r
11313 \r
11314                         t.value = c;\r
11315                 },\r
11316 \r
11317                 postRender : function() {\r
11318                         var t = this, id = t.id;\r
11319 \r
11320                         t.parent();\r
11321                         DOM.add(id + '_action', 'div', {id : id + '_preview', 'class' : 'mceColorPreview'});\r
11322                         DOM.setStyle(t.id + '_preview', 'backgroundColor', t.value);\r
11323                 },\r
11324 \r
11325                 destroy : function() {\r
11326                         this.parent();\r
11327 \r
11328                         Event.clear(this.id + '_menu');\r
11329                         Event.clear(this.id + '_more');\r
11330                         DOM.remove(this.id + '_menu');\r
11331                 }\r
11332         });\r
11333 })(tinymce);\r
11334 \r
11335 (function(tinymce) {\r
11336 // Shorten class names\r
11337 var dom = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event;\r
11338 tinymce.create('tinymce.ui.ToolbarGroup:tinymce.ui.Container', {\r
11339         renderHTML : function() {\r
11340                 var t = this, h = [], controls = t.controls, each = tinymce.each, settings = t.settings;\r
11341 \r
11342                 h.push('<div id="' + t.id + '" role="group" aria-labelledby="' + t.id + '_voice">');\r
11343                 //TODO: ACC test this out - adding a role = application for getting the landmarks working well.\r
11344                 h.push("<span role='application'>");\r
11345                 h.push('<span id="' + t.id + '_voice" class="mceVoiceLabel" style="display:none;">' + dom.encode(settings.name) + '</span>');\r
11346                 each(controls, function(toolbar) {\r
11347                         h.push(toolbar.renderHTML());\r
11348                 });\r
11349                 h.push("</span>");\r
11350                 h.push('</div>');\r
11351 \r
11352                 return h.join('');\r
11353         },\r
11354         \r
11355         focus : function() {\r
11356                 var t = this;\r
11357                 dom.get(t.id).focus();\r
11358         },\r
11359         \r
11360         postRender : function() {\r
11361                 var t = this, items = [];\r
11362 \r
11363                 each(t.controls, function(toolbar) {\r
11364                         each (toolbar.controls, function(control) {\r
11365                                 if (control.id) {\r
11366                                         items.push(control);\r
11367                                 }\r
11368                         });\r
11369                 });\r
11370 \r
11371                 t.keyNav = new tinymce.ui.KeyboardNavigation({\r
11372                         root: t.id,\r
11373                         items: items,\r
11374                         onCancel: function() {\r
11375                                 //Move focus if webkit so that navigation back will read the item.\r
11376                                 if (tinymce.isWebKit) {\r
11377                                         dom.get(t.editor.id+"_ifr").focus();\r
11378                                 }\r
11379                                 t.editor.focus();\r
11380                         },\r
11381                         excludeFromTabOrder: !t.settings.tab_focus_toolbar\r
11382                 });\r
11383         },\r
11384         \r
11385         destroy : function() {\r
11386                 var self = this;\r
11387 \r
11388                 self.parent();\r
11389                 self.keyNav.destroy();\r
11390                 Event.clear(self.id);\r
11391         }\r
11392 });\r
11393 })(tinymce);\r
11394 \r
11395 (function(tinymce) {\r
11396 // Shorten class names\r
11397 var dom = tinymce.DOM, each = tinymce.each;\r
11398 tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {\r
11399         renderHTML : function() {\r
11400                 var t = this, h = '', c, co, s = t.settings, i, pr, nx, cl;\r
11401 \r
11402                 cl = t.controls;\r
11403                 for (i=0; i<cl.length; i++) {\r
11404                         // Get current control, prev control, next control and if the control is a list box or not\r
11405                         co = cl[i];\r
11406                         pr = cl[i - 1];\r
11407                         nx = cl[i + 1];\r
11408 \r
11409                         // Add toolbar start\r
11410                         if (i === 0) {\r
11411                                 c = 'mceToolbarStart';\r
11412 \r
11413                                 if (co.Button)\r
11414                                         c += ' mceToolbarStartButton';\r
11415                                 else if (co.SplitButton)\r
11416                                         c += ' mceToolbarStartSplitButton';\r
11417                                 else if (co.ListBox)\r
11418                                         c += ' mceToolbarStartListBox';\r
11419 \r
11420                                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
11421                         }\r
11422 \r
11423                         // Add toolbar end before list box and after the previous button\r
11424                         // This is to fix the o2k7 editor skins\r
11425                         if (pr && co.ListBox) {\r
11426                                 if (pr.Button || pr.SplitButton)\r
11427                                         h += dom.createHTML('td', {'class' : 'mceToolbarEnd'}, dom.createHTML('span', null, '<!-- IE -->'));\r
11428                         }\r
11429 \r
11430                         // Render control HTML\r
11431 \r
11432                         // IE 8 quick fix, needed to propertly generate a hit area for anchors\r
11433                         if (dom.stdMode)\r
11434                                 h += '<td style="position: relative">' + co.renderHTML() + '</td>';\r
11435                         else\r
11436                                 h += '<td>' + co.renderHTML() + '</td>';\r
11437 \r
11438                         // Add toolbar start after list box and before the next button\r
11439                         // This is to fix the o2k7 editor skins\r
11440                         if (nx && co.ListBox) {\r
11441                                 if (nx.Button || nx.SplitButton)\r
11442                                         h += dom.createHTML('td', {'class' : 'mceToolbarStart'}, dom.createHTML('span', null, '<!-- IE -->'));\r
11443                         }\r
11444                 }\r
11445 \r
11446                 c = 'mceToolbarEnd';\r
11447 \r
11448                 if (co.Button)\r
11449                         c += ' mceToolbarEndButton';\r
11450                 else if (co.SplitButton)\r
11451                         c += ' mceToolbarEndSplitButton';\r
11452                 else if (co.ListBox)\r
11453                         c += ' mceToolbarEndListBox';\r
11454 \r
11455                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
11456 \r
11457                 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
11458         }\r
11459 });\r
11460 })(tinymce);\r
11461 \r
11462 (function(tinymce) {\r
11463         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;\r
11464 \r
11465         tinymce.create('tinymce.AddOnManager', {\r
11466                 AddOnManager : function() {\r
11467                         var self = this;\r
11468 \r
11469                         self.items = [];\r
11470                         self.urls = {};\r
11471                         self.lookup = {};\r
11472                         self.onAdd = new Dispatcher(self);\r
11473                 },\r
11474 \r
11475                 get : function(n) {\r
11476                         if (this.lookup[n]) {\r
11477                                 return this.lookup[n].instance;\r
11478                         } else {\r
11479                                 return undefined;\r
11480                         }\r
11481                 },\r
11482 \r
11483                 dependencies : function(n) {\r
11484                         var result;\r
11485                         if (this.lookup[n]) {\r
11486                                 result = this.lookup[n].dependencies;\r
11487                         }\r
11488                         return result || [];\r
11489                 },\r
11490 \r
11491                 requireLangPack : function(n) {\r
11492                         var s = tinymce.settings;\r
11493 \r
11494                         if (s && s.language && s.language_load !== false)\r
11495                                 tinymce.ScriptLoader.add(this.urls[n] + '/langs/' + s.language + '.js');\r
11496                 },\r
11497 \r
11498                 add : function(id, o, dependencies) {\r
11499                         this.items.push(o);\r
11500                         this.lookup[id] = {instance:o, dependencies:dependencies};\r
11501                         this.onAdd.dispatch(this, id, o);\r
11502 \r
11503                         return o;\r
11504                 },\r
11505                 createUrl: function(baseUrl, dep) {\r
11506                         if (typeof dep === "object") {\r
11507                                 return dep\r
11508                         } else {\r
11509                                 return {prefix: baseUrl.prefix, resource: dep, suffix: baseUrl.suffix};\r
11510                         }\r
11511                 },\r
11512 \r
11513                 addComponents: function(pluginName, scripts) {\r
11514                         var pluginUrl = this.urls[pluginName];\r
11515                         tinymce.each(scripts, function(script){\r
11516                                 tinymce.ScriptLoader.add(pluginUrl+"/"+script); \r
11517                         });\r
11518                 },\r
11519 \r
11520                 load : function(n, u, cb, s) {\r
11521                         var t = this, url = u;\r
11522 \r
11523                         function loadDependencies() {\r
11524                                 var dependencies = t.dependencies(n);\r
11525                                 tinymce.each(dependencies, function(dep) {\r
11526                                         var newUrl = t.createUrl(u, dep);\r
11527                                         t.load(newUrl.resource, newUrl, undefined, undefined);\r
11528                                 });\r
11529                                 if (cb) {\r
11530                                         if (s) {\r
11531                                                 cb.call(s);\r
11532                                         } else {\r
11533                                                 cb.call(tinymce.ScriptLoader);\r
11534                                         }\r
11535                                 }\r
11536                         }\r
11537 \r
11538                         if (t.urls[n])\r
11539                                 return;\r
11540                         if (typeof u === "object")\r
11541                                 url = u.prefix + u.resource + u.suffix;\r
11542 \r
11543                         if (url.indexOf('/') != 0 && url.indexOf('://') == -1)\r
11544                                 url = tinymce.baseURL + '/' + url;\r
11545 \r
11546                         t.urls[n] = url.substring(0, url.lastIndexOf('/'));\r
11547 \r
11548                         if (t.lookup[n]) {\r
11549                                 loadDependencies();\r
11550                         } else {\r
11551                                 tinymce.ScriptLoader.add(url, loadDependencies, s);\r
11552                         }\r
11553                 }\r
11554         });\r
11555 \r
11556         // Create plugin and theme managers\r
11557         tinymce.PluginManager = new tinymce.AddOnManager();\r
11558         tinymce.ThemeManager = new tinymce.AddOnManager();\r
11559 }(tinymce));\r
11560 \r
11561 (function(tinymce) {\r
11562         // Shorten names\r
11563         var each = tinymce.each, extend = tinymce.extend,\r
11564                 DOM = tinymce.DOM, Event = tinymce.dom.Event,\r
11565                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
11566                 explode = tinymce.explode,\r
11567                 Dispatcher = tinymce.util.Dispatcher, undefined, instanceCounter = 0;\r
11568 \r
11569         // Setup some URLs where the editor API is located and where the document is\r
11570         tinymce.documentBaseURL = window.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');\r
11571         if (!/[\/\\]$/.test(tinymce.documentBaseURL))\r
11572                 tinymce.documentBaseURL += '/';\r
11573 \r
11574         tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL);\r
11575 \r
11576         tinymce.baseURI = new tinymce.util.URI(tinymce.baseURL);\r
11577 \r
11578         // Add before unload listener\r
11579         // This was required since IE was leaking memory if you added and removed beforeunload listeners\r
11580         // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event\r
11581         tinymce.onBeforeUnload = new Dispatcher(tinymce);\r
11582 \r
11583         // Must be on window or IE will leak if the editor is placed in frame or iframe\r
11584         Event.add(window, 'beforeunload', function(e) {\r
11585                 tinymce.onBeforeUnload.dispatch(tinymce, e);\r
11586         });\r
11587 \r
11588         tinymce.onAddEditor = new Dispatcher(tinymce);\r
11589 \r
11590         tinymce.onRemoveEditor = new Dispatcher(tinymce);\r
11591 \r
11592         tinymce.EditorManager = extend(tinymce, {\r
11593                 editors : [],\r
11594 \r
11595                 i18n : {},\r
11596 \r
11597                 activeEditor : null,\r
11598 \r
11599                 init : function(s) {\r
11600                         var t = this, pl, sl = tinymce.ScriptLoader, e, el = [], ed;\r
11601 \r
11602                         function createId(elm) {\r
11603                                 var id = elm.id;\r
11604         \r
11605                                 // Use element id, or unique name or generate a unique id\r
11606                                 if (!id) {\r
11607                                         id = elm.name;\r
11608         \r
11609                                         if (id && !DOM.get(id)) {\r
11610                                                 id = elm.name;\r
11611                                         } else {\r
11612                                                 // Generate unique name\r
11613                                                 id = DOM.uniqueId();\r
11614                                         }\r
11615 \r
11616                                         elm.setAttribute('id', id);\r
11617                                 }\r
11618 \r
11619                                 return id;\r
11620                         };\r
11621 \r
11622                         function execCallback(se, n, s) {\r
11623                                 var f = se[n];\r
11624 \r
11625                                 if (!f)\r
11626                                         return;\r
11627 \r
11628                                 if (tinymce.is(f, 'string')) {\r
11629                                         s = f.replace(/\.\w+$/, '');\r
11630                                         s = s ? tinymce.resolve(s) : 0;\r
11631                                         f = tinymce.resolve(f);\r
11632                                 }\r
11633 \r
11634                                 return f.apply(s || this, Array.prototype.slice.call(arguments, 2));\r
11635                         };\r
11636 \r
11637                         s = extend({\r
11638                                 theme : "simple",\r
11639                                 language : "en"\r
11640                         }, s);\r
11641 \r
11642                         t.settings = s;\r
11643 \r
11644                         // Legacy call\r
11645                         Event.bind(window, 'ready', function() {\r
11646                                 var l, co;\r
11647 \r
11648                                 execCallback(s, 'onpageload');\r
11649 \r
11650                                 switch (s.mode) {\r
11651                                         case "exact":\r
11652                                                 l = s.elements || '';\r
11653 \r
11654                                                 if(l.length > 0) {\r
11655                                                         each(explode(l), function(v) {\r
11656                                                                 if (DOM.get(v)) {\r
11657                                                                         ed = new tinymce.Editor(v, s);\r
11658                                                                         el.push(ed);\r
11659                                                                         ed.render(1);\r
11660                                                                 } else {\r
11661                                                                         each(document.forms, function(f) {\r
11662                                                                                 each(f.elements, function(e) {\r
11663                                                                                         if (e.name === v) {\r
11664                                                                                                 v = 'mce_editor_' + instanceCounter++;\r
11665                                                                                                 DOM.setAttrib(e, 'id', v);\r
11666 \r
11667                                                                                                 ed = new tinymce.Editor(v, s);\r
11668                                                                                                 el.push(ed);\r
11669                                                                                                 ed.render(1);\r
11670                                                                                         }\r
11671                                                                                 });\r
11672                                                                         });\r
11673                                                                 }\r
11674                                                         });\r
11675                                                 }\r
11676                                                 break;\r
11677 \r
11678                                         case "textareas":\r
11679                                         case "specific_textareas":\r
11680                                                 function hasClass(n, c) {\r
11681                                                         return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);\r
11682                                                 };\r
11683 \r
11684                                                 each(DOM.select('textarea'), function(elm) {\r
11685                                                         if (s.editor_deselector && hasClass(elm, s.editor_deselector))\r
11686                                                                 return;\r
11687 \r
11688                                                         if (!s.editor_selector || hasClass(elm, s.editor_selector)) {\r
11689                                                                 ed = new tinymce.Editor(createId(elm), s);\r
11690                                                                 el.push(ed);\r
11691                                                                 ed.render(1);\r
11692                                                         }\r
11693                                                 });\r
11694                                                 break;\r
11695                                         \r
11696                                         default:\r
11697                                                 if (s.types) {\r
11698                                                         // Process type specific selector\r
11699                                                         each(s.types, function(type) {\r
11700                                                                 each(DOM.select(type.selector), function(elm) {\r
11701                                                                         var editor = new tinymce.Editor(createId(elm), tinymce.extend({}, s, type));\r
11702                                                                         el.push(editor);\r
11703                                                                         editor.render(1);\r
11704                                                                 });\r
11705                                                         });\r
11706                                                 } else if (s.selector) {\r
11707                                                         // Process global selector\r
11708                                                         each(DOM.select(s.selector), function(elm) {\r
11709                                                                 var editor = new tinymce.Editor(createId(elm), s);\r
11710                                                                 el.push(editor);\r
11711                                                                 editor.render(1);\r
11712                                                         });\r
11713                                                 }\r
11714                                 }\r
11715 \r
11716                                 // Call onInit when all editors are initialized\r
11717                                 if (s.oninit) {\r
11718                                         l = co = 0;\r
11719 \r
11720                                         each(el, function(ed) {\r
11721                                                 co++;\r
11722 \r
11723                                                 if (!ed.initialized) {\r
11724                                                         // Wait for it\r
11725                                                         ed.onInit.add(function() {\r
11726                                                                 l++;\r
11727 \r
11728                                                                 // All done\r
11729                                                                 if (l == co)\r
11730                                                                         execCallback(s, 'oninit');\r
11731                                                         });\r
11732                                                 } else\r
11733                                                         l++;\r
11734 \r
11735                                                 // All done\r
11736                                                 if (l == co)\r
11737                                                         execCallback(s, 'oninit');                                      \r
11738                                         });\r
11739                                 }\r
11740                         });\r
11741                 },\r
11742 \r
11743                 get : function(id) {\r
11744                         if (id === undefined)\r
11745                                 return this.editors;\r
11746 \r
11747                         return this.editors[id];\r
11748                 },\r
11749 \r
11750                 getInstanceById : function(id) {\r
11751                         return this.get(id);\r
11752                 },\r
11753 \r
11754                 add : function(editor) {\r
11755                         var self = this, editors = self.editors;\r
11756 \r
11757                         // Add named and index editor instance\r
11758                         editors[editor.id] = editor;\r
11759                         editors.push(editor);\r
11760 \r
11761                         self._setActive(editor);\r
11762                         self.onAddEditor.dispatch(self, editor);\r
11763 \r
11764 \r
11765                         return editor;\r
11766                 },\r
11767 \r
11768                 remove : function(editor) {\r
11769                         var t = this, i, editors = t.editors;\r
11770 \r
11771                         // Not in the collection\r
11772                         if (!editors[editor.id])\r
11773                                 return null;\r
11774 \r
11775                         delete editors[editor.id];\r
11776 \r
11777                         for (i = 0; i < editors.length; i++) {\r
11778                                 if (editors[i] == editor) {\r
11779                                         editors.splice(i, 1);\r
11780                                         break;\r
11781                                 }\r
11782                         }\r
11783 \r
11784                         // Select another editor since the active one was removed\r
11785                         if (t.activeEditor == editor)\r
11786                                 t._setActive(editors[0]);\r
11787 \r
11788                         editor.destroy();\r
11789                         t.onRemoveEditor.dispatch(t, editor);\r
11790 \r
11791                         return editor;\r
11792                 },\r
11793 \r
11794                 execCommand : function(c, u, v) {\r
11795                         var t = this, ed = t.get(v), w;\r
11796 \r
11797                         // Manager commands\r
11798                         switch (c) {\r
11799                                 case "mceFocus":\r
11800                                         ed.focus();\r
11801                                         return true;\r
11802 \r
11803                                 case "mceAddEditor":\r
11804                                 case "mceAddControl":\r
11805                                         if (!t.get(v))\r
11806                                                 new tinymce.Editor(v, t.settings).render();\r
11807 \r
11808                                         return true;\r
11809 \r
11810                                 case "mceAddFrameControl":\r
11811                                         w = v.window;\r
11812 \r
11813                                         // Add tinyMCE global instance and tinymce namespace to specified window\r
11814                                         w.tinyMCE = tinyMCE;\r
11815                                         w.tinymce = tinymce;\r
11816 \r
11817                                         tinymce.DOM.doc = w.document;\r
11818                                         tinymce.DOM.win = w;\r
11819 \r
11820                                         ed = new tinymce.Editor(v.element_id, v);\r
11821                                         ed.render();\r
11822 \r
11823                                         // Fix IE memory leaks\r
11824                                         if (tinymce.isIE) {\r
11825                                                 function clr() {\r
11826                                                         ed.destroy();\r
11827                                                         w.detachEvent('onunload', clr);\r
11828                                                         w = w.tinyMCE = w.tinymce = null; // IE leak\r
11829                                                 };\r
11830 \r
11831                                                 w.attachEvent('onunload', clr);\r
11832                                         }\r
11833 \r
11834                                         v.page_window = null;\r
11835 \r
11836                                         return true;\r
11837 \r
11838                                 case "mceRemoveEditor":\r
11839                                 case "mceRemoveControl":\r
11840                                         if (ed)\r
11841                                                 ed.remove();\r
11842 \r
11843                                         return true;\r
11844 \r
11845                                 case 'mceToggleEditor':\r
11846                                         if (!ed) {\r
11847                                                 t.execCommand('mceAddControl', 0, v);\r
11848                                                 return true;\r
11849                                         }\r
11850 \r
11851                                         if (ed.isHidden())\r
11852                                                 ed.show();\r
11853                                         else\r
11854                                                 ed.hide();\r
11855 \r
11856                                         return true;\r
11857                         }\r
11858 \r
11859                         // Run command on active editor\r
11860                         if (t.activeEditor)\r
11861                                 return t.activeEditor.execCommand(c, u, v);\r
11862 \r
11863                         return false;\r
11864                 },\r
11865 \r
11866                 execInstanceCommand : function(id, c, u, v) {\r
11867                         var ed = this.get(id);\r
11868 \r
11869                         if (ed)\r
11870                                 return ed.execCommand(c, u, v);\r
11871 \r
11872                         return false;\r
11873                 },\r
11874 \r
11875                 triggerSave : function() {\r
11876                         each(this.editors, function(e) {\r
11877                                 e.save();\r
11878                         });\r
11879                 },\r
11880 \r
11881                 addI18n : function(p, o) {\r
11882                         var lo, i18n = this.i18n;\r
11883 \r
11884                         if (!tinymce.is(p, 'string')) {\r
11885                                 each(p, function(o, lc) {\r
11886                                         each(o, function(o, g) {\r
11887                                                 each(o, function(o, k) {\r
11888                                                         if (g === 'common')\r
11889                                                                 i18n[lc + '.' + k] = o;\r
11890                                                         else\r
11891                                                                 i18n[lc + '.' + g + '.' + k] = o;\r
11892                                                 });\r
11893                                         });\r
11894                                 });\r
11895                         } else {\r
11896                                 each(o, function(o, k) {\r
11897                                         i18n[p + '.' + k] = o;\r
11898                                 });\r
11899                         }\r
11900                 },\r
11901 \r
11902                 // Private methods\r
11903 \r
11904                 _setActive : function(editor) {\r
11905                         this.selectedInstance = this.activeEditor = editor;\r
11906                 }\r
11907         });\r
11908 })(tinymce);\r
11909 \r
11910 (function(tinymce) {\r
11911         // Shorten these names\r
11912         var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend,\r
11913                 Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isGecko = tinymce.isGecko,\r
11914                 isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, is = tinymce.is,\r
11915                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
11916                 inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode, VK = tinymce.VK;\r
11917 \r
11918         tinymce.create('tinymce.Editor', {\r
11919                 Editor : function(id, s) {\r
11920                         var t = this;\r
11921 \r
11922                         t.id = t.editorId = id;\r
11923 \r
11924                         t.execCommands = {};\r
11925                         t.queryStateCommands = {};\r
11926                         t.queryValueCommands = {};\r
11927 \r
11928                         t.isNotDirty = false;\r
11929 \r
11930                         t.plugins = {};\r
11931 \r
11932                         // Add events to the editor\r
11933                         each([\r
11934                                 'onPreInit',\r
11935 \r
11936                                 'onBeforeRenderUI',\r
11937 \r
11938                                 'onPostRender',\r
11939 \r
11940                                 'onLoad',\r
11941 \r
11942                                 'onInit',\r
11943 \r
11944                                 'onRemove',\r
11945 \r
11946                                 'onActivate',\r
11947 \r
11948                                 'onDeactivate',\r
11949 \r
11950                                 'onClick',\r
11951 \r
11952                                 'onEvent',\r
11953 \r
11954                                 'onMouseUp',\r
11955 \r
11956                                 'onMouseDown',\r
11957 \r
11958                                 'onDblClick',\r
11959 \r
11960                                 'onKeyDown',\r
11961 \r
11962                                 'onKeyUp',\r
11963 \r
11964                                 'onKeyPress',\r
11965 \r
11966                                 'onContextMenu',\r
11967 \r
11968                                 'onSubmit',\r
11969 \r
11970                                 'onReset',\r
11971 \r
11972                                 'onPaste',\r
11973 \r
11974                                 'onPreProcess',\r
11975 \r
11976                                 'onPostProcess',\r
11977 \r
11978                                 'onBeforeSetContent',\r
11979 \r
11980                                 'onBeforeGetContent',\r
11981 \r
11982                                 'onSetContent',\r
11983 \r
11984                                 'onGetContent',\r
11985 \r
11986                                 'onLoadContent',\r
11987 \r
11988                                 'onSaveContent',\r
11989 \r
11990                                 'onNodeChange',\r
11991 \r
11992                                 'onChange',\r
11993 \r
11994                                 'onBeforeExecCommand',\r
11995 \r
11996                                 'onExecCommand',\r
11997 \r
11998                                 'onUndo',\r
11999 \r
12000                                 'onRedo',\r
12001 \r
12002                                 'onVisualAid',\r
12003 \r
12004                                 'onSetProgressState',\r
12005 \r
12006                                 'onSetAttrib'\r
12007                         ], function(e) {\r
12008                                 t[e] = new Dispatcher(t);\r
12009                         });\r
12010 \r
12011                         t.settings = s = extend({\r
12012                                 id : id,\r
12013                                 language : 'en',\r
12014                                 docs_language : 'en',\r
12015                                 theme : 'simple',\r
12016                                 skin : 'default',\r
12017                                 delta_width : 0,\r
12018                                 delta_height : 0,\r
12019                                 popup_css : '',\r
12020                                 plugins : '',\r
12021                                 document_base_url : tinymce.documentBaseURL,\r
12022                                 add_form_submit_trigger : 1,\r
12023                                 submit_patch : 1,\r
12024                                 add_unload_trigger : 1,\r
12025                                 convert_urls : 1,\r
12026                                 relative_urls : 1,\r
12027                                 remove_script_host : 1,\r
12028                                 table_inline_editing : 0,\r
12029                                 object_resizing : 1,\r
12030                                 cleanup : 1,\r
12031                                 accessibility_focus : 1,\r
12032                                 custom_shortcuts : 1,\r
12033                                 custom_undo_redo_keyboard_shortcuts : 1,\r
12034                                 custom_undo_redo_restore_selection : 1,\r
12035                                 custom_undo_redo : 1,\r
12036                                 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
12037                                 visual_table_class : 'mceItemTable',\r
12038                                 visual : 1,\r
12039                                 font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',\r
12040                                 font_size_legacy_values : 'xx-small,small,medium,large,x-large,xx-large,300%', // See: http://www.w3.org/TR/CSS2/fonts.html#propdef-font-size\r
12041                                 apply_source_formatting : 1,\r
12042                                 directionality : 'ltr',\r
12043                                 forced_root_block : 'p',\r
12044                                 hidden_input : 1,\r
12045                                 padd_empty_editor : 1,\r
12046                                 render_ui : 1,\r
12047                                 init_theme : 1,\r
12048                                 force_p_newlines : 1,\r
12049                                 indentation : '30px',\r
12050                                 keep_styles : 1,\r
12051                                 fix_table_elements : 1,\r
12052                                 inline_styles : 1,\r
12053                                 convert_fonts_to_spans : true,\r
12054                                 indent : 'simple',\r
12055                                 indent_before : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside',\r
12056                                 indent_after : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside',\r
12057                                 validate : true,\r
12058                                 entity_encoding : 'named',\r
12059                                 url_converter : t.convertURL,\r
12060                                 url_converter_scope : t,\r
12061                                 ie7_compat : true\r
12062                         }, s);\r
12063 \r
12064                         t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {\r
12065                                 base_uri : tinyMCE.baseURI\r
12066                         });\r
12067 \r
12068                         t.baseURI = tinymce.baseURI;\r
12069 \r
12070                         t.contentCSS = [];\r
12071 \r
12072                         // Call setup\r
12073                         t.execCallback('setup', t);\r
12074                 },\r
12075 \r
12076                 render : function(nst) {\r
12077                         var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader;\r
12078 \r
12079                         // Page is not loaded yet, wait for it\r
12080                         if (!Event.domLoaded) {\r
12081                                 Event.add(window, 'ready', function() {\r
12082                                         t.render();\r
12083                                 });\r
12084                                 return;\r
12085                         }\r
12086 \r
12087                         tinyMCE.settings = s;\r
12088 \r
12089                         // Element not found, then skip initialization\r
12090                         if (!t.getElement())\r
12091                                 return;\r
12092 \r
12093                         // Is a iPad/iPhone and not on iOS5, then skip initialization. We need to sniff \r
12094                         // here since the browser says it has contentEditable support but there is no visible\r
12095                         // caret We will remove this check ones Apple implements full contentEditable support\r
12096                         if (tinymce.isIDevice && !tinymce.isIOS5)\r
12097                                 return;\r
12098 \r
12099                         // Add hidden input for non input elements inside form elements\r
12100                         if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))\r
12101                                 DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);\r
12102 \r
12103                         if (tinymce.WindowManager)\r
12104                                 t.windowManager = new tinymce.WindowManager(t);\r
12105 \r
12106                         if (s.encoding == 'xml') {\r
12107                                 t.onGetContent.add(function(ed, o) {\r
12108                                         if (o.save)\r
12109                                                 o.content = DOM.encode(o.content);\r
12110                                 });\r
12111                         }\r
12112 \r
12113                         if (s.add_form_submit_trigger) {\r
12114                                 t.onSubmit.addToTop(function() {\r
12115                                         if (t.initialized) {\r
12116                                                 t.save();\r
12117                                                 t.isNotDirty = 1;\r
12118                                         }\r
12119                                 });\r
12120                         }\r
12121 \r
12122                         if (s.add_unload_trigger) {\r
12123                                 t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {\r
12124                                         if (t.initialized && !t.destroyed && !t.isHidden())\r
12125                                                 t.save({format : 'raw', no_events : true});\r
12126                                 });\r
12127                         }\r
12128 \r
12129                         tinymce.addUnload(t.destroy, t);\r
12130 \r
12131                         if (s.submit_patch) {\r
12132                                 t.onBeforeRenderUI.add(function() {\r
12133                                         var n = t.getElement().form;\r
12134 \r
12135                                         if (!n)\r
12136                                                 return;\r
12137 \r
12138                                         // Already patched\r
12139                                         if (n._mceOldSubmit)\r
12140                                                 return;\r
12141 \r
12142                                         // Check page uses id="submit" or name="submit" for it's submit button\r
12143                                         if (!n.submit.nodeType && !n.submit.length) {\r
12144                                                 t.formElement = n;\r
12145                                                 n._mceOldSubmit = n.submit;\r
12146                                                 n.submit = function() {\r
12147                                                         // Save all instances\r
12148                                                         tinymce.triggerSave();\r
12149                                                         t.isNotDirty = 1;\r
12150 \r
12151                                                         return t.formElement._mceOldSubmit(t.formElement);\r
12152                                                 };\r
12153                                         }\r
12154 \r
12155                                         n = null;\r
12156                                 });\r
12157                         }\r
12158 \r
12159                         // Load scripts\r
12160                         function loadScripts() {\r
12161                                 if (s.language && s.language_load !== false)\r
12162                                         sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');\r
12163 \r
12164                                 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])\r
12165                                         ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');\r
12166 \r
12167                                 each(explode(s.plugins), function(p) {\r
12168                                         if (p &&!PluginManager.urls[p]) {\r
12169                                                 if (p.charAt(0) == '-') {\r
12170                                                         p = p.substr(1, p.length);\r
12171                                                         var dependencies = PluginManager.dependencies(p);\r
12172                                                         each(dependencies, function(dep) {\r
12173                                                                 var defaultSettings = {prefix:'plugins/', resource: dep, suffix:'/editor_plugin' + tinymce.suffix + '.js'};\r
12174                                                                 var dep = PluginManager.createUrl(defaultSettings, dep);\r
12175                                                                 PluginManager.load(dep.resource, dep);\r
12176                                                                 \r
12177                                                         });\r
12178                                                 } else {\r
12179                                                         // Skip safari plugin, since it is removed as of 3.3b1\r
12180                                                         if (p == 'safari') {\r
12181                                                                 return;\r
12182                                                         }\r
12183                                                         PluginManager.load(p, {prefix:'plugins/', resource: p, suffix:'/editor_plugin' + tinymce.suffix + '.js'});\r
12184                                                 }\r
12185                                         }\r
12186                                 });\r
12187 \r
12188                                 // Init when que is loaded\r
12189                                 sl.loadQueue(function() {\r
12190                                         if (!t.removed)\r
12191                                                 t.init();\r
12192                                 });\r
12193                         };\r
12194 \r
12195                         loadScripts();\r
12196                 },\r
12197 \r
12198                 init : function() {\r
12199                         var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re, i, initializedPlugins = [];\r
12200 \r
12201                         tinymce.add(t);\r
12202 \r
12203                         s.aria_label = s.aria_label || DOM.getAttrib(e, 'aria-label', t.getLang('aria.rich_text_area'));\r
12204 \r
12205                         if (s.theme) {\r
12206                                 s.theme = s.theme.replace(/-/, '');\r
12207                                 o = ThemeManager.get(s.theme);\r
12208                                 t.theme = new o();\r
12209 \r
12210                                 if (t.theme.init && s.init_theme)\r
12211                                         t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));\r
12212                         }\r
12213                         function initPlugin(p) {\r
12214                                 var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;\r
12215                                 if (c && tinymce.inArray(initializedPlugins,p) === -1) {\r
12216                                         each(PluginManager.dependencies(p), function(dep){\r
12217                                                 initPlugin(dep);\r
12218                                         });\r
12219                                         po = new c(t, u);\r
12220 \r
12221                                         t.plugins[p] = po;\r
12222 \r
12223                                         if (po.init) {\r
12224                                                 po.init(t, u);\r
12225                                                 initializedPlugins.push(p);\r
12226                                         }\r
12227                                 }\r
12228                         }\r
12229                         \r
12230                         // Create all plugins\r
12231                         each(explode(s.plugins.replace(/\-/g, '')), initPlugin);\r
12232 \r
12233                         // Setup popup CSS path(s)\r
12234                         if (s.popup_css !== false) {\r
12235                                 if (s.popup_css)\r
12236                                         s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);\r
12237                                 else\r
12238                                         s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");\r
12239                         }\r
12240 \r
12241                         if (s.popup_css_add)\r
12242                                 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);\r
12243 \r
12244                         t.controlManager = new tinymce.ControlManager(t);\r
12245 \r
12246                         if (s.custom_undo_redo) {\r
12247                                 t.onBeforeExecCommand.add(function(ed, cmd, ui, val, a) {\r
12248                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))\r
12249                                                 t.undoManager.beforeChange();\r
12250                                 });\r
12251 \r
12252                                 t.onExecCommand.add(function(ed, cmd, ui, val, a) {\r
12253                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))\r
12254                                                 t.undoManager.add();\r
12255                                 });\r
12256                         }\r
12257 \r
12258                         t.onExecCommand.add(function(ed, c) {\r
12259                                 // Don't refresh the select lists until caret move\r
12260                                 if (!/^(FontName|FontSize)$/.test(c))\r
12261                                         t.nodeChanged();\r
12262                         });\r
12263 \r
12264                         // Remove ghost selections on images and tables in Gecko\r
12265                         if (isGecko) {\r
12266                                 function repaint(a, o) {\r
12267                                         if (!o || !o.initial)\r
12268                                                 t.execCommand('mceRepaint');\r
12269                                 };\r
12270 \r
12271                                 t.onUndo.add(repaint);\r
12272                                 t.onRedo.add(repaint);\r
12273                                 t.onSetContent.add(repaint);\r
12274                         }\r
12275 \r
12276                         // Enables users to override the control factory\r
12277                         t.onBeforeRenderUI.dispatch(t, t.controlManager);\r
12278 \r
12279                         // Measure box\r
12280                         if (s.render_ui) {\r
12281                                 w = s.width || e.style.width || e.offsetWidth;\r
12282                                 h = s.height || e.style.height || e.offsetHeight;\r
12283                                 t.orgDisplay = e.style.display;\r
12284                                 re = /^[0-9\.]+(|px)$/i;\r
12285 \r
12286                                 if (re.test('' + w))\r
12287                                         w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);\r
12288 \r
12289                                 if (re.test('' + h))\r
12290                                         h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);\r
12291 \r
12292                                 // Render UI\r
12293                                 o = t.theme.renderUI({\r
12294                                         targetNode : e,\r
12295                                         width : w,\r
12296                                         height : h,\r
12297                                         deltaWidth : s.delta_width,\r
12298                                         deltaHeight : s.delta_height\r
12299                                 });\r
12300 \r
12301                                 t.editorContainer = o.editorContainer;\r
12302                         }\r
12303 \r
12304 \r
12305                         // User specified a document.domain value\r
12306                         if (document.domain && location.hostname != document.domain)\r
12307                                 tinymce.relaxedDomain = document.domain;\r
12308 \r
12309                         // Resize editor\r
12310                         DOM.setStyles(o.sizeContainer || o.editorContainer, {\r
12311                                 width : w,\r
12312                                 height : h\r
12313                         });\r
12314 \r
12315                         // Load specified content CSS last\r
12316                         if (s.content_css) {\r
12317                                 tinymce.each(explode(s.content_css), function(u) {\r
12318                                         t.contentCSS.push(t.documentBaseURI.toAbsolute(u));\r
12319                                 });\r
12320                         }\r
12321 \r
12322                         h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');\r
12323                         if (h < 100)\r
12324                                 h = 100;\r
12325 \r
12326                         t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">';\r
12327 \r
12328                         // We only need to override paths if we have to\r
12329                         // IE has a bug where it remove site absolute urls to relative ones if this is specified\r
12330                         if (s.document_base_url != tinymce.documentBaseURL)\r
12331                                 t.iframeHTML += '<base href="' + t.documentBaseURI.getURI() + '" />';\r
12332 \r
12333                         // IE8 doesn't support carets behind images setting ie7_compat would force IE8+ to run in IE7 compat mode.\r
12334                         if (s.ie7_compat)\r
12335                                 t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" />';\r
12336                         else\r
12337                                 t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=edge" />';\r
12338 \r
12339                         t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';\r
12340 \r
12341                         // Load the CSS by injecting them into the HTML this will reduce "flicker"\r
12342                         for (i = 0; i < t.contentCSS.length; i++) {\r
12343                                 t.iframeHTML += '<link type="text/css" rel="stylesheet" href="' + t.contentCSS[i] + '" />';\r
12344                         }\r
12345 \r
12346                         t.contentCSS = [];\r
12347 \r
12348                         bi = s.body_id || 'tinymce';\r
12349                         if (bi.indexOf('=') != -1) {\r
12350                                 bi = t.getParam('body_id', '', 'hash');\r
12351                                 bi = bi[t.id] || bi;\r
12352                         }\r
12353 \r
12354                         bc = s.body_class || '';\r
12355                         if (bc.indexOf('=') != -1) {\r
12356                                 bc = t.getParam('body_class', '', 'hash');\r
12357                                 bc = bc[t.id] || '';\r
12358                         }\r
12359 \r
12360                         t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '" onload="window.parent.tinyMCE.get(\'' + t.id + '\').onLoad.dispatch();"><br></body></html>';\r
12361 \r
12362                         // Domain relaxing enabled, then set document domain\r
12363                         if (tinymce.relaxedDomain && (isIE || (tinymce.isOpera && parseFloat(opera.version()) < 11))) {\r
12364                                 // We need to write the contents here in IE since multiple writes messes up refresh button and back button\r
12365                                 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
12366                         }\r
12367 \r
12368                         // Create iframe\r
12369                         // TODO: ACC add the appropriate description on this.\r
12370                         n = DOM.add(o.iframeContainer, 'iframe', { \r
12371                                 id : t.id + "_ifr",\r
12372                                 src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7\r
12373                                 frameBorder : '0',\r
12374                                 allowTransparency : "true",\r
12375                                 title : s.aria_label,\r
12376                                 style : {\r
12377                                         width : '100%',\r
12378                                         height : h,\r
12379                                         display : 'block' // Important for Gecko to render the iframe correctly\r
12380                                 }\r
12381                         });\r
12382 \r
12383                         t.contentAreaContainer = o.iframeContainer;\r
12384                         DOM.get(o.editorContainer).style.display = t.orgDisplay;\r
12385                         DOM.get(t.id).style.display = 'none';\r
12386                         DOM.setAttrib(t.id, 'aria-hidden', true);\r
12387 \r
12388                         if (!tinymce.relaxedDomain || !u)\r
12389                                 t.setupIframe();\r
12390 \r
12391                         e = n = o = null; // Cleanup\r
12392                 },\r
12393 \r
12394                 setupIframe : function() {\r
12395                         var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;\r
12396 \r
12397                         // Setup iframe body\r
12398                         if (!isIE || !tinymce.relaxedDomain) {\r
12399                                 d.open();\r
12400                                 d.write(t.iframeHTML);\r
12401                                 d.close();\r
12402 \r
12403                                 if (tinymce.relaxedDomain)\r
12404                                         d.domain = tinymce.relaxedDomain;\r
12405                         }\r
12406 \r
12407                         // It will not steal focus while setting contentEditable\r
12408                         b = t.getBody();\r
12409                         b.disabled = true;\r
12410 \r
12411                         if (!s.readonly)\r
12412                                 b.contentEditable = true;\r
12413 \r
12414                         b.disabled = false;\r
12415 \r
12416                         t.schema = new tinymce.html.Schema(s);\r
12417 \r
12418                         t.dom = new tinymce.dom.DOMUtils(t.getDoc(), {\r
12419                                 keep_values : true,\r
12420                                 url_converter : t.convertURL,\r
12421                                 url_converter_scope : t,\r
12422                                 hex_colors : s.force_hex_style_colors,\r
12423                                 class_filter : s.class_filter,\r
12424                                 update_styles : 1,\r
12425                                 fix_ie_paragraphs : 1,\r
12426                                 schema : t.schema\r
12427                         });\r
12428 \r
12429                         t.parser = new tinymce.html.DomParser(s, t.schema);\r
12430 \r
12431                         // Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.\r
12432                         if (!t.settings.allow_html_in_named_anchor) {\r
12433                                 t.parser.addAttributeFilter('name', function(nodes, name) {\r
12434                                         var i = nodes.length, sibling, prevSibling, parent, node;\r
12435         \r
12436                                         while (i--) {\r
12437                                                 node = nodes[i];\r
12438                                                 if (node.name === 'a' && node.firstChild) {\r
12439                                                         parent = node.parent;\r
12440         \r
12441                                                         // Move children after current node\r
12442                                                         sibling = node.lastChild;\r
12443                                                         do {\r
12444                                                                 prevSibling = sibling.prev;\r
12445                                                                 parent.insert(sibling, node);\r
12446                                                                 sibling = prevSibling;\r
12447                                                         } while (sibling);\r
12448                                                 }\r
12449                                         }\r
12450                                 });\r
12451                         }\r
12452 \r
12453                         // Convert src and href into data-mce-src, data-mce-href and data-mce-style\r
12454                         t.parser.addAttributeFilter('src,href,style', function(nodes, name) {\r
12455                                 var i = nodes.length, node, dom = t.dom, value, internalName;\r
12456 \r
12457                                 while (i--) {\r
12458                                         node = nodes[i];\r
12459                                         value = node.attr(name);\r
12460                                         internalName = 'data-mce-' + name;\r
12461 \r
12462                                         // Add internal attribute if we need to we don't on a refresh of the document\r
12463                                         if (!node.attributes.map[internalName]) {       \r
12464                                                 if (name === "style")\r
12465                                                         node.attr(internalName, dom.serializeStyle(dom.parseStyle(value), node.name));\r
12466                                                 else\r
12467                                                         node.attr(internalName, t.convertURL(value, name, node.name));\r
12468                                         }\r
12469                                 }\r
12470                         });\r
12471 \r
12472                         // Keep scripts from executing\r
12473                         t.parser.addNodeFilter('script', function(nodes, name) {\r
12474                                 var i = nodes.length, node;\r
12475 \r
12476                                 while (i--) {\r
12477                                         node = nodes[i];\r
12478                                         node.attr('type', 'mce-' + (node.attr('type') || 'text/javascript'));\r
12479                                 }\r
12480                         });\r
12481 \r
12482                         t.parser.addNodeFilter('#cdata', function(nodes, name) {\r
12483                                 var i = nodes.length, node;\r
12484 \r
12485                                 while (i--) {\r
12486                                         node = nodes[i];\r
12487                                         node.type = 8;\r
12488                                         node.name = '#comment';\r
12489                                         node.value = '[CDATA[' + node.value + ']]';\r
12490                                 }\r
12491                         });\r
12492 \r
12493                         t.parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes, name) {\r
12494                                 var i = nodes.length, node, nonEmptyElements = t.schema.getNonEmptyElements();\r
12495 \r
12496                                 while (i--) {\r
12497                                         node = nodes[i];\r
12498 \r
12499                                         if (node.isEmpty(nonEmptyElements))\r
12500                                                 node.empty().append(new tinymce.html.Node('br', 1)).shortEnded = true;\r
12501                                 }\r
12502                         });\r
12503 \r
12504                         t.serializer = new tinymce.dom.Serializer(s, t.dom, t.schema);\r
12505 \r
12506                         t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);\r
12507 \r
12508                         t.formatter = new tinymce.Formatter(this);\r
12509 \r
12510                         // Register default formats\r
12511                         t.formatter.register({\r
12512                                 alignleft : [\r
12513                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}, defaultBlock: 'div'},\r
12514                                         {selector : 'img,table', collapsed : false, styles : {'float' : 'left'}}\r
12515                                 ],\r
12516 \r
12517                                 aligncenter : [\r
12518                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}, defaultBlock: 'div'},\r
12519                                         {selector : 'img', collapsed : false, styles : {display : 'block', marginLeft : 'auto', marginRight : 'auto'}},\r
12520                                         {selector : 'table', collapsed : false, styles : {marginLeft : 'auto', marginRight : 'auto'}}\r
12521                                 ],\r
12522 \r
12523                                 alignright : [\r
12524                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}, defaultBlock: 'div'},\r
12525                                         {selector : 'img,table', collapsed : false, styles : {'float' : 'right'}}\r
12526                                 ],\r
12527 \r
12528                                 alignfull : [\r
12529                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'justify'}, defaultBlock: 'div'}\r
12530                                 ],\r
12531 \r
12532                                 bold : [\r
12533                                         {inline : 'strong', remove : 'all'},\r
12534                                         {inline : 'span', styles : {fontWeight : 'bold'}},\r
12535                                         {inline : 'b', remove : 'all'}\r
12536                                 ],\r
12537 \r
12538                                 italic : [\r
12539                                         {inline : 'em', remove : 'all'},\r
12540                                         {inline : 'span', styles : {fontStyle : 'italic'}},\r
12541                                         {inline : 'i', remove : 'all'}\r
12542                                 ],\r
12543 \r
12544                                 underline : [\r
12545                                         {inline : 'span', styles : {textDecoration : 'underline'}, exact : true},\r
12546                                         {inline : 'u', remove : 'all'}\r
12547                                 ],\r
12548 \r
12549                                 strikethrough : [\r
12550                                         {inline : 'span', styles : {textDecoration : 'line-through'}, exact : true},\r
12551                                         {inline : 'strike', remove : 'all'}\r
12552                                 ],\r
12553 \r
12554                                 forecolor : {inline : 'span', styles : {color : '%value'}, wrap_links : false},\r
12555                                 hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}, wrap_links : false},\r
12556                                 fontname : {inline : 'span', styles : {fontFamily : '%value'}},\r
12557                                 fontsize : {inline : 'span', styles : {fontSize : '%value'}},\r
12558                                 fontsize_class : {inline : 'span', attributes : {'class' : '%value'}},\r
12559                                 blockquote : {block : 'blockquote', wrapper : 1, remove : 'all'},\r
12560                                 subscript : {inline : 'sub'},\r
12561                                 superscript : {inline : 'sup'},\r
12562 \r
12563                                 link : {inline : 'a', selector : 'a', remove : 'all', split : true, deep : true,\r
12564                                         onmatch : function(node) {\r
12565                                                 return true;\r
12566                                         },\r
12567 \r
12568                                         onformat : function(elm, fmt, vars) {\r
12569                                                 each(vars, function(value, key) {\r
12570                                                         t.dom.setAttrib(elm, key, value);\r
12571                                                 });\r
12572                                         }\r
12573                                 },\r
12574 \r
12575                                 removeformat : [\r
12576                                         {selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},\r
12577                                         {selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},\r
12578                                         {selector : '*', attributes : ['style', 'class'], split : false, expand : false, deep : true}\r
12579                                 ]\r
12580                         });\r
12581 \r
12582                         // Register default block formats\r
12583                         each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) {\r
12584                                 t.formatter.register(name, {block : name, remove : 'all'});\r
12585                         });\r
12586 \r
12587                         // Register user defined formats\r
12588                         t.formatter.register(t.settings.formats);\r
12589 \r
12590                         t.undoManager = new tinymce.UndoManager(t);\r
12591 \r
12592                         // Pass through\r
12593                         t.undoManager.onAdd.add(function(um, l) {\r
12594                                 if (um.hasUndo())\r
12595                                         return t.onChange.dispatch(t, l, um);\r
12596                         });\r
12597 \r
12598                         t.undoManager.onUndo.add(function(um, l) {\r
12599                                 return t.onUndo.dispatch(t, l, um);\r
12600                         });\r
12601 \r
12602                         t.undoManager.onRedo.add(function(um, l) {\r
12603                                 return t.onRedo.dispatch(t, l, um);\r
12604                         });\r
12605 \r
12606                         t.forceBlocks = new tinymce.ForceBlocks(t);\r
12607                         t.enterKey = new tinymce.EnterKey(t);\r
12608 \r
12609                         t.editorCommands = new tinymce.EditorCommands(t);\r
12610 \r
12611                         // Pass through\r
12612                         t.serializer.onPreProcess.add(function(se, o) {\r
12613                                 return t.onPreProcess.dispatch(t, o, se);\r
12614                         });\r
12615 \r
12616                         t.serializer.onPostProcess.add(function(se, o) {\r
12617                                 return t.onPostProcess.dispatch(t, o, se);\r
12618                         });\r
12619 \r
12620                         t.onPreInit.dispatch(t);\r
12621 \r
12622                         if (!s.gecko_spellcheck)\r
12623                                 t.getBody().spellcheck = 0;\r
12624 \r
12625                         if (!s.readonly)\r
12626                                 t._addEvents();\r
12627 \r
12628                         t.controlManager.onPostRender.dispatch(t, t.controlManager);\r
12629                         t.onPostRender.dispatch(t);\r
12630 \r
12631                         t.quirks = new tinymce.util.Quirks(this);\r
12632 \r
12633                         if (s.directionality)\r
12634                                 t.getBody().dir = s.directionality;\r
12635 \r
12636                         if (s.nowrap)\r
12637                                 t.getBody().style.whiteSpace = "nowrap";\r
12638 \r
12639                         if (s.handle_node_change_callback) {\r
12640                                 t.onNodeChange.add(function(ed, cm, n) {\r
12641                                         t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());\r
12642                                 });\r
12643                         }\r
12644 \r
12645                         if (s.save_callback) {\r
12646                                 t.onSaveContent.add(function(ed, o) {\r
12647                                         var h = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
12648 \r
12649                                         if (h)\r
12650                                                 o.content = h;\r
12651                                 });\r
12652                         }\r
12653 \r
12654                         if (s.onchange_callback) {\r
12655                                 t.onChange.add(function(ed, l) {\r
12656                                         t.execCallback('onchange_callback', t, l);\r
12657                                 });\r
12658                         }\r
12659 \r
12660                         if (s.protect) {\r
12661                                 t.onBeforeSetContent.add(function(ed, o) {\r
12662                                         if (s.protect) {\r
12663                                                 each(s.protect, function(pattern) {\r
12664                                                         o.content = o.content.replace(pattern, function(str) {\r
12665                                                                 return '<!--mce:protected ' + escape(str) + '-->';\r
12666                                                         });\r
12667                                                 });\r
12668                                         }\r
12669                                 });\r
12670                         }\r
12671 \r
12672                         if (s.convert_newlines_to_brs) {\r
12673                                 t.onBeforeSetContent.add(function(ed, o) {\r
12674                                         if (o.initial)\r
12675                                                 o.content = o.content.replace(/\r?\n/g, '<br />');\r
12676                                 });\r
12677                         }\r
12678 \r
12679                         if (s.preformatted) {\r
12680                                 t.onPostProcess.add(function(ed, o) {\r
12681                                         o.content = o.content.replace(/^\s*<pre.*?>/, '');\r
12682                                         o.content = o.content.replace(/<\/pre>\s*$/, '');\r
12683 \r
12684                                         if (o.set)\r
12685                                                 o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';\r
12686                                 });\r
12687                         }\r
12688 \r
12689                         if (s.verify_css_classes) {\r
12690                                 t.serializer.attribValueFilter = function(n, v) {\r
12691                                         var s, cl;\r
12692 \r
12693                                         if (n == 'class') {\r
12694                                                 // Build regexp for classes\r
12695                                                 if (!t.classesRE) {\r
12696                                                         cl = t.dom.getClasses();\r
12697 \r
12698                                                         if (cl.length > 0) {\r
12699                                                                 s = '';\r
12700 \r
12701                                                                 each (cl, function(o) {\r
12702                                                                         s += (s ? '|' : '') + o['class'];\r
12703                                                                 });\r
12704 \r
12705                                                                 t.classesRE = new RegExp('(' + s + ')', 'gi');\r
12706                                                         }\r
12707                                                 }\r
12708 \r
12709                                                 return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';\r
12710                                         }\r
12711 \r
12712                                         return v;\r
12713                                 };\r
12714                         }\r
12715 \r
12716                         if (s.cleanup_callback) {\r
12717                                 t.onBeforeSetContent.add(function(ed, o) {\r
12718                                         o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
12719                                 });\r
12720 \r
12721                                 t.onPreProcess.add(function(ed, o) {\r
12722                                         if (o.set)\r
12723                                                 t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);\r
12724 \r
12725                                         if (o.get)\r
12726                                                 t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);\r
12727                                 });\r
12728 \r
12729                                 t.onPostProcess.add(function(ed, o) {\r
12730                                         if (o.set)\r
12731                                                 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
12732 \r
12733                                         if (o.get)                                              \r
12734                                                 o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);\r
12735                                 });\r
12736                         }\r
12737 \r
12738                         if (s.save_callback) {\r
12739                                 t.onGetContent.add(function(ed, o) {\r
12740                                         if (o.save)\r
12741                                                 o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
12742                                 });\r
12743                         }\r
12744 \r
12745                         if (s.handle_event_callback) {\r
12746                                 t.onEvent.add(function(ed, e, o) {\r
12747                                         if (t.execCallback('handle_event_callback', e, ed, o) === false)\r
12748                                                 Event.cancel(e);\r
12749                                 });\r
12750                         }\r
12751 \r
12752                         // Add visual aids when new contents is added\r
12753                         t.onSetContent.add(function() {\r
12754                                 t.addVisual(t.getBody());\r
12755                         });\r
12756 \r
12757                         // Remove empty contents\r
12758                         if (s.padd_empty_editor) {\r
12759                                 t.onPostProcess.add(function(ed, o) {\r
12760                                         o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');\r
12761                                 });\r
12762                         }\r
12763 \r
12764                         if (isGecko) {\r
12765                                 // Fix gecko link bug, when a link is placed at the end of block elements there is\r
12766                                 // no way to move the caret behind the link. This fix adds a bogus br element after the link\r
12767                                 function fixLinks(ed, o) {\r
12768                                         each(ed.dom.select('a'), function(n) {\r
12769                                                 var pn = n.parentNode;\r
12770 \r
12771                                                 if (ed.dom.isBlock(pn) && pn.lastChild === n)\r
12772                                                         ed.dom.add(pn, 'br', {'data-mce-bogus' : 1});\r
12773                                         });\r
12774                                 };\r
12775 \r
12776                                 t.onExecCommand.add(function(ed, cmd) {\r
12777                                         if (cmd === 'CreateLink')\r
12778                                                 fixLinks(ed);\r
12779                                 });\r
12780 \r
12781                                 t.onSetContent.add(t.selection.onSetContent.add(fixLinks));\r
12782                         }\r
12783 \r
12784                         t.load({initial : true, format : 'html'});\r
12785                         t.startContent = t.getContent({format : 'raw'});\r
12786                         t.undoManager.add();\r
12787                         t.initialized = true;\r
12788 \r
12789                         t.onInit.dispatch(t);\r
12790                         t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());\r
12791                         t.execCallback('init_instance_callback', t);\r
12792                         t.focus(true);\r
12793                         t.nodeChanged({initial : 1});\r
12794 \r
12795                         // Load specified content CSS last\r
12796                         each(t.contentCSS, function(u) {\r
12797                                 t.dom.loadCSS(u);\r
12798                         });\r
12799 \r
12800                         // Handle auto focus\r
12801                         if (s.auto_focus) {\r
12802                                 setTimeout(function () {\r
12803                                         var ed = tinymce.get(s.auto_focus);\r
12804 \r
12805                                         ed.selection.select(ed.getBody(), 1);\r
12806                                         ed.selection.collapse(1);\r
12807                                         ed.getBody().focus();\r
12808                                         ed.getWin().focus();\r
12809                                 }, 100);\r
12810                         }\r
12811 \r
12812                         e = null;\r
12813                 },\r
12814 \r
12815 \r
12816                 focus : function(sf) {\r
12817                         var oed, t = this, selection = t.selection, ce = t.settings.content_editable, ieRng, controlElm, doc = t.getDoc();\r
12818 \r
12819                         if (!sf) {\r
12820                                 // Get selected control element\r
12821                                 ieRng = selection.getRng();\r
12822                                 if (ieRng.item) {\r
12823                                         controlElm = ieRng.item(0);\r
12824                                 }\r
12825 \r
12826                                 t._refreshContentEditable();\r
12827 \r
12828                                 // Is not content editable\r
12829                                 if (!ce)\r
12830                                         t.getWin().focus();\r
12831 \r
12832                                 // Focus the body as well since it's contentEditable\r
12833                                 if (tinymce.isGecko) {\r
12834                                         t.getBody().focus();\r
12835                                 }\r
12836 \r
12837                                 // Restore selected control element\r
12838                                 // This is needed when for example an image is selected within a\r
12839                                 // layer a call to focus will then remove the control selection\r
12840                                 if (controlElm && controlElm.ownerDocument == doc) {\r
12841                                         ieRng = doc.body.createControlRange();\r
12842                                         ieRng.addElement(controlElm);\r
12843                                         ieRng.select();\r
12844                                 }\r
12845 \r
12846                         }\r
12847 \r
12848                         if (tinymce.activeEditor != t) {\r
12849                                 if ((oed = tinymce.activeEditor) != null)\r
12850                                         oed.onDeactivate.dispatch(oed, t);\r
12851 \r
12852                                 t.onActivate.dispatch(t, oed);\r
12853                         }\r
12854 \r
12855                         tinymce._setActive(t);\r
12856                 },\r
12857 \r
12858                 execCallback : function(n) {\r
12859                         var t = this, f = t.settings[n], s;\r
12860 \r
12861                         if (!f)\r
12862                                 return;\r
12863 \r
12864                         // Look through lookup\r
12865                         if (t.callbackLookup && (s = t.callbackLookup[n])) {\r
12866                                 f = s.func;\r
12867                                 s = s.scope;\r
12868                         }\r
12869 \r
12870                         if (is(f, 'string')) {\r
12871                                 s = f.replace(/\.\w+$/, '');\r
12872                                 s = s ? tinymce.resolve(s) : 0;\r
12873                                 f = tinymce.resolve(f);\r
12874                                 t.callbackLookup = t.callbackLookup || {};\r
12875                                 t.callbackLookup[n] = {func : f, scope : s};\r
12876                         }\r
12877 \r
12878                         return f.apply(s || t, Array.prototype.slice.call(arguments, 1));\r
12879                 },\r
12880 \r
12881                 translate : function(s) {\r
12882                         var c = this.settings.language || 'en', i18n = tinymce.i18n;\r
12883 \r
12884                         if (!s)\r
12885                                 return '';\r
12886 \r
12887                         return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {\r
12888                                 return i18n[c + '.' + b] || '{#' + b + '}';\r
12889                         });\r
12890                 },\r
12891 \r
12892                 getLang : function(n, dv) {\r
12893                         return tinymce.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}');\r
12894                 },\r
12895 \r
12896                 getParam : function(n, dv, ty) {\r
12897                         var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;\r
12898 \r
12899                         if (ty === 'hash') {\r
12900                                 o = {};\r
12901 \r
12902                                 if (is(v, 'string')) {\r
12903                                         each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {\r
12904                                                 v = v.split('=');\r
12905 \r
12906                                                 if (v.length > 1)\r
12907                                                         o[tr(v[0])] = tr(v[1]);\r
12908                                                 else\r
12909                                                         o[tr(v[0])] = tr(v);\r
12910                                         });\r
12911                                 } else\r
12912                                         o = v;\r
12913 \r
12914                                 return o;\r
12915                         }\r
12916 \r
12917                         return v;\r
12918                 },\r
12919 \r
12920                 nodeChanged : function(o) {\r
12921                         var t = this, s = t.selection, n = s.getStart() || t.getBody();\r
12922 \r
12923                         // Fix for bug #1896577 it seems that this can not be fired while the editor is loading\r
12924                         if (t.initialized) {\r
12925                                 o = o || {};\r
12926                                 n = isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n; // Fix for IE initial state\r
12927 \r
12928                                 // Get parents and add them to object\r
12929                                 o.parents = [];\r
12930                                 t.dom.getParent(n, function(node) {\r
12931                                         if (node.nodeName == 'BODY')\r
12932                                                 return true;\r
12933 \r
12934                                         o.parents.push(node);\r
12935                                 });\r
12936 \r
12937                                 t.onNodeChange.dispatch(\r
12938                                         t,\r
12939                                         o ? o.controlManager || t.controlManager : t.controlManager,\r
12940                                         n,\r
12941                                         s.isCollapsed(),\r
12942                                         o\r
12943                                 );\r
12944                         }\r
12945                 },\r
12946 \r
12947                 addButton : function(n, s) {\r
12948                         var t = this;\r
12949 \r
12950                         t.buttons = t.buttons || {};\r
12951                         t.buttons[n] = s;\r
12952                 },\r
12953 \r
12954                 addCommand : function(name, callback, scope) {\r
12955                         this.execCommands[name] = {func : callback, scope : scope || this};\r
12956                 },\r
12957 \r
12958                 addQueryStateHandler : function(name, callback, scope) {\r
12959                         this.queryStateCommands[name] = {func : callback, scope : scope || this};\r
12960                 },\r
12961 \r
12962                 addQueryValueHandler : function(name, callback, scope) {\r
12963                         this.queryValueCommands[name] = {func : callback, scope : scope || this};\r
12964                 },\r
12965 \r
12966                 addShortcut : function(pa, desc, cmd_func, sc) {\r
12967                         var t = this, c;\r
12968 \r
12969                         if (!t.settings.custom_shortcuts)\r
12970                                 return false;\r
12971 \r
12972                         t.shortcuts = t.shortcuts || {};\r
12973 \r
12974                         if (is(cmd_func, 'string')) {\r
12975                                 c = cmd_func;\r
12976 \r
12977                                 cmd_func = function() {\r
12978                                         t.execCommand(c, false, null);\r
12979                                 };\r
12980                         }\r
12981 \r
12982                         if (is(cmd_func, 'object')) {\r
12983                                 c = cmd_func;\r
12984 \r
12985                                 cmd_func = function() {\r
12986                                         t.execCommand(c[0], c[1], c[2]);\r
12987                                 };\r
12988                         }\r
12989 \r
12990                         each(explode(pa), function(pa) {\r
12991                                 var o = {\r
12992                                         func : cmd_func,\r
12993                                         scope : sc || this,\r
12994                                         desc : desc,\r
12995                                         alt : false,\r
12996                                         ctrl : false,\r
12997                                         shift : false\r
12998                                 };\r
12999 \r
13000                                 each(explode(pa, '+'), function(v) {\r
13001                                         switch (v) {\r
13002                                                 case 'alt':\r
13003                                                 case 'ctrl':\r
13004                                                 case 'shift':\r
13005                                                         o[v] = true;\r
13006                                                         break;\r
13007 \r
13008                                                 default:\r
13009                                                         o.charCode = v.charCodeAt(0);\r
13010                                                         o.keyCode = v.toUpperCase().charCodeAt(0);\r
13011                                         }\r
13012                                 });\r
13013 \r
13014                                 t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;\r
13015                         });\r
13016 \r
13017                         return true;\r
13018                 },\r
13019 \r
13020                 execCommand : function(cmd, ui, val, a) {\r
13021                         var t = this, s = 0, o, st;\r
13022 \r
13023                         if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))\r
13024                                 t.focus();\r
13025 \r
13026                         a = extend({}, a);\r
13027                         t.onBeforeExecCommand.dispatch(t, cmd, ui, val, a);\r
13028                         if (a.terminate)\r
13029                                 return false;\r
13030 \r
13031                         // Command callback\r
13032                         if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {\r
13033                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
13034                                 return true;\r
13035                         }\r
13036 \r
13037                         // Registred commands\r
13038                         if (o = t.execCommands[cmd]) {\r
13039                                 st = o.func.call(o.scope, ui, val);\r
13040 \r
13041                                 // Fall through on true\r
13042                                 if (st !== true) {\r
13043                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
13044                                         return st;\r
13045                                 }\r
13046                         }\r
13047 \r
13048                         // Plugin commands\r
13049                         each(t.plugins, function(p) {\r
13050                                 if (p.execCommand && p.execCommand(cmd, ui, val)) {\r
13051                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
13052                                         s = 1;\r
13053                                         return false;\r
13054                                 }\r
13055                         });\r
13056 \r
13057                         if (s)\r
13058                                 return true;\r
13059 \r
13060                         // Theme commands\r
13061                         if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {\r
13062                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
13063                                 return true;\r
13064                         }\r
13065 \r
13066                         // Editor commands\r
13067                         if (t.editorCommands.execCommand(cmd, ui, val)) {\r
13068                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
13069                                 return true;\r
13070                         }\r
13071 \r
13072                         // Browser commands\r
13073                         t.getDoc().execCommand(cmd, ui, val);\r
13074                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
13075                 },\r
13076 \r
13077                 queryCommandState : function(cmd) {\r
13078                         var t = this, o, s;\r
13079 \r
13080                         // Is hidden then return undefined\r
13081                         if (t._isHidden())\r
13082                                 return;\r
13083 \r
13084                         // Registred commands\r
13085                         if (o = t.queryStateCommands[cmd]) {\r
13086                                 s = o.func.call(o.scope);\r
13087 \r
13088                                 // Fall though on true\r
13089                                 if (s !== true)\r
13090                                         return s;\r
13091                         }\r
13092 \r
13093                         // Registred commands\r
13094                         o = t.editorCommands.queryCommandState(cmd);\r
13095                         if (o !== -1)\r
13096                                 return o;\r
13097 \r
13098                         // Browser commands\r
13099                         try {\r
13100                                 return this.getDoc().queryCommandState(cmd);\r
13101                         } catch (ex) {\r
13102                                 // Fails sometimes see bug: 1896577\r
13103                         }\r
13104                 },\r
13105 \r
13106                 queryCommandValue : function(c) {\r
13107                         var t = this, o, s;\r
13108 \r
13109                         // Is hidden then return undefined\r
13110                         if (t._isHidden())\r
13111                                 return;\r
13112 \r
13113                         // Registred commands\r
13114                         if (o = t.queryValueCommands[c]) {\r
13115                                 s = o.func.call(o.scope);\r
13116 \r
13117                                 // Fall though on true\r
13118                                 if (s !== true)\r
13119                                         return s;\r
13120                         }\r
13121 \r
13122                         // Registred commands\r
13123                         o = t.editorCommands.queryCommandValue(c);\r
13124                         if (is(o))\r
13125                                 return o;\r
13126 \r
13127                         // Browser commands\r
13128                         try {\r
13129                                 return this.getDoc().queryCommandValue(c);\r
13130                         } catch (ex) {\r
13131                                 // Fails sometimes see bug: 1896577\r
13132                         }\r
13133                 },\r
13134 \r
13135                 show : function() {\r
13136                         var t = this;\r
13137 \r
13138                         DOM.show(t.getContainer());\r
13139                         DOM.hide(t.id);\r
13140                         t.load();\r
13141                 },\r
13142 \r
13143                 hide : function() {\r
13144                         var t = this, d = t.getDoc();\r
13145 \r
13146                         // Fixed bug where IE has a blinking cursor left from the editor\r
13147                         if (isIE && d)\r
13148                                 d.execCommand('SelectAll');\r
13149 \r
13150                         // We must save before we hide so Safari doesn't crash\r
13151                         t.save();\r
13152                         DOM.hide(t.getContainer());\r
13153                         DOM.setStyle(t.id, 'display', t.orgDisplay);\r
13154                 },\r
13155 \r
13156                 isHidden : function() {\r
13157                         return !DOM.isHidden(this.id);\r
13158                 },\r
13159 \r
13160                 setProgressState : function(b, ti, o) {\r
13161                         this.onSetProgressState.dispatch(this, b, ti, o);\r
13162 \r
13163                         return b;\r
13164                 },\r
13165 \r
13166                 load : function(o) {\r
13167                         var t = this, e = t.getElement(), h;\r
13168 \r
13169                         if (e) {\r
13170                                 o = o || {};\r
13171                                 o.load = true;\r
13172 \r
13173                                 // Double encode existing entities in the value\r
13174                                 h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);\r
13175                                 o.element = e;\r
13176 \r
13177                                 if (!o.no_events)\r
13178                                         t.onLoadContent.dispatch(t, o);\r
13179 \r
13180                                 o.element = e = null;\r
13181 \r
13182                                 return h;\r
13183                         }\r
13184                 },\r
13185 \r
13186                 save : function(o) {\r
13187                         var t = this, e = t.getElement(), h, f;\r
13188 \r
13189                         if (!e || !t.initialized)\r
13190                                 return;\r
13191 \r
13192                         o = o || {};\r
13193                         o.save = true;\r
13194 \r
13195                         // Add undo level will trigger onchange event\r
13196                         if (!o.no_events) {\r
13197                                 t.undoManager.typing = false;\r
13198                                 t.undoManager.add();\r
13199                         }\r
13200 \r
13201                         o.element = e;\r
13202                         h = o.content = t.getContent(o);\r
13203 \r
13204                         if (!o.no_events)\r
13205                                 t.onSaveContent.dispatch(t, o);\r
13206 \r
13207                         h = o.content;\r
13208 \r
13209                         if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {\r
13210                                 e.innerHTML = h;\r
13211 \r
13212                                 // Update hidden form element\r
13213                                 if (f = DOM.getParent(t.id, 'form')) {\r
13214                                         each(f.elements, function(e) {\r
13215                                                 if (e.name == t.id) {\r
13216                                                         e.value = h;\r
13217                                                         return false;\r
13218                                                 }\r
13219                                         });\r
13220                                 }\r
13221                         } else\r
13222                                 e.value = h;\r
13223 \r
13224                         o.element = e = null;\r
13225 \r
13226                         return h;\r
13227                 },\r
13228 \r
13229                 setContent : function(content, args) {\r
13230                         var self = this, rootNode, body = self.getBody(), forcedRootBlockName;\r
13231 \r
13232                         // Setup args object\r
13233                         args = args || {};\r
13234                         args.format = args.format || 'html';\r
13235                         args.set = true;\r
13236                         args.content = content;\r
13237 \r
13238                         // Do preprocessing\r
13239                         if (!args.no_events)\r
13240                                 self.onBeforeSetContent.dispatch(self, args);\r
13241 \r
13242                         content = args.content;\r
13243 \r
13244                         // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content\r
13245                         // It will also be impossible to place the caret in the editor unless there is a BR element present\r
13246                         if (!tinymce.isIE && (content.length === 0 || /^\s+$/.test(content))) {\r
13247                                 forcedRootBlockName = self.settings.forced_root_block;\r
13248                                 if (forcedRootBlockName)\r
13249                                         content = '<' + forcedRootBlockName + '><br data-mce-bogus="1"></' + forcedRootBlockName + '>';\r
13250                                 else\r
13251                                         content = '<br data-mce-bogus="1">';\r
13252 \r
13253                                 body.innerHTML = content;\r
13254                                 self.selection.select(body, true);\r
13255                                 self.selection.collapse(true);\r
13256                                 return;\r
13257                         }\r
13258 \r
13259                         // Parse and serialize the html\r
13260                         if (args.format !== 'raw') {\r
13261                                 content = new tinymce.html.Serializer({}, self.schema).serialize(\r
13262                                         self.parser.parse(content)\r
13263                                 );\r
13264                         }\r
13265 \r
13266                         // Set the new cleaned contents to the editor\r
13267                         args.content = tinymce.trim(content);\r
13268                         self.dom.setHTML(body, args.content);\r
13269 \r
13270                         // Do post processing\r
13271                         if (!args.no_events)\r
13272                                 self.onSetContent.dispatch(self, args);\r
13273 \r
13274                         self.selection.normalize();\r
13275 \r
13276                         return args.content;\r
13277                 },\r
13278 \r
13279                 getContent : function(args) {\r
13280                         var self = this, content;\r
13281 \r
13282                         // Setup args object\r
13283                         args = args || {};\r
13284                         args.format = args.format || 'html';\r
13285                         args.get = true;\r
13286 \r
13287                         // Do preprocessing\r
13288                         if (!args.no_events)\r
13289                                 self.onBeforeGetContent.dispatch(self, args);\r
13290 \r
13291                         // Get raw contents or by default the cleaned contents\r
13292                         if (args.format == 'raw')\r
13293                                 content = self.getBody().innerHTML;\r
13294                         else\r
13295                                 content = self.serializer.serialize(self.getBody(), args);\r
13296 \r
13297                         args.content = tinymce.trim(content);\r
13298 \r
13299                         // Do post processing\r
13300                         if (!args.no_events)\r
13301                                 self.onGetContent.dispatch(self, args);\r
13302 \r
13303                         return args.content;\r
13304                 },\r
13305 \r
13306                 isDirty : function() {\r
13307                         var self = this;\r
13308 \r
13309                         return tinymce.trim(self.startContent) != tinymce.trim(self.getContent({format : 'raw', no_events : 1})) && !self.isNotDirty;\r
13310                 },\r
13311 \r
13312                 getContainer : function() {\r
13313                         var t = this;\r
13314 \r
13315                         if (!t.container)\r
13316                                 t.container = DOM.get(t.editorContainer || t.id + '_parent');\r
13317 \r
13318                         return t.container;\r
13319                 },\r
13320 \r
13321                 getContentAreaContainer : function() {\r
13322                         return this.contentAreaContainer;\r
13323                 },\r
13324 \r
13325                 getElement : function() {\r
13326                         return DOM.get(this.settings.content_element || this.id);\r
13327                 },\r
13328 \r
13329                 getWin : function() {\r
13330                         var t = this, e;\r
13331 \r
13332                         if (!t.contentWindow) {\r
13333                                 e = DOM.get(t.id + "_ifr");\r
13334 \r
13335                                 if (e)\r
13336                                         t.contentWindow = e.contentWindow;\r
13337                         }\r
13338 \r
13339                         return t.contentWindow;\r
13340                 },\r
13341 \r
13342                 getDoc : function() {\r
13343                         var t = this, w;\r
13344 \r
13345                         if (!t.contentDocument) {\r
13346                                 w = t.getWin();\r
13347 \r
13348                                 if (w)\r
13349                                         t.contentDocument = w.document;\r
13350                         }\r
13351 \r
13352                         return t.contentDocument;\r
13353                 },\r
13354 \r
13355                 getBody : function() {\r
13356                         return this.bodyElement || this.getDoc().body;\r
13357                 },\r
13358 \r
13359                 convertURL : function(u, n, e) {\r
13360                         var t = this, s = t.settings;\r
13361 \r
13362                         // Use callback instead\r
13363                         if (s.urlconverter_callback)\r
13364                                 return t.execCallback('urlconverter_callback', u, e, true, n);\r
13365 \r
13366                         // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs\r
13367                         if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)\r
13368                                 return u;\r
13369 \r
13370                         // Convert to relative\r
13371                         if (s.relative_urls)\r
13372                                 return t.documentBaseURI.toRelative(u);\r
13373 \r
13374                         // Convert to absolute\r
13375                         u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);\r
13376 \r
13377                         return u;\r
13378                 },\r
13379 \r
13380                 addVisual : function(e) {\r
13381                         var t = this, s = t.settings;\r
13382 \r
13383                         e = e || t.getBody();\r
13384 \r
13385                         if (!is(t.hasVisual))\r
13386                                 t.hasVisual = s.visual;\r
13387 \r
13388                         each(t.dom.select('table,a', e), function(e) {\r
13389                                 var v;\r
13390 \r
13391                                 switch (e.nodeName) {\r
13392                                         case 'TABLE':\r
13393                                                 v = t.dom.getAttrib(e, 'border');\r
13394 \r
13395                                                 if (!v || v == '0') {\r
13396                                                         if (t.hasVisual)\r
13397                                                                 t.dom.addClass(e, s.visual_table_class);\r
13398                                                         else\r
13399                                                                 t.dom.removeClass(e, s.visual_table_class);\r
13400                                                 }\r
13401 \r
13402                                                 return;\r
13403 \r
13404                                         case 'A':\r
13405                                                 v = t.dom.getAttrib(e, 'name');\r
13406 \r
13407                                                 if (v) {\r
13408                                                         if (t.hasVisual)\r
13409                                                                 t.dom.addClass(e, 'mceItemAnchor');\r
13410                                                         else\r
13411                                                                 t.dom.removeClass(e, 'mceItemAnchor');\r
13412                                                 }\r
13413 \r
13414                                                 return;\r
13415                                 }\r
13416                         });\r
13417 \r
13418                         t.onVisualAid.dispatch(t, e, t.hasVisual);\r
13419                 },\r
13420 \r
13421                 remove : function() {\r
13422                         var t = this, e = t.getContainer();\r
13423 \r
13424                         if (!t.removed) {\r
13425                                 t.removed = 1; // Cancels post remove event execution\r
13426                                 t.hide();\r
13427 \r
13428                                 // Remove all events\r
13429 \r
13430                                 // Don't clear the window or document if content editable\r
13431                                 // is enabled since other instances might still be present\r
13432                                 if (!t.settings.content_editable) {\r
13433                                         Event.clear(t.getWin());\r
13434                                         Event.clear(t.getDoc());\r
13435                                 }\r
13436 \r
13437                                 Event.clear(t.getBody());\r
13438                                 Event.clear(t.formElement);\r
13439                                 Event.unbind(e);\r
13440 \r
13441                                 t.execCallback('remove_instance_callback', t);\r
13442                                 t.onRemove.dispatch(t);\r
13443 \r
13444                                 // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command\r
13445                                 t.onExecCommand.listeners = [];\r
13446 \r
13447                                 tinymce.remove(t);\r
13448                                 DOM.remove(e);\r
13449                         }\r
13450                 },\r
13451 \r
13452                 destroy : function(s) {\r
13453                         var t = this;\r
13454 \r
13455                         // One time is enough\r
13456                         if (t.destroyed)\r
13457                                 return;\r
13458 \r
13459                         // We must unbind on Gecko since it would otherwise produce the pesky "attempt to run compile-and-go script on a cleared scope" message\r
13460                         if (isGecko) {\r
13461                                 Event.unbind(t.getDoc());\r
13462                                 Event.unbind(t.getWin());\r
13463                                 Event.unbind(t.getBody());\r
13464                         }\r
13465 \r
13466                         if (!s) {\r
13467                                 tinymce.removeUnload(t.destroy);\r
13468                                 tinyMCE.onBeforeUnload.remove(t._beforeUnload);\r
13469 \r
13470                                 // Manual destroy\r
13471                                 if (t.theme && t.theme.destroy)\r
13472                                         t.theme.destroy();\r
13473 \r
13474                                 // Destroy controls, selection and dom\r
13475                                 t.controlManager.destroy();\r
13476                                 t.selection.destroy();\r
13477                                 t.dom.destroy();\r
13478                         }\r
13479 \r
13480                         if (t.formElement) {\r
13481                                 t.formElement.submit = t.formElement._mceOldSubmit;\r
13482                                 t.formElement._mceOldSubmit = null;\r
13483                         }\r
13484 \r
13485                         t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;\r
13486 \r
13487                         if (t.selection)\r
13488                                 t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;\r
13489 \r
13490                         t.destroyed = 1;\r
13491                 },\r
13492 \r
13493                 // Internal functions\r
13494 \r
13495                 _addEvents : function() {\r
13496                         // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset\r
13497                         var t = this, i, s = t.settings, dom = t.dom, lo = {\r
13498                                 mouseup : 'onMouseUp',\r
13499                                 mousedown : 'onMouseDown',\r
13500                                 click : 'onClick',\r
13501                                 keyup : 'onKeyUp',\r
13502                                 keydown : 'onKeyDown',\r
13503                                 keypress : 'onKeyPress',\r
13504                                 submit : 'onSubmit',\r
13505                                 reset : 'onReset',\r
13506                                 contextmenu : 'onContextMenu',\r
13507                                 dblclick : 'onDblClick',\r
13508                                 paste : 'onPaste' // Doesn't work in all browsers yet\r
13509                         };\r
13510 \r
13511                         function eventHandler(e, o) {\r
13512                                 var ty = e.type;\r
13513 \r
13514                                 // Don't fire events when it's removed\r
13515                                 if (t.removed)\r
13516                                         return;\r
13517 \r
13518                                 // Generic event handler\r
13519                                 if (t.onEvent.dispatch(t, e, o) !== false) {\r
13520                                         // Specific event handler\r
13521                                         t[lo[e.fakeType || e.type]].dispatch(t, e, o);\r
13522                                 }\r
13523                         };\r
13524 \r
13525                         // Add DOM events\r
13526                         each(lo, function(v, k) {\r
13527                                 switch (k) {\r
13528                                         case 'contextmenu':\r
13529                                                 dom.bind(t.getDoc(), k, eventHandler);\r
13530                                                 break;\r
13531 \r
13532                                         case 'paste':\r
13533                                                 dom.bind(t.getBody(), k, function(e) {\r
13534                                                         eventHandler(e);\r
13535                                                 });\r
13536                                                 break;\r
13537 \r
13538                                         case 'submit':\r
13539                                         case 'reset':\r
13540                                                 dom.bind(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);\r
13541                                                 break;\r
13542 \r
13543                                         default:\r
13544                                                 dom.bind(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler);\r
13545                                 }\r
13546                         });\r
13547 \r
13548                         dom.bind(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) {\r
13549                                 t.focus(true);\r
13550                         });\r
13551 \r
13552 \r
13553                         // Fixes bug where a specified document_base_uri could result in broken images\r
13554                         // This will also fix drag drop of images in Gecko\r
13555                         if (tinymce.isGecko) {\r
13556                                 dom.bind(t.getDoc(), 'DOMNodeInserted', function(e) {\r
13557                                         var v;\r
13558 \r
13559                                         e = e.target;\r
13560 \r
13561                                         if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('data-mce-src')))\r
13562                                                 e.src = t.documentBaseURI.toAbsolute(v);\r
13563                                 });\r
13564                         }\r
13565 \r
13566                         // Set various midas options in Gecko\r
13567                         if (isGecko) {\r
13568                                 function setOpts() {\r
13569                                         var t = this, d = t.getDoc(), s = t.settings;\r
13570 \r
13571                                         if (isGecko && !s.readonly) {\r
13572                                                 t._refreshContentEditable();\r
13573 \r
13574                                                 try {\r
13575                                                         // Try new Gecko method\r
13576                                                         d.execCommand("styleWithCSS", 0, false);\r
13577                                                 } catch (ex) {\r
13578                                                         // Use old method\r
13579                                                         if (!t._isHidden())\r
13580                                                                 try {d.execCommand("useCSS", 0, true);} catch (ex) {}\r
13581                                                 }\r
13582 \r
13583                                                 if (!s.table_inline_editing)\r
13584                                                         try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {}\r
13585 \r
13586                                                 if (!s.object_resizing)\r
13587                                                         try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {}\r
13588                                         }\r
13589                                 };\r
13590 \r
13591                                 t.onBeforeExecCommand.add(setOpts);\r
13592                                 t.onMouseDown.add(setOpts);\r
13593                         }\r
13594 \r
13595                         // Add node change handlers\r
13596                         t.onMouseUp.add(t.nodeChanged);\r
13597                         //t.onClick.add(t.nodeChanged);\r
13598                         t.onKeyUp.add(function(ed, e) {\r
13599                                 var c = e.keyCode;\r
13600 \r
13601                                 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
13602                                         t.nodeChanged();\r
13603                         });\r
13604 \r
13605 \r
13606                         // Add block quote deletion handler\r
13607                         t.onKeyDown.add(function(ed, e) {\r
13608                                 if (e.keyCode != VK.BACKSPACE)\r
13609                                         return;\r
13610 \r
13611                                 var rng = ed.selection.getRng();\r
13612                                 if (!rng.collapsed)\r
13613                                         return;\r
13614 \r
13615                                 var n = rng.startContainer;\r
13616                                 var offset = rng.startOffset;\r
13617 \r
13618                                 while (n && n.nodeType && n.nodeType != 1 && n.parentNode)\r
13619                                         n = n.parentNode;\r
13620 \r
13621                                 // Is the cursor at the beginning of a blockquote?\r
13622                                 if (n && n.parentNode && n.parentNode.tagName === 'BLOCKQUOTE' && n.parentNode.firstChild == n && offset == 0) {\r
13623                                         // Remove the blockquote\r
13624                                         ed.formatter.toggle('blockquote', null, n.parentNode);\r
13625 \r
13626                                         // Move the caret to the beginning of n\r
13627                                         rng.setStart(n, 0);\r
13628                                         rng.setEnd(n, 0);\r
13629                                         ed.selection.setRng(rng);\r
13630                                         ed.selection.collapse(false);\r
13631                                 }\r
13632                         });\r
13633 \r
13634 \r
13635 \r
13636                         // Add reset handler\r
13637                         t.onReset.add(function() {\r
13638                                 t.setContent(t.startContent, {format : 'raw'});\r
13639                         });\r
13640 \r
13641                         // Add shortcuts\r
13642                         if (s.custom_shortcuts) {\r
13643                                 if (s.custom_undo_redo_keyboard_shortcuts) {\r
13644                                         t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo');\r
13645                                         t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo');\r
13646                                 }\r
13647 \r
13648                                 // Add default shortcuts for gecko\r
13649                                 t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');\r
13650                                 t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');\r
13651                                 t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');\r
13652 \r
13653                                 // BlockFormat shortcuts keys\r
13654                                 for (i=1; i<=6; i++)\r
13655                                         t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);\r
13656 \r
13657                                 t.addShortcut('ctrl+7', '', ['FormatBlock', false, 'p']);\r
13658                                 t.addShortcut('ctrl+8', '', ['FormatBlock', false, 'div']);\r
13659                                 t.addShortcut('ctrl+9', '', ['FormatBlock', false, 'address']);\r
13660 \r
13661                                 function find(e) {\r
13662                                         var v = null;\r
13663 \r
13664                                         if (!e.altKey && !e.ctrlKey && !e.metaKey)\r
13665                                                 return v;\r
13666 \r
13667                                         each(t.shortcuts, function(o) {\r
13668                                                 if (tinymce.isMac && o.ctrl != e.metaKey)\r
13669                                                         return;\r
13670                                                 else if (!tinymce.isMac && o.ctrl != e.ctrlKey)\r
13671                                                         return;\r
13672 \r
13673                                                 if (o.alt != e.altKey)\r
13674                                                         return;\r
13675 \r
13676                                                 if (o.shift != e.shiftKey)\r
13677                                                         return;\r
13678 \r
13679                                                 if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {\r
13680                                                         v = o;\r
13681                                                         return false;\r
13682                                                 }\r
13683                                         });\r
13684 \r
13685                                         return v;\r
13686                                 };\r
13687 \r
13688                                 t.onKeyUp.add(function(ed, e) {\r
13689                                         var o = find(e);\r
13690 \r
13691                                         if (o)\r
13692                                                 return Event.cancel(e);\r
13693                                 });\r
13694 \r
13695                                 t.onKeyPress.add(function(ed, e) {\r
13696                                         var o = find(e);\r
13697 \r
13698                                         if (o)\r
13699                                                 return Event.cancel(e);\r
13700                                 });\r
13701 \r
13702                                 t.onKeyDown.add(function(ed, e) {\r
13703                                         var o = find(e);\r
13704 \r
13705                                         if (o) {\r
13706                                                 o.func.call(o.scope);\r
13707                                                 return Event.cancel(e);\r
13708                                         }\r
13709                                 });\r
13710                         }\r
13711 \r
13712                         if (tinymce.isIE) {\r
13713                                 // Fix so resize will only update the width and height attributes not the styles of an image\r
13714                                 // It will also block mceItemNoResize items\r
13715                                 dom.bind(t.getDoc(), 'controlselect', function(e) {\r
13716                                         var re = t.resizeInfo, cb;\r
13717 \r
13718                                         e = e.target;\r
13719 \r
13720                                         // Don't do this action for non image elements\r
13721                                         if (e.nodeName !== 'IMG')\r
13722                                                 return;\r
13723 \r
13724                                         if (re)\r
13725                                                 dom.unbind(re.node, re.ev, re.cb);\r
13726 \r
13727                                         if (!dom.hasClass(e, 'mceItemNoResize')) {\r
13728                                                 ev = 'resizeend';\r
13729                                                 cb = dom.bind(e, ev, function(e) {\r
13730                                                         var v;\r
13731 \r
13732                                                         e = e.target;\r
13733 \r
13734                                                         if (v = dom.getStyle(e, 'width')) {\r
13735                                                                 dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, ''));\r
13736                                                                 dom.setStyle(e, 'width', '');\r
13737                                                         }\r
13738 \r
13739                                                         if (v = dom.getStyle(e, 'height')) {\r
13740                                                                 dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, ''));\r
13741                                                                 dom.setStyle(e, 'height', '');\r
13742                                                         }\r
13743                                                 });\r
13744                                         } else {\r
13745                                                 ev = 'resizestart';\r
13746                                                 cb = dom.bind(e, 'resizestart', Event.cancel, Event);\r
13747                                         }\r
13748 \r
13749                                         re = t.resizeInfo = {\r
13750                                                 node : e,\r
13751                                                 ev : ev,\r
13752                                                 cb : cb\r
13753                                         };\r
13754                                 });\r
13755                         }\r
13756 \r
13757                         if (tinymce.isOpera) {\r
13758                                 t.onClick.add(function(ed, e) {\r
13759                                         Event.prevent(e);\r
13760                                 });\r
13761                         }\r
13762 \r
13763                         // Add custom undo/redo handlers\r
13764                         if (s.custom_undo_redo) {\r
13765                                 function addUndo() {\r
13766                                         t.undoManager.typing = false;\r
13767                                         t.undoManager.add();\r
13768                                 };\r
13769 \r
13770                                 var focusLostFunc = tinymce.isGecko ? 'blur' : 'focusout';\r
13771                                 dom.bind(t.getDoc(), focusLostFunc, function(e){\r
13772                                         if (!t.removed && t.undoManager.typing)\r
13773                                                 addUndo();\r
13774                                 });\r
13775 \r
13776                                 // Add undo level when contents is drag/dropped within the editor\r
13777                                 t.dom.bind(t.dom.getRoot(), 'dragend', function(e) {\r
13778                                         addUndo();\r
13779                                 });\r
13780 \r
13781                                 t.onKeyUp.add(function(ed, e) {\r
13782                                         var keyCode = e.keyCode;\r
13783 \r
13784                                         if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 13 || keyCode == 45 || e.ctrlKey)\r
13785                                                 addUndo();\r
13786                                 });\r
13787 \r
13788                                 t.onKeyDown.add(function(ed, e) {\r
13789                                         var keyCode = e.keyCode, sel;\r
13790 \r
13791                                         if (keyCode == 8) {\r
13792                                                 sel = t.getDoc().selection;\r
13793 \r
13794                                                 // Fix IE control + backspace browser bug\r
13795                                                 if (sel && sel.createRange && sel.createRange().item) {\r
13796                                                         t.undoManager.beforeChange();\r
13797                                                         ed.dom.remove(sel.createRange().item(0));\r
13798                                                         addUndo();\r
13799 \r
13800                                                         return Event.cancel(e);\r
13801                                                 }\r
13802                                         }\r
13803 \r
13804                                         // Is caracter positon keys left,right,up,down,home,end,pgdown,pgup,enter\r
13805                                         if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 13 || keyCode == 45) {\r
13806                                                 // Add position before enter key is pressed, used by IE since it still uses the default browser behavior\r
13807                                                 // Todo: Remove this once we normalize enter behavior on IE\r
13808                                                 if (tinymce.isIE && keyCode == 13)\r
13809                                                         t.undoManager.beforeChange();\r
13810 \r
13811                                                 if (t.undoManager.typing)\r
13812                                                         addUndo();\r
13813 \r
13814                                                 return;\r
13815                                         }\r
13816 \r
13817                                         // If key isn't shift,ctrl,alt,capslock,metakey\r
13818                                         if ((keyCode < 16 || keyCode > 20) && keyCode != 224 && keyCode != 91 && !t.undoManager.typing) {\r
13819                                                 t.undoManager.beforeChange();\r
13820                                                 t.undoManager.typing = true;\r
13821                                                 t.undoManager.add();\r
13822                                         }\r
13823                                 });\r
13824 \r
13825                                 t.onMouseDown.add(function() {\r
13826                                         if (t.undoManager.typing)\r
13827                                                 addUndo();\r
13828                                 });\r
13829                         }\r
13830                 },\r
13831 \r
13832                 _refreshContentEditable : function() {\r
13833                         var self = this, body, parent;\r
13834 \r
13835                         // Check if the editor was hidden and the re-initalize contentEditable mode by removing and adding the body again\r
13836                         if (self._isHidden()) {\r
13837                                 body = self.getBody();\r
13838                                 parent = body.parentNode;\r
13839 \r
13840                                 parent.removeChild(body);\r
13841                                 parent.appendChild(body);\r
13842 \r
13843                                 body.focus();\r
13844                         }\r
13845                 },\r
13846 \r
13847                 _isHidden : function() {\r
13848                         var s;\r
13849 \r
13850                         if (!isGecko)\r
13851                                 return 0;\r
13852 \r
13853                         // Weird, wheres that cursor selection?\r
13854                         s = this.selection.getSel();\r
13855                         return (!s || !s.rangeCount || s.rangeCount == 0);\r
13856                 }\r
13857         });\r
13858 })(tinymce);\r
13859 \r
13860 (function(tinymce) {\r
13861         // Added for compression purposes\r
13862         var each = tinymce.each, undefined, TRUE = true, FALSE = false;\r
13863 \r
13864         tinymce.EditorCommands = function(editor) {\r
13865                 var dom = editor.dom,\r
13866                         selection = editor.selection,\r
13867                         commands = {state: {}, exec : {}, value : {}},\r
13868                         settings = editor.settings,\r
13869                         formatter = editor.formatter,\r
13870                         bookmark;\r
13871 \r
13872                 function execCommand(command, ui, value) {\r
13873                         var func;\r
13874 \r
13875                         command = command.toLowerCase();\r
13876                         if (func = commands.exec[command]) {\r
13877                                 func(command, ui, value);\r
13878                                 return TRUE;\r
13879                         }\r
13880 \r
13881                         return FALSE;\r
13882                 };\r
13883 \r
13884                 function queryCommandState(command) {\r
13885                         var func;\r
13886 \r
13887                         command = command.toLowerCase();\r
13888                         if (func = commands.state[command])\r
13889                                 return func(command);\r
13890 \r
13891                         return -1;\r
13892                 };\r
13893 \r
13894                 function queryCommandValue(command) {\r
13895                         var func;\r
13896 \r
13897                         command = command.toLowerCase();\r
13898                         if (func = commands.value[command])\r
13899                                 return func(command);\r
13900 \r
13901                         return FALSE;\r
13902                 };\r
13903 \r
13904                 function addCommands(command_list, type) {\r
13905                         type = type || 'exec';\r
13906 \r
13907                         each(command_list, function(callback, command) {\r
13908                                 each(command.toLowerCase().split(','), function(command) {\r
13909                                         commands[type][command] = callback;\r
13910                                 });\r
13911                         });\r
13912                 };\r
13913 \r
13914                 // Expose public methods\r
13915                 tinymce.extend(this, {\r
13916                         execCommand : execCommand,\r
13917                         queryCommandState : queryCommandState,\r
13918                         queryCommandValue : queryCommandValue,\r
13919                         addCommands : addCommands\r
13920                 });\r
13921 \r
13922                 // Private methods\r
13923 \r
13924                 function execNativeCommand(command, ui, value) {\r
13925                         if (ui === undefined)\r
13926                                 ui = FALSE;\r
13927 \r
13928                         if (value === undefined)\r
13929                                 value = null;\r
13930 \r
13931                         return editor.getDoc().execCommand(command, ui, value);\r
13932                 };\r
13933 \r
13934                 function isFormatMatch(name) {\r
13935                         return formatter.match(name);\r
13936                 };\r
13937 \r
13938                 function toggleFormat(name, value) {\r
13939                         formatter.toggle(name, value ? {value : value} : undefined);\r
13940                 };\r
13941 \r
13942                 function storeSelection(type) {\r
13943                         bookmark = selection.getBookmark(type);\r
13944                 };\r
13945 \r
13946                 function restoreSelection() {\r
13947                         selection.moveToBookmark(bookmark);\r
13948                 };\r
13949 \r
13950                 // Add execCommand overrides\r
13951                 addCommands({\r
13952                         // Ignore these, added for compatibility\r
13953                         'mceResetDesignMode,mceBeginUndoLevel' : function() {},\r
13954 \r
13955                         // Add undo manager logic\r
13956                         'mceEndUndoLevel,mceAddUndoLevel' : function() {\r
13957                                 editor.undoManager.add();\r
13958                         },\r
13959 \r
13960                         'Cut,Copy,Paste' : function(command) {\r
13961                                 var doc = editor.getDoc(), failed;\r
13962 \r
13963                                 // Try executing the native command\r
13964                                 try {\r
13965                                         execNativeCommand(command);\r
13966                                 } catch (ex) {\r
13967                                         // Command failed\r
13968                                         failed = TRUE;\r
13969                                 }\r
13970 \r
13971                                 // Present alert message about clipboard access not being available\r
13972                                 if (failed || !doc.queryCommandSupported(command)) {\r
13973                                         if (tinymce.isGecko) {\r
13974                                                 editor.windowManager.confirm(editor.getLang('clipboard_msg'), function(state) {\r
13975                                                         if (state)\r
13976                                                                 open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', '_blank');\r
13977                                                 });\r
13978                                         } else\r
13979                                                 editor.windowManager.alert(editor.getLang('clipboard_no_support'));\r
13980                                 }\r
13981                         },\r
13982 \r
13983                         // Override unlink command\r
13984                         unlink : function(command) {\r
13985                                 if (selection.isCollapsed())\r
13986                                         selection.select(selection.getNode());\r
13987 \r
13988                                 execNativeCommand(command);\r
13989                                 selection.collapse(FALSE);\r
13990                         },\r
13991 \r
13992                         // Override justify commands to use the text formatter engine\r
13993                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
13994                                 var align = command.substring(7);\r
13995 \r
13996                                 // Remove all other alignments first\r
13997                                 each('left,center,right,full'.split(','), function(name) {\r
13998                                         if (align != name)\r
13999                                                 formatter.remove('align' + name);\r
14000                                 });\r
14001 \r
14002                                 toggleFormat('align' + align);\r
14003                                 execCommand('mceRepaint');\r
14004                         },\r
14005 \r
14006                         // Override list commands to fix WebKit bug\r
14007                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
14008                                 var listElm, listParent;\r
14009 \r
14010                                 execNativeCommand(command);\r
14011 \r
14012                                 // WebKit produces lists within block elements so we need to split them\r
14013                                 // we will replace the native list creation logic to custom logic later on\r
14014                                 // TODO: Remove this when the list creation logic is removed\r
14015                                 listElm = dom.getParent(selection.getNode(), 'ol,ul');\r
14016                                 if (listElm) {\r
14017                                         listParent = listElm.parentNode;\r
14018 \r
14019                                         // If list is within a text block then split that block\r
14020                                         if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {\r
14021                                                 storeSelection();\r
14022                                                 dom.split(listParent, listElm);\r
14023                                                 restoreSelection();\r
14024                                         }\r
14025                                 }\r
14026                         },\r
14027 \r
14028                         // Override commands to use the text formatter engine\r
14029                         'Bold,Italic,Underline,Strikethrough,Superscript,Subscript' : function(command) {\r
14030                                 toggleFormat(command);\r
14031                         },\r
14032 \r
14033                         // Override commands to use the text formatter engine\r
14034                         'ForeColor,HiliteColor,FontName' : function(command, ui, value) {\r
14035                                 toggleFormat(command, value);\r
14036                         },\r
14037 \r
14038                         FontSize : function(command, ui, value) {\r
14039                                 var fontClasses, fontSizes;\r
14040 \r
14041                                 // Convert font size 1-7 to styles\r
14042                                 if (value >= 1 && value <= 7) {\r
14043                                         fontSizes = tinymce.explode(settings.font_size_style_values);\r
14044                                         fontClasses = tinymce.explode(settings.font_size_classes);\r
14045 \r
14046                                         if (fontClasses)\r
14047                                                 value = fontClasses[value - 1] || value;\r
14048                                         else\r
14049                                                 value = fontSizes[value - 1] || value;\r
14050                                 }\r
14051 \r
14052                                 toggleFormat(command, value);\r
14053                         },\r
14054 \r
14055                         RemoveFormat : function(command) {\r
14056                                 formatter.remove(command);\r
14057                         },\r
14058 \r
14059                         mceBlockQuote : function(command) {\r
14060                                 toggleFormat('blockquote');\r
14061                         },\r
14062 \r
14063                         FormatBlock : function(command, ui, value) {\r
14064                                 return toggleFormat(value || 'p');\r
14065                         },\r
14066 \r
14067                         mceCleanup : function() {\r
14068                                 var bookmark = selection.getBookmark();\r
14069 \r
14070                                 editor.setContent(editor.getContent({cleanup : TRUE}), {cleanup : TRUE});\r
14071 \r
14072                                 selection.moveToBookmark(bookmark);\r
14073                         },\r
14074 \r
14075                         mceRemoveNode : function(command, ui, value) {\r
14076                                 var node = value || selection.getNode();\r
14077 \r
14078                                 // Make sure that the body node isn't removed\r
14079                                 if (node != editor.getBody()) {\r
14080                                         storeSelection();\r
14081                                         editor.dom.remove(node, TRUE);\r
14082                                         restoreSelection();\r
14083                                 }\r
14084                         },\r
14085 \r
14086                         mceSelectNodeDepth : function(command, ui, value) {\r
14087                                 var counter = 0;\r
14088 \r
14089                                 dom.getParent(selection.getNode(), function(node) {\r
14090                                         if (node.nodeType == 1 && counter++ == value) {\r
14091                                                 selection.select(node);\r
14092                                                 return FALSE;\r
14093                                         }\r
14094                                 }, editor.getBody());\r
14095                         },\r
14096 \r
14097                         mceSelectNode : function(command, ui, value) {\r
14098                                 selection.select(value);\r
14099                         },\r
14100 \r
14101                         mceInsertContent : function(command, ui, value) {\r
14102                                 var parser, serializer, parentNode, rootNode, fragment, args,\r
14103                                         marker, nodeRect, viewPortRect, rng, node, node2, bookmarkHtml, viewportBodyElement;\r
14104 \r
14105                                 //selection.normalize();\r
14106 \r
14107                                 // Setup parser and serializer\r
14108                                 parser = editor.parser;\r
14109                                 serializer = new tinymce.html.Serializer({}, editor.schema);\r
14110                                 bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">\uFEFF</span>';\r
14111 \r
14112                                 // Run beforeSetContent handlers on the HTML to be inserted\r
14113                                 args = {content: value, format: 'html'};\r
14114                                 selection.onBeforeSetContent.dispatch(selection, args);\r
14115                                 value = args.content;\r
14116 \r
14117                                 // Add caret at end of contents if it's missing\r
14118                                 if (value.indexOf('{$caret}') == -1)\r
14119                                         value += '{$caret}';\r
14120 \r
14121                                 // Replace the caret marker with a span bookmark element\r
14122                                 value = value.replace(/\{\$caret\}/, bookmarkHtml);\r
14123 \r
14124                                 // Insert node maker where we will insert the new HTML and get it's parent\r
14125                                 if (!selection.isCollapsed())\r
14126                                         editor.getDoc().execCommand('Delete', false, null);\r
14127 \r
14128                                 parentNode = selection.getNode();\r
14129 \r
14130                                 // Parse the fragment within the context of the parent node\r
14131                                 args = {context : parentNode.nodeName.toLowerCase()};\r
14132                                 fragment = parser.parse(value, args);\r
14133 \r
14134                                 // Move the caret to a more suitable location\r
14135                                 node = fragment.lastChild;\r
14136                                 if (node.attr('id') == 'mce_marker') {\r
14137                                         marker = node;\r
14138 \r
14139                                         for (node = node.prev; node; node = node.walk(true)) {\r
14140                                                 if (node.type == 3 || !dom.isBlock(node.name)) {\r
14141                                                         node.parent.insert(marker, node, node.name === 'br');\r
14142                                                         break;\r
14143                                                 }\r
14144                                         }\r
14145                                 }\r
14146 \r
14147                                 // If parser says valid we can insert the contents into that parent\r
14148                                 if (!args.invalid) {\r
14149                                         value = serializer.serialize(fragment);\r
14150 \r
14151                                         // Check if parent is empty or only has one BR element then set the innerHTML of that parent\r
14152                                         node = parentNode.firstChild;\r
14153                                         node2 = parentNode.lastChild;\r
14154                                         if (!node || (node === node2 && node.nodeName === 'BR'))\r
14155                                                 dom.setHTML(parentNode, value);\r
14156                                         else\r
14157                                                 selection.setContent(value);\r
14158                                 } else {\r
14159                                         // If the fragment was invalid within that context then we need\r
14160                                         // to parse and process the parent it's inserted into\r
14161 \r
14162                                         // Insert bookmark node and get the parent\r
14163                                         selection.setContent(bookmarkHtml);\r
14164                                         parentNode = editor.selection.getNode();\r
14165                                         rootNode = editor.getBody();\r
14166 \r
14167                                         // Opera will return the document node when selection is in root\r
14168                                         if (parentNode.nodeType == 9)\r
14169                                                 parentNode = node = rootNode;\r
14170                                         else\r
14171                                                 node = parentNode;\r
14172 \r
14173                                         // Find the ancestor just before the root element\r
14174                                         while (node !== rootNode) {\r
14175                                                 parentNode = node;\r
14176                                                 node = node.parentNode;\r
14177                                         }\r
14178 \r
14179                                         // Get the outer/inner HTML depending on if we are in the root and parser and serialize that\r
14180                                         value = parentNode == rootNode ? rootNode.innerHTML : dom.getOuterHTML(parentNode);\r
14181                                         value = serializer.serialize(\r
14182                                                 parser.parse(\r
14183                                                         // Need to replace by using a function since $ in the contents would otherwise be a problem\r
14184                                                         value.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i, function() {\r
14185                                                                 return serializer.serialize(fragment);\r
14186                                                         })\r
14187                                                 )\r
14188                                         );\r
14189 \r
14190                                         // Set the inner/outer HTML depending on if we are in the root or not\r
14191                                         if (parentNode == rootNode)\r
14192                                                 dom.setHTML(rootNode, value);\r
14193                                         else\r
14194                                                 dom.setOuterHTML(parentNode, value);\r
14195                                 }\r
14196 \r
14197                                 marker = dom.get('mce_marker');\r
14198 \r
14199                                 // Scroll range into view scrollIntoView on element can't be used since it will scroll the main view port as well\r
14200                                 nodeRect = dom.getRect(marker);\r
14201                                 viewPortRect = dom.getViewPort(editor.getWin());\r
14202 \r
14203                                 // Check if node is out side the viewport if it is then scroll to it\r
14204                                 if ((nodeRect.y + nodeRect.h > viewPortRect.y + viewPortRect.h || nodeRect.y < viewPortRect.y) ||\r
14205                                         (nodeRect.x > viewPortRect.x + viewPortRect.w || nodeRect.x < viewPortRect.x)) {\r
14206                                         viewportBodyElement = tinymce.isIE ? editor.getDoc().documentElement : editor.getBody();\r
14207                                         viewportBodyElement.scrollLeft = nodeRect.x;\r
14208                                         viewportBodyElement.scrollTop = nodeRect.y - viewPortRect.h + 25;\r
14209                                 }\r
14210 \r
14211                                 // Move selection before marker and remove it\r
14212                                 rng = dom.createRng();\r
14213 \r
14214                                 // If previous sibling is a text node set the selection to the end of that node\r
14215                                 node = marker.previousSibling;\r
14216                                 if (node && node.nodeType == 3) {\r
14217                                         rng.setStart(node, node.nodeValue.length);\r
14218                                 } else {\r
14219                                         // If the previous sibling isn't a text node or doesn't exist set the selection before the marker node\r
14220                                         rng.setStartBefore(marker);\r
14221                                         rng.setEndBefore(marker);\r
14222                                 }\r
14223 \r
14224                                 // Remove the marker node and set the new range\r
14225                                 dom.remove(marker);\r
14226                                 selection.setRng(rng);\r
14227 \r
14228                                 // Dispatch after event and add any visual elements needed\r
14229                                 selection.onSetContent.dispatch(selection, args);\r
14230                                 editor.addVisual();\r
14231                         },\r
14232 \r
14233                         mceInsertRawHTML : function(command, ui, value) {\r
14234                                 selection.setContent('tiny_mce_marker');\r
14235                                 editor.setContent(editor.getContent().replace(/tiny_mce_marker/g, function() { return value }));\r
14236                         },\r
14237 \r
14238                         mceSetContent : function(command, ui, value) {\r
14239                                 editor.setContent(value);\r
14240                         },\r
14241 \r
14242                         'Indent,Outdent' : function(command) {\r
14243                                 var intentValue, indentUnit, value;\r
14244 \r
14245                                 // Setup indent level\r
14246                                 intentValue = settings.indentation;\r
14247                                 indentUnit = /[a-z%]+$/i.exec(intentValue);\r
14248                                 intentValue = parseInt(intentValue);\r
14249 \r
14250                                 if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) {\r
14251                                         // If forced_root_blocks is set to false we don't have a block to indent so lets create a div\r
14252                                         if (!settings.forced_root_block && !dom.getParent(selection.getNode(), dom.isBlock)) {\r
14253                                                 formatter.apply('div');\r
14254                                         }\r
14255 \r
14256                                         each(selection.getSelectedBlocks(), function(element) {\r
14257                                                 if (command == 'outdent') {\r
14258                                                         value = Math.max(0, parseInt(element.style.paddingLeft || 0) - intentValue);\r
14259                                                         dom.setStyle(element, 'paddingLeft', value ? value + indentUnit : '');\r
14260                                                 } else\r
14261                                                         dom.setStyle(element, 'paddingLeft', (parseInt(element.style.paddingLeft || 0) + intentValue) + indentUnit);\r
14262                                         });\r
14263                                 } else\r
14264                                         execNativeCommand(command);\r
14265                         },\r
14266 \r
14267                         mceRepaint : function() {\r
14268                                 var bookmark;\r
14269 \r
14270                                 if (tinymce.isGecko) {\r
14271                                         try {\r
14272                                                 storeSelection(TRUE);\r
14273 \r
14274                                                 if (selection.getSel())\r
14275                                                         selection.getSel().selectAllChildren(editor.getBody());\r
14276 \r
14277                                                 selection.collapse(TRUE);\r
14278                                                 restoreSelection();\r
14279                                         } catch (ex) {\r
14280                                                 // Ignore\r
14281                                         }\r
14282                                 }\r
14283                         },\r
14284 \r
14285                         mceToggleFormat : function(command, ui, value) {\r
14286                                 formatter.toggle(value);\r
14287                         },\r
14288 \r
14289                         InsertHorizontalRule : function() {\r
14290                                 editor.execCommand('mceInsertContent', false, '<hr />');\r
14291                         },\r
14292 \r
14293                         mceToggleVisualAid : function() {\r
14294                                 editor.hasVisual = !editor.hasVisual;\r
14295                                 editor.addVisual();\r
14296                         },\r
14297 \r
14298                         mceReplaceContent : function(command, ui, value) {\r
14299                                 editor.execCommand('mceInsertContent', false, value.replace(/\{\$selection\}/g, selection.getContent({format : 'text'})));\r
14300                         },\r
14301 \r
14302                         mceInsertLink : function(command, ui, value) {\r
14303                                 var anchor;\r
14304 \r
14305                                 if (typeof(value) == 'string')\r
14306                                         value = {href : value};\r
14307 \r
14308                                 anchor = dom.getParent(selection.getNode(), 'a');\r
14309 \r
14310                                 // Spaces are never valid in URLs and it's a very common mistake for people to make so we fix it here.\r
14311                                 value.href = value.href.replace(' ', '%20');\r
14312 \r
14313                                 // Remove existing links if there could be child links or that the href isn't specified\r
14314                                 if (!anchor || !value.href) {\r
14315                                         formatter.remove('link');\r
14316                                 }               \r
14317 \r
14318                                 // Apply new link to selection\r
14319                                 if (value.href) {\r
14320                                         formatter.apply('link', value, anchor);\r
14321                                 }\r
14322                         },\r
14323 \r
14324                         selectAll : function() {\r
14325                                 var root = dom.getRoot(), rng = dom.createRng();\r
14326 \r
14327                                 rng.setStart(root, 0);\r
14328                                 rng.setEnd(root, root.childNodes.length);\r
14329 \r
14330                                 editor.selection.setRng(rng);\r
14331                         }\r
14332                 });\r
14333 \r
14334                 // Add queryCommandState overrides\r
14335                 addCommands({\r
14336                         // Override justify commands\r
14337                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
14338                                 var name = 'align' + command.substring(7);\r
14339                                 // Use Formatter.matchNode instead of Formatter.match so that we don't match on parent node. This fixes bug where for both left\r
14340                                 // and right align buttons can be active. This could occur when selected nodes have align right and the parent has align left.\r
14341                                 var nodes = selection.isCollapsed() ? [selection.getNode()] : selection.getSelectedBlocks();\r
14342                                 var matches = tinymce.map(nodes, function(node) {\r
14343                                         return !!formatter.matchNode(node, name);\r
14344                                 });\r
14345                                 return tinymce.inArray(matches, TRUE) !== -1;\r
14346                         },\r
14347 \r
14348                         'Bold,Italic,Underline,Strikethrough,Superscript,Subscript' : function(command) {\r
14349                                 return isFormatMatch(command);\r
14350                         },\r
14351 \r
14352                         mceBlockQuote : function() {\r
14353                                 return isFormatMatch('blockquote');\r
14354                         },\r
14355 \r
14356                         Outdent : function() {\r
14357                                 var node;\r
14358 \r
14359                                 if (settings.inline_styles) {\r
14360                                         if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
14361                                                 return TRUE;\r
14362 \r
14363                                         if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
14364                                                 return TRUE;\r
14365                                 }\r
14366 \r
14367                                 return queryCommandState('InsertUnorderedList') || queryCommandState('InsertOrderedList') || (!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE'));\r
14368                         },\r
14369 \r
14370                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
14371                                 return dom.getParent(selection.getNode(), command == 'insertunorderedlist' ? 'UL' : 'OL');\r
14372                         }\r
14373                 }, 'state');\r
14374 \r
14375                 // Add queryCommandValue overrides\r
14376                 addCommands({\r
14377                         'FontSize,FontName' : function(command) {\r
14378                                 var value = 0, parent;\r
14379 \r
14380                                 if (parent = dom.getParent(selection.getNode(), 'span')) {\r
14381                                         if (command == 'fontsize')\r
14382                                                 value = parent.style.fontSize;\r
14383                                         else\r
14384                                                 value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();\r
14385                                 }\r
14386 \r
14387                                 return value;\r
14388                         }\r
14389                 }, 'value');\r
14390 \r
14391                 // Add undo manager logic\r
14392                 if (settings.custom_undo_redo) {\r
14393                         addCommands({\r
14394                                 Undo : function() {\r
14395                                         editor.undoManager.undo();\r
14396                                 },\r
14397 \r
14398                                 Redo : function() {\r
14399                                         editor.undoManager.redo();\r
14400                                 }\r
14401                         });\r
14402                 }\r
14403         };\r
14404 })(tinymce);\r
14405 \r
14406 (function(tinymce) {\r
14407         var Dispatcher = tinymce.util.Dispatcher;\r
14408 \r
14409         tinymce.UndoManager = function(editor) {\r
14410                 var self, index = 0, data = [], beforeBookmark;\r
14411 \r
14412                 function getContent() {\r
14413                         // Remove whitespace before/after and remove pure bogus nodes\r
14414                         return tinymce.trim(editor.getContent({format : 'raw', no_events : 1}).replace(/<span[^>]+data-mce-bogus[^>]+>[\u200B\uFEFF]+<\/span>/g, ''));\r
14415                 };\r
14416 \r
14417                 return self = {\r
14418                         typing : false,\r
14419 \r
14420                         onAdd : new Dispatcher(self),\r
14421 \r
14422                         onUndo : new Dispatcher(self),\r
14423 \r
14424                         onRedo : new Dispatcher(self),\r
14425 \r
14426                         beforeChange : function() {\r
14427                                 beforeBookmark = editor.selection.getBookmark(2, true);\r
14428                         },\r
14429 \r
14430                         add : function(level) {\r
14431                                 var i, settings = editor.settings, lastLevel;\r
14432 \r
14433                                 level = level || {};\r
14434                                 level.content = getContent();\r
14435 \r
14436                                 // Add undo level if needed\r
14437                                 lastLevel = data[index];\r
14438                                 if (lastLevel && lastLevel.content == level.content)\r
14439                                         return null;\r
14440 \r
14441                                 // Set before bookmark on previous level\r
14442                                 if (data[index])\r
14443                                         data[index].beforeBookmark = beforeBookmark;\r
14444 \r
14445                                 // Time to compress\r
14446                                 if (settings.custom_undo_redo_levels) {\r
14447                                         if (data.length > settings.custom_undo_redo_levels) {\r
14448                                                 for (i = 0; i < data.length - 1; i++)\r
14449                                                         data[i] = data[i + 1];\r
14450 \r
14451                                                 data.length--;\r
14452                                                 index = data.length;\r
14453                                         }\r
14454                                 }\r
14455 \r
14456                                 // Get a non intrusive normalized bookmark\r
14457                                 level.bookmark = editor.selection.getBookmark(2, true);\r
14458 \r
14459                                 // Crop array if needed\r
14460                                 if (index < data.length - 1)\r
14461                                         data.length = index + 1;\r
14462 \r
14463                                 data.push(level);\r
14464                                 index = data.length - 1;\r
14465 \r
14466                                 self.onAdd.dispatch(self, level);\r
14467                                 editor.isNotDirty = 0;\r
14468 \r
14469                                 return level;\r
14470                         },\r
14471 \r
14472                         undo : function() {\r
14473                                 var level, i;\r
14474 \r
14475                                 if (self.typing) {\r
14476                                         self.add();\r
14477                                         self.typing = false;\r
14478                                 }\r
14479 \r
14480                                 if (index > 0) {\r
14481                                         level = data[--index];\r
14482 \r
14483                                         editor.setContent(level.content, {format : 'raw'});\r
14484                                         editor.selection.moveToBookmark(level.beforeBookmark);\r
14485 \r
14486                                         self.onUndo.dispatch(self, level);\r
14487                                 }\r
14488 \r
14489                                 return level;\r
14490                         },\r
14491 \r
14492                         redo : function() {\r
14493                                 var level;\r
14494 \r
14495                                 if (index < data.length - 1) {\r
14496                                         level = data[++index];\r
14497 \r
14498                                         editor.setContent(level.content, {format : 'raw'});\r
14499                                         editor.selection.moveToBookmark(level.bookmark);\r
14500 \r
14501                                         self.onRedo.dispatch(self, level);\r
14502                                 }\r
14503 \r
14504                                 return level;\r
14505                         },\r
14506 \r
14507                         clear : function() {\r
14508                                 data = [];\r
14509                                 index = 0;\r
14510                                 self.typing = false;\r
14511                         },\r
14512 \r
14513                         hasUndo : function() {\r
14514                                 return index > 0 || this.typing;\r
14515                         },\r
14516 \r
14517                         hasRedo : function() {\r
14518                                 return index < data.length - 1 && !this.typing;\r
14519                         }\r
14520                 };\r
14521         };\r
14522 })(tinymce);\r
14523 \r
14524 tinymce.ForceBlocks = function(editor) {\r
14525         var settings = editor.settings, dom = editor.dom, selection = editor.selection, blockElements = editor.schema.getBlockElements();\r
14526 \r
14527         // Force root blocks\r
14528         if (settings.forced_root_block) {\r
14529                 function addRootBlocks() {\r
14530                         var node = selection.getStart(), rootNode = editor.getBody(), rng, startContainer, startOffset, endContainer, endOffset, rootBlockNode, tempNode, offset = -0xFFFFFF;\r
14531 \r
14532                         if (!node || node.nodeType !== 1 || !settings.forced_root_block)\r
14533                                 return;\r
14534 \r
14535                         // Check if node is wrapped in block\r
14536                         while (node != rootNode) {\r
14537                                 if (blockElements[node.nodeName])\r
14538                                         return;\r
14539 \r
14540                                 node = node.parentNode;\r
14541                         }\r
14542 \r
14543                         // Get current selection\r
14544                         rng = selection.getRng();\r
14545                         if (rng.setStart) {\r
14546                                 startContainer = rng.startContainer;\r
14547                                 startOffset = rng.startOffset;\r
14548                                 endContainer = rng.endContainer;\r
14549                                 endOffset = rng.endOffset;\r
14550                         } else {\r
14551                                 // Force control range into text range\r
14552                                 if (rng.item) {\r
14553                                         node = rng.item(0);\r
14554                                         rng = editor.getDoc().body.createTextRange();\r
14555                                         rng.moveToElementText(node);\r
14556                                 }\r
14557 \r
14558                                 tmpRng = rng.duplicate();\r
14559                                 tmpRng.collapse(true);\r
14560                                 startOffset = tmpRng.move('character', offset) * -1;\r
14561 \r
14562                                 if (!tmpRng.collapsed) {\r
14563                                         tmpRng = rng.duplicate();\r
14564                                         tmpRng.collapse(false);\r
14565                                         endOffset = (tmpRng.move('character', offset) * -1) - startOffset;\r
14566                                 }\r
14567                         }\r
14568 \r
14569                         // Wrap non block elements and text nodes\r
14570                         for (node = rootNode.firstChild; node; node) {\r
14571                                 if (node.nodeType === 3 || (node.nodeType == 1 && !blockElements[node.nodeName])) {\r
14572                                         if (!rootBlockNode) {\r
14573                                                 rootBlockNode = dom.create(settings.forced_root_block);\r
14574                                                 node.parentNode.insertBefore(rootBlockNode, node);\r
14575                                         }\r
14576 \r
14577                                         tempNode = node;\r
14578                                         node = node.nextSibling;\r
14579                                         rootBlockNode.appendChild(tempNode);\r
14580                                 } else {\r
14581                                         rootBlockNode = null;\r
14582                                         node = node.nextSibling;\r
14583                                 }\r
14584                         }\r
14585 \r
14586                         if (rng.setStart) {\r
14587                                 rng.setStart(startContainer, startOffset);\r
14588                                 rng.setEnd(endContainer, endOffset);\r
14589                                 selection.setRng(rng);\r
14590                         } else {\r
14591                                 try {\r
14592                                         rng = editor.getDoc().body.createTextRange();\r
14593                                         rng.moveToElementText(rootNode);\r
14594                                         rng.collapse(true);\r
14595                                         rng.moveStart('character', startOffset);\r
14596 \r
14597                                         if (endOffset > 0)\r
14598                                                 rng.moveEnd('character', endOffset);\r
14599 \r
14600                                         rng.select();\r
14601                                 } catch (ex) {\r
14602                                         // Ignore\r
14603                                 }\r
14604                         }\r
14605 \r
14606                         editor.nodeChanged();\r
14607                 };\r
14608 \r
14609                 editor.onKeyUp.add(addRootBlocks);\r
14610                 editor.onClick.add(addRootBlocks);\r
14611         }\r
14612 };\r
14613 \r
14614 (function(tinymce) {\r
14615         // Shorten names\r
14616         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;\r
14617 \r
14618         tinymce.create('tinymce.ControlManager', {\r
14619                 ControlManager : function(ed, s) {\r
14620                         var t = this, i;\r
14621 \r
14622                         s = s || {};\r
14623                         t.editor = ed;\r
14624                         t.controls = {};\r
14625                         t.onAdd = new tinymce.util.Dispatcher(t);\r
14626                         t.onPostRender = new tinymce.util.Dispatcher(t);\r
14627                         t.prefix = s.prefix || ed.id + '_';\r
14628                         t._cls = {};\r
14629 \r
14630                         t.onPostRender.add(function() {\r
14631                                 each(t.controls, function(c) {\r
14632                                         c.postRender();\r
14633                                 });\r
14634                         });\r
14635                 },\r
14636 \r
14637                 get : function(id) {\r
14638                         return this.controls[this.prefix + id] || this.controls[id];\r
14639                 },\r
14640 \r
14641                 setActive : function(id, s) {\r
14642                         var c = null;\r
14643 \r
14644                         if (c = this.get(id))\r
14645                                 c.setActive(s);\r
14646 \r
14647                         return c;\r
14648                 },\r
14649 \r
14650                 setDisabled : function(id, s) {\r
14651                         var c = null;\r
14652 \r
14653                         if (c = this.get(id))\r
14654                                 c.setDisabled(s);\r
14655 \r
14656                         return c;\r
14657                 },\r
14658 \r
14659                 add : function(c) {\r
14660                         var t = this;\r
14661 \r
14662                         if (c) {\r
14663                                 t.controls[c.id] = c;\r
14664                                 t.onAdd.dispatch(c, t);\r
14665                         }\r
14666 \r
14667                         return c;\r
14668                 },\r
14669 \r
14670                 createControl : function(n) {\r
14671                         var c, t = this, ed = t.editor;\r
14672 \r
14673                         each(ed.plugins, function(p) {\r
14674                                 if (p.createControl) {\r
14675                                         c = p.createControl(n, t);\r
14676 \r
14677                                         if (c)\r
14678                                                 return false;\r
14679                                 }\r
14680                         });\r
14681 \r
14682                         switch (n) {\r
14683                                 case "|":\r
14684                                 case "separator":\r
14685                                         return t.createSeparator();\r
14686                         }\r
14687 \r
14688                         if (!c && ed.buttons && (c = ed.buttons[n]))\r
14689                                 return t.createButton(n, c);\r
14690 \r
14691                         return t.add(c);\r
14692                 },\r
14693 \r
14694                 createDropMenu : function(id, s, cc) {\r
14695                         var t = this, ed = t.editor, c, bm, v, cls;\r
14696 \r
14697                         s = extend({\r
14698                                 'class' : 'mceDropDown',\r
14699                                 constrain : ed.settings.constrain_menus\r
14700                         }, s);\r
14701 \r
14702                         s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin';\r
14703                         if (v = ed.getParam('skin_variant'))\r
14704                                 s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);\r
14705 \r
14706                         id = t.prefix + id;\r
14707                         cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;\r
14708                         c = t.controls[id] = new cls(id, s);\r
14709                         c.onAddItem.add(function(c, o) {\r
14710                                 var s = o.settings;\r
14711 \r
14712                                 s.title = ed.getLang(s.title, s.title);\r
14713 \r
14714                                 if (!s.onclick) {\r
14715                                         s.onclick = function(v) {\r
14716                                                 if (s.cmd)\r
14717                                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
14718                                         };\r
14719                                 }\r
14720                         });\r
14721 \r
14722                         ed.onRemove.add(function() {\r
14723                                 c.destroy();\r
14724                         });\r
14725 \r
14726                         // Fix for bug #1897785, #1898007\r
14727                         if (tinymce.isIE) {\r
14728                                 c.onShowMenu.add(function() {\r
14729                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
14730                                         ed.focus();\r
14731 \r
14732                                         bm = ed.selection.getBookmark(1);\r
14733                                 });\r
14734 \r
14735                                 c.onHideMenu.add(function() {\r
14736                                         if (bm) {\r
14737                                                 ed.selection.moveToBookmark(bm);\r
14738                                                 bm = 0;\r
14739                                         }\r
14740                                 });\r
14741                         }\r
14742 \r
14743                         return t.add(c);\r
14744                 },\r
14745 \r
14746                 createListBox : function(id, s, cc) {\r
14747                         var t = this, ed = t.editor, cmd, c, cls;\r
14748 \r
14749                         if (t.get(id))\r
14750                                 return null;\r
14751 \r
14752                         s.title = ed.translate(s.title);\r
14753                         s.scope = s.scope || ed;\r
14754 \r
14755                         if (!s.onselect) {\r
14756                                 s.onselect = function(v) {\r
14757                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
14758                                 };\r
14759                         }\r
14760 \r
14761                         s = extend({\r
14762                                 title : s.title,\r
14763                                 'class' : 'mce_' + id,\r
14764                                 scope : s.scope,\r
14765                                 control_manager : t\r
14766                         }, s);\r
14767 \r
14768                         id = t.prefix + id;\r
14769 \r
14770 \r
14771                         function useNativeListForAccessibility(ed) {\r
14772                                 return ed.settings.use_accessible_selects && !tinymce.isGecko\r
14773                         }\r
14774 \r
14775                         if (ed.settings.use_native_selects || useNativeListForAccessibility(ed))\r
14776                                 c = new tinymce.ui.NativeListBox(id, s);\r
14777                         else {\r
14778                                 cls = cc || t._cls.listbox || tinymce.ui.ListBox;\r
14779                                 c = new cls(id, s, ed);\r
14780                         }\r
14781 \r
14782                         t.controls[id] = c;\r
14783 \r
14784                         // Fix focus problem in Safari\r
14785                         if (tinymce.isWebKit) {\r
14786                                 c.onPostRender.add(function(c, n) {\r
14787                                         // Store bookmark on mousedown\r
14788                                         Event.add(n, 'mousedown', function() {\r
14789                                                 ed.bookmark = ed.selection.getBookmark(1);\r
14790                                         });\r
14791 \r
14792                                         // Restore on focus, since it might be lost\r
14793                                         Event.add(n, 'focus', function() {\r
14794                                                 ed.selection.moveToBookmark(ed.bookmark);\r
14795                                                 ed.bookmark = null;\r
14796                                         });\r
14797                                 });\r
14798                         }\r
14799 \r
14800                         if (c.hideMenu)\r
14801                                 ed.onMouseDown.add(c.hideMenu, c);\r
14802 \r
14803                         return t.add(c);\r
14804                 },\r
14805 \r
14806                 createButton : function(id, s, cc) {\r
14807                         var t = this, ed = t.editor, o, c, cls;\r
14808 \r
14809                         if (t.get(id))\r
14810                                 return null;\r
14811 \r
14812                         s.title = ed.translate(s.title);\r
14813                         s.label = ed.translate(s.label);\r
14814                         s.scope = s.scope || ed;\r
14815 \r
14816                         if (!s.onclick && !s.menu_button) {\r
14817                                 s.onclick = function() {\r
14818                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
14819                                 };\r
14820                         }\r
14821 \r
14822                         s = extend({\r
14823                                 title : s.title,\r
14824                                 'class' : 'mce_' + id,\r
14825                                 unavailable_prefix : ed.getLang('unavailable', ''),\r
14826                                 scope : s.scope,\r
14827                                 control_manager : t\r
14828                         }, s);\r
14829 \r
14830                         id = t.prefix + id;\r
14831 \r
14832                         if (s.menu_button) {\r
14833                                 cls = cc || t._cls.menubutton || tinymce.ui.MenuButton;\r
14834                                 c = new cls(id, s, ed);\r
14835                                 ed.onMouseDown.add(c.hideMenu, c);\r
14836                         } else {\r
14837                                 cls = t._cls.button || tinymce.ui.Button;\r
14838                                 c = new cls(id, s, ed);\r
14839                         }\r
14840 \r
14841                         return t.add(c);\r
14842                 },\r
14843 \r
14844                 createMenuButton : function(id, s, cc) {\r
14845                         s = s || {};\r
14846                         s.menu_button = 1;\r
14847 \r
14848                         return this.createButton(id, s, cc);\r
14849                 },\r
14850 \r
14851                 createSplitButton : function(id, s, cc) {\r
14852                         var t = this, ed = t.editor, cmd, c, cls;\r
14853 \r
14854                         if (t.get(id))\r
14855                                 return null;\r
14856 \r
14857                         s.title = ed.translate(s.title);\r
14858                         s.scope = s.scope || ed;\r
14859 \r
14860                         if (!s.onclick) {\r
14861                                 s.onclick = function(v) {\r
14862                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
14863                                 };\r
14864                         }\r
14865 \r
14866                         if (!s.onselect) {\r
14867                                 s.onselect = function(v) {\r
14868                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
14869                                 };\r
14870                         }\r
14871 \r
14872                         s = extend({\r
14873                                 title : s.title,\r
14874                                 'class' : 'mce_' + id,\r
14875                                 scope : s.scope,\r
14876                                 control_manager : t\r
14877                         }, s);\r
14878 \r
14879                         id = t.prefix + id;\r
14880                         cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton;\r
14881                         c = t.add(new cls(id, s, ed));\r
14882                         ed.onMouseDown.add(c.hideMenu, c);\r
14883 \r
14884                         return c;\r
14885                 },\r
14886 \r
14887                 createColorSplitButton : function(id, s, cc) {\r
14888                         var t = this, ed = t.editor, cmd, c, cls, bm;\r
14889 \r
14890                         if (t.get(id))\r
14891                                 return null;\r
14892 \r
14893                         s.title = ed.translate(s.title);\r
14894                         s.scope = s.scope || ed;\r
14895 \r
14896                         if (!s.onclick) {\r
14897                                 s.onclick = function(v) {\r
14898                                         if (tinymce.isIE)\r
14899                                                 bm = ed.selection.getBookmark(1);\r
14900 \r
14901                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
14902                                 };\r
14903                         }\r
14904 \r
14905                         if (!s.onselect) {\r
14906                                 s.onselect = function(v) {\r
14907                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
14908                                 };\r
14909                         }\r
14910 \r
14911                         s = extend({\r
14912                                 title : s.title,\r
14913                                 'class' : 'mce_' + id,\r
14914                                 'menu_class' : ed.getParam('skin') + 'Skin',\r
14915                                 scope : s.scope,\r
14916                                 more_colors_title : ed.getLang('more_colors')\r
14917                         }, s);\r
14918 \r
14919                         id = t.prefix + id;\r
14920                         cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton;\r
14921                         c = new cls(id, s, ed);\r
14922                         ed.onMouseDown.add(c.hideMenu, c);\r
14923 \r
14924                         // Remove the menu element when the editor is removed\r
14925                         ed.onRemove.add(function() {\r
14926                                 c.destroy();\r
14927                         });\r
14928 \r
14929                         // Fix for bug #1897785, #1898007\r
14930                         if (tinymce.isIE) {\r
14931                                 c.onShowMenu.add(function() {\r
14932                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
14933                                         ed.focus();\r
14934                                         bm = ed.selection.getBookmark(1);\r
14935                                 });\r
14936 \r
14937                                 c.onHideMenu.add(function() {\r
14938                                         if (bm) {\r
14939                                                 ed.selection.moveToBookmark(bm);\r
14940                                                 bm = 0;\r
14941                                         }\r
14942                                 });\r
14943                         }\r
14944 \r
14945                         return t.add(c);\r
14946                 },\r
14947 \r
14948                 createToolbar : function(id, s, cc) {\r
14949                         var c, t = this, cls;\r
14950 \r
14951                         id = t.prefix + id;\r
14952                         cls = cc || t._cls.toolbar || tinymce.ui.Toolbar;\r
14953                         c = new cls(id, s, t.editor);\r
14954 \r
14955                         if (t.get(id))\r
14956                                 return null;\r
14957 \r
14958                         return t.add(c);\r
14959                 },\r
14960                 \r
14961                 createToolbarGroup : function(id, s, cc) {\r
14962                         var c, t = this, cls;\r
14963                         id = t.prefix + id;\r
14964                         cls = cc || this._cls.toolbarGroup || tinymce.ui.ToolbarGroup;\r
14965                         c = new cls(id, s, t.editor);\r
14966                         \r
14967                         if (t.get(id))\r
14968                                 return null;\r
14969                         \r
14970                         return t.add(c);\r
14971                 },\r
14972 \r
14973                 createSeparator : function(cc) {\r
14974                         var cls = cc || this._cls.separator || tinymce.ui.Separator;\r
14975 \r
14976                         return new cls();\r
14977                 },\r
14978 \r
14979                 setControlType : function(n, c) {\r
14980                         return this._cls[n.toLowerCase()] = c;\r
14981                 },\r
14982         \r
14983                 destroy : function() {\r
14984                         each(this.controls, function(c) {\r
14985                                 c.destroy();\r
14986                         });\r
14987 \r
14988                         this.controls = null;\r
14989                 }\r
14990         });\r
14991 })(tinymce);\r
14992 \r
14993 (function(tinymce) {\r
14994         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;\r
14995 \r
14996         tinymce.create('tinymce.WindowManager', {\r
14997                 WindowManager : function(ed) {\r
14998                         var t = this;\r
14999 \r
15000                         t.editor = ed;\r
15001                         t.onOpen = new Dispatcher(t);\r
15002                         t.onClose = new Dispatcher(t);\r
15003                         t.params = {};\r
15004                         t.features = {};\r
15005                 },\r
15006 \r
15007                 open : function(s, p) {\r
15008                         var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u;\r
15009 \r
15010                         // Default some options\r
15011                         s = s || {};\r
15012                         p = p || {};\r
15013                         sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window\r
15014                         sh = isOpera ? vp.h : screen.height;\r
15015                         s.name = s.name || 'mc_' + new Date().getTime();\r
15016                         s.width = parseInt(s.width || 320);\r
15017                         s.height = parseInt(s.height || 240);\r
15018                         s.resizable = true;\r
15019                         s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0);\r
15020                         s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0);\r
15021                         p.inline = false;\r
15022                         p.mce_width = s.width;\r
15023                         p.mce_height = s.height;\r
15024                         p.mce_auto_focus = s.auto_focus;\r
15025 \r
15026                         if (mo) {\r
15027                                 if (isIE) {\r
15028                                         s.center = true;\r
15029                                         s.help = false;\r
15030                                         s.dialogWidth = s.width + 'px';\r
15031                                         s.dialogHeight = s.height + 'px';\r
15032                                         s.scroll = s.scrollbars || false;\r
15033                                 }\r
15034                         }\r
15035 \r
15036                         // Build features string\r
15037                         each(s, function(v, k) {\r
15038                                 if (tinymce.is(v, 'boolean'))\r
15039                                         v = v ? 'yes' : 'no';\r
15040 \r
15041                                 if (!/^(name|url)$/.test(k)) {\r
15042                                         if (isIE && mo)\r
15043                                                 f += (f ? ';' : '') + k + ':' + v;\r
15044                                         else\r
15045                                                 f += (f ? ',' : '') + k + '=' + v;\r
15046                                 }\r
15047                         });\r
15048 \r
15049                         t.features = s;\r
15050                         t.params = p;\r
15051                         t.onOpen.dispatch(t, s, p);\r
15052 \r
15053                         u = s.url || s.file;\r
15054                         u = tinymce._addVer(u);\r
15055 \r
15056                         try {\r
15057                                 if (isIE && mo) {\r
15058                                         w = 1;\r
15059                                         window.showModalDialog(u, window, f);\r
15060                                 } else\r
15061                                         w = window.open(u, s.name, f);\r
15062                         } catch (ex) {\r
15063                                 // Ignore\r
15064                         }\r
15065 \r
15066                         if (!w)\r
15067                                 alert(t.editor.getLang('popup_blocked'));\r
15068                 },\r
15069 \r
15070                 close : function(w) {\r
15071                         w.close();\r
15072                         this.onClose.dispatch(this);\r
15073                 },\r
15074 \r
15075                 createInstance : function(cl, a, b, c, d, e) {\r
15076                         var f = tinymce.resolve(cl);\r
15077 \r
15078                         return new f(a, b, c, d, e);\r
15079                 },\r
15080 \r
15081                 confirm : function(t, cb, s, w) {\r
15082                         w = w || window;\r
15083 \r
15084                         cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t))));\r
15085                 },\r
15086 \r
15087                 alert : function(tx, cb, s, w) {\r
15088                         var t = this;\r
15089 \r
15090                         w = w || window;\r
15091                         w.alert(t._decode(t.editor.getLang(tx, tx)));\r
15092 \r
15093                         if (cb)\r
15094                                 cb.call(s || t);\r
15095                 },\r
15096 \r
15097                 resizeBy : function(dw, dh, win) {\r
15098                         win.resizeBy(dw, dh);\r
15099                 },\r
15100 \r
15101                 // Internal functions\r
15102 \r
15103                 _decode : function(s) {\r
15104                         return tinymce.DOM.decode(s).replace(/\\n/g, '\n');\r
15105                 }\r
15106         });\r
15107 }(tinymce));\r
15108 (function(tinymce) {\r
15109         tinymce.Formatter = function(ed) {\r
15110                 var formats = {},\r
15111                         each = tinymce.each,\r
15112                         dom = ed.dom,\r
15113                         selection = ed.selection,\r
15114                         TreeWalker = tinymce.dom.TreeWalker,\r
15115                         rangeUtils = new tinymce.dom.RangeUtils(dom),\r
15116                         isValid = ed.schema.isValidChild,\r
15117                         isBlock = dom.isBlock,\r
15118                         forcedRootBlock = ed.settings.forced_root_block,\r
15119                         nodeIndex = dom.nodeIndex,\r
15120                         INVISIBLE_CHAR = tinymce.isGecko ? '\u200B' : '\uFEFF',\r
15121                         MCE_ATTR_RE = /^(src|href|style)$/,\r
15122                         FALSE = false,\r
15123                         TRUE = true,\r
15124                         undefined;\r
15125 \r
15126                 // Returns the content editable state of a node\r
15127                 function getContentEditable(node) {\r
15128                         var contentEditable = node.getAttribute("data-mce-contenteditable");\r
15129 \r
15130                         // Check for fake content editable\r
15131                         if (contentEditable && contentEditable !== "inherit") {\r
15132                                 return contentEditable;\r
15133                         }\r
15134 \r
15135                         // Check for real content editable\r
15136                         return node.contentEditable !== "inherit" ? node.contentEditable : null;\r
15137                 };\r
15138 \r
15139                 function isArray(obj) {\r
15140                         return obj instanceof Array;\r
15141                 };\r
15142 \r
15143                 function getParents(node, selector) {\r
15144                         return dom.getParents(node, selector, dom.getRoot());\r
15145                 };\r
15146 \r
15147                 function isCaretNode(node) {\r
15148                         return node.nodeType === 1 && node.id === '_mce_caret';\r
15149                 };\r
15150 \r
15151                 // Public functions\r
15152 \r
15153                 function get(name) {\r
15154                         return name ? formats[name] : formats;\r
15155                 };\r
15156 \r
15157                 function register(name, format) {\r
15158                         if (name) {\r
15159                                 if (typeof(name) !== 'string') {\r
15160                                         each(name, function(format, name) {\r
15161                                                 register(name, format);\r
15162                                         });\r
15163                                 } else {\r
15164                                         // Force format into array and add it to internal collection\r
15165                                         format = format.length ? format : [format];\r
15166 \r
15167                                         each(format, function(format) {\r
15168                                                 // Set deep to false by default on selector formats this to avoid removing\r
15169                                                 // alignment on images inside paragraphs when alignment is changed on paragraphs\r
15170                                                 if (format.deep === undefined)\r
15171                                                         format.deep = !format.selector;\r
15172 \r
15173                                                 // Default to true\r
15174                                                 if (format.split === undefined)\r
15175                                                         format.split = !format.selector || format.inline;\r
15176 \r
15177                                                 // Default to true\r
15178                                                 if (format.remove === undefined && format.selector && !format.inline)\r
15179                                                         format.remove = 'none';\r
15180 \r
15181                                                 // Mark format as a mixed format inline + block level\r
15182                                                 if (format.selector && format.inline) {\r
15183                                                         format.mixed = true;\r
15184                                                         format.block_expand = true;\r
15185                                                 }\r
15186 \r
15187                                                 // Split classes if needed\r
15188                                                 if (typeof(format.classes) === 'string')\r
15189                                                         format.classes = format.classes.split(/\s+/);\r
15190                                         });\r
15191 \r
15192                                         formats[name] = format;\r
15193                                 }\r
15194                         }\r
15195                 };\r
15196 \r
15197                 var getTextDecoration = function(node) {\r
15198                         var decoration;\r
15199 \r
15200                         ed.dom.getParent(node, function(n) {\r
15201                                 decoration = ed.dom.getStyle(n, 'text-decoration');\r
15202                                 return decoration && decoration !== 'none';\r
15203                         });\r
15204 \r
15205                         return decoration;\r
15206                 };\r
15207 \r
15208                 var processUnderlineAndColor = function(node) {\r
15209                         var textDecoration;\r
15210                         if (node.nodeType === 1 && node.parentNode && node.parentNode.nodeType === 1) {\r
15211                                 textDecoration = getTextDecoration(node.parentNode);\r
15212                                 if (ed.dom.getStyle(node, 'color') && textDecoration) {\r
15213                                         ed.dom.setStyle(node, 'text-decoration', textDecoration);\r
15214                                 } else if (ed.dom.getStyle(node, 'textdecoration') === textDecoration) {\r
15215                                         ed.dom.setStyle(node, 'text-decoration', null);\r
15216                                 }\r
15217                         }\r
15218                 };\r
15219 \r
15220                 function apply(name, vars, node) {\r
15221                         var formatList = get(name), format = formatList[0], bookmark, rng, i, isCollapsed = selection.isCollapsed();\r
15222 \r
15223                         function setElementFormat(elm, fmt) {\r
15224                                 fmt = fmt || format;\r
15225 \r
15226                                 if (elm) {\r
15227                                         if (fmt.onformat) {\r
15228                                                 fmt.onformat(elm, fmt, vars, node);\r
15229                                         }\r
15230 \r
15231                                         each(fmt.styles, function(value, name) {\r
15232                                                 dom.setStyle(elm, name, replaceVars(value, vars));\r
15233                                         });\r
15234 \r
15235                                         each(fmt.attributes, function(value, name) {\r
15236                                                 dom.setAttrib(elm, name, replaceVars(value, vars));\r
15237                                         });\r
15238 \r
15239                                         each(fmt.classes, function(value) {\r
15240                                                 value = replaceVars(value, vars);\r
15241 \r
15242                                                 if (!dom.hasClass(elm, value))\r
15243                                                         dom.addClass(elm, value);\r
15244                                         });\r
15245                                 }\r
15246                         };\r
15247                         function adjustSelectionToVisibleSelection() {\r
15248                                 function findSelectionEnd(start, end) {\r
15249                                         var walker = new TreeWalker(end);\r
15250                                         for (node = walker.current(); node; node = walker.prev()) {\r
15251                                                 if (node.childNodes.length > 1 || node == start) {\r
15252                                                         return node;\r
15253                                                 }\r
15254                                         }\r
15255                                 };\r
15256 \r
15257                                 // Adjust selection so that a end container with a end offset of zero is not included in the selection\r
15258                                 // as this isn't visible to the user.\r
15259                                 var rng = ed.selection.getRng();\r
15260                                 var start = rng.startContainer;\r
15261                                 var end = rng.endContainer;\r
15262 \r
15263                                 if (start != end && rng.endOffset == 0) {\r
15264                                         var newEnd = findSelectionEnd(start, end);\r
15265                                         var endOffset = newEnd.nodeType == 3 ? newEnd.length : newEnd.childNodes.length;\r
15266 \r
15267                                         rng.setEnd(newEnd, endOffset);\r
15268                                 }\r
15269 \r
15270                                 return rng;\r
15271                         }\r
15272                         \r
15273                         function applyStyleToList(node, bookmark, wrapElm, newWrappers, process){\r
15274                                 var nodes = [], listIndex = -1, list, startIndex = -1, endIndex = -1, currentWrapElm;\r
15275                                 \r
15276                                 // find the index of the first child list.\r
15277                                 each(node.childNodes, function(n, index) {\r
15278                                         if (n.nodeName === "UL" || n.nodeName === "OL") {\r
15279                                                 listIndex = index;\r
15280                                                 list = n;\r
15281                                                 return false;\r
15282                                         }\r
15283                                 });\r
15284                                 \r
15285                                 // get the index of the bookmarks\r
15286                                 each(node.childNodes, function(n, index) {\r
15287                                         if (n.nodeName === "SPAN" && dom.getAttrib(n, "data-mce-type") == "bookmark") {\r
15288                                                 if (n.id == bookmark.id + "_start") {\r
15289                                                         startIndex = index;\r
15290                                                 } else if (n.id == bookmark.id + "_end") {\r
15291                                                         endIndex = index;\r
15292                                                 }\r
15293                                         }\r
15294                                 });\r
15295                                 \r
15296                                 // if the selection spans across an embedded list, or there isn't an embedded list - handle processing normally\r
15297                                 if (listIndex <= 0 || (startIndex < listIndex && endIndex > listIndex)) {\r
15298                                         each(tinymce.grep(node.childNodes), process);\r
15299                                         return 0;\r
15300                                 } else {\r
15301                                         currentWrapElm = dom.clone(wrapElm, FALSE);\r
15302 \r
15303                                         // create a list of the nodes on the same side of the list as the selection\r
15304                                         each(tinymce.grep(node.childNodes), function(n, index) {\r
15305                                                 if ((startIndex < listIndex && index < listIndex) || (startIndex > listIndex && index > listIndex)) {\r
15306                                                         nodes.push(n); \r
15307                                                         n.parentNode.removeChild(n);\r
15308                                                 }\r
15309                                         });\r
15310 \r
15311                                         // insert the wrapping element either before or after the list.\r
15312                                         if (startIndex < listIndex) {\r
15313                                                 node.insertBefore(currentWrapElm, list);\r
15314                                         } else if (startIndex > listIndex) {\r
15315                                                 node.insertBefore(currentWrapElm, list.nextSibling);\r
15316                                         }\r
15317                                         \r
15318                                         // add the new nodes to the list.\r
15319                                         newWrappers.push(currentWrapElm);\r
15320 \r
15321                                         each(nodes, function(node) {\r
15322                                                 currentWrapElm.appendChild(node);\r
15323                                         });\r
15324 \r
15325                                         return currentWrapElm;\r
15326                                 }\r
15327                         };\r
15328 \r
15329                         function applyRngStyle(rng, bookmark, node_specific) {\r
15330                                 var newWrappers = [], wrapName, wrapElm, contentEditable = true;\r
15331 \r
15332                                 // Setup wrapper element\r
15333                                 wrapName = format.inline || format.block;\r
15334                                 wrapElm = dom.create(wrapName);\r
15335                                 setElementFormat(wrapElm);\r
15336 \r
15337                                 rangeUtils.walk(rng, function(nodes) {\r
15338                                         var currentWrapElm;\r
15339 \r
15340                                         function process(node) {\r
15341                                                 var nodeName, parentName, found, hasContentEditableState, lastContentEditable;\r
15342 \r
15343                                                 lastContentEditable = contentEditable;\r
15344                                                 nodeName = node.nodeName.toLowerCase();\r
15345                                                 parentName = node.parentNode.nodeName.toLowerCase();\r
15346 \r
15347                                                 // Node has a contentEditable value\r
15348                                                 if (node.nodeType === 1 && getContentEditable(node)) {\r
15349                                                         lastContentEditable = contentEditable;\r
15350                                                         contentEditable = getContentEditable(node) === "true";\r
15351                                                         hasContentEditableState = true; // We don't want to wrap the container only it's children\r
15352                                                 }\r
15353 \r
15354                                                 // Stop wrapping on br elements\r
15355                                                 if (isEq(nodeName, 'br')) {\r
15356                                                         currentWrapElm = 0;\r
15357 \r
15358                                                         // Remove any br elements when we wrap things\r
15359                                                         if (format.block)\r
15360                                                                 dom.remove(node);\r
15361 \r
15362                                                         return;\r
15363                                                 }\r
15364 \r
15365                                                 // If node is wrapper type\r
15366                                                 if (format.wrapper && matchNode(node, name, vars)) {\r
15367                                                         currentWrapElm = 0;\r
15368                                                         return;\r
15369                                                 }\r
15370 \r
15371                                                 // Can we rename the block\r
15372                                                 if (contentEditable && !hasContentEditableState && format.block && !format.wrapper && isTextBlock(nodeName)) {\r
15373                                                         node = dom.rename(node, wrapName);\r
15374                                                         setElementFormat(node);\r
15375                                                         newWrappers.push(node);\r
15376                                                         currentWrapElm = 0;\r
15377                                                         return;\r
15378                                                 }\r
15379 \r
15380                                                 // Handle selector patterns\r
15381                                                 if (format.selector) {\r
15382                                                         // Look for matching formats\r
15383                                                         each(formatList, function(format) {\r
15384                                                                 // Check collapsed state if it exists\r
15385                                                                 if ('collapsed' in format && format.collapsed !== isCollapsed) {\r
15386                                                                         return;\r
15387                                                                 }\r
15388 \r
15389                                                                 if (dom.is(node, format.selector) && !isCaretNode(node)) {\r
15390                                                                         setElementFormat(node, format);\r
15391                                                                         found = true;\r
15392                                                                 }\r
15393                                                         });\r
15394 \r
15395                                                         // Continue processing if a selector match wasn't found and a inline element is defined\r
15396                                                         if (!format.inline || found) {\r
15397                                                                 currentWrapElm = 0;\r
15398                                                                 return;\r
15399                                                         }\r
15400                                                 }\r
15401 \r
15402                                                 // Is it valid to wrap this item\r
15403                                                 if (contentEditable && !hasContentEditableState && isValid(wrapName, nodeName) && isValid(parentName, wrapName) &&\r
15404                                                                 !(!node_specific && node.nodeType === 3 && node.nodeValue.length === 1 && node.nodeValue.charCodeAt(0) === 65279) && !isCaretNode(node)) {\r
15405                                                         // Start wrapping\r
15406                                                         if (!currentWrapElm) {\r
15407                                                                 // Wrap the node\r
15408                                                                 currentWrapElm = dom.clone(wrapElm, FALSE);\r
15409                                                                 node.parentNode.insertBefore(currentWrapElm, node);\r
15410                                                                 newWrappers.push(currentWrapElm);\r
15411                                                         }\r
15412 \r
15413                                                         currentWrapElm.appendChild(node);\r
15414                                                 } else if (nodeName == 'li' && bookmark) {\r
15415                                                         // Start wrapping - if we are in a list node and have a bookmark, then we will always begin by wrapping in a new element.\r
15416                                                         currentWrapElm = applyStyleToList(node, bookmark, wrapElm, newWrappers, process);\r
15417                                                 } else {\r
15418                                                         // Start a new wrapper for possible children\r
15419                                                         currentWrapElm = 0;\r
15420                                                         \r
15421                                                         each(tinymce.grep(node.childNodes), process);\r
15422 \r
15423                                                         if (hasContentEditableState) {\r
15424                                                                 contentEditable = lastContentEditable; // Restore last contentEditable state from stack\r
15425                                                         }\r
15426 \r
15427                                                         // End the last wrapper\r
15428                                                         currentWrapElm = 0;\r
15429                                                 }\r
15430                                         };\r
15431 \r
15432                                         // Process siblings from range\r
15433                                         each(nodes, process);\r
15434                                 });\r
15435 \r
15436                                 // Wrap links inside as well, for example color inside a link when the wrapper is around the link\r
15437                                 if (format.wrap_links === false) {\r
15438                                         each(newWrappers, function(node) {\r
15439                                                 function process(node) {\r
15440                                                         var i, currentWrapElm, children;\r
15441 \r
15442                                                         if (node.nodeName === 'A') {\r
15443                                                                 currentWrapElm = dom.clone(wrapElm, FALSE);\r
15444                                                                 newWrappers.push(currentWrapElm);\r
15445 \r
15446                                                                 children = tinymce.grep(node.childNodes);\r
15447                                                                 for (i = 0; i < children.length; i++)\r
15448                                                                         currentWrapElm.appendChild(children[i]);\r
15449 \r
15450                                                                 node.appendChild(currentWrapElm);\r
15451                                                         }\r
15452 \r
15453                                                         each(tinymce.grep(node.childNodes), process);\r
15454                                                 };\r
15455 \r
15456                                                 process(node);\r
15457                                         });\r
15458                                 }\r
15459 \r
15460                                 // Cleanup\r
15461                                 \r
15462                                 each(newWrappers, function(node) {\r
15463                                         var childCount;\r
15464 \r
15465                                         function getChildCount(node) {\r
15466                                                 var count = 0;\r
15467 \r
15468                                                 each(node.childNodes, function(node) {\r
15469                                                         if (!isWhiteSpaceNode(node) && !isBookmarkNode(node))\r
15470                                                                 count++;\r
15471                                                 });\r
15472 \r
15473                                                 return count;\r
15474                                         };\r
15475 \r
15476                                         function mergeStyles(node) {\r
15477                                                 var child, clone;\r
15478 \r
15479                                                 each(node.childNodes, function(node) {\r
15480                                                         if (node.nodeType == 1 && !isBookmarkNode(node) && !isCaretNode(node)) {\r
15481                                                                 child = node;\r
15482                                                                 return FALSE; // break loop\r
15483                                                         }\r
15484                                                 });\r
15485 \r
15486                                                 // If child was found and of the same type as the current node\r
15487                                                 if (child && matchName(child, format)) {\r
15488                                                         clone = dom.clone(child, FALSE);\r
15489                                                         setElementFormat(clone);\r
15490 \r
15491                                                         dom.replace(clone, node, TRUE);\r
15492                                                         dom.remove(child, 1);\r
15493                                                 }\r
15494 \r
15495                                                 return clone || node;\r
15496                                         };\r
15497 \r
15498                                         childCount = getChildCount(node);\r
15499 \r
15500                                         // Remove empty nodes but only if there is multiple wrappers and they are not block\r
15501                                         // elements so never remove single <h1></h1> since that would remove the currrent empty block element where the caret is at\r
15502                                         if ((newWrappers.length > 1 || !isBlock(node)) && childCount === 0) {\r
15503                                                 dom.remove(node, 1);\r
15504                                                 return;\r
15505                                         }\r
15506 \r
15507                                         if (format.inline || format.wrapper) {\r
15508                                                 // Merges the current node with it's children of similar type to reduce the number of elements\r
15509                                                 if (!format.exact && childCount === 1)\r
15510                                                         node = mergeStyles(node);\r
15511 \r
15512                                                 // Remove/merge children\r
15513                                                 each(formatList, function(format) {\r
15514                                                         // Merge all children of similar type will move styles from child to parent\r
15515                                                         // this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span>\r
15516                                                         // will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span>\r
15517                                                         each(dom.select(format.inline, node), function(child) {\r
15518                                                                 var parent;\r
15519 \r
15520                                                                 // When wrap_links is set to false we don't want\r
15521                                                                 // to remove the format on children within links\r
15522                                                                 if (format.wrap_links === false) {\r
15523                                                                         parent = child.parentNode;\r
15524 \r
15525                                                                         do {\r
15526                                                                                 if (parent.nodeName === 'A')\r
15527                                                                                         return;\r
15528                                                                         } while (parent = parent.parentNode);\r
15529                                                                 }\r
15530 \r
15531                                                                 removeFormat(format, vars, child, format.exact ? child : null);\r
15532                                                         });\r
15533                                                 });\r
15534 \r
15535                                                 // Remove child if direct parent is of same type\r
15536                                                 if (matchNode(node.parentNode, name, vars)) {\r
15537                                                         dom.remove(node, 1);\r
15538                                                         node = 0;\r
15539                                                         return TRUE;\r
15540                                                 }\r
15541 \r
15542                                                 // Look for parent with similar style format\r
15543                                                 if (format.merge_with_parents) {\r
15544                                                         dom.getParent(node.parentNode, function(parent) {\r
15545                                                                 if (matchNode(parent, name, vars)) {\r
15546                                                                         dom.remove(node, 1);\r
15547                                                                         node = 0;\r
15548                                                                         return TRUE;\r
15549                                                                 }\r
15550                                                         });\r
15551                                                 }\r
15552 \r
15553                                                 // Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>\r
15554                                                 if (node && format.merge_siblings !== false) {\r
15555                                                         node = mergeSiblings(getNonWhiteSpaceSibling(node), node);\r
15556                                                         node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE));\r
15557                                                 }\r
15558                                         }\r
15559                                 });\r
15560                         };\r
15561 \r
15562                         if (format) {\r
15563                                 if (node) {\r
15564                                         if (node.nodeType) {\r
15565                                                 rng = dom.createRng();\r
15566                                                 rng.setStartBefore(node);\r
15567                                                 rng.setEndAfter(node);\r
15568                                                 applyRngStyle(expandRng(rng, formatList), null, true);\r
15569                                         } else {\r
15570                                                 applyRngStyle(node, null, true);\r
15571                                         }\r
15572                                 } else {\r
15573                                         if (!isCollapsed || !format.inline || dom.select('td.mceSelected,th.mceSelected').length) {\r
15574                                                 // Obtain selection node before selection is unselected by applyRngStyle()\r
15575                                                 var curSelNode = ed.selection.getNode();\r
15576 \r
15577                                                 // If the formats have a default block and we can't find a parent block then start wrapping it with a DIV this is for forced_root_blocks: false\r
15578                                                 // It's kind of a hack but people should be using the default block type P since all desktop editors work that way\r
15579                                                 if (!forcedRootBlock && formatList[0].defaultBlock && !dom.getParent(curSelNode, dom.isBlock)) {\r
15580                                                         apply(formatList[0].defaultBlock);\r
15581                                                 }\r
15582 \r
15583                                                 // Apply formatting to selection\r
15584                                                 ed.selection.setRng(adjustSelectionToVisibleSelection());\r
15585                                                 bookmark = selection.getBookmark();\r
15586                                                 applyRngStyle(expandRng(selection.getRng(TRUE), formatList), bookmark);\r
15587 \r
15588                                                 // Colored nodes should be underlined so that the color of the underline matches the text color.\r
15589                                                 if (format.styles && (format.styles.color || format.styles.textDecoration)) {\r
15590                                                         tinymce.walk(curSelNode, processUnderlineAndColor, 'childNodes');\r
15591                                                         processUnderlineAndColor(curSelNode);\r
15592                                                 }\r
15593 \r
15594                                                 selection.moveToBookmark(bookmark);\r
15595                                                 moveStart(selection.getRng(TRUE));\r
15596                                                 ed.nodeChanged();\r
15597                                         } else\r
15598                                                 performCaretAction('apply', name, vars);\r
15599                                 }\r
15600                         }\r
15601                 };\r
15602 \r
15603                 function remove(name, vars, node) {\r
15604                         var formatList = get(name), format = formatList[0], bookmark, i, rng, contentEditable = true;\r
15605 \r
15606                         // Merges the styles for each node\r
15607                         function process(node) {\r
15608                                 var children, i, l, localContentEditable, lastContentEditable, hasContentEditableState;\r
15609 \r
15610                                 // Node has a contentEditable value\r
15611                                 if (node.nodeType === 1 && getContentEditable(node)) {\r
15612                                         lastContentEditable = contentEditable;\r
15613                                         contentEditable = getContentEditable(node) === "true";\r
15614                                         hasContentEditableState = true; // We don't want to wrap the container only it's children\r
15615                                 }\r
15616 \r
15617                                 // Grab the children first since the nodelist might be changed\r
15618                                 children = tinymce.grep(node.childNodes);\r
15619 \r
15620                                 // Process current node\r
15621                                 if (contentEditable && !hasContentEditableState) {\r
15622                                         for (i = 0, l = formatList.length; i < l; i++) {\r
15623                                                 if (removeFormat(formatList[i], vars, node, node))\r
15624                                                         break;\r
15625                                         }\r
15626                                 }\r
15627 \r
15628                                 // Process the children\r
15629                                 if (format.deep) {\r
15630                                         if (children.length) {                                  \r
15631                                                 for (i = 0, l = children.length; i < l; i++)\r
15632                                                         process(children[i]);\r
15633 \r
15634                                                 if (hasContentEditableState) {\r
15635                                                         contentEditable = lastContentEditable; // Restore last contentEditable state from stack\r
15636                                                 }\r
15637                                         }\r
15638                                 }\r
15639                         };\r
15640 \r
15641                         function findFormatRoot(container) {\r
15642                                 var formatRoot;\r
15643 \r
15644                                 // Find format root\r
15645                                 each(getParents(container.parentNode).reverse(), function(parent) {\r
15646                                         var format;\r
15647 \r
15648                                         // Find format root element\r
15649                                         if (!formatRoot && parent.id != '_start' && parent.id != '_end') {\r
15650                                                 // Is the node matching the format we are looking for\r
15651                                                 format = matchNode(parent, name, vars);\r
15652                                                 if (format && format.split !== false)\r
15653                                                         formatRoot = parent;\r
15654                                         }\r
15655                                 });\r
15656 \r
15657                                 return formatRoot;\r
15658                         };\r
15659 \r
15660                         function wrapAndSplit(format_root, container, target, split) {\r
15661                                 var parent, clone, lastClone, firstClone, i, formatRootParent;\r
15662 \r
15663                                 // Format root found then clone formats and split it\r
15664                                 if (format_root) {\r
15665                                         formatRootParent = format_root.parentNode;\r
15666 \r
15667                                         for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) {\r
15668                                                 clone = dom.clone(parent, FALSE);\r
15669 \r
15670                                                 for (i = 0; i < formatList.length; i++) {\r
15671                                                         if (removeFormat(formatList[i], vars, clone, clone)) {\r
15672                                                                 clone = 0;\r
15673                                                                 break;\r
15674                                                         }\r
15675                                                 }\r
15676 \r
15677                                                 // Build wrapper node\r
15678                                                 if (clone) {\r
15679                                                         if (lastClone)\r
15680                                                                 clone.appendChild(lastClone);\r
15681 \r
15682                                                         if (!firstClone)\r
15683                                                                 firstClone = clone;\r
15684 \r
15685                                                         lastClone = clone;\r
15686                                                 }\r
15687                                         }\r
15688 \r
15689                                         // Never split block elements if the format is mixed\r
15690                                         if (split && (!format.mixed || !isBlock(format_root)))\r
15691                                                 container = dom.split(format_root, container);\r
15692 \r
15693                                         // Wrap container in cloned formats\r
15694                                         if (lastClone) {\r
15695                                                 target.parentNode.insertBefore(lastClone, target);\r
15696                                                 firstClone.appendChild(target);\r
15697                                         }\r
15698                                 }\r
15699 \r
15700                                 return container;\r
15701                         };\r
15702 \r
15703                         function splitToFormatRoot(container) {\r
15704                                 return wrapAndSplit(findFormatRoot(container), container, container, true);\r
15705                         };\r
15706 \r
15707                         function unwrap(start) {\r
15708                                 var node = dom.get(start ? '_start' : '_end'),\r
15709                                         out = node[start ? 'firstChild' : 'lastChild'];\r
15710 \r
15711                                 // If the end is placed within the start the result will be removed\r
15712                                 // So this checks if the out node is a bookmark node if it is it\r
15713                                 // checks for another more suitable node\r
15714                                 if (isBookmarkNode(out))\r
15715                                         out = out[start ? 'firstChild' : 'lastChild'];\r
15716 \r
15717                                 dom.remove(node, true);\r
15718 \r
15719                                 return out;\r
15720                         };\r
15721 \r
15722                         function removeRngStyle(rng) {\r
15723                                 var startContainer, endContainer;\r
15724 \r
15725                                 rng = expandRng(rng, formatList, TRUE);\r
15726 \r
15727                                 if (format.split) {\r
15728                                         startContainer = getContainer(rng, TRUE);\r
15729                                         endContainer = getContainer(rng);\r
15730 \r
15731                                         if (startContainer != endContainer) {\r
15732                                                 // Wrap start/end nodes in span element since these might be cloned/moved\r
15733                                                 startContainer = wrap(startContainer, 'span', {id : '_start', 'data-mce-type' : 'bookmark'});\r
15734                                                 endContainer = wrap(endContainer, 'span', {id : '_end', 'data-mce-type' : 'bookmark'});\r
15735 \r
15736                                                 // Split start/end\r
15737                                                 splitToFormatRoot(startContainer);\r
15738                                                 splitToFormatRoot(endContainer);\r
15739 \r
15740                                                 // Unwrap start/end to get real elements again\r
15741                                                 startContainer = unwrap(TRUE);\r
15742                                                 endContainer = unwrap();\r
15743                                         } else\r
15744                                                 startContainer = endContainer = splitToFormatRoot(startContainer);\r
15745 \r
15746                                         // Update range positions since they might have changed after the split operations\r
15747                                         rng.startContainer = startContainer.parentNode;\r
15748                                         rng.startOffset = nodeIndex(startContainer);\r
15749                                         rng.endContainer = endContainer.parentNode;\r
15750                                         rng.endOffset = nodeIndex(endContainer) + 1;\r
15751                                 }\r
15752 \r
15753                                 // Remove items between start/end\r
15754                                 rangeUtils.walk(rng, function(nodes) {\r
15755                                         each(nodes, function(node) {\r
15756                                                 process(node);\r
15757 \r
15758                                                 // Remove parent span if it only contains text-decoration: underline, yet a parent node is also underlined.\r
15759                                                 if (node.nodeType === 1 && ed.dom.getStyle(node, 'text-decoration') === 'underline' && node.parentNode && getTextDecoration(node.parentNode) === 'underline') {\r
15760                                                         removeFormat({'deep': false, 'exact': true, 'inline': 'span', 'styles': {'textDecoration' : 'underline'}}, null, node);\r
15761                                                 }\r
15762                                         });\r
15763                                 });\r
15764                         };\r
15765 \r
15766                         // Handle node\r
15767                         if (node) {\r
15768                                 if (node.nodeType) {\r
15769                                         rng = dom.createRng();\r
15770                                         rng.setStartBefore(node);\r
15771                                         rng.setEndAfter(node);\r
15772                                         removeRngStyle(rng);\r
15773                                 } else {\r
15774                                         removeRngStyle(node);\r
15775                                 }\r
15776 \r
15777                                 return;\r
15778                         }\r
15779 \r
15780                         if (!selection.isCollapsed() || !format.inline || dom.select('td.mceSelected,th.mceSelected').length) {\r
15781                                 bookmark = selection.getBookmark();\r
15782                                 removeRngStyle(selection.getRng(TRUE));\r
15783                                 selection.moveToBookmark(bookmark);\r
15784 \r
15785                                 // 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
15786                                 if (format.inline && match(name, vars, selection.getStart())) {\r
15787                                         moveStart(selection.getRng(true));\r
15788                                 }\r
15789 \r
15790                                 ed.nodeChanged();\r
15791                         } else\r
15792                                 performCaretAction('remove', name, vars);\r
15793 \r
15794                         // Removed this logic since it breaks unit tests and produces empty caret elements since they will be destroyed in the cleanup process\r
15795                         // Also there must be a better way to rerender a table and I couldn't reproduce the case causing this might be some old WebKit\r
15796                         /*\r
15797                         // When you remove formatting from a table cell in WebKit (cell, not the contents of a cell) there is a rendering issue with column width\r
15798                         if (tinymce.isWebKit) {\r
15799                                 ed.execCommand('mceCleanup');\r
15800                         }*/\r
15801                 };\r
15802 \r
15803                 function toggle(name, vars, node) {\r
15804                         var fmt = get(name);\r
15805 \r
15806                         if (match(name, vars, node) && (!('toggle' in fmt[0]) || fmt[0]['toggle']))\r
15807                                 remove(name, vars, node);\r
15808                         else\r
15809                                 apply(name, vars, node);\r
15810                 };\r
15811 \r
15812                 function matchNode(node, name, vars, similar) {\r
15813                         var formatList = get(name), format, i, classes;\r
15814 \r
15815                         function matchItems(node, format, item_name) {\r
15816                                 var key, value, items = format[item_name], i;\r
15817 \r
15818                                 // Custom match\r
15819                                 if (format.onmatch) {\r
15820                                         return format.onmatch(node, format, item_name);\r
15821                                 }\r
15822 \r
15823                                 // Check all items\r
15824                                 if (items) {\r
15825                                         // Non indexed object\r
15826                                         if (items.length === undefined) {\r
15827                                                 for (key in items) {\r
15828                                                         if (items.hasOwnProperty(key)) {\r
15829                                                                 if (item_name === 'attributes')\r
15830                                                                         value = dom.getAttrib(node, key);\r
15831                                                                 else\r
15832                                                                         value = getStyle(node, key);\r
15833 \r
15834                                                                 if (similar && !value && !format.exact)\r
15835                                                                         return;\r
15836 \r
15837                                                                 if ((!similar || format.exact) && !isEq(value, replaceVars(items[key], vars)))\r
15838                                                                         return;\r
15839                                                         }\r
15840                                                 }\r
15841                                         } else {\r
15842                                                 // Only one match needed for indexed arrays\r
15843                                                 for (i = 0; i < items.length; i++) {\r
15844                                                         if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i]))\r
15845                                                                 return format;\r
15846                                                 }\r
15847                                         }\r
15848                                 }\r
15849 \r
15850                                 return format;\r
15851                         };\r
15852 \r
15853                         if (formatList && node) {\r
15854                                 // Check each format in list\r
15855                                 for (i = 0; i < formatList.length; i++) {\r
15856                                         format = formatList[i];\r
15857 \r
15858                                         // Name name, attributes, styles and classes\r
15859                                         if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) {\r
15860                                                 // Match classes\r
15861                                                 if (classes = format.classes) {\r
15862                                                         for (i = 0; i < classes.length; i++) {\r
15863                                                                 if (!dom.hasClass(node, classes[i]))\r
15864                                                                         return;\r
15865                                                         }\r
15866                                                 }\r
15867 \r
15868                                                 return format;\r
15869                                         }\r
15870                                 }\r
15871                         }\r
15872                 };\r
15873 \r
15874                 function match(name, vars, node) {\r
15875                         var startNode;\r
15876 \r
15877                         function matchParents(node) {\r
15878                                 // Find first node with similar format settings\r
15879                                 node = dom.getParent(node, function(node) {\r
15880                                         return !!matchNode(node, name, vars, true);\r
15881                                 });\r
15882 \r
15883                                 // Do an exact check on the similar format element\r
15884                                 return matchNode(node, name, vars);\r
15885                         };\r
15886 \r
15887                         // Check specified node\r
15888                         if (node)\r
15889                                 return matchParents(node);\r
15890 \r
15891                         // Check selected node\r
15892                         node = selection.getNode();\r
15893                         if (matchParents(node))\r
15894                                 return TRUE;\r
15895 \r
15896                         // Check start node if it's different\r
15897                         startNode = selection.getStart();\r
15898                         if (startNode != node) {\r
15899                                 if (matchParents(startNode))\r
15900                                         return TRUE;\r
15901                         }\r
15902 \r
15903                         return FALSE;\r
15904                 };\r
15905 \r
15906                 function matchAll(names, vars) {\r
15907                         var startElement, matchedFormatNames = [], checkedMap = {}, i, ni, name;\r
15908 \r
15909                         // Check start of selection for formats\r
15910                         startElement = selection.getStart();\r
15911                         dom.getParent(startElement, function(node) {\r
15912                                 var i, name;\r
15913 \r
15914                                 for (i = 0; i < names.length; i++) {\r
15915                                         name = names[i];\r
15916 \r
15917                                         if (!checkedMap[name] && matchNode(node, name, vars)) {\r
15918                                                 checkedMap[name] = true;\r
15919                                                 matchedFormatNames.push(name);\r
15920                                         }\r
15921                                 }\r
15922                         });\r
15923 \r
15924                         return matchedFormatNames;\r
15925                 };\r
15926 \r
15927                 function canApply(name) {\r
15928                         var formatList = get(name), startNode, parents, i, x, selector;\r
15929 \r
15930                         if (formatList) {\r
15931                                 startNode = selection.getStart();\r
15932                                 parents = getParents(startNode);\r
15933 \r
15934                                 for (x = formatList.length - 1; x >= 0; x--) {\r
15935                                         selector = formatList[x].selector;\r
15936 \r
15937                                         // Format is not selector based, then always return TRUE\r
15938                                         if (!selector)\r
15939                                                 return TRUE;\r
15940 \r
15941                                         for (i = parents.length - 1; i >= 0; i--) {\r
15942                                                 if (dom.is(parents[i], selector))\r
15943                                                         return TRUE;\r
15944                                         }\r
15945                                 }\r
15946                         }\r
15947 \r
15948                         return FALSE;\r
15949                 };\r
15950 \r
15951                 // Expose to public\r
15952                 tinymce.extend(this, {\r
15953                         get : get,\r
15954                         register : register,\r
15955                         apply : apply,\r
15956                         remove : remove,\r
15957                         toggle : toggle,\r
15958                         match : match,\r
15959                         matchAll : matchAll,\r
15960                         matchNode : matchNode,\r
15961                         canApply : canApply\r
15962                 });\r
15963 \r
15964                 // Private functions\r
15965 \r
15966                 function matchName(node, format) {\r
15967                         // Check for inline match\r
15968                         if (isEq(node, format.inline))\r
15969                                 return TRUE;\r
15970 \r
15971                         // Check for block match\r
15972                         if (isEq(node, format.block))\r
15973                                 return TRUE;\r
15974 \r
15975                         // Check for selector match\r
15976                         if (format.selector)\r
15977                                 return dom.is(node, format.selector);\r
15978                 };\r
15979 \r
15980                 function isEq(str1, str2) {\r
15981                         str1 = str1 || '';\r
15982                         str2 = str2 || '';\r
15983 \r
15984                         str1 = '' + (str1.nodeName || str1);\r
15985                         str2 = '' + (str2.nodeName || str2);\r
15986 \r
15987                         return str1.toLowerCase() == str2.toLowerCase();\r
15988                 };\r
15989 \r
15990                 function getStyle(node, name) {\r
15991                         var styleVal = dom.getStyle(node, name);\r
15992 \r
15993                         // Force the format to hex\r
15994                         if (name == 'color' || name == 'backgroundColor')\r
15995                                 styleVal = dom.toHex(styleVal);\r
15996 \r
15997                         // Opera will return bold as 700\r
15998                         if (name == 'fontWeight' && styleVal == 700)\r
15999                                 styleVal = 'bold';\r
16000 \r
16001                         return '' + styleVal;\r
16002                 };\r
16003 \r
16004                 function replaceVars(value, vars) {\r
16005                         if (typeof(value) != "string")\r
16006                                 value = value(vars);\r
16007                         else if (vars) {\r
16008                                 value = value.replace(/%(\w+)/g, function(str, name) {\r
16009                                         return vars[name] || str;\r
16010                                 });\r
16011                         }\r
16012 \r
16013                         return value;\r
16014                 };\r
16015 \r
16016                 function isWhiteSpaceNode(node) {\r
16017                         return node && node.nodeType === 3 && /^([\t \r\n]+|)$/.test(node.nodeValue);\r
16018                 };\r
16019 \r
16020                 function wrap(node, name, attrs) {\r
16021                         var wrapper = dom.create(name, attrs);\r
16022 \r
16023                         node.parentNode.insertBefore(wrapper, node);\r
16024                         wrapper.appendChild(node);\r
16025 \r
16026                         return wrapper;\r
16027                 };\r
16028 \r
16029                 function expandRng(rng, format, remove) {\r
16030                         var sibling, lastIdx, leaf,\r
16031                                 startContainer = rng.startContainer,\r
16032                                 startOffset = rng.startOffset,\r
16033                                 endContainer = rng.endContainer,\r
16034                                 endOffset = rng.endOffset, sibling, lastIdx, leaf, endPoint;\r
16035 \r
16036                         // This function walks up the tree if there is no siblings before/after the node\r
16037                         function findParentContainer(start) {\r
16038                                 var container, parent, child, sibling, siblingName;\r
16039 \r
16040                                 container = parent = start ? startContainer : endContainer;\r
16041                                 siblingName = start ? 'previousSibling' : 'nextSibling';\r
16042                                 root = dom.getRoot();\r
16043 \r
16044                                 // If it's a text node and the offset is inside the text\r
16045                                 if (container.nodeType == 3 && !isWhiteSpaceNode(container)) {\r
16046                                         if (start ? startOffset > 0 : endOffset < container.nodeValue.length) {\r
16047                                                 return container;\r
16048                                         }\r
16049                                 }\r
16050 \r
16051                                 for (;;) {\r
16052                                         // Stop expanding on block elements\r
16053                                         if (!format[0].block_expand && isBlock(parent))\r
16054                                                 return parent;\r
16055 \r
16056                                         // Walk left/right\r
16057                                         for (sibling = parent[siblingName]; sibling; sibling = sibling[siblingName]) {\r
16058                                                 if (!isBookmarkNode(sibling) && !isWhiteSpaceNode(sibling)) {\r
16059                                                         return parent;\r
16060                                                 }\r
16061                                         }\r
16062 \r
16063                                         // Check if we can move up are we at root level or body level\r
16064                                         if (parent.parentNode == root) {\r
16065                                                 container = parent;\r
16066                                                 break;\r
16067                                         }\r
16068 \r
16069                                         parent = parent.parentNode;\r
16070                                 }\r
16071 \r
16072                                 return container;\r
16073                         };\r
16074 \r
16075                         // This function walks down the tree to find the leaf at the selection.\r
16076                         // The offset is also returned as if node initially a leaf, the offset may be in the middle of the text node.\r
16077                         function findLeaf(node, offset) {\r
16078                                 if (offset === undefined)\r
16079                                         offset = node.nodeType === 3 ? node.length : node.childNodes.length;\r
16080                                 while (node && node.hasChildNodes()) {\r
16081                                         node = node.childNodes[offset];\r
16082                                         if (node)\r
16083                                                 offset = node.nodeType === 3 ? node.length : node.childNodes.length;\r
16084                                 }\r
16085                                 return { node: node, offset: offset };\r
16086                         }\r
16087 \r
16088                         // If index based start position then resolve it\r
16089                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {\r
16090                                 lastIdx = startContainer.childNodes.length - 1;\r
16091                                 startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset];\r
16092 \r
16093                                 if (startContainer.nodeType == 3)\r
16094                                         startOffset = 0;\r
16095                         }\r
16096 \r
16097                         // If index based end position then resolve it\r
16098                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {\r
16099                                 lastIdx = endContainer.childNodes.length - 1;\r
16100                                 endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1];\r
16101 \r
16102                                 if (endContainer.nodeType == 3)\r
16103                                         endOffset = endContainer.nodeValue.length;\r
16104                         }\r
16105 \r
16106                         // Expands the node to the closes contentEditable false element if it exists\r
16107                         function findParentContentEditable(node) {\r
16108                                 var parent = node;\r
16109 \r
16110                                 while (parent) {\r
16111                                         if (parent.nodeType === 1 && getContentEditable(parent)) {\r
16112                                                 return getContentEditable(parent) === "false" ? parent : node;\r
16113                                         }\r
16114 \r
16115                                         parent = parent.parentNode;\r
16116                                 }\r
16117 \r
16118                                 return node;\r
16119                         };\r
16120 \r
16121                         // Expand to closest contentEditable element\r
16122                         startContainer = findParentContentEditable(startContainer);\r
16123                         endContainer = findParentContentEditable(endContainer);\r
16124 \r
16125                         // Exclude bookmark nodes if possible\r
16126                         if (isBookmarkNode(startContainer.parentNode) || isBookmarkNode(startContainer)) {\r
16127                                 startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode;\r
16128                                 startContainer = startContainer.nextSibling || startContainer;\r
16129 \r
16130                                 if (startContainer.nodeType == 3)\r
16131                                         startOffset = 0;\r
16132                         }\r
16133 \r
16134                         if (isBookmarkNode(endContainer.parentNode) || isBookmarkNode(endContainer)) {\r
16135                                 endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode;\r
16136                                 endContainer = endContainer.previousSibling || endContainer;\r
16137 \r
16138                                 if (endContainer.nodeType == 3)\r
16139                                         endOffset = endContainer.length;\r
16140                         }\r
16141 \r
16142                         if (format[0].inline) {\r
16143                                 if (rng.collapsed) {\r
16144                                         function findWordEndPoint(container, offset, start) {\r
16145                                                 var walker, node, pos, lastTextNode;\r
16146 \r
16147                                                 function findSpace(node, offset) {\r
16148                                                         var pos, pos2, str = node.nodeValue;\r
16149 \r
16150                                                         if (typeof(offset) == "undefined") {\r
16151                                                                 offset = start ? str.length : 0;\r
16152                                                         }\r
16153 \r
16154                                                         if (start) {\r
16155                                                                 pos = str.lastIndexOf(' ', offset);\r
16156                                                                 pos2 = str.lastIndexOf('\u00a0', offset);\r
16157                                                                 pos = pos > pos2 ? pos : pos2;\r
16158 \r
16159                                                                 // Include the space on remove to avoid tag soup\r
16160                                                                 if (pos !== -1 && !remove) {\r
16161                                                                         pos++;\r
16162                                                                 }\r
16163                                                         } else {\r
16164                                                                 pos = str.indexOf(' ', offset);\r
16165                                                                 pos2 = str.indexOf('\u00a0', offset);\r
16166                                                                 pos = pos !== -1 && (pos2 === -1 || pos < pos2) ? pos : pos2;\r
16167                                                         }\r
16168 \r
16169                                                         return pos;\r
16170                                                 };\r
16171 \r
16172                                                 if (container.nodeType === 3) {\r
16173                                                         pos = findSpace(container, offset);\r
16174 \r
16175                                                         if (pos !== -1) {\r
16176                                                                 return {container : container, offset : pos};\r
16177                                                         }\r
16178 \r
16179                                                         lastTextNode = container;\r
16180                                                 }\r
16181 \r
16182                                                 // Walk the nodes inside the block\r
16183                                                 walker = new TreeWalker(container, dom.getParent(container, isBlock) || ed.getBody());\r
16184                                                 while (node = walker[start ? 'prev' : 'next']()) {\r
16185                                                         if (node.nodeType === 3) {\r
16186                                                                 lastTextNode = node;\r
16187                                                                 pos = findSpace(node);\r
16188 \r
16189                                                                 if (pos !== -1) {\r
16190                                                                         return {container : node, offset : pos};\r
16191                                                                 }\r
16192                                                         } else if (isBlock(node)) {\r
16193                                                                 break;\r
16194                                                         }\r
16195                                                 }\r
16196 \r
16197                                                 if (lastTextNode) {\r
16198                                                         if (start) {\r
16199                                                                 offset = 0;\r
16200                                                         } else {\r
16201                                                                 offset = lastTextNode.length;\r
16202                                                         }\r
16203 \r
16204                                                         return {container: lastTextNode, offset: offset};\r
16205                                                 }\r
16206                                         }\r
16207 \r
16208                                         // Expand left to closest word boundery\r
16209                                         endPoint = findWordEndPoint(startContainer, startOffset, true);\r
16210                                         if (endPoint) {\r
16211                                                 startContainer = endPoint.container;\r
16212                                                 startOffset = endPoint.offset;\r
16213                                         }\r
16214 \r
16215                                         // Expand right to closest word boundery\r
16216                                         endPoint = findWordEndPoint(endContainer, endOffset);\r
16217                                         if (endPoint) {\r
16218                                                 endContainer = endPoint.container;\r
16219                                                 endOffset = endPoint.offset;\r
16220                                         }\r
16221                                 }\r
16222 \r
16223                                 // Avoid applying formatting to a trailing space.\r
16224                                 leaf = findLeaf(endContainer, endOffset);\r
16225                                 if (leaf.node) {\r
16226                                         while (leaf.node && leaf.offset === 0 && leaf.node.previousSibling)\r
16227                                                 leaf = findLeaf(leaf.node.previousSibling);\r
16228 \r
16229                                         if (leaf.node && leaf.offset > 0 && leaf.node.nodeType === 3 &&\r
16230                                                         leaf.node.nodeValue.charAt(leaf.offset - 1) === ' ') {\r
16231 \r
16232                                                 if (leaf.offset > 1) {\r
16233                                                         endContainer = leaf.node;\r
16234                                                         endContainer.splitText(leaf.offset - 1);\r
16235                                                 } else if (leaf.node.previousSibling) {\r
16236                                                         // TODO: Figure out why this is in here\r
16237                                                         //endContainer = leaf.node.previousSibling;\r
16238                                                 }\r
16239                                         }\r
16240                                 }\r
16241                         }\r
16242 \r
16243                         // Move start/end point up the tree if the leaves are sharp and if we are in different containers\r
16244                         // Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!\r
16245                         // This will reduce the number of wrapper elements that needs to be created\r
16246                         // Move start point up the tree\r
16247                         if (format[0].inline || format[0].block_expand) {\r
16248                                 if (!format[0].inline || (startContainer.nodeType != 3 || startOffset === 0)) {\r
16249                                         startContainer = findParentContainer(true);\r
16250                                 }\r
16251 \r
16252                                 if (!format[0].inline || (endContainer.nodeType != 3 || endOffset === endContainer.nodeValue.length)) {\r
16253                                         endContainer = findParentContainer();\r
16254                                 }\r
16255                         }\r
16256 \r
16257                         // Expand start/end container to matching selector\r
16258                         if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) {\r
16259                                 function findSelectorEndPoint(container, sibling_name) {\r
16260                                         var parents, i, y, curFormat;\r
16261 \r
16262                                         if (container.nodeType == 3 && container.nodeValue.length == 0 && container[sibling_name])\r
16263                                                 container = container[sibling_name];\r
16264 \r
16265                                         parents = getParents(container);\r
16266                                         for (i = 0; i < parents.length; i++) {\r
16267                                                 for (y = 0; y < format.length; y++) {\r
16268                                                         curFormat = format[y];\r
16269 \r
16270                                                         // If collapsed state is set then skip formats that doesn't match that\r
16271                                                         if ("collapsed" in curFormat && curFormat.collapsed !== rng.collapsed)\r
16272                                                                 continue;\r
16273 \r
16274                                                         if (dom.is(parents[i], curFormat.selector))\r
16275                                                                 return parents[i];\r
16276                                                 }\r
16277                                         }\r
16278 \r
16279                                         return container;\r
16280                                 };\r
16281 \r
16282                                 // Find new startContainer/endContainer if there is better one\r
16283                                 startContainer = findSelectorEndPoint(startContainer, 'previousSibling');\r
16284                                 endContainer = findSelectorEndPoint(endContainer, 'nextSibling');\r
16285                         }\r
16286 \r
16287                         // Expand start/end container to matching block element or text node\r
16288                         if (format[0].block || format[0].selector) {\r
16289                                 function findBlockEndPoint(container, sibling_name, sibling_name2) {\r
16290                                         var node;\r
16291 \r
16292                                         // Expand to block of similar type\r
16293                                         if (!format[0].wrapper)\r
16294                                                 node = dom.getParent(container, format[0].block);\r
16295 \r
16296                                         // Expand to first wrappable block element or any block element\r
16297                                         if (!node)\r
16298                                                 node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, isBlock);\r
16299 \r
16300                                         // Exclude inner lists from wrapping\r
16301                                         if (node && format[0].wrapper)\r
16302                                                 node = getParents(node, 'ul,ol').reverse()[0] || node;\r
16303 \r
16304                                         // Didn't find a block element look for first/last wrappable element\r
16305                                         if (!node) {\r
16306                                                 node = container;\r
16307 \r
16308                                                 while (node[sibling_name] && !isBlock(node[sibling_name])) {\r
16309                                                         node = node[sibling_name];\r
16310 \r
16311                                                         // Break on BR but include it will be removed later on\r
16312                                                         // we can't remove it now since we need to check if it can be wrapped\r
16313                                                         if (isEq(node, 'br'))\r
16314                                                                 break;\r
16315                                                 }\r
16316                                         }\r
16317 \r
16318                                         return node || container;\r
16319                                 };\r
16320 \r
16321                                 // Find new startContainer/endContainer if there is better one\r
16322                                 startContainer = findBlockEndPoint(startContainer, 'previousSibling');\r
16323                                 endContainer = findBlockEndPoint(endContainer, 'nextSibling');\r
16324 \r
16325                                 // Non block element then try to expand up the leaf\r
16326                                 if (format[0].block) {\r
16327                                         if (!isBlock(startContainer))\r
16328                                                 startContainer = findParentContainer(true);\r
16329 \r
16330                                         if (!isBlock(endContainer))\r
16331                                                 endContainer = findParentContainer();\r
16332                                 }\r
16333                         }\r
16334 \r
16335                         // Setup index for startContainer\r
16336                         if (startContainer.nodeType == 1) {\r
16337                                 startOffset = nodeIndex(startContainer);\r
16338                                 startContainer = startContainer.parentNode;\r
16339                         }\r
16340 \r
16341                         // Setup index for endContainer\r
16342                         if (endContainer.nodeType == 1) {\r
16343                                 endOffset = nodeIndex(endContainer) + 1;\r
16344                                 endContainer = endContainer.parentNode;\r
16345                         }\r
16346 \r
16347                         // Return new range like object\r
16348                         return {\r
16349                                 startContainer : startContainer,\r
16350                                 startOffset : startOffset,\r
16351                                 endContainer : endContainer,\r
16352                                 endOffset : endOffset\r
16353                         };\r
16354                 }\r
16355 \r
16356                 function removeFormat(format, vars, node, compare_node) {\r
16357                         var i, attrs, stylesModified;\r
16358 \r
16359                         // Check if node matches format\r
16360                         if (!matchName(node, format))\r
16361                                 return FALSE;\r
16362 \r
16363                         // Should we compare with format attribs and styles\r
16364                         if (format.remove != 'all') {\r
16365                                 // Remove styles\r
16366                                 each(format.styles, function(value, name) {\r
16367                                         value = replaceVars(value, vars);\r
16368 \r
16369                                         // Indexed array\r
16370                                         if (typeof(name) === 'number') {\r
16371                                                 name = value;\r
16372                                                 compare_node = 0;\r
16373                                         }\r
16374 \r
16375                                         if (!compare_node || isEq(getStyle(compare_node, name), value))\r
16376                                                 dom.setStyle(node, name, '');\r
16377 \r
16378                                         stylesModified = 1;\r
16379                                 });\r
16380 \r
16381                                 // Remove style attribute if it's empty\r
16382                                 if (stylesModified && dom.getAttrib(node, 'style') == '') {\r
16383                                         node.removeAttribute('style');\r
16384                                         node.removeAttribute('data-mce-style');\r
16385                                 }\r
16386 \r
16387                                 // Remove attributes\r
16388                                 each(format.attributes, function(value, name) {\r
16389                                         var valueOut;\r
16390 \r
16391                                         value = replaceVars(value, vars);\r
16392 \r
16393                                         // Indexed array\r
16394                                         if (typeof(name) === 'number') {\r
16395                                                 name = value;\r
16396                                                 compare_node = 0;\r
16397                                         }\r
16398 \r
16399                                         if (!compare_node || isEq(dom.getAttrib(compare_node, name), value)) {\r
16400                                                 // Keep internal classes\r
16401                                                 if (name == 'class') {\r
16402                                                         value = dom.getAttrib(node, name);\r
16403                                                         if (value) {\r
16404                                                                 // Build new class value where everything is removed except the internal prefixed classes\r
16405                                                                 valueOut = '';\r
16406                                                                 each(value.split(/\s+/), function(cls) {\r
16407                                                                         if (/mce\w+/.test(cls))\r
16408                                                                                 valueOut += (valueOut ? ' ' : '') + cls;\r
16409                                                                 });\r
16410 \r
16411                                                                 // We got some internal classes left\r
16412                                                                 if (valueOut) {\r
16413                                                                         dom.setAttrib(node, name, valueOut);\r
16414                                                                         return;\r
16415                                                                 }\r
16416                                                         }\r
16417                                                 }\r
16418 \r
16419                                                 // IE6 has a bug where the attribute doesn't get removed correctly\r
16420                                                 if (name == "class")\r
16421                                                         node.removeAttribute('className');\r
16422 \r
16423                                                 // Remove mce prefixed attributes\r
16424                                                 if (MCE_ATTR_RE.test(name))\r
16425                                                         node.removeAttribute('data-mce-' + name);\r
16426 \r
16427                                                 node.removeAttribute(name);\r
16428                                         }\r
16429                                 });\r
16430 \r
16431                                 // Remove classes\r
16432                                 each(format.classes, function(value) {\r
16433                                         value = replaceVars(value, vars);\r
16434 \r
16435                                         if (!compare_node || dom.hasClass(compare_node, value))\r
16436                                                 dom.removeClass(node, value);\r
16437                                 });\r
16438 \r
16439                                 // Check for non internal attributes\r
16440                                 attrs = dom.getAttribs(node);\r
16441                                 for (i = 0; i < attrs.length; i++) {\r
16442                                         if (attrs[i].nodeName.indexOf('_') !== 0)\r
16443                                                 return FALSE;\r
16444                                 }\r
16445                         }\r
16446 \r
16447                         // Remove the inline child if it's empty for example <b> or <span>\r
16448                         if (format.remove != 'none') {\r
16449                                 removeNode(node, format);\r
16450                                 return TRUE;\r
16451                         }\r
16452                 };\r
16453 \r
16454                 function removeNode(node, format) {\r
16455                         var parentNode = node.parentNode, rootBlockElm;\r
16456 \r
16457                         if (format.block) {\r
16458                                 if (!forcedRootBlock) {\r
16459                                         function find(node, next, inc) {\r
16460                                                 node = getNonWhiteSpaceSibling(node, next, inc);\r
16461 \r
16462                                                 return !node || (node.nodeName == 'BR' || isBlock(node));\r
16463                                         };\r
16464 \r
16465                                         // Append BR elements if needed before we remove the block\r
16466                                         if (isBlock(node) && !isBlock(parentNode)) {\r
16467                                                 if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1))\r
16468                                                         node.insertBefore(dom.create('br'), node.firstChild);\r
16469 \r
16470                                                 if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1))\r
16471                                                         node.appendChild(dom.create('br'));\r
16472                                         }\r
16473                                 } else {\r
16474                                         // Wrap the block in a forcedRootBlock if we are at the root of document\r
16475                                         if (parentNode == dom.getRoot()) {\r
16476                                                 if (!format.list_block || !isEq(node, format.list_block)) {\r
16477                                                         each(tinymce.grep(node.childNodes), function(node) {\r
16478                                                                 if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) {\r
16479                                                                         if (!rootBlockElm)\r
16480                                                                                 rootBlockElm = wrap(node, forcedRootBlock);\r
16481                                                                         else\r
16482                                                                                 rootBlockElm.appendChild(node);\r
16483                                                                 } else\r
16484                                                                         rootBlockElm = 0;\r
16485                                                         });\r
16486                                                 }\r
16487                                         }\r
16488                                 }\r
16489                         }\r
16490 \r
16491                         // Never remove nodes that isn't the specified inline element if a selector is specified too\r
16492                         if (format.selector && format.inline && !isEq(format.inline, node))\r
16493                                 return;\r
16494 \r
16495                         dom.remove(node, 1);\r
16496                 };\r
16497 \r
16498                 function getNonWhiteSpaceSibling(node, next, inc) {\r
16499                         if (node) {\r
16500                                 next = next ? 'nextSibling' : 'previousSibling';\r
16501 \r
16502                                 for (node = inc ? node : node[next]; node; node = node[next]) {\r
16503                                         if (node.nodeType == 1 || !isWhiteSpaceNode(node))\r
16504                                                 return node;\r
16505                                 }\r
16506                         }\r
16507                 };\r
16508 \r
16509                 function isBookmarkNode(node) {\r
16510                         return node && node.nodeType == 1 && node.getAttribute('data-mce-type') == 'bookmark';\r
16511                 };\r
16512 \r
16513                 function mergeSiblings(prev, next) {\r
16514                         var marker, sibling, tmpSibling;\r
16515 \r
16516                         function compareElements(node1, node2) {\r
16517                                 // Not the same name\r
16518                                 if (node1.nodeName != node2.nodeName)\r
16519                                         return FALSE;\r
16520 \r
16521                                 function getAttribs(node) {\r
16522                                         var attribs = {};\r
16523 \r
16524                                         each(dom.getAttribs(node), function(attr) {\r
16525                                                 var name = attr.nodeName.toLowerCase();\r
16526 \r
16527                                                 // Don't compare internal attributes or style\r
16528                                                 if (name.indexOf('_') !== 0 && name !== 'style')\r
16529                                                         attribs[name] = dom.getAttrib(node, name);\r
16530                                         });\r
16531 \r
16532                                         return attribs;\r
16533                                 };\r
16534 \r
16535                                 function compareObjects(obj1, obj2) {\r
16536                                         var value, name;\r
16537 \r
16538                                         for (name in obj1) {\r
16539                                                 // Obj1 has item obj2 doesn't have\r
16540                                                 if (obj1.hasOwnProperty(name)) {\r
16541                                                         value = obj2[name];\r
16542 \r
16543                                                         // Obj2 doesn't have obj1 item\r
16544                                                         if (value === undefined)\r
16545                                                                 return FALSE;\r
16546 \r
16547                                                         // Obj2 item has a different value\r
16548                                                         if (obj1[name] != value)\r
16549                                                                 return FALSE;\r
16550 \r
16551                                                         // Delete similar value\r
16552                                                         delete obj2[name];\r
16553                                                 }\r
16554                                         }\r
16555 \r
16556                                         // Check if obj 2 has something obj 1 doesn't have\r
16557                                         for (name in obj2) {\r
16558                                                 // Obj2 has item obj1 doesn't have\r
16559                                                 if (obj2.hasOwnProperty(name))\r
16560                                                         return FALSE;\r
16561                                         }\r
16562 \r
16563                                         return TRUE;\r
16564                                 };\r
16565 \r
16566                                 // Attribs are not the same\r
16567                                 if (!compareObjects(getAttribs(node1), getAttribs(node2)))\r
16568                                         return FALSE;\r
16569 \r
16570                                 // Styles are not the same\r
16571                                 if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style'))))\r
16572                                         return FALSE;\r
16573 \r
16574                                 return TRUE;\r
16575                         };\r
16576 \r
16577                         // Check if next/prev exists and that they are elements\r
16578                         if (prev && next) {\r
16579                                 function findElementSibling(node, sibling_name) {\r
16580                                         for (sibling = node; sibling; sibling = sibling[sibling_name]) {\r
16581                                                 if (sibling.nodeType == 3 && sibling.nodeValue.length !== 0)\r
16582                                                         return node;\r
16583 \r
16584                                                 if (sibling.nodeType == 1 && !isBookmarkNode(sibling))\r
16585                                                         return sibling;\r
16586                                         }\r
16587 \r
16588                                         return node;\r
16589                                 };\r
16590 \r
16591                                 // If previous sibling is empty then jump over it\r
16592                                 prev = findElementSibling(prev, 'previousSibling');\r
16593                                 next = findElementSibling(next, 'nextSibling');\r
16594 \r
16595                                 // Compare next and previous nodes\r
16596                                 if (compareElements(prev, next)) {\r
16597                                         // Append nodes between\r
16598                                         for (sibling = prev.nextSibling; sibling && sibling != next;) {\r
16599                                                 tmpSibling = sibling;\r
16600                                                 sibling = sibling.nextSibling;\r
16601                                                 prev.appendChild(tmpSibling);\r
16602                                         }\r
16603 \r
16604                                         // Remove next node\r
16605                                         dom.remove(next);\r
16606 \r
16607                                         // Move children into prev node\r
16608                                         each(tinymce.grep(next.childNodes), function(node) {\r
16609                                                 prev.appendChild(node);\r
16610                                         });\r
16611 \r
16612                                         return prev;\r
16613                                 }\r
16614                         }\r
16615 \r
16616                         return next;\r
16617                 };\r
16618 \r
16619                 function isTextBlock(name) {\r
16620                         return /^(h[1-6]|p|div|pre|address|dl|dt|dd)$/.test(name);\r
16621                 };\r
16622 \r
16623                 function getContainer(rng, start) {\r
16624                         var container, offset, lastIdx, walker;\r
16625 \r
16626                         container = rng[start ? 'startContainer' : 'endContainer'];\r
16627                         offset = rng[start ? 'startOffset' : 'endOffset'];\r
16628 \r
16629                         if (container.nodeType == 1) {\r
16630                                 lastIdx = container.childNodes.length - 1;\r
16631 \r
16632                                 if (!start && offset)\r
16633                                         offset--;\r
16634 \r
16635                                 container = container.childNodes[offset > lastIdx ? lastIdx : offset];\r
16636                         }\r
16637 \r
16638                         // If start text node is excluded then walk to the next node\r
16639                         if (container.nodeType === 3 && start && offset >= container.nodeValue.length) {\r
16640                                 container = new TreeWalker(container, ed.getBody()).next() || container;\r
16641                         }\r
16642 \r
16643                         // If end text node is excluded then walk to the previous node\r
16644                         if (container.nodeType === 3 && !start && offset == 0) {\r
16645                                 container = new TreeWalker(container, ed.getBody()).prev() || container;\r
16646                         }\r
16647 \r
16648                         return container;\r
16649                 };\r
16650 \r
16651                 function performCaretAction(type, name, vars) {\r
16652                         var caretContainerId = '_mce_caret', debug = ed.settings.caret_debug;\r
16653 \r
16654                         // Creates a caret container bogus element\r
16655                         function createCaretContainer(fill) {\r
16656                                 var caretContainer = dom.create('span', {id: caretContainerId, 'data-mce-bogus': true, style: debug ? 'color:red' : ''});\r
16657 \r
16658                                 if (fill) {\r
16659                                         caretContainer.appendChild(ed.getDoc().createTextNode(INVISIBLE_CHAR));\r
16660                                 }\r
16661 \r
16662                                 return caretContainer;\r
16663                         };\r
16664 \r
16665                         function isCaretContainerEmpty(node, nodes) {\r
16666                                 while (node) {\r
16667                                         if ((node.nodeType === 3 && node.nodeValue !== INVISIBLE_CHAR) || node.childNodes.length > 1) {\r
16668                                                 return false;\r
16669                                         }\r
16670 \r
16671                                         // Collect nodes\r
16672                                         if (nodes && node.nodeType === 1) {\r
16673                                                 nodes.push(node);\r
16674                                         }\r
16675 \r
16676                                         node = node.firstChild;\r
16677                                 }\r
16678 \r
16679                                 return true;\r
16680                         };\r
16681                         \r
16682                         // Returns any parent caret container element\r
16683                         function getParentCaretContainer(node) {\r
16684                                 while (node) {\r
16685                                         if (node.id === caretContainerId) {\r
16686                                                 return node;\r
16687                                         }\r
16688 \r
16689                                         node = node.parentNode;\r
16690                                 }\r
16691                         };\r
16692 \r
16693                         // Finds the first text node in the specified node\r
16694                         function findFirstTextNode(node) {\r
16695                                 var walker;\r
16696 \r
16697                                 if (node) {\r
16698                                         walker = new TreeWalker(node, node);\r
16699 \r
16700                                         for (node = walker.current(); node; node = walker.next()) {\r
16701                                                 if (node.nodeType === 3) {\r
16702                                                         return node;\r
16703                                                 }\r
16704                                         }\r
16705                                 }\r
16706                         };\r
16707 \r
16708                         // Removes the caret container for the specified node or all on the current document\r
16709                         function removeCaretContainer(node, move_caret) {\r
16710                                 var child, rng;\r
16711 \r
16712                                 if (!node) {\r
16713                                         node = getParentCaretContainer(selection.getStart());\r
16714 \r
16715                                         if (!node) {\r
16716                                                 while (node = dom.get(caretContainerId)) {\r
16717                                                         removeCaretContainer(node, false);\r
16718                                                 }\r
16719                                         }\r
16720                                 } else {\r
16721                                         rng = selection.getRng(true);\r
16722 \r
16723                                         if (isCaretContainerEmpty(node)) {\r
16724                                                 if (move_caret !== false) {\r
16725                                                         rng.setStartBefore(node);\r
16726                                                         rng.setEndBefore(node);\r
16727                                                 }\r
16728 \r
16729                                                 dom.remove(node);\r
16730                                         } else {\r
16731                                                 child = findFirstTextNode(node);\r
16732 \r
16733                                                 if (child.nodeValue.charAt(0) === INVISIBLE_CHAR) {\r
16734                                                         child = child.deleteData(0, 1);\r
16735                                                 }\r
16736 \r
16737                                                 dom.remove(node, 1);\r
16738                                         }\r
16739 \r
16740                                         selection.setRng(rng);\r
16741                                 }\r
16742                         };\r
16743                         \r
16744                         // Applies formatting to the caret postion\r
16745                         function applyCaretFormat() {\r
16746                                 var rng, caretContainer, textNode, offset, bookmark, container, text;\r
16747 \r
16748                                 rng = selection.getRng(true);\r
16749                                 offset = rng.startOffset;\r
16750                                 container = rng.startContainer;\r
16751                                 text = container.nodeValue;\r
16752 \r
16753                                 caretContainer = getParentCaretContainer(selection.getStart());\r
16754                                 if (caretContainer) {\r
16755                                         textNode = findFirstTextNode(caretContainer);\r
16756                                 }\r
16757 \r
16758                                 // Expand to word is caret is in the middle of a text node and the char before/after is a alpha numeric character\r
16759                                 if (text && offset > 0 && offset < text.length && /\w/.test(text.charAt(offset)) && /\w/.test(text.charAt(offset - 1))) {\r
16760                                         // Get bookmark of caret position\r
16761                                         bookmark = selection.getBookmark();\r
16762 \r
16763                                         // Collapse bookmark range (WebKit)\r
16764                                         rng.collapse(true);\r
16765 \r
16766                                         // Expand the range to the closest word and split it at those points\r
16767                                         rng = expandRng(rng, get(name));\r
16768                                         rng = rangeUtils.split(rng);\r
16769 \r
16770                                         // Apply the format to the range\r
16771                                         apply(name, vars, rng);\r
16772 \r
16773                                         // Move selection back to caret position\r
16774                                         selection.moveToBookmark(bookmark);\r
16775                                 } else {\r
16776                                         if (!caretContainer || textNode.nodeValue !== INVISIBLE_CHAR) {\r
16777                                                 caretContainer = createCaretContainer(true);\r
16778                                                 textNode = caretContainer.firstChild;\r
16779 \r
16780                                                 rng.insertNode(caretContainer);\r
16781                                                 offset = 1;\r
16782 \r
16783                                                 apply(name, vars, caretContainer);\r
16784                                         } else {\r
16785                                                 apply(name, vars, caretContainer);\r
16786                                         }\r
16787 \r
16788                                         // Move selection to text node\r
16789                                         selection.setCursorLocation(textNode, offset);\r
16790                                 }\r
16791                         };\r
16792 \r
16793                         function removeCaretFormat() {\r
16794                                 var rng = selection.getRng(true), container, offset, bookmark,\r
16795                                         hasContentAfter, node, formatNode, parents = [], i, caretContainer;\r
16796 \r
16797                                 container = rng.startContainer;\r
16798                                 offset = rng.startOffset;\r
16799                                 node = container;\r
16800 \r
16801                                 if (container.nodeType == 3) {\r
16802                                         if (offset != container.nodeValue.length || container.nodeValue === INVISIBLE_CHAR) {\r
16803                                                 hasContentAfter = true;\r
16804                                         }\r
16805 \r
16806                                         node = node.parentNode;\r
16807                                 }\r
16808 \r
16809                                 while (node) {\r
16810                                         if (matchNode(node, name, vars)) {\r
16811                                                 formatNode = node;\r
16812                                                 break;\r
16813                                         }\r
16814 \r
16815                                         if (node.nextSibling) {\r
16816                                                 hasContentAfter = true;\r
16817                                         }\r
16818 \r
16819                                         parents.push(node);\r
16820                                         node = node.parentNode;\r
16821                                 }\r
16822 \r
16823                                 // Node doesn't have the specified format\r
16824                                 if (!formatNode) {\r
16825                                         return;\r
16826                                 }\r
16827 \r
16828                                 // Is there contents after the caret then remove the format on the element\r
16829                                 if (hasContentAfter) {\r
16830                                         // Get bookmark of caret position\r
16831                                         bookmark = selection.getBookmark();\r
16832 \r
16833                                         // Collapse bookmark range (WebKit)\r
16834                                         rng.collapse(true);\r
16835 \r
16836                                         // Expand the range to the closest word and split it at those points\r
16837                                         rng = expandRng(rng, get(name), true);\r
16838                                         rng = rangeUtils.split(rng);\r
16839 \r
16840                                         // Remove the format from the range\r
16841                                         remove(name, vars, rng);\r
16842 \r
16843                                         // Move selection back to caret position\r
16844                                         selection.moveToBookmark(bookmark);\r
16845                                 } else {\r
16846                                         caretContainer = createCaretContainer();\r
16847 \r
16848                                         node = caretContainer;\r
16849                                         for (i = parents.length - 1; i >= 0; i--) {\r
16850                                                 node.appendChild(dom.clone(parents[i], false));\r
16851                                                 node = node.firstChild;\r
16852                                         }\r
16853 \r
16854                                         // Insert invisible character into inner most format element\r
16855                                         node.appendChild(dom.doc.createTextNode(INVISIBLE_CHAR));\r
16856                                         node = node.firstChild;\r
16857 \r
16858                                         // Insert caret container after the formated node\r
16859                                         dom.insertAfter(caretContainer, formatNode);\r
16860 \r
16861                                         // Move selection to text node\r
16862                                         selection.setCursorLocation(node, 1);\r
16863                                 }\r
16864                         };\r
16865 \r
16866                         // Only bind the caret events once\r
16867                         if (!self._hasCaretEvents) {\r
16868                                 // Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements\r
16869                                 ed.onBeforeGetContent.addToTop(function() {\r
16870                                         var nodes = [], i;\r
16871 \r
16872                                         if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) {\r
16873                                                 // Mark children\r
16874                                                 i = nodes.length;\r
16875                                                 while (i--) {\r
16876                                                         dom.setAttrib(nodes[i], 'data-mce-bogus', '1');\r
16877                                                 }\r
16878                                         }\r
16879                                 });\r
16880 \r
16881                                 // Remove caret container on mouse up and on key up\r
16882                                 tinymce.each('onMouseUp onKeyUp'.split(' '), function(name) {\r
16883                                         ed[name].addToTop(function() {\r
16884                                                 removeCaretContainer();\r
16885                                         });\r
16886                                 });\r
16887 \r
16888                                 // Remove caret container on keydown and it's a backspace, enter or left/right arrow keys\r
16889                                 ed.onKeyDown.addToTop(function(ed, e) {\r
16890                                         var keyCode = e.keyCode;\r
16891 \r
16892                                         if (keyCode == 8 || keyCode == 37 || keyCode == 39) {\r
16893                                                 removeCaretContainer(getParentCaretContainer(selection.getStart()));\r
16894                                         }\r
16895                                 });\r
16896 \r
16897                                 self._hasCaretEvents = true;\r
16898                         }\r
16899 \r
16900                         // Do apply or remove caret format\r
16901                         if (type == "apply") {\r
16902                                 applyCaretFormat();\r
16903                         } else {\r
16904                                 removeCaretFormat();\r
16905                         }\r
16906                 };\r
16907 \r
16908                 function moveStart(rng) {\r
16909                         var container = rng.startContainer,\r
16910                                         offset = rng.startOffset,\r
16911                                         walker, node, nodes, tmpNode;\r
16912 \r
16913                         // Convert text node into index if possible\r
16914                         if (container.nodeType == 3 && offset >= container.nodeValue.length) {\r
16915                                 // Get the parent container location and walk from there\r
16916                                 container = container.parentNode;\r
16917                                 offset = nodeIndex(container) + 1;\r
16918                         }\r
16919 \r
16920                         // Move startContainer/startOffset in to a suitable node\r
16921                         if (container.nodeType == 1) {\r
16922                                 nodes = container.childNodes;\r
16923                                 container = nodes[Math.min(offset, nodes.length - 1)];\r
16924                                 walker = new TreeWalker(container, dom.getParent(container, dom.isBlock));\r
16925 \r
16926                                 // If offset is at end of the parent node walk to the next one\r
16927                                 if (offset > nodes.length - 1)\r
16928                                         walker.next();\r
16929 \r
16930                                 for (node = walker.current(); node; node = walker.next()) {\r
16931                                         if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {\r
16932                                                 // IE has a "neat" feature where it moves the start node into the closest element\r
16933                                                 // we can avoid this by inserting an element before it and then remove it after we set the selection\r
16934                                                 tmpNode = dom.create('a', null, INVISIBLE_CHAR);\r
16935                                                 node.parentNode.insertBefore(tmpNode, node);\r
16936 \r
16937                                                 // Set selection and remove tmpNode\r
16938                                                 rng.setStart(node, 0);\r
16939                                                 selection.setRng(rng);\r
16940                                                 dom.remove(tmpNode);\r
16941 \r
16942                                                 return;\r
16943                                         }\r
16944                                 }\r
16945                         }\r
16946                 };\r
16947         };\r
16948 })(tinymce);\r
16949 \r
16950 tinymce.onAddEditor.add(function(tinymce, ed) {\r
16951         var filters, fontSizes, dom, settings = ed.settings;\r
16952 \r
16953         if (settings.inline_styles) {\r
16954                 fontSizes = tinymce.explode(settings.font_size_legacy_values);\r
16955 \r
16956                 function replaceWithSpan(node, styles) {\r
16957                         tinymce.each(styles, function(value, name) {\r
16958                                 if (value)\r
16959                                         dom.setStyle(node, name, value);\r
16960                         });\r
16961 \r
16962                         dom.rename(node, 'span');\r
16963                 };\r
16964 \r
16965                 filters = {\r
16966                         font : function(dom, node) {\r
16967                                 replaceWithSpan(node, {\r
16968                                         backgroundColor : node.style.backgroundColor,\r
16969                                         color : node.color,\r
16970                                         fontFamily : node.face,\r
16971                                         fontSize : fontSizes[parseInt(node.size) - 1]\r
16972                                 });\r
16973                         },\r
16974 \r
16975                         u : function(dom, node) {\r
16976                                 replaceWithSpan(node, {\r
16977                                         textDecoration : 'underline'\r
16978                                 });\r
16979                         },\r
16980 \r
16981                         strike : function(dom, node) {\r
16982                                 replaceWithSpan(node, {\r
16983                                         textDecoration : 'line-through'\r
16984                                 });\r
16985                         }\r
16986                 };\r
16987 \r
16988                 function convert(editor, params) {\r
16989                         dom = editor.dom;\r
16990 \r
16991                         if (settings.convert_fonts_to_spans) {\r
16992                                 tinymce.each(dom.select('font,u,strike', params.node), function(node) {\r
16993                                         filters[node.nodeName.toLowerCase()](ed.dom, node);\r
16994                                 });\r
16995                         }\r
16996                 };\r
16997 \r
16998                 ed.onPreProcess.add(convert);\r
16999                 ed.onSetContent.add(convert);\r
17000 \r
17001                 ed.onInit.add(function() {\r
17002                         ed.selection.onSetContent.add(convert);\r
17003                 });\r
17004         }\r
17005 });\r
17006 \r
17007 (function(tinymce) {\r
17008         var TreeWalker = tinymce.dom.TreeWalker;\r
17009 \r
17010         tinymce.EnterKey = function(editor) {\r
17011                 var dom = editor.dom, selection = editor.selection, settings = editor.settings, undoManager = editor.undoManager;\r
17012 \r
17013                 function handleEnterKey(evt) {\r
17014                         var rng = selection.getRng(true), tmpRng, container, offset, parentBlock, newBlock, fragment, containerBlock, parentBlockName, containerBlockName, newBlockName;\r
17015 \r
17016                         // Returns true if the block can be split into two blocks or not\r
17017                         function canSplitBlock(node) {\r
17018                                 return node && dom.isBlock(node) && !/^(TD|TH|CAPTION)$/.test(node.nodeName) && !/^(fixed|absolute)/i.test(node.style.position);\r
17019                         };\r
17020 \r
17021                         // Moves the caret to a suitable position within the root for example in the first non pure whitespace text node or before an image\r
17022                         function moveToCaretPosition(root) {\r
17023                                 var walker, node, rng, y, viewPort, lastNode = root;\r
17024 \r
17025                                 rng = dom.createRng();\r
17026 \r
17027                                 if (root.hasChildNodes()) {\r
17028                                         walker = new TreeWalker(root, root);\r
17029 \r
17030                                         while (node = walker.current()) {\r
17031                                                 if (node.nodeType == 3) {\r
17032                                                         rng.setStart(node, 0);\r
17033                                                         rng.setEnd(node, 0);\r
17034                                                         break;\r
17035                                                 }\r
17036 \r
17037                                                 if (/^(BR|IMG)$/.test(node.nodeName)) {\r
17038                                                         rng.setStartBefore(node);\r
17039                                                         rng.setEndBefore(node);\r
17040                                                         break;\r
17041                                                 }\r
17042 \r
17043                                                 lastNode = node;\r
17044                                                 node = walker.next();\r
17045                                         }\r
17046 \r
17047                                         if (!node) {\r
17048                                                 rng.setStart(lastNode, 0);\r
17049                                                 rng.setEnd(lastNode, 0);\r
17050                                         }\r
17051                                 } else {\r
17052                                         if (root.nodeName == 'BR') {\r
17053                                                 rng.setStartAfter(root);\r
17054                                                 rng.setEndAfter(root);\r
17055                                         } else {\r
17056                                                 rng.setStart(root, 0);\r
17057                                                 rng.setEnd(root, 0);\r
17058                                         }\r
17059                                 }\r
17060 \r
17061                                 selection.setRng(rng);\r
17062 \r
17063                                 viewPort = dom.getViewPort(editor.getWin());\r
17064 \r
17065                                 // 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
17066                                 y = dom.getPos(root).y;\r
17067                                 if (y < viewPort.y || y + 25 > viewPort.y + viewPort.h) {\r
17068                                         editor.getWin().scrollTo(0, y < viewPort.y ? y : y - viewPort.h + 25); // Needs to be hardcoded to roughly one line of text if a huge text block is broken into two blocks\r
17069                                 }\r
17070                         };\r
17071 \r
17072                         // Creates a new block element by cloning the current one or creating a new one if the name is specified\r
17073                         // This function will also copy any text formatting from the parent block and add it to the new one\r
17074                         function createNewBlock(name) {\r
17075                                 var node = container, block, clonedNode, caretNode;\r
17076 \r
17077                                 block = name ? dom.create(name) : parentBlock.cloneNode(false);\r
17078                                 caretNode = block;\r
17079 \r
17080                                 // Clone any parent styles\r
17081                                 do {\r
17082                                         if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) {\r
17083                                                 clonedNode = node.cloneNode(false);\r
17084                                                 dom.setAttrib(clonedNode, 'id', ''); // Remove ID since it needs to be document unique\r
17085 \r
17086                                                 if (block.hasChildNodes()) {\r
17087                                                         clonedNode.appendChild(block.firstChild);\r
17088                                                         block.appendChild(clonedNode);\r
17089                                                 } else {\r
17090                                                         caretNode = clonedNode;\r
17091                                                         block.appendChild(clonedNode);\r
17092                                                 }\r
17093                                         }\r
17094                                 } while (node = node.parentNode);\r
17095 \r
17096                                 // BR is needed in empty blocks on non IE browsers\r
17097                                 if (!tinymce.isIE) {\r
17098                                         caretNode.innerHTML = '<br>';\r
17099                                 }\r
17100 \r
17101                                 return block;\r
17102                         };\r
17103 \r
17104                         // Returns true/false if the caret is at the start/end of the parent block element\r
17105                         function isCaretAtStartOrEndOfBlock(start) {\r
17106                                 var walker, node;\r
17107 \r
17108                                 // Caret is in the middle of a text node like "a|b"\r
17109                                 if (container.nodeType == 3 && (start ? offset > 0 : offset < container.nodeValue.length)) {\r
17110                                         return false;\r
17111                                 }\r
17112 \r
17113                                 // Walk the DOM and look for text nodes or non empty elements\r
17114                                 walker = new TreeWalker(container, parentBlock);\r
17115                                 while (node = (start ? walker.prev() : walker.next())) {\r
17116                                         if (node.nodeType === 1) {\r
17117                                                 // Ignore bogus elements\r
17118                                                 if (node.getAttribute('data-mce-bogus')) {\r
17119                                                         continue;\r
17120                                                 }\r
17121 \r
17122                                                 // Keep empty elements like <img />\r
17123                                                 name = node.nodeName.toLowerCase();\r
17124                                                 if (name === 'IMG') {\r
17125                                                         return false;\r
17126                                                 }\r
17127                                         } else if (node.nodeType === 3 && !/^[ \t\r\n]*$/.test(node.nodeValue)) {\r
17128                                                 return false;\r
17129                                         }\r
17130                                 }\r
17131 \r
17132                                 return true;\r
17133                         };\r
17134 \r
17135                         // Wraps any text nodes or inline elements in the specified forced root block name\r
17136                         function wrapSelfAndSiblingsInDefaultBlock(container, offset) {\r
17137                                 var newBlock, parentBlock, startNode, node, next;\r
17138 \r
17139                                 // Not in a block element or in a table cell or caption\r
17140                                 parentBlock = dom.getParent(container, dom.isBlock);\r
17141                                 if (newBlockName && !evt.shiftKey && (!parentBlock || !canSplitBlock(parentBlock))) {\r
17142                                         parentBlock = parentBlock || dom.getRoot();\r
17143 \r
17144                                         if (!parentBlock.hasChildNodes()) {\r
17145                                                 newBlock = dom.create(newBlockName);\r
17146                                                 parentBlock.appendChild(newBlock);\r
17147                                                 rng.setStart(newBlock, 0);\r
17148                                                 rng.setEnd(newBlock, 0);\r
17149                                                 return newBlock;\r
17150                                         }\r
17151 \r
17152                                         // Find parent that is the first child of parentBlock\r
17153                                         node = container;\r
17154                                         while (node.parentNode != parentBlock) {\r
17155                                                 node = node.parentNode;\r
17156                                         }\r
17157 \r
17158                                         // Loop left to find start node start wrapping at\r
17159                                         while (node && !dom.isBlock(node)) {\r
17160                                                 startNode = node;\r
17161                                                 node = node.previousSibling;\r
17162                                         }\r
17163 \r
17164                                         if (startNode) {\r
17165                                                 newBlock = dom.create(newBlockName);\r
17166                                                 startNode.parentNode.insertBefore(newBlock, startNode);\r
17167 \r
17168                                                 // Start wrapping until we hit a block\r
17169                                                 node = startNode;\r
17170                                                 while (node && !dom.isBlock(node)) {\r
17171                                                         next = node.nextSibling;\r
17172                                                         newBlock.appendChild(node);\r
17173                                                         node = next;\r
17174                                                 }\r
17175 \r
17176                                                 // Restore range to it's past location\r
17177                                                 rng.setStart(container, offset);\r
17178                                                 rng.setEnd(container, offset);\r
17179                                         }\r
17180                                 }\r
17181 \r
17182                                 return container;\r
17183                         };\r
17184 \r
17185                         // Inserts a block or br before/after or in the middle of a split list of the LI is empty\r
17186                         function handleEmptyListItem() {\r
17187                                 function isFirstOrLastLi(first) {\r
17188                                         var node = containerBlock[first ? 'firstChild' : 'lastChild'];\r
17189 \r
17190                                         // Find first/last element since there might be whitespace there\r
17191                                         while (node) {\r
17192                                                 if (node.nodeType == 1) {\r
17193                                                         break;\r
17194                                                 }\r
17195 \r
17196                                                 node = node[first ? 'nextSibling' : 'previousSibling'];\r
17197                                         }\r
17198 \r
17199                                         return node === parentBlock;\r
17200                                 };\r
17201 \r
17202                                 newBlock = newBlockName ? createNewBlock(newBlockName) : dom.create('BR');\r
17203 \r
17204                                 if (isFirstOrLastLi(true) && isFirstOrLastLi()) {\r
17205                                         // Is first and last list item then replace the OL/UL with a text block\r
17206                                         dom.replace(newBlock, containerBlock);\r
17207                                 } else if (isFirstOrLastLi(true)) {\r
17208                                         // First LI in list then remove LI and add text block before list\r
17209                                         containerBlock.parentNode.insertBefore(newBlock, containerBlock);\r
17210                                 } else if (isFirstOrLastLi()) {\r
17211                                         // Last LI in list then temove LI and add text block after list\r
17212                                         dom.insertAfter(newBlock, containerBlock);\r
17213                                 } else {\r
17214                                         // Middle LI in list the split the list and insert a text block in the middle\r
17215                                         // Extract after fragment and insert it after the current block\r
17216                                         tmpRng = rng.cloneRange();\r
17217                                         tmpRng.setStartAfter(parentBlock);\r
17218                                         tmpRng.setEndAfter(containerBlock);\r
17219                                         fragment = tmpRng.extractContents();\r
17220                                         dom.insertAfter(fragment, containerBlock);\r
17221                                         dom.insertAfter(newBlock, containerBlock);\r
17222                                 }\r
17223 \r
17224                                 dom.remove(parentBlock);\r
17225                                 moveToCaretPosition(newBlock);\r
17226                                 undoManager.add();\r
17227                         };\r
17228 \r
17229                         // Walks the parent block to the right and look for BR elements\r
17230                         function hasRightSideBr() {\r
17231                                 var walker = new TreeWalker(container, parentBlock), node;\r
17232 \r
17233                                 while (node = walker.current()) {\r
17234                                         if (node.nodeName == 'BR') {\r
17235                                                 return true;\r
17236                                         }\r
17237 \r
17238                                         node = walker.next();\r
17239                                 }\r
17240                         }\r
17241                         \r
17242                         // Inserts a BR element if the forced_root_block option is set to false or empty string\r
17243                         function insertBr() {\r
17244                                 var brElm, extraBr, documentMode;\r
17245 \r
17246                                 if (container && container.nodeType == 3 && offset >= container.nodeValue.length) {\r
17247                                         // Insert extra BR element at the end block elements\r
17248                                         if (!tinymce.isIE && !hasRightSideBr()) {\r
17249                                                 brElm = dom.create('br')\r
17250                                                 rng.insertNode(brElm);\r
17251                                                 rng.setStartAfter(brElm);\r
17252                                                 rng.setEndAfter(brElm);\r
17253                                                 extraBr = true;\r
17254                                         }\r
17255                                 }\r
17256 \r
17257                                 brElm = dom.create('br');\r
17258                                 rng.insertNode(brElm);\r
17259 \r
17260                                 // Rendering modes below IE8 doesn't display BR elements in PRE unless we have a \n before it\r
17261                                 documentMode = dom.doc.documentMode;\r
17262                                 if (tinymce.isIE && parentBlockName == 'PRE' && (!documentMode || documentMode < 8)) {\r
17263                                         brElm.parentNode.insertBefore(dom.doc.createTextNode('\r'), brElm);\r
17264                                 }\r
17265 \r
17266                                 if (!extraBr) {\r
17267                                         rng.setStartAfter(brElm);\r
17268                                         rng.setEndAfter(brElm);\r
17269                                 } else {\r
17270                                         rng.setStartBefore(brElm);\r
17271                                         rng.setEndBefore(brElm);\r
17272                                 }\r
17273 \r
17274                                 selection.setRng(rng);\r
17275                                 undoManager.add();\r
17276                         };\r
17277 \r
17278                         // Trims any linebreaks at the beginning of node user for example when pressing enter in a PRE element\r
17279                         function trimLeadingLineBreaks(node) {\r
17280                                 do {\r
17281                                         if (node.nodeType === 3) {\r
17282                                                 node.nodeValue = node.nodeValue.replace(/^[\r\n]+/, '');\r
17283                                         }\r
17284 \r
17285                                         node = node.firstChild;\r
17286                                 } while (node);\r
17287                         };\r
17288                 \r
17289                         // Delete any selected contents\r
17290                         if (!rng.collapsed) {\r
17291                                 editor.execCommand('Delete');\r
17292                                 return;\r
17293                         }\r
17294 \r
17295                         // Event is blocked by some other handler for example the lists plugin\r
17296                         if (evt.isDefaultPrevented()) {\r
17297                                 return;\r
17298                         }\r
17299 \r
17300                         // Setup range items and newBlockName\r
17301                         container = rng.startContainer;\r
17302                         offset = rng.startOffset;\r
17303                         newBlockName = settings.forced_root_block;\r
17304                         newBlockName = newBlockName ? newBlockName.toUpperCase() : '';\r
17305 \r
17306                         // Resolve node index\r
17307                         if (container.nodeType == 1 && container.hasChildNodes()) {\r
17308                                 container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;\r
17309                                 offset = 0;\r
17310                         }\r
17311 \r
17312                         undoManager.beforeChange();\r
17313 \r
17314                         // Wrap the current node and it's sibling in a default block if it's needed.\r
17315                         // for example this <td>text|<b>text2</b></td> will become this <td><p>text|<b>text2</p></b></td>\r
17316                         container = wrapSelfAndSiblingsInDefaultBlock(container, offset);\r
17317 \r
17318                         // Find parent block and setup empty block paddings\r
17319                         parentBlock = dom.getParent(container, dom.isBlock);\r
17320                         containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;\r
17321 \r
17322                         // Setup block names\r
17323                         parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5\r
17324                         containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5\r
17325 \r
17326                         // Handle enter inside an empty list item\r
17327                         if (parentBlockName == 'LI' && dom.isEmpty(parentBlock)) {\r
17328                                 // Let the list plugin or browser handle nested lists for now\r
17329                                 if (/^(UL|OL|LI)$/.test(containerBlock.parentNode.nodeName)) {\r
17330                                         return false;\r
17331                                 }\r
17332 \r
17333                                 handleEmptyListItem();\r
17334                                 return;\r
17335                         }\r
17336 \r
17337                         // Don't split PRE tags but insert a BR instead easier when writing code samples etc\r
17338                         if (parentBlockName == 'PRE' && settings.br_in_pre !== false) {\r
17339                                 if (!evt.shiftKey) {\r
17340                                         insertBr();\r
17341                                         return;\r
17342                                 }\r
17343                         } else {\r
17344                                 // If no root block is configured then insert a BR by default or if the shiftKey is pressed\r
17345                                 if ((!newBlockName && !evt.shiftKey && parentBlockName != 'LI') || (newBlockName && evt.shiftKey)) {\r
17346                                         insertBr();\r
17347                                         return;\r
17348                                 }\r
17349                         }\r
17350 \r
17351                         // Default block name if it's not configured\r
17352                         newBlockName = newBlockName || 'P';\r
17353 \r
17354                         // Insert new block before/after the parent block depending on caret location\r
17355                         if (isCaretAtStartOrEndOfBlock()) {\r
17356                                 // If the caret is at the end of a header we produce a P tag after it similar to Word unless we are in a hgroup\r
17357                                 if (/^(H[1-6]|PRE)$/.test(parentBlockName) && containerBlockName != 'HGROUP') {\r
17358                                         newBlock = createNewBlock(newBlockName);\r
17359                                 } else {\r
17360                                         newBlock = createNewBlock();\r
17361                                 }\r
17362 \r
17363                                 // Split the current container block element if enter is pressed inside an empty inner block element\r
17364                                 if (settings.end_container_on_empty_block && canSplitBlock(containerBlock) && dom.isEmpty(parentBlock)) {\r
17365                                         // Split container block for example a BLOCKQUOTE at the current blockParent location for example a P\r
17366                                         newBlock = dom.split(containerBlock, parentBlock);\r
17367                                 } else {\r
17368                                         dom.insertAfter(newBlock, parentBlock);\r
17369                                 }\r
17370                         } else if (isCaretAtStartOrEndOfBlock(true)) {\r
17371                                 // Insert new block before\r
17372                                 newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock);\r
17373                         } else {\r
17374                                 // Extract after fragment and insert it after the current block\r
17375                                 tmpRng = rng.cloneRange();\r
17376                                 tmpRng.setEndAfter(parentBlock);\r
17377                                 fragment = tmpRng.extractContents();\r
17378                                 trimLeadingLineBreaks(fragment);\r
17379                                 newBlock = fragment.firstChild;\r
17380                                 dom.insertAfter(fragment, parentBlock);\r
17381                         }\r
17382 \r
17383                         dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique\r
17384                         moveToCaretPosition(newBlock);\r
17385                         undoManager.add();\r
17386                 }\r
17387 \r
17388                 editor.onKeyDown.add(function(ed, evt) {\r
17389                         if (evt.keyCode == 13) {\r
17390                                 if (handleEnterKey(evt) !== false) {\r
17391                                         evt.preventDefault();\r
17392                                 }\r
17393                         }\r
17394                 });\r
17395         };\r
17396 })(tinymce);\r