Upgrade TinyMCE to v3.4.5
[citadel.git] / webcit / tiny_mce / tiny_mce_src.js
1 (function(win) {\r
2         var whiteSpaceRe = /^\s*|\s*$/g,\r
3                 undefined, isRegExpBroken = 'B'.replace(/A(.)|B/, '$1') === '$1';\r
4 \r
5         var tinymce = {\r
6                 majorVersion : '3',\r
7 \r
8                 minorVersion : '4.5',\r
9 \r
10                 releaseDate : '2011-09-06',\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);\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                                         v += typeof o[i] != 'function' ? (v.length > 1 ? ',' + quote : quote) + i + quote +':' + serialize(o[i], quote) : '';\r
906 \r
907                                 return v + '}';\r
908                 }\r
909 \r
910                 return '' + o;\r
911         };\r
912 \r
913         tinymce.util.JSON = {\r
914                 serialize: serialize,\r
915 \r
916                 parse: function(s) {\r
917                         try {\r
918                                 return eval('(' + s + ')');\r
919                         } catch (ex) {\r
920                                 // Ignore\r
921                         }\r
922                 }\r
923 \r
924                 };\r
925 })();\r
926 tinymce.create('static tinymce.util.XHR', {\r
927         send : function(o) {\r
928                 var x, t, w = window, c = 0;\r
929 \r
930                 // Default settings\r
931                 o.scope = o.scope || this;\r
932                 o.success_scope = o.success_scope || o.scope;\r
933                 o.error_scope = o.error_scope || o.scope;\r
934                 o.async = o.async === false ? false : true;\r
935                 o.data = o.data || '';\r
936 \r
937                 function get(s) {\r
938                         x = 0;\r
939 \r
940                         try {\r
941                                 x = new ActiveXObject(s);\r
942                         } catch (ex) {\r
943                         }\r
944 \r
945                         return x;\r
946                 };\r
947 \r
948                 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');\r
949 \r
950                 if (x) {\r
951                         if (x.overrideMimeType)\r
952                                 x.overrideMimeType(o.content_type);\r
953 \r
954                         x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);\r
955 \r
956                         if (o.content_type)\r
957                                 x.setRequestHeader('Content-Type', o.content_type);\r
958 \r
959                         x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\r
960 \r
961                         x.send(o.data);\r
962 \r
963                         function ready() {\r
964                                 if (!o.async || x.readyState == 4 || c++ > 10000) {\r
965                                         if (o.success && c < 10000 && x.status == 200)\r
966                                                 o.success.call(o.success_scope, '' + x.responseText, x, o);\r
967                                         else if (o.error)\r
968                                                 o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);\r
969 \r
970                                         x = null;\r
971                                 } else\r
972                                         w.setTimeout(ready, 10);\r
973                         };\r
974 \r
975                         // Syncronous request\r
976                         if (!o.async)\r
977                                 return ready();\r
978 \r
979                         // Wait for response, onReadyStateChange can not be used since it leaks memory in IE\r
980                         t = w.setTimeout(ready, 10);\r
981                 }\r
982         }\r
983 });\r
984 \r
985 (function() {\r
986         var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;\r
987 \r
988         tinymce.create('tinymce.util.JSONRequest', {\r
989                 JSONRequest : function(s) {\r
990                         this.settings = extend({\r
991                         }, s);\r
992                         this.count = 0;\r
993                 },\r
994 \r
995                 send : function(o) {\r
996                         var ecb = o.error, scb = o.success;\r
997 \r
998                         o = extend(this.settings, o);\r
999 \r
1000                         o.success = function(c, x) {\r
1001                                 c = JSON.parse(c);\r
1002 \r
1003                                 if (typeof(c) == 'undefined') {\r
1004                                         c = {\r
1005                                                 error : 'JSON Parse error.'\r
1006                                         };\r
1007                                 }\r
1008 \r
1009                                 if (c.error)\r
1010                                         ecb.call(o.error_scope || o.scope, c.error, x);\r
1011                                 else\r
1012                                         scb.call(o.success_scope || o.scope, c.result);\r
1013                         };\r
1014 \r
1015                         o.error = function(ty, x) {\r
1016                                 if (ecb)\r
1017                                         ecb.call(o.error_scope || o.scope, ty, x);\r
1018                         };\r
1019 \r
1020                         o.data = JSON.serialize({\r
1021                                 id : o.id || 'c' + (this.count++),\r
1022                                 method : o.method,\r
1023                                 params : o.params\r
1024                         });\r
1025 \r
1026                         // JSON content type for Ruby on rails. Bug: #1883287\r
1027                         o.content_type = 'application/json';\r
1028 \r
1029                         XHR.send(o);\r
1030                 },\r
1031 \r
1032                 'static' : {\r
1033                         sendRPC : function(o) {\r
1034                                 return new tinymce.util.JSONRequest().send(o);\r
1035                         }\r
1036                 }\r
1037         });\r
1038 }());\r
1039 (function(tinymce){\r
1040         tinymce.VK = {\r
1041                 DELETE:46,\r
1042                 BACKSPACE:8\r
1043                 \r
1044         }\r
1045 \r
1046 })(tinymce);\r
1047 \r
1048 (function(tinymce) {\r
1049         var VK = tinymce.VK, BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE;\r
1050 \r
1051         function cleanupStylesWhenDeleting(ed) {\r
1052                 var dom = ed.dom, selection = ed.selection;\r
1053 \r
1054                 ed.onKeyDown.add(function(ed, e) {\r
1055                         var rng, blockElm, node, clonedSpan, isDelete;\r
1056 \r
1057                         isDelete = e.keyCode == DELETE;\r
1058                         if (isDelete || e.keyCode == BACKSPACE) {\r
1059                                 e.preventDefault();\r
1060                                 rng = selection.getRng();\r
1061 \r
1062                                 // Find root block\r
1063                                 blockElm = dom.getParent(rng.startContainer, dom.isBlock);\r
1064 \r
1065                                 // On delete clone the root span of the next block element\r
1066                                 if (isDelete)\r
1067                                         blockElm = dom.getNext(blockElm, dom.isBlock);\r
1068 \r
1069                                 // Locate root span element and clone it since it would otherwise get merged by the "apple-style-span" on delete/backspace\r
1070                                 if (blockElm) {\r
1071                                         node = blockElm.firstChild;\r
1072 \r
1073                                         if (node && node.nodeName === 'SPAN') {\r
1074                                                 clonedSpan = node.cloneNode(false);\r
1075                                         }\r
1076                                 }\r
1077 \r
1078                                 // Do the backspace/delete actiopn\r
1079                                 ed.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);\r
1080 \r
1081                                 // Find all odd apple-style-spans\r
1082                                 blockElm = dom.getParent(rng.startContainer, dom.isBlock);\r
1083                                 tinymce.each(dom.select('span.Apple-style-span,font.Apple-style-span', blockElm), function(span) {\r
1084                                         var rng = dom.createRng();\r
1085 \r
1086                                         // Set range selection before the span we are about to remove\r
1087                                         rng.setStartBefore(span);\r
1088                                         rng.setEndBefore(span);\r
1089 \r
1090                                         if (clonedSpan) {\r
1091                                                 dom.replace(clonedSpan.cloneNode(false), span, true);\r
1092                                         } else {\r
1093                                                 dom.remove(span, true);\r
1094                                         }\r
1095 \r
1096                                         // Restore the selection\r
1097                                         selection.setRng(rng);\r
1098                                 });\r
1099                         }\r
1100                 });\r
1101         };\r
1102 \r
1103         function emptyEditorWhenDeleting(ed) {\r
1104                 ed.onKeyUp.add(function(ed, e) {\r
1105                         var keyCode = e.keyCode;\r
1106 \r
1107                         if (keyCode == DELETE || keyCode == BACKSPACE) {\r
1108                                 if (ed.dom.isEmpty(ed.getBody())) {\r
1109                                         ed.setContent('', {format : 'raw'});\r
1110                                         ed.nodeChanged();\r
1111                                         return;\r
1112                                 }\r
1113                         }\r
1114                 });\r
1115         };\r
1116         \r
1117         tinymce.create('tinymce.util.Quirks', {\r
1118                 Quirks: function(ed) {\r
1119                         // Load WebKit specific fixed\r
1120                         if (tinymce.isWebKit) {\r
1121                                 cleanupStylesWhenDeleting(ed);\r
1122                                 emptyEditorWhenDeleting(ed);\r
1123                         }\r
1124 \r
1125                         // Load IE specific fixes\r
1126                         if (tinymce.isIE) {\r
1127                                 emptyEditorWhenDeleting(ed);\r
1128                         }\r
1129                 }\r
1130         });\r
1131 })(tinymce);\r
1132 (function(tinymce) {\r
1133         var namedEntities, baseEntities, reverseEntities,\r
1134                 attrsCharsRegExp = /[&<>\"\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,\r
1135                 textCharsRegExp = /[<>&\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,\r
1136                 rawCharsRegExp = /[<>&\"\']/g,\r
1137                 entityRegExp = /&(#x|#)?([\w]+);/g,\r
1138                 asciiMap = {\r
1139                                 128 : "\u20AC", 130 : "\u201A", 131 : "\u0192", 132 : "\u201E", 133 : "\u2026", 134 : "\u2020",\r
1140                                 135 : "\u2021", 136 : "\u02C6", 137 : "\u2030", 138 : "\u0160", 139 : "\u2039", 140 : "\u0152",\r
1141                                 142 : "\u017D", 145 : "\u2018", 146 : "\u2019", 147 : "\u201C", 148 : "\u201D", 149 : "\u2022",\r
1142                                 150 : "\u2013", 151 : "\u2014", 152 : "\u02DC", 153 : "\u2122", 154 : "\u0161", 155 : "\u203A",\r
1143                                 156 : "\u0153", 158 : "\u017E", 159 : "\u0178"\r
1144                 };\r
1145 \r
1146         // Raw entities\r
1147         baseEntities = {\r
1148                 '\"' : '&quot;', // Needs to be escaped since the YUI compressor would otherwise break the code\r
1149                 "'" : '&#39;',\r
1150                 '<' : '&lt;',\r
1151                 '>' : '&gt;',\r
1152                 '&' : '&amp;'\r
1153         };\r
1154 \r
1155         // Reverse lookup table for raw entities\r
1156         reverseEntities = {\r
1157                 '&lt;' : '<',\r
1158                 '&gt;' : '>',\r
1159                 '&amp;' : '&',\r
1160                 '&quot;' : '"',\r
1161                 '&apos;' : "'"\r
1162         };\r
1163 \r
1164         // Decodes text by using the browser\r
1165         function nativeDecode(text) {\r
1166                 var elm;\r
1167 \r
1168                 elm = document.createElement("div");\r
1169                 elm.innerHTML = text;\r
1170 \r
1171                 return elm.textContent || elm.innerText || text;\r
1172         };\r
1173 \r
1174         // Build a two way lookup table for the entities\r
1175         function buildEntitiesLookup(items, radix) {\r
1176                 var i, chr, entity, lookup = {};\r
1177 \r
1178                 if (items) {\r
1179                         items = items.split(',');\r
1180                         radix = radix || 10;\r
1181 \r
1182                         // Build entities lookup table\r
1183                         for (i = 0; i < items.length; i += 2) {\r
1184                                 chr = String.fromCharCode(parseInt(items[i], radix));\r
1185 \r
1186                                 // Only add non base entities\r
1187                                 if (!baseEntities[chr]) {\r
1188                                         entity = '&' + items[i + 1] + ';';\r
1189                                         lookup[chr] = entity;\r
1190                                         lookup[entity] = chr;\r
1191                                 }\r
1192                         }\r
1193 \r
1194                         return lookup;\r
1195                 }\r
1196         };\r
1197 \r
1198         // Unpack entities lookup where the numbers are in radix 32 to reduce the size\r
1199         namedEntities = buildEntitiesLookup(\r
1200                 '50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' +\r
1201                 '5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' +\r
1202                 '5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' +\r
1203                 '5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' +\r
1204                 '68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' +\r
1205                 '6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' +\r
1206                 '6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' +\r
1207                 '75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' +\r
1208                 '7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' +\r
1209                 '7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' +\r
1210                 'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' +\r
1211                 'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' +\r
1212                 't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' +\r
1213                 'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' +\r
1214                 'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' +\r
1215                 '81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' +\r
1216                 '8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' +\r
1217                 '8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' +\r
1218                 '8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' +\r
1219                 '8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' +\r
1220                 'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' +\r
1221                 'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' +\r
1222                 'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' +\r
1223                 '80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' +\r
1224                 '811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro'\r
1225         , 32);\r
1226 \r
1227         tinymce.html = tinymce.html || {};\r
1228 \r
1229         tinymce.html.Entities = {\r
1230                 encodeRaw : function(text, attr) {\r
1231                         return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {\r
1232                                 return baseEntities[chr] || chr;\r
1233                         });\r
1234                 },\r
1235 \r
1236                 encodeAllRaw : function(text) {\r
1237                         return ('' + text).replace(rawCharsRegExp, function(chr) {\r
1238                                 return baseEntities[chr] || chr;\r
1239                         });\r
1240                 },\r
1241 \r
1242                 encodeNumeric : function(text, attr) {\r
1243                         return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {\r
1244                                 // Multi byte sequence convert it to a single entity\r
1245                                 if (chr.length > 1)\r
1246                                         return '&#' + (((chr.charCodeAt(0) - 0xD800) * 0x400) + (chr.charCodeAt(1) - 0xDC00) + 0x10000) + ';';\r
1247 \r
1248                                 return baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';\r
1249                         });\r
1250                 },\r
1251 \r
1252                 encodeNamed : function(text, attr, entities) {\r
1253                         entities = entities || namedEntities;\r
1254 \r
1255                         return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {\r
1256                                 return baseEntities[chr] || entities[chr] || chr;\r
1257                         });\r
1258                 },\r
1259 \r
1260                 getEncodeFunc : function(name, entities) {\r
1261                         var Entities = tinymce.html.Entities;\r
1262 \r
1263                         entities = buildEntitiesLookup(entities) || namedEntities;\r
1264 \r
1265                         function encodeNamedAndNumeric(text, attr) {\r
1266                                 return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {\r
1267                                         return baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;\r
1268                                 });\r
1269                         };\r
1270 \r
1271                         function encodeCustomNamed(text, attr) {\r
1272                                 return Entities.encodeNamed(text, attr, entities);\r
1273                         };\r
1274 \r
1275                         // Replace + with , to be compatible with previous TinyMCE versions\r
1276                         name = tinymce.makeMap(name.replace(/\+/g, ','));\r
1277 \r
1278                         // Named and numeric encoder\r
1279                         if (name.named && name.numeric)\r
1280                                 return encodeNamedAndNumeric;\r
1281 \r
1282                         // Named encoder\r
1283                         if (name.named) {\r
1284                                 // Custom names\r
1285                                 if (entities)\r
1286                                         return encodeCustomNamed;\r
1287 \r
1288                                 return Entities.encodeNamed;\r
1289                         }\r
1290 \r
1291                         // Numeric\r
1292                         if (name.numeric)\r
1293                                 return Entities.encodeNumeric;\r
1294 \r
1295                         // Raw encoder\r
1296                         return Entities.encodeRaw;\r
1297                 },\r
1298 \r
1299                 decode : function(text) {\r
1300                         return text.replace(entityRegExp, function(all, numeric, value) {\r
1301                                 if (numeric) {\r
1302                                         value = parseInt(value, numeric.length === 2 ? 16 : 10);\r
1303 \r
1304                                         // Support upper UTF\r
1305                                         if (value > 0xFFFF) {\r
1306                                                 value -= 0x10000;\r
1307 \r
1308                                                 return String.fromCharCode(0xD800 + (value >> 10), 0xDC00 + (value & 0x3FF));\r
1309                                         } else\r
1310                                                 return asciiMap[value] || String.fromCharCode(value);\r
1311                                 }\r
1312 \r
1313                                 return reverseEntities[all] || namedEntities[all] || nativeDecode(all);\r
1314                         });\r
1315                 }\r
1316         };\r
1317 })(tinymce);\r
1318 \r
1319 tinymce.html.Styles = function(settings, schema) {\r
1320         var rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,\r
1321                 urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,\r
1322                 styleRegExp = /\s*([^:]+):\s*([^;]+);?/g,\r
1323                 trimRightRegExp = /\s+$/,\r
1324                 urlColorRegExp = /rgb/,\r
1325                 undef, i, encodingLookup = {}, encodingItems;\r
1326 \r
1327         settings = settings || {};\r
1328 \r
1329         encodingItems = '\\" \\\' \\; \\: ; : \uFEFF'.split(' ');\r
1330         for (i = 0; i < encodingItems.length; i++) {\r
1331                 encodingLookup[encodingItems[i]] = '\uFEFF' + i;\r
1332                 encodingLookup['\uFEFF' + i] = encodingItems[i];\r
1333         }\r
1334 \r
1335         function toHex(match, r, g, b) {\r
1336                 function hex(val) {\r
1337                         val = parseInt(val).toString(16);\r
1338 \r
1339                         return val.length > 1 ? val : '0' + val; // 0 -> 00\r
1340                 };\r
1341 \r
1342                 return '#' + hex(r) + hex(g) + hex(b);\r
1343         };\r
1344 \r
1345         return {\r
1346                 toHex : function(color) {\r
1347                         return color.replace(rgbRegExp, toHex);\r
1348                 },\r
1349 \r
1350                 parse : function(css) {\r
1351                         var styles = {}, matches, name, value, isEncoded, urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope || this;\r
1352 \r
1353                         function compress(prefix, suffix) {\r
1354                                 var top, right, bottom, left;\r
1355 \r
1356                                 // Get values and check it it needs compressing\r
1357                                 top = styles[prefix + '-top' + suffix];\r
1358                                 if (!top)\r
1359                                         return;\r
1360 \r
1361                                 right = styles[prefix + '-right' + suffix];\r
1362                                 if (top != right)\r
1363                                         return;\r
1364 \r
1365                                 bottom = styles[prefix + '-bottom' + suffix];\r
1366                                 if (right != bottom)\r
1367                                         return;\r
1368 \r
1369                                 left = styles[prefix + '-left' + suffix];\r
1370                                 if (bottom != left)\r
1371                                         return;\r
1372 \r
1373                                 // Compress\r
1374                                 styles[prefix + suffix] = left;\r
1375                                 delete styles[prefix + '-top' + suffix];\r
1376                                 delete styles[prefix + '-right' + suffix];\r
1377                                 delete styles[prefix + '-bottom' + suffix];\r
1378                                 delete styles[prefix + '-left' + suffix];\r
1379                         };\r
1380 \r
1381                         function canCompress(key) {\r
1382                                 var value = styles[key], i;\r
1383 \r
1384                                 if (!value || value.indexOf(' ') < 0)\r
1385                                         return;\r
1386 \r
1387                                 value = value.split(' ');\r
1388                                 i = value.length;\r
1389                                 while (i--) {\r
1390                                         if (value[i] !== value[0])\r
1391                                                 return false;\r
1392                                 }\r
1393 \r
1394                                 styles[key] = value[0];\r
1395 \r
1396                                 return true;\r
1397                         };\r
1398 \r
1399                         function compress2(target, a, b, c) {\r
1400                                 if (!canCompress(a))\r
1401                                         return;\r
1402 \r
1403                                 if (!canCompress(b))\r
1404                                         return;\r
1405 \r
1406                                 if (!canCompress(c))\r
1407                                         return;\r
1408 \r
1409                                 // Compress\r
1410                                 styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c];\r
1411                                 delete styles[a];\r
1412                                 delete styles[b];\r
1413                                 delete styles[c];\r
1414                         };\r
1415 \r
1416                         // Encodes the specified string by replacing all \" \' ; : with _<num>\r
1417                         function encode(str) {\r
1418                                 isEncoded = true;\r
1419 \r
1420                                 return encodingLookup[str];\r
1421                         };\r
1422 \r
1423                         // Decodes the specified string by replacing all _<num> with it's original value \" \' etc\r
1424                         // It will also decode the \" \' if keep_slashes is set to fale or omitted\r
1425                         function decode(str, keep_slashes) {\r
1426                                 if (isEncoded) {\r
1427                                         str = str.replace(/\uFEFF[0-9]/g, function(str) {\r
1428                                                 return encodingLookup[str];\r
1429                                         });\r
1430                                 }\r
1431 \r
1432                                 if (!keep_slashes)\r
1433                                         str = str.replace(/\\([\'\";:])/g, "$1");\r
1434 \r
1435                                 return str;\r
1436                         }\r
1437 \r
1438                         if (css) {\r
1439                                 // Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing\r
1440                                 css = css.replace(/\\[\"\';:\uFEFF]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, function(str) {\r
1441                                         return str.replace(/[;:]/g, encode);\r
1442                                 });\r
1443 \r
1444                                 // Parse styles\r
1445                                 while (matches = styleRegExp.exec(css)) {\r
1446                                         name = matches[1].replace(trimRightRegExp, '').toLowerCase();\r
1447                                         value = matches[2].replace(trimRightRegExp, '');\r
1448 \r
1449                                         if (name && value.length > 0) {\r
1450                                                 // Opera will produce 700 instead of bold in their style values\r
1451                                                 if (name === 'font-weight' && value === '700')\r
1452                                                         value = 'bold';\r
1453                                                 else if (name === 'color' || name === 'background-color') // Lowercase colors like RED\r
1454                                                         value = value.toLowerCase();            \r
1455 \r
1456                                                 // Convert RGB colors to HEX\r
1457                                                 value = value.replace(rgbRegExp, toHex);\r
1458 \r
1459                                                 // Convert URLs and force them into url('value') format\r
1460                                                 value = value.replace(urlOrStrRegExp, function(match, url, url2, url3, str, str2) {\r
1461                                                         str = str || str2;\r
1462 \r
1463                                                         if (str) {\r
1464                                                                 str = decode(str);\r
1465 \r
1466                                                                 // Force strings into single quote format\r
1467                                                                 return "'" + str.replace(/\'/g, "\\'") + "'";\r
1468                                                         }\r
1469 \r
1470                                                         url = decode(url || url2 || url3);\r
1471 \r
1472                                                         // Convert the URL to relative/absolute depending on config\r
1473                                                         if (urlConverter)\r
1474                                                                 url = urlConverter.call(urlConverterScope, url, 'style');\r
1475 \r
1476                                                         // Output new URL format\r
1477                                                         return "url('" + url.replace(/\'/g, "\\'") + "')";\r
1478                                                 });\r
1479 \r
1480                                                 styles[name] = isEncoded ? decode(value, true) : value;\r
1481                                         }\r
1482 \r
1483                                         styleRegExp.lastIndex = matches.index + matches[0].length;\r
1484                                 }\r
1485 \r
1486                                 // Compress the styles to reduce it's size for example IE will expand styles\r
1487                                 compress("border", "");\r
1488                                 compress("border", "-width");\r
1489                                 compress("border", "-color");\r
1490                                 compress("border", "-style");\r
1491                                 compress("padding", "");\r
1492                                 compress("margin", "");\r
1493                                 compress2('border', 'border-width', 'border-style', 'border-color');\r
1494 \r
1495                                 // Remove pointless border, IE produces these\r
1496                                 if (styles.border === 'medium none')\r
1497                                         delete styles.border;\r
1498                         }\r
1499 \r
1500                         return styles;\r
1501                 },\r
1502 \r
1503                 serialize : function(styles, element_name) {\r
1504                         var css = '', name, value;\r
1505 \r
1506                         function serializeStyles(name) {\r
1507                                 var styleList, i, l, value;\r
1508 \r
1509                                 styleList = schema.styles[name];\r
1510                                 if (styleList) {\r
1511                                         for (i = 0, l = styleList.length; i < l; i++) {\r
1512                                                 name = styleList[i];\r
1513                                                 value = styles[name];\r
1514 \r
1515                                                 if (value !== undef && value.length > 0)\r
1516                                                         css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';\r
1517                                         }\r
1518                                 }\r
1519                         };\r
1520 \r
1521                         // Serialize styles according to schema\r
1522                         if (element_name && schema && schema.styles) {\r
1523                                 // Serialize global styles and element specific styles\r
1524                                 serializeStyles('*');\r
1525                                 serializeStyles(element_name);\r
1526                         } else {\r
1527                                 // Output the styles in the order they are inside the object\r
1528                                 for (name in styles) {\r
1529                                         value = styles[name];\r
1530 \r
1531                                         if (value !== undef && value.length > 0)\r
1532                                                 css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';\r
1533                                 }\r
1534                         }\r
1535 \r
1536                         return css;\r
1537                 }\r
1538         };\r
1539 };\r
1540 \r
1541 (function(tinymce) {\r
1542         var transitional = {}, boolAttrMap, blockElementsMap, shortEndedElementsMap, nonEmptyElementsMap, customElementsMap = {},\r
1543                 defaultWhiteSpaceElementsMap, selfClosingElementsMap, makeMap = tinymce.makeMap, each = tinymce.each;\r
1544 \r
1545         function split(str, delim) {\r
1546                 return str.split(delim || ',');\r
1547         };\r
1548 \r
1549         function unpack(lookup, data) {\r
1550                 var key, elements = {};\r
1551 \r
1552                 function replace(value) {\r
1553                         return value.replace(/[A-Z]+/g, function(key) {\r
1554                                 return replace(lookup[key]);\r
1555                         });\r
1556                 };\r
1557 \r
1558                 // Unpack lookup\r
1559                 for (key in lookup) {\r
1560                         if (lookup.hasOwnProperty(key))\r
1561                                 lookup[key] = replace(lookup[key]);\r
1562                 }\r
1563 \r
1564                 // Unpack and parse data into object map\r
1565                 replace(data).replace(/#/g, '#text').replace(/(\w+)\[([^\]]+)\]\[([^\]]*)\]/g, function(str, name, attributes, children) {\r
1566                         attributes = split(attributes, '|');\r
1567 \r
1568                         elements[name] = {\r
1569                                 attributes : makeMap(attributes),\r
1570                                 attributesOrder : attributes,\r
1571                                 children : makeMap(children, '|', {'#comment' : {}})\r
1572                         }\r
1573                 });\r
1574 \r
1575                 return elements;\r
1576         };\r
1577 \r
1578         // Build a lookup table for block elements both lowercase and uppercase\r
1579         blockElementsMap = 'h1,h2,h3,h4,h5,h6,hr,p,div,address,pre,form,table,tbody,thead,tfoot,' + \r
1580                                                 'th,tr,td,li,ol,ul,caption,blockquote,center,dl,dt,dd,dir,fieldset,' + \r
1581                                                 'noscript,menu,isindex,samp,header,footer,article,section,hgroup';\r
1582         blockElementsMap = makeMap(blockElementsMap, ',', makeMap(blockElementsMap.toUpperCase()));\r
1583 \r
1584         // This is the XHTML 1.0 transitional elements with it's attributes and children packed to reduce it's size\r
1585         transitional = unpack({\r
1586                 Z : 'H|K|N|O|P',\r
1587                 Y : 'X|form|R|Q',\r
1588                 ZG : 'E|span|width|align|char|charoff|valign',\r
1589                 X : 'p|T|div|U|W|isindex|fieldset|table',\r
1590                 ZF : 'E|align|char|charoff|valign',\r
1591                 W : 'pre|hr|blockquote|address|center|noframes',\r
1592                 ZE : 'abbr|axis|headers|scope|rowspan|colspan|align|char|charoff|valign|nowrap|bgcolor|width|height',\r
1593                 ZD : '[E][S]',\r
1594                 U : 'ul|ol|dl|menu|dir',\r
1595                 ZC : 'p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q',\r
1596                 T : 'h1|h2|h3|h4|h5|h6',\r
1597                 ZB : 'X|S|Q',\r
1598                 S : 'R|P',\r
1599                 ZA : 'a|G|J|M|O|P',\r
1600                 R : 'a|H|K|N|O',\r
1601                 Q : 'noscript|P',\r
1602                 P : 'ins|del|script',\r
1603                 O : 'input|select|textarea|label|button',\r
1604                 N : 'M|L',\r
1605                 M : 'em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym',\r
1606                 L : 'sub|sup',\r
1607                 K : 'J|I',\r
1608                 J : 'tt|i|b|u|s|strike',\r
1609                 I : 'big|small|font|basefont',\r
1610                 H : 'G|F',\r
1611                 G : 'br|span|bdo',\r
1612                 F : 'object|applet|img|map|iframe',\r
1613                 E : 'A|B|C',\r
1614                 D : 'accesskey|tabindex|onfocus|onblur',\r
1615                 C : 'onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup',\r
1616                 B : 'lang|xml:lang|dir',\r
1617                 A : 'id|class|style|title'\r
1618         }, 'script[id|charset|type|language|src|defer|xml:space][]' + \r
1619                 'style[B|id|type|media|title|xml:space][]' + \r
1620                 'object[E|declare|classid|codebase|data|type|codetype|archive|standby|width|height|usemap|name|tabindex|align|border|hspace|vspace][#|param|Y]' + \r
1621                 'param[id|name|value|valuetype|type][]' + \r
1622                 'p[E|align][#|S]' + \r
1623                 'a[E|D|charset|type|name|href|hreflang|rel|rev|shape|coords|target][#|Z]' + \r
1624                 'br[A|clear][]' + \r
1625                 'span[E][#|S]' + \r
1626                 'bdo[A|C|B][#|S]' + \r
1627                 'applet[A|codebase|archive|code|object|alt|name|width|height|align|hspace|vspace][#|param|Y]' + \r
1628                 'h1[E|align][#|S]' + \r
1629                 'img[E|src|alt|name|longdesc|width|height|usemap|ismap|align|border|hspace|vspace][]' + \r
1630                 'map[B|C|A|name][X|form|Q|area]' + \r
1631                 'h2[E|align][#|S]' + \r
1632                 'iframe[A|longdesc|name|src|frameborder|marginwidth|marginheight|scrolling|align|width|height][#|Y]' + \r
1633                 'h3[E|align][#|S]' + \r
1634                 'tt[E][#|S]' + \r
1635                 'i[E][#|S]' + \r
1636                 'b[E][#|S]' + \r
1637                 'u[E][#|S]' + \r
1638                 's[E][#|S]' + \r
1639                 'strike[E][#|S]' + \r
1640                 'big[E][#|S]' + \r
1641                 'small[E][#|S]' + \r
1642                 'font[A|B|size|color|face][#|S]' + \r
1643                 'basefont[id|size|color|face][]' + \r
1644                 'em[E][#|S]' + \r
1645                 'strong[E][#|S]' + \r
1646                 'dfn[E][#|S]' + \r
1647                 'code[E][#|S]' + \r
1648                 'q[E|cite][#|S]' + \r
1649                 'samp[E][#|S]' + \r
1650                 'kbd[E][#|S]' + \r
1651                 'var[E][#|S]' + \r
1652                 'cite[E][#|S]' + \r
1653                 'abbr[E][#|S]' + \r
1654                 'acronym[E][#|S]' + \r
1655                 'sub[E][#|S]' + \r
1656                 'sup[E][#|S]' + \r
1657                 'input[E|D|type|name|value|checked|disabled|readonly|size|maxlength|src|alt|usemap|onselect|onchange|accept|align][]' + \r
1658                 'select[E|name|size|multiple|disabled|tabindex|onfocus|onblur|onchange][optgroup|option]' + \r
1659                 'optgroup[E|disabled|label][option]' + \r
1660                 'option[E|selected|disabled|label|value][]' + \r
1661                 'textarea[E|D|name|rows|cols|disabled|readonly|onselect|onchange][]' + \r
1662                 'label[E|for|accesskey|onfocus|onblur][#|S]' + \r
1663                 'button[E|D|name|value|type|disabled][#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]' + \r
1664                 'h4[E|align][#|S]' + \r
1665                 'ins[E|cite|datetime][#|Y]' + \r
1666                 'h5[E|align][#|S]' + \r
1667                 'del[E|cite|datetime][#|Y]' + \r
1668                 'h6[E|align][#|S]' + \r
1669                 'div[E|align][#|Y]' + \r
1670                 'ul[E|type|compact][li]' + \r
1671                 'li[E|type|value][#|Y]' + \r
1672                 'ol[E|type|compact|start][li]' + \r
1673                 'dl[E|compact][dt|dd]' + \r
1674                 'dt[E][#|S]' + \r
1675                 'dd[E][#|Y]' + \r
1676                 'menu[E|compact][li]' + \r
1677                 'dir[E|compact][li]' + \r
1678                 'pre[E|width|xml:space][#|ZA]' + \r
1679                 'hr[E|align|noshade|size|width][]' + \r
1680                 'blockquote[E|cite][#|Y]' + \r
1681                 'address[E][#|S|p]' + \r
1682                 'center[E][#|Y]' + \r
1683                 'noframes[E][#|Y]' + \r
1684                 'isindex[A|B|prompt][]' + \r
1685                 'fieldset[E][#|legend|Y]' + \r
1686                 'legend[E|accesskey|align][#|S]' + \r
1687                 'table[E|summary|width|border|frame|rules|cellspacing|cellpadding|align|bgcolor][caption|col|colgroup|thead|tfoot|tbody|tr]' + \r
1688                 'caption[E|align][#|S]' + \r
1689                 'col[ZG][]' + \r
1690                 'colgroup[ZG][col]' + \r
1691                 'thead[ZF][tr]' + \r
1692                 'tr[ZF|bgcolor][th|td]' + \r
1693                 'th[E|ZE][#|Y]' + \r
1694                 'form[E|action|method|name|enctype|onsubmit|onreset|accept|accept-charset|target][#|X|R|Q]' + \r
1695                 'noscript[E][#|Y]' + \r
1696                 'td[E|ZE][#|Y]' + \r
1697                 'tfoot[ZF][tr]' + \r
1698                 'tbody[ZF][tr]' + \r
1699                 'area[E|D|shape|coords|href|nohref|alt|target][]' + \r
1700                 'base[id|href|target][]' + \r
1701                 'body[E|onload|onunload|background|bgcolor|text|link|vlink|alink][#|Y]'\r
1702         );\r
1703 \r
1704         boolAttrMap = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected,autoplay,loop,controls');\r
1705         shortEndedElementsMap = makeMap('area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,source');\r
1706         nonEmptyElementsMap = tinymce.extend(makeMap('td,th,iframe,video,audio,object'), shortEndedElementsMap);\r
1707         defaultWhiteSpaceElementsMap = makeMap('pre,script,style,textarea');\r
1708         selfClosingElementsMap = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr');\r
1709 \r
1710         tinymce.html.Schema = function(settings) {\r
1711                 var self = this, elements = {}, children = {}, patternElements = [], validStyles, whiteSpaceElementsMap;\r
1712 \r
1713                 settings = settings || {};\r
1714 \r
1715                 // Allow all elements and attributes if verify_html is set to false\r
1716                 if (settings.verify_html === false)\r
1717                         settings.valid_elements = '*[*]';\r
1718 \r
1719                 // Build styles list\r
1720                 if (settings.valid_styles) {\r
1721                         validStyles = {};\r
1722 \r
1723                         // Convert styles into a rule list\r
1724                         each(settings.valid_styles, function(value, key) {\r
1725                                 validStyles[key] = tinymce.explode(value);\r
1726                         });\r
1727                 }\r
1728 \r
1729                 whiteSpaceElementsMap = settings.whitespace_elements ? makeMap(settings.whitespace_elements) : defaultWhiteSpaceElementsMap;\r
1730 \r
1731                 // Converts a wildcard expression string to a regexp for example *a will become /.*a/.\r
1732                 function patternToRegExp(str) {\r
1733                         return new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$');\r
1734                 };\r
1735 \r
1736                 // Parses the specified valid_elements string and adds to the current rules\r
1737                 // This function is a bit hard to read since it's heavily optimized for speed\r
1738                 function addValidElements(valid_elements) {\r
1739                         var ei, el, ai, al, yl, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder,\r
1740                                 prefix, outputName, globalAttributes, globalAttributesOrder, transElement, key, childKey, value,\r
1741                                 elementRuleRegExp = /^([#+-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,\r
1742                                 attrRuleRegExp = /^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,\r
1743                                 hasPatternsRegExp = /[*?+]/;\r
1744 \r
1745                         if (valid_elements) {\r
1746                                 // Split valid elements into an array with rules\r
1747                                 valid_elements = split(valid_elements);\r
1748 \r
1749                                 if (elements['@']) {\r
1750                                         globalAttributes = elements['@'].attributes;\r
1751                                         globalAttributesOrder = elements['@'].attributesOrder;\r
1752                                 }\r
1753 \r
1754                                 // Loop all rules\r
1755                                 for (ei = 0, el = valid_elements.length; ei < el; ei++) {\r
1756                                         // Parse element rule\r
1757                                         matches = elementRuleRegExp.exec(valid_elements[ei]);\r
1758                                         if (matches) {\r
1759                                                 // Setup local names for matches\r
1760                                                 prefix = matches[1];\r
1761                                                 elementName = matches[2];\r
1762                                                 outputName = matches[3];\r
1763                                                 attrData = matches[4];\r
1764 \r
1765                                                 // Create new attributes and attributesOrder\r
1766                                                 attributes = {};\r
1767                                                 attributesOrder = [];\r
1768 \r
1769                                                 // Create the new element\r
1770                                                 element = {\r
1771                                                         attributes : attributes,\r
1772                                                         attributesOrder : attributesOrder\r
1773                                                 };\r
1774 \r
1775                                                 // Padd empty elements prefix\r
1776                                                 if (prefix === '#')\r
1777                                                         element.paddEmpty = true;\r
1778 \r
1779                                                 // Remove empty elements prefix\r
1780                                                 if (prefix === '-')\r
1781                                                         element.removeEmpty = true;\r
1782 \r
1783                                                 // Copy attributes from global rule into current rule\r
1784                                                 if (globalAttributes) {\r
1785                                                         for (key in globalAttributes)\r
1786                                                                 attributes[key] = globalAttributes[key];\r
1787 \r
1788                                                         attributesOrder.push.apply(attributesOrder, globalAttributesOrder);\r
1789                                                 }\r
1790 \r
1791                                                 // Attributes defined\r
1792                                                 if (attrData) {\r
1793                                                         attrData = split(attrData, '|');\r
1794                                                         for (ai = 0, al = attrData.length; ai < al; ai++) {\r
1795                                                                 matches = attrRuleRegExp.exec(attrData[ai]);\r
1796                                                                 if (matches) {\r
1797                                                                         attr = {};\r
1798                                                                         attrType = matches[1];\r
1799                                                                         attrName = matches[2].replace(/::/g, ':');\r
1800                                                                         prefix = matches[3];\r
1801                                                                         value = matches[4];\r
1802 \r
1803                                                                         // Required\r
1804                                                                         if (attrType === '!') {\r
1805                                                                                 element.attributesRequired = element.attributesRequired || [];\r
1806                                                                                 element.attributesRequired.push(attrName);\r
1807                                                                                 attr.required = true;\r
1808                                                                         }\r
1809 \r
1810                                                                         // Denied from global\r
1811                                                                         if (attrType === '-') {\r
1812                                                                                 delete attributes[attrName];\r
1813                                                                                 attributesOrder.splice(tinymce.inArray(attributesOrder, attrName), 1);\r
1814                                                                                 continue;\r
1815                                                                         }\r
1816 \r
1817                                                                         // Default value\r
1818                                                                         if (prefix) {\r
1819                                                                                 // Default value\r
1820                                                                                 if (prefix === '=') {\r
1821                                                                                         element.attributesDefault = element.attributesDefault || [];\r
1822                                                                                         element.attributesDefault.push({name: attrName, value: value});\r
1823                                                                                         attr.defaultValue = value;\r
1824                                                                                 }\r
1825 \r
1826                                                                                 // Forced value\r
1827                                                                                 if (prefix === ':') {\r
1828                                                                                         element.attributesForced = element.attributesForced || [];\r
1829                                                                                         element.attributesForced.push({name: attrName, value: value});\r
1830                                                                                         attr.forcedValue = value;\r
1831                                                                                 }\r
1832 \r
1833                                                                                 // Required values\r
1834                                                                                 if (prefix === '<')\r
1835                                                                                         attr.validValues = makeMap(value, '?');\r
1836                                                                         }\r
1837 \r
1838                                                                         // Check for attribute patterns\r
1839                                                                         if (hasPatternsRegExp.test(attrName)) {\r
1840                                                                                 element.attributePatterns = element.attributePatterns || [];\r
1841                                                                                 attr.pattern = patternToRegExp(attrName);\r
1842                                                                                 element.attributePatterns.push(attr);\r
1843                                                                         } else {\r
1844                                                                                 // Add attribute to order list if it doesn't already exist\r
1845                                                                                 if (!attributes[attrName])\r
1846                                                                                         attributesOrder.push(attrName);\r
1847 \r
1848                                                                                 attributes[attrName] = attr;\r
1849                                                                         }\r
1850                                                                 }\r
1851                                                         }\r
1852                                                 }\r
1853 \r
1854                                                 // Global rule, store away these for later usage\r
1855                                                 if (!globalAttributes && elementName == '@') {\r
1856                                                         globalAttributes = attributes;\r
1857                                                         globalAttributesOrder = attributesOrder;\r
1858                                                 }\r
1859 \r
1860                                                 // Handle substitute elements such as b/strong\r
1861                                                 if (outputName) {\r
1862                                                         element.outputName = elementName;\r
1863                                                         elements[outputName] = element;\r
1864                                                 }\r
1865 \r
1866                                                 // Add pattern or exact element\r
1867                                                 if (hasPatternsRegExp.test(elementName)) {\r
1868                                                         element.pattern = patternToRegExp(elementName);\r
1869                                                         patternElements.push(element);\r
1870                                                 } else\r
1871                                                         elements[elementName] = element;\r
1872                                         }\r
1873                                 }\r
1874                         }\r
1875                 };\r
1876 \r
1877                 function setValidElements(valid_elements) {\r
1878                         elements = {};\r
1879                         patternElements = [];\r
1880 \r
1881                         addValidElements(valid_elements);\r
1882 \r
1883                         each(transitional, function(element, name) {\r
1884                                 children[name] = element.children;\r
1885                         });\r
1886                 };\r
1887 \r
1888                 // Adds custom non HTML elements to the schema\r
1889                 function addCustomElements(custom_elements) {\r
1890                         var customElementRegExp = /^(~)?(.+)$/;\r
1891 \r
1892                         if (custom_elements) {\r
1893                                 each(split(custom_elements), function(rule) {\r
1894                                         var matches = customElementRegExp.exec(rule),\r
1895                                                 inline = matches[1] === '~',\r
1896                                                 cloneName = inline ? 'span' : 'div',\r
1897                                                 name = matches[2];\r
1898 \r
1899                                         children[name] = children[cloneName];\r
1900                                         customElementsMap[name] = cloneName;\r
1901 \r
1902                                         // If it's not marked as inline then add it to valid block elements\r
1903                                         if (!inline)\r
1904                                                 blockElementsMap[name] = {};\r
1905 \r
1906                                         // Add custom elements at span/div positions\r
1907                                         each(children, function(element, child) {\r
1908                                                 if (element[cloneName])\r
1909                                                         element[name] = element[cloneName];\r
1910                                         });\r
1911                                 });\r
1912                         }\r
1913                 };\r
1914 \r
1915                 // Adds valid children to the schema object\r
1916                 function addValidChildren(valid_children) {\r
1917                         var childRuleRegExp = /^([+\-]?)(\w+)\[([^\]]+)\]$/;\r
1918 \r
1919                         if (valid_children) {\r
1920                                 each(split(valid_children), function(rule) {\r
1921                                         var matches = childRuleRegExp.exec(rule), parent, prefix;\r
1922 \r
1923                                         if (matches) {\r
1924                                                 prefix = matches[1];\r
1925 \r
1926                                                 // Add/remove items from default\r
1927                                                 if (prefix)\r
1928                                                         parent = children[matches[2]];\r
1929                                                 else\r
1930                                                         parent = children[matches[2]] = {'#comment' : {}};\r
1931 \r
1932                                                 parent = children[matches[2]];\r
1933 \r
1934                                                 each(split(matches[3], '|'), function(child) {\r
1935                                                         if (prefix === '-')\r
1936                                                                 delete parent[child];\r
1937                                                         else\r
1938                                                                 parent[child] = {};\r
1939                                                 });\r
1940                                         }\r
1941                                 });\r
1942                         }\r
1943                 };\r
1944 \r
1945                 function getElementRule(name) {\r
1946                         var element = elements[name], i;\r
1947 \r
1948                         // Exact match found\r
1949                         if (element)\r
1950                                 return element;\r
1951 \r
1952                         // No exact match then try the patterns\r
1953                         i = patternElements.length;\r
1954                         while (i--) {\r
1955                                 element = patternElements[i];\r
1956 \r
1957                                 if (element.pattern.test(name))\r
1958                                         return element;\r
1959                         }\r
1960                 };\r
1961 \r
1962                 if (!settings.valid_elements) {\r
1963                         // No valid elements defined then clone the elements from the transitional spec\r
1964                         each(transitional, function(element, name) {\r
1965                                 elements[name] = {\r
1966                                         attributes : element.attributes,\r
1967                                         attributesOrder : element.attributesOrder\r
1968                                 };\r
1969 \r
1970                                 children[name] = element.children;\r
1971                         });\r
1972 \r
1973                         // Switch these\r
1974                         each(split('strong/b,em/i'), function(item) {\r
1975                                 item = split(item, '/');\r
1976                                 elements[item[1]].outputName = item[0];\r
1977                         });\r
1978 \r
1979                         // Add default alt attribute for images\r
1980                         elements.img.attributesDefault = [{name: 'alt', value: ''}];\r
1981 \r
1982                         // Remove these if they are empty by default\r
1983                         each(split('ol,ul,sub,sup,blockquote,span,font,a,table,tbody,tr'), function(name) {\r
1984                                 elements[name].removeEmpty = true;\r
1985                         });\r
1986 \r
1987                         // Padd these by default\r
1988                         each(split('p,h1,h2,h3,h4,h5,h6,th,td,pre,div,address,caption'), function(name) {\r
1989                                 elements[name].paddEmpty = true;\r
1990                         });\r
1991                 } else\r
1992                         setValidElements(settings.valid_elements);\r
1993 \r
1994                 addCustomElements(settings.custom_elements);\r
1995                 addValidChildren(settings.valid_children);\r
1996                 addValidElements(settings.extended_valid_elements);\r
1997 \r
1998                 // Todo: Remove this when we fix list handling to be valid\r
1999                 addValidChildren('+ol[ul|ol],+ul[ul|ol]');\r
2000 \r
2001                 // If the user didn't allow span only allow internal spans\r
2002                 if (!getElementRule('span'))\r
2003                         addValidElements('span[!data-mce-type|*]');\r
2004 \r
2005                 // Delete invalid elements\r
2006                 if (settings.invalid_elements) {\r
2007                         tinymce.each(tinymce.explode(settings.invalid_elements), function(item) {\r
2008                                 if (elements[item])\r
2009                                         delete elements[item];\r
2010                         });\r
2011                 }\r
2012 \r
2013                 self.children = children;\r
2014 \r
2015                 self.styles = validStyles;\r
2016 \r
2017                 self.getBoolAttrs = function() {\r
2018                         return boolAttrMap;\r
2019                 };\r
2020 \r
2021                 self.getBlockElements = function() {\r
2022                         return blockElementsMap;\r
2023                 };\r
2024 \r
2025                 self.getShortEndedElements = function() {\r
2026                         return shortEndedElementsMap;\r
2027                 };\r
2028 \r
2029                 self.getSelfClosingElements = function() {\r
2030                         return selfClosingElementsMap;\r
2031                 };\r
2032 \r
2033                 self.getNonEmptyElements = function() {\r
2034                         return nonEmptyElementsMap;\r
2035                 };\r
2036 \r
2037                 self.getWhiteSpaceElements = function() {\r
2038                         return whiteSpaceElementsMap;\r
2039                 };\r
2040 \r
2041                 self.isValidChild = function(name, child) {\r
2042                         var parent = children[name];\r
2043 \r
2044                         return !!(parent && parent[child]);\r
2045                 };\r
2046 \r
2047                 self.getElementRule = getElementRule;\r
2048 \r
2049                 self.getCustomElements = function() {\r
2050                         return customElementsMap;\r
2051                 };\r
2052 \r
2053                 self.addValidElements = addValidElements;\r
2054 \r
2055                 self.setValidElements = setValidElements;\r
2056 \r
2057                 self.addCustomElements = addCustomElements;\r
2058 \r
2059                 self.addValidChildren = addValidChildren;\r
2060         };\r
2061 \r
2062         // Expose boolMap and blockElementMap as static properties for usage in DOMUtils\r
2063         tinymce.html.Schema.boolAttrMap = boolAttrMap;\r
2064         tinymce.html.Schema.blockElementsMap = blockElementsMap;\r
2065 })(tinymce);\r
2066 \r
2067 (function(tinymce) {\r
2068         tinymce.html.SaxParser = function(settings, schema) {\r
2069                 var self = this, noop = function() {};\r
2070 \r
2071                 settings = settings || {};\r
2072                 self.schema = schema = schema || new tinymce.html.Schema();\r
2073 \r
2074                 if (settings.fix_self_closing !== false)\r
2075                         settings.fix_self_closing = true;\r
2076 \r
2077                 // Add handler functions from settings and setup default handlers\r
2078                 tinymce.each('comment cdata text start end pi doctype'.split(' '), function(name) {\r
2079                         if (name)\r
2080                                 self[name] = settings[name] || noop;\r
2081                 });\r
2082 \r
2083                 self.parse = function(html) {\r
2084                         var self = this, matches, index = 0, value, endRegExp, stack = [], attrList, i, text, name, isInternalElement, removeInternalElements,\r
2085                                 shortEndedElements, fillAttrsMap, isShortEnded, validate, elementRule, isValidElement, attr, attribsValue, invalidPrefixRegExp,\r
2086                                 validAttributesMap, validAttributePatterns, attributesRequired, attributesDefault, attributesForced, selfClosing,\r
2087                                 tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0, decode = tinymce.html.Entities.decode, fixSelfClosing, isIE;\r
2088 \r
2089                         function processEndTag(name) {\r
2090                                 var pos, i;\r
2091 \r
2092                                 // Find position of parent of the same type\r
2093                                 pos = stack.length;\r
2094                                 while (pos--) {\r
2095                                         if (stack[pos].name === name)\r
2096                                                 break;                                          \r
2097                                 }\r
2098 \r
2099                                 // Found parent\r
2100                                 if (pos >= 0) {\r
2101                                         // Close all the open elements\r
2102                                         for (i = stack.length - 1; i >= pos; i--) {\r
2103                                                 name = stack[i];\r
2104 \r
2105                                                 if (name.valid)\r
2106                                                         self.end(name.name);\r
2107                                         }\r
2108 \r
2109                                         // Remove the open elements from the stack\r
2110                                         stack.length = pos;\r
2111                                 }\r
2112                         };\r
2113 \r
2114                         // Precompile RegExps and map objects\r
2115                         tokenRegExp = new RegExp('<(?:' +\r
2116                                 '(?:!--([\\w\\W]*?)-->)|' + // Comment\r
2117                                 '(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|' + // CDATA\r
2118                                 '(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE\r
2119                                 '(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI\r
2120                                 '(?:\\/([^>]+)>)|' + // End element\r
2121                                 '(?:([^\\s\\/<>]+)\\s*((?:[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*)>)' + // Start element\r
2122                         ')', 'g');\r
2123 \r
2124                         attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:\\.|[^\"])*)\")|(?:\'((?:\\.|[^\'])*)\')|([^>\s]+)))?/g;\r
2125                         specialElements = {\r
2126                                 'script' : /<\/script[^>]*>/gi,\r
2127                                 'style' : /<\/style[^>]*>/gi,\r
2128                                 'noscript' : /<\/noscript[^>]*>/gi\r
2129                         };\r
2130 \r
2131                         // Setup lookup tables for empty elements and boolean attributes\r
2132                         shortEndedElements = schema.getShortEndedElements();\r
2133                         selfClosing = schema.getSelfClosingElements();\r
2134                         fillAttrsMap = schema.getBoolAttrs();\r
2135                         validate = settings.validate;\r
2136                         removeInternalElements = settings.remove_internals;\r
2137                         fixSelfClosing = settings.fix_self_closing;\r
2138                         isIE = tinymce.isIE;\r
2139                         invalidPrefixRegExp = /^:/;\r
2140 \r
2141                         while (matches = tokenRegExp.exec(html)) {\r
2142                                 // Text\r
2143                                 if (index < matches.index)\r
2144                                         self.text(decode(html.substr(index, matches.index - index)));\r
2145 \r
2146                                 if (value = matches[6]) { // End element\r
2147                                         value = value.toLowerCase();\r
2148 \r
2149                                         // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements\r
2150                                         if (isIE && invalidPrefixRegExp.test(value))\r
2151                                                 value = value.substr(1);\r
2152 \r
2153                                         processEndTag(value);\r
2154                                 } else if (value = matches[7]) { // Start element\r
2155                                         value = value.toLowerCase();\r
2156 \r
2157                                         // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements\r
2158                                         if (isIE && invalidPrefixRegExp.test(value))\r
2159                                                 value = value.substr(1);\r
2160 \r
2161                                         isShortEnded = value in shortEndedElements;\r
2162 \r
2163                                         // Is self closing tag for example an <li> after an open <li>\r
2164                                         if (fixSelfClosing && selfClosing[value] && stack.length > 0 && stack[stack.length - 1].name === value)\r
2165                                                 processEndTag(value);\r
2166 \r
2167                                         // Validate element\r
2168                                         if (!validate || (elementRule = schema.getElementRule(value))) {\r
2169                                                 isValidElement = true;\r
2170 \r
2171                                                 // Grab attributes map and patters when validation is enabled\r
2172                                                 if (validate) {\r
2173                                                         validAttributesMap = elementRule.attributes;\r
2174                                                         validAttributePatterns = elementRule.attributePatterns;\r
2175                                                 }\r
2176 \r
2177                                                 // Parse attributes\r
2178                                                 if (attribsValue = matches[8]) {\r
2179                                                         isInternalElement = attribsValue.indexOf('data-mce-type') !== -1; // Check if the element is an internal element\r
2180 \r
2181                                                         // If the element has internal attributes then remove it if we are told to do so\r
2182                                                         if (isInternalElement && removeInternalElements)\r
2183                                                                 isValidElement = false;\r
2184 \r
2185                                                         attrList = [];\r
2186                                                         attrList.map = {};\r
2187 \r
2188                                                         attribsValue.replace(attrRegExp, function(match, name, value, val2, val3) {\r
2189                                                                 var attrRule, i;\r
2190 \r
2191                                                                 name = name.toLowerCase();\r
2192                                                                 value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute\r
2193 \r
2194                                                                 // Validate name and value\r
2195                                                                 if (validate && !isInternalElement && name.indexOf('data-') !== 0) {\r
2196                                                                         attrRule = validAttributesMap[name];\r
2197 \r
2198                                                                         // Find rule by pattern matching\r
2199                                                                         if (!attrRule && validAttributePatterns) {\r
2200                                                                                 i = validAttributePatterns.length;\r
2201                                                                                 while (i--) {\r
2202                                                                                         attrRule = validAttributePatterns[i];\r
2203                                                                                         if (attrRule.pattern.test(name))\r
2204                                                                                                 break;\r
2205                                                                                 }\r
2206 \r
2207                                                                                 // No rule matched\r
2208                                                                                 if (i === -1)\r
2209                                                                                         attrRule = null;\r
2210                                                                         }\r
2211 \r
2212                                                                         // No attribute rule found\r
2213                                                                         if (!attrRule)\r
2214                                                                                 return;\r
2215 \r
2216                                                                         // Validate value\r
2217                                                                         if (attrRule.validValues && !(value in attrRule.validValues))\r
2218                                                                                 return;\r
2219                                                                 }\r
2220 \r
2221                                                                 // Add attribute to list and map\r
2222                                                                 attrList.map[name] = value;\r
2223                                                                 attrList.push({\r
2224                                                                         name: name,\r
2225                                                                         value: value\r
2226                                                                 });\r
2227                                                         });\r
2228                                                 } else {\r
2229                                                         attrList = [];\r
2230                                                         attrList.map = {};\r
2231                                                 }\r
2232 \r
2233                                                 // Process attributes if validation is enabled\r
2234                                                 if (validate && !isInternalElement) {\r
2235                                                         attributesRequired = elementRule.attributesRequired;\r
2236                                                         attributesDefault = elementRule.attributesDefault;\r
2237                                                         attributesForced = elementRule.attributesForced;\r
2238 \r
2239                                                         // Handle forced attributes\r
2240                                                         if (attributesForced) {\r
2241                                                                 i = attributesForced.length;\r
2242                                                                 while (i--) {\r
2243                                                                         attr = attributesForced[i];\r
2244                                                                         name = attr.name;\r
2245                                                                         attrValue = attr.value;\r
2246 \r
2247                                                                         if (attrValue === '{$uid}')\r
2248                                                                                 attrValue = 'mce_' + idCount++;\r
2249 \r
2250                                                                         attrList.map[name] = attrValue;\r
2251                                                                         attrList.push({name: name, value: attrValue});\r
2252                                                                 }\r
2253                                                         }\r
2254 \r
2255                                                         // Handle default attributes\r
2256                                                         if (attributesDefault) {\r
2257                                                                 i = attributesDefault.length;\r
2258                                                                 while (i--) {\r
2259                                                                         attr = attributesDefault[i];\r
2260                                                                         name = attr.name;\r
2261 \r
2262                                                                         if (!(name in attrList.map)) {\r
2263                                                                                 attrValue = attr.value;\r
2264 \r
2265                                                                                 if (attrValue === '{$uid}')\r
2266                                                                                         attrValue = 'mce_' + idCount++;\r
2267 \r
2268                                                                                 attrList.map[name] = attrValue;\r
2269                                                                                 attrList.push({name: name, value: attrValue});\r
2270                                                                         }\r
2271                                                                 }\r
2272                                                         }\r
2273 \r
2274                                                         // Handle required attributes\r
2275                                                         if (attributesRequired) {\r
2276                                                                 i = attributesRequired.length;\r
2277                                                                 while (i--) {\r
2278                                                                         if (attributesRequired[i] in attrList.map)\r
2279                                                                                 break;\r
2280                                                                 }\r
2281 \r
2282                                                                 // None of the required attributes where found\r
2283                                                                 if (i === -1)\r
2284                                                                         isValidElement = false;\r
2285                                                         }\r
2286 \r
2287                                                         // Invalidate element if it's marked as bogus\r
2288                                                         if (attrList.map['data-mce-bogus'])\r
2289                                                                 isValidElement = false;\r
2290                                                 }\r
2291 \r
2292                                                 if (isValidElement)\r
2293                                                         self.start(value, attrList, isShortEnded);\r
2294                                         } else\r
2295                                                 isValidElement = false;\r
2296 \r
2297                                         // Treat script, noscript and style a bit different since they may include code that looks like elements\r
2298                                         if (endRegExp = specialElements[value]) {\r
2299                                                 endRegExp.lastIndex = index = matches.index + matches[0].length;\r
2300 \r
2301                                                 if (matches = endRegExp.exec(html)) {\r
2302                                                         if (isValidElement)\r
2303                                                                 text = html.substr(index, matches.index - index);\r
2304 \r
2305                                                         index = matches.index + matches[0].length;\r
2306                                                 } else {\r
2307                                                         text = html.substr(index);\r
2308                                                         index = html.length;\r
2309                                                 }\r
2310 \r
2311                                                 if (isValidElement && text.length > 0)\r
2312                                                         self.text(text, true);\r
2313 \r
2314                                                 if (isValidElement)\r
2315                                                         self.end(value);\r
2316 \r
2317                                                 tokenRegExp.lastIndex = index;\r
2318                                                 continue;\r
2319                                         }\r
2320 \r
2321                                         // Push value on to stack\r
2322                                         if (!isShortEnded) {\r
2323                                                 if (!attribsValue || attribsValue.indexOf('/') != attribsValue.length - 1)\r
2324                                                         stack.push({name: value, valid: isValidElement});\r
2325                                                 else if (isValidElement)\r
2326                                                         self.end(value);\r
2327                                         }\r
2328                                 } else if (value = matches[1]) { // Comment\r
2329                                         self.comment(value);\r
2330                                 } else if (value = matches[2]) { // CDATA\r
2331                                         self.cdata(value);\r
2332                                 } else if (value = matches[3]) { // DOCTYPE\r
2333                                         self.doctype(value);\r
2334                                 } else if (value = matches[4]) { // PI\r
2335                                         self.pi(value, matches[5]);\r
2336                                 }\r
2337 \r
2338                                 index = matches.index + matches[0].length;\r
2339                         }\r
2340 \r
2341                         // Text\r
2342                         if (index < html.length)\r
2343                                 self.text(decode(html.substr(index)));\r
2344 \r
2345                         // Close any open elements\r
2346                         for (i = stack.length - 1; i >= 0; i--) {\r
2347                                 value = stack[i];\r
2348 \r
2349                                 if (value.valid)\r
2350                                         self.end(value.name);\r
2351                         }\r
2352                 };\r
2353         }\r
2354 })(tinymce);\r
2355 \r
2356 (function(tinymce) {\r
2357         var whiteSpaceRegExp = /^[ \t\r\n]*$/, typeLookup = {\r
2358                 '#text' : 3,\r
2359                 '#comment' : 8,\r
2360                 '#cdata' : 4,\r
2361                 '#pi' : 7,\r
2362                 '#doctype' : 10,\r
2363                 '#document-fragment' : 11\r
2364         };\r
2365 \r
2366         // Walks the tree left/right\r
2367         function walk(node, root_node, prev) {\r
2368                 var sibling, parent, startName = prev ? 'lastChild' : 'firstChild', siblingName = prev ? 'prev' : 'next';\r
2369 \r
2370                 // Walk into nodes if it has a start\r
2371                 if (node[startName])\r
2372                         return node[startName];\r
2373 \r
2374                 // Return the sibling if it has one\r
2375                 if (node !== root_node) {\r
2376                         sibling = node[siblingName];\r
2377 \r
2378                         if (sibling)\r
2379                                 return sibling;\r
2380 \r
2381                         // Walk up the parents to look for siblings\r
2382                         for (parent = node.parent; parent && parent !== root_node; parent = parent.parent) {\r
2383                                 sibling = parent[siblingName];\r
2384 \r
2385                                 if (sibling)\r
2386                                         return sibling;\r
2387                         }\r
2388                 }\r
2389         };\r
2390 \r
2391         function Node(name, type) {\r
2392                 this.name = name;\r
2393                 this.type = type;\r
2394 \r
2395                 if (type === 1) {\r
2396                         this.attributes = [];\r
2397                         this.attributes.map = {};\r
2398                 }\r
2399         }\r
2400 \r
2401         tinymce.extend(Node.prototype, {\r
2402                 replace : function(node) {\r
2403                         var self = this;\r
2404 \r
2405                         if (node.parent)\r
2406                                 node.remove();\r
2407 \r
2408                         self.insert(node, self);\r
2409                         self.remove();\r
2410 \r
2411                         return self;\r
2412                 },\r
2413 \r
2414                 attr : function(name, value) {\r
2415                         var self = this, attrs, i, undef;\r
2416 \r
2417                         if (typeof name !== "string") {\r
2418                                 for (i in name)\r
2419                                         self.attr(i, name[i]);\r
2420 \r
2421                                 return self;\r
2422                         }\r
2423 \r
2424                         if (attrs = self.attributes) {\r
2425                                 if (value !== undef) {\r
2426                                         // Remove attribute\r
2427                                         if (value === null) {\r
2428                                                 if (name in attrs.map) {\r
2429                                                         delete attrs.map[name];\r
2430 \r
2431                                                         i = attrs.length;\r
2432                                                         while (i--) {\r
2433                                                                 if (attrs[i].name === name) {\r
2434                                                                         attrs = attrs.splice(i, 1);\r
2435                                                                         return self;\r
2436                                                                 }\r
2437                                                         }\r
2438                                                 }\r
2439 \r
2440                                                 return self;\r
2441                                         }\r
2442 \r
2443                                         // Set attribute\r
2444                                         if (name in attrs.map) {\r
2445                                                 // Set attribute\r
2446                                                 i = attrs.length;\r
2447                                                 while (i--) {\r
2448                                                         if (attrs[i].name === name) {\r
2449                                                                 attrs[i].value = value;\r
2450                                                                 break;\r
2451                                                         }\r
2452                                                 }\r
2453                                         } else\r
2454                                                 attrs.push({name: name, value: value});\r
2455 \r
2456                                         attrs.map[name] = value;\r
2457 \r
2458                                         return self;\r
2459                                 } else {\r
2460                                         return attrs.map[name];\r
2461                                 }\r
2462                         }\r
2463                 },\r
2464 \r
2465                 clone : function() {\r
2466                         var self = this, clone = new Node(self.name, self.type), i, l, selfAttrs, selfAttr, cloneAttrs;\r
2467 \r
2468                         // Clone element attributes\r
2469                         if (selfAttrs = self.attributes) {\r
2470                                 cloneAttrs = [];\r
2471                                 cloneAttrs.map = {};\r
2472 \r
2473                                 for (i = 0, l = selfAttrs.length; i < l; i++) {\r
2474                                         selfAttr = selfAttrs[i];\r
2475 \r
2476                                         // Clone everything except id\r
2477                                         if (selfAttr.name !== 'id') {\r
2478                                                 cloneAttrs[cloneAttrs.length] = {name: selfAttr.name, value: selfAttr.value};\r
2479                                                 cloneAttrs.map[selfAttr.name] = selfAttr.value;\r
2480                                         }\r
2481                                 }\r
2482 \r
2483                                 clone.attributes = cloneAttrs;\r
2484                         }\r
2485 \r
2486                         clone.value = self.value;\r
2487                         clone.shortEnded = self.shortEnded;\r
2488 \r
2489                         return clone;\r
2490                 },\r
2491 \r
2492                 wrap : function(wrapper) {\r
2493                         var self = this;\r
2494 \r
2495                         self.parent.insert(wrapper, self);\r
2496                         wrapper.append(self);\r
2497 \r
2498                         return self;\r
2499                 },\r
2500 \r
2501                 unwrap : function() {\r
2502                         var self = this, node, next;\r
2503 \r
2504                         for (node = self.firstChild; node; ) {\r
2505                                 next = node.next;\r
2506                                 self.insert(node, self, true);\r
2507                                 node = next;\r
2508                         }\r
2509 \r
2510                         self.remove();\r
2511                 },\r
2512 \r
2513                 remove : function() {\r
2514                         var self = this, parent = self.parent, next = self.next, prev = self.prev;\r
2515 \r
2516                         if (parent) {\r
2517                                 if (parent.firstChild === self) {\r
2518                                         parent.firstChild = next;\r
2519 \r
2520                                         if (next)\r
2521                                                 next.prev = null;\r
2522                                 } else {\r
2523                                         prev.next = next;\r
2524                                 }\r
2525 \r
2526                                 if (parent.lastChild === self) {\r
2527                                         parent.lastChild = prev;\r
2528 \r
2529                                         if (prev)\r
2530                                                 prev.next = null;\r
2531                                 } else {\r
2532                                         next.prev = prev;\r
2533                                 }\r
2534 \r
2535                                 self.parent = self.next = self.prev = null;\r
2536                         }\r
2537 \r
2538                         return self;\r
2539                 },\r
2540 \r
2541                 append : function(node) {\r
2542                         var self = this, last;\r
2543 \r
2544                         if (node.parent)\r
2545                                 node.remove();\r
2546 \r
2547                         last = self.lastChild;\r
2548                         if (last) {\r
2549                                 last.next = node;\r
2550                                 node.prev = last;\r
2551                                 self.lastChild = node;\r
2552                         } else\r
2553                                 self.lastChild = self.firstChild = node;\r
2554 \r
2555                         node.parent = self;\r
2556 \r
2557                         return node;\r
2558                 },\r
2559 \r
2560                 insert : function(node, ref_node, before) {\r
2561                         var parent;\r
2562 \r
2563                         if (node.parent)\r
2564                                 node.remove();\r
2565 \r
2566                         parent = ref_node.parent || this;\r
2567 \r
2568                         if (before) {\r
2569                                 if (ref_node === parent.firstChild)\r
2570                                         parent.firstChild = node;\r
2571                                 else\r
2572                                         ref_node.prev.next = node;\r
2573 \r
2574                                 node.prev = ref_node.prev;\r
2575                                 node.next = ref_node;\r
2576                                 ref_node.prev = node;\r
2577                         } else {\r
2578                                 if (ref_node === parent.lastChild)\r
2579                                         parent.lastChild = node;\r
2580                                 else\r
2581                                         ref_node.next.prev = node;\r
2582 \r
2583                                 node.next = ref_node.next;\r
2584                                 node.prev = ref_node;\r
2585                                 ref_node.next = node;\r
2586                         }\r
2587 \r
2588                         node.parent = parent;\r
2589 \r
2590                         return node;\r
2591                 },\r
2592 \r
2593                 getAll : function(name) {\r
2594                         var self = this, node, collection = [];\r
2595 \r
2596                         for (node = self.firstChild; node; node = walk(node, self)) {\r
2597                                 if (node.name === name)\r
2598                                         collection.push(node);\r
2599                         }\r
2600 \r
2601                         return collection;\r
2602                 },\r
2603 \r
2604                 empty : function() {\r
2605                         var self = this, nodes, i, node;\r
2606 \r
2607                         // Remove all children\r
2608                         if (self.firstChild) {\r
2609                                 nodes = [];\r
2610 \r
2611                                 // Collect the children\r
2612                                 for (node = self.firstChild; node; node = walk(node, self))\r
2613                                         nodes.push(node);\r
2614 \r
2615                                 // Remove the children\r
2616                                 i = nodes.length;\r
2617                                 while (i--) {\r
2618                                         node = nodes[i];\r
2619                                         node.parent = node.firstChild = node.lastChild = node.next = node.prev = null;\r
2620                                 }\r
2621                         }\r
2622 \r
2623                         self.firstChild = self.lastChild = null;\r
2624 \r
2625                         return self;\r
2626                 },\r
2627 \r
2628                 isEmpty : function(elements) {\r
2629                         var self = this, node = self.firstChild, i, name;\r
2630 \r
2631                         if (node) {\r
2632                                 do {\r
2633                                         if (node.type === 1) {\r
2634                                                 // Ignore bogus elements\r
2635                                                 if (node.attributes.map['data-mce-bogus'])\r
2636                                                         continue;\r
2637 \r
2638                                                 // Keep empty elements like <img />\r
2639                                                 if (elements[node.name])\r
2640                                                         return false;\r
2641 \r
2642                                                 // Keep elements with data attributes or name attribute like <a name="1"></a>\r
2643                                                 i = node.attributes.length;\r
2644                                                 while (i--) {\r
2645                                                         name = node.attributes[i].name;\r
2646                                                         if (name === "name" || name.indexOf('data-') === 0)\r
2647                                                                 return false;\r
2648                                                 }\r
2649                                         }\r
2650 \r
2651                                         // Keep non whitespace text nodes\r
2652                                         if ((node.type === 3 && !whiteSpaceRegExp.test(node.value)))\r
2653                                                 return false;\r
2654                                 } while (node = walk(node, self));\r
2655                         }\r
2656 \r
2657                         return true;\r
2658                 },\r
2659 \r
2660                 walk : function(prev) {\r
2661                         return walk(this, null, prev);\r
2662                 }\r
2663         });\r
2664 \r
2665         tinymce.extend(Node, {\r
2666                 create : function(name, attrs) {\r
2667                         var node, attrName;\r
2668 \r
2669                         // Create node\r
2670                         node = new Node(name, typeLookup[name] || 1);\r
2671 \r
2672                         // Add attributes if needed\r
2673                         if (attrs) {\r
2674                                 for (attrName in attrs)\r
2675                                         node.attr(attrName, attrs[attrName]);\r
2676                         }\r
2677 \r
2678                         return node;\r
2679                 }\r
2680         });\r
2681 \r
2682         tinymce.html.Node = Node;\r
2683 })(tinymce);\r
2684 \r
2685 (function(tinymce) {\r
2686         var Node = tinymce.html.Node;\r
2687 \r
2688         tinymce.html.DomParser = function(settings, schema) {\r
2689                 var self = this, nodeFilters = {}, attributeFilters = [], matchedNodes = {}, matchedAttributes = {};\r
2690 \r
2691                 settings = settings || {};\r
2692                 settings.validate = "validate" in settings ? settings.validate : true;\r
2693                 settings.root_name = settings.root_name || 'body';\r
2694                 self.schema = schema = schema || new tinymce.html.Schema();\r
2695 \r
2696                 function fixInvalidChildren(nodes) {\r
2697                         var ni, node, parent, parents, newParent, currentNode, tempNode, childNode, i,\r
2698                                 childClone, nonEmptyElements, nonSplitableElements, sibling, nextNode;\r
2699 \r
2700                         nonSplitableElements = tinymce.makeMap('tr,td,th,tbody,thead,tfoot,table');\r
2701                         nonEmptyElements = schema.getNonEmptyElements();\r
2702 \r
2703                         for (ni = 0; ni < nodes.length; ni++) {\r
2704                                 node = nodes[ni];\r
2705 \r
2706                                 // Already removed\r
2707                                 if (!node.parent)\r
2708                                         continue;\r
2709 \r
2710                                 // Get list of all parent nodes until we find a valid parent to stick the child into\r
2711                                 parents = [node];\r
2712                                 for (parent = node.parent; parent && !schema.isValidChild(parent.name, node.name) && !nonSplitableElements[parent.name]; parent = parent.parent)\r
2713                                         parents.push(parent);\r
2714 \r
2715                                 // Found a suitable parent\r
2716                                 if (parent && parents.length > 1) {\r
2717                                         // Reverse the array since it makes looping easier\r
2718                                         parents.reverse();\r
2719 \r
2720                                         // Clone the related parent and insert that after the moved node\r
2721                                         newParent = currentNode = self.filterNode(parents[0].clone());\r
2722 \r
2723                                         // Start cloning and moving children on the left side of the target node\r
2724                                         for (i = 0; i < parents.length - 1; i++) {\r
2725                                                 if (schema.isValidChild(currentNode.name, parents[i].name)) {\r
2726                                                         tempNode = self.filterNode(parents[i].clone());\r
2727                                                         currentNode.append(tempNode);\r
2728                                                 } else\r
2729                                                         tempNode = currentNode;\r
2730 \r
2731                                                 for (childNode = parents[i].firstChild; childNode && childNode != parents[i + 1]; ) {\r
2732                                                         nextNode = childNode.next;\r
2733                                                         tempNode.append(childNode);\r
2734                                                         childNode = nextNode;\r
2735                                                 }\r
2736 \r
2737                                                 currentNode = tempNode;\r
2738                                         }\r
2739 \r
2740                                         if (!newParent.isEmpty(nonEmptyElements)) {\r
2741                                                 parent.insert(newParent, parents[0], true);\r
2742                                                 parent.insert(node, newParent);\r
2743                                         } else {\r
2744                                                 parent.insert(node, parents[0], true);\r
2745                                         }\r
2746 \r
2747                                         // Check if the element is empty by looking through it's contents and special treatment for <p><br /></p>\r
2748                                         parent = parents[0];\r
2749                                         if (parent.isEmpty(nonEmptyElements) || parent.firstChild === parent.lastChild && parent.firstChild.name === 'br') {\r
2750                                                 parent.empty().remove();\r
2751                                         }\r
2752                                 } else if (node.parent) {\r
2753                                         // If it's an LI try to find a UL/OL for it or wrap it\r
2754                                         if (node.name === 'li') {\r
2755                                                 sibling = node.prev;\r
2756                                                 if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {\r
2757                                                         sibling.append(node);\r
2758                                                         continue;\r
2759                                                 }\r
2760 \r
2761                                                 sibling = node.next;\r
2762                                                 if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {\r
2763                                                         sibling.insert(node, sibling.firstChild, true);\r
2764                                                         continue;\r
2765                                                 }\r
2766 \r
2767                                                 node.wrap(self.filterNode(new Node('ul', 1)));\r
2768                                                 continue;\r
2769                                         }\r
2770 \r
2771                                         // Try wrapping the element in a DIV\r
2772                                         if (schema.isValidChild(node.parent.name, 'div') && schema.isValidChild('div', node.name)) {\r
2773                                                 node.wrap(self.filterNode(new Node('div', 1)));\r
2774                                         } else {\r
2775                                                 // We failed wrapping it, then remove or unwrap it\r
2776                                                 if (node.name === 'style' || node.name === 'script')\r
2777                                                         node.empty().remove();\r
2778                                                 else\r
2779                                                         node.unwrap();\r
2780                                         }\r
2781                                 }\r
2782                         }\r
2783                 };\r
2784 \r
2785                 self.filterNode = function(node) {\r
2786                         var i, name, list;\r
2787 \r
2788                         // Run element filters\r
2789                         if (name in nodeFilters) {\r
2790                                 list = matchedNodes[name];\r
2791 \r
2792                                 if (list)\r
2793                                         list.push(node);\r
2794                                 else\r
2795                                         matchedNodes[name] = [node];\r
2796                         }\r
2797 \r
2798                         // Run attribute filters\r
2799                         i = attributeFilters.length;\r
2800                         while (i--) {\r
2801                                 name = attributeFilters[i].name;\r
2802 \r
2803                                 if (name in node.attributes.map) {\r
2804                                         list = matchedAttributes[name];\r
2805 \r
2806                                         if (list)\r
2807                                                 list.push(node);\r
2808                                         else\r
2809                                                 matchedAttributes[name] = [node];\r
2810                                 }\r
2811                         }\r
2812 \r
2813                         return node;\r
2814                 };\r
2815 \r
2816                 self.addNodeFilter = function(name, callback) {\r
2817                         tinymce.each(tinymce.explode(name), function(name) {\r
2818                                 var list = nodeFilters[name];\r
2819 \r
2820                                 if (!list)\r
2821                                         nodeFilters[name] = list = [];\r
2822 \r
2823                                 list.push(callback);\r
2824                         });\r
2825                 };\r
2826 \r
2827                 self.addAttributeFilter = function(name, callback) {\r
2828                         tinymce.each(tinymce.explode(name), function(name) {\r
2829                                 var i;\r
2830 \r
2831                                 for (i = 0; i < attributeFilters.length; i++) {\r
2832                                         if (attributeFilters[i].name === name) {\r
2833                                                 attributeFilters[i].callbacks.push(callback);\r
2834                                                 return;\r
2835                                         }\r
2836                                 }\r
2837 \r
2838                                 attributeFilters.push({name: name, callbacks: [callback]});\r
2839                         });\r
2840                 };\r
2841 \r
2842                 self.parse = function(html, args) {\r
2843                         var parser, rootNode, node, nodes, i, l, fi, fl, list, name, validate,\r
2844                                 blockElements, startWhiteSpaceRegExp, invalidChildren = [],\r
2845                                 endWhiteSpaceRegExp, allWhiteSpaceRegExp, whiteSpaceElements, children, nonEmptyElements, rootBlockName;\r
2846 \r
2847                         args = args || {};\r
2848                         matchedNodes = {};\r
2849                         matchedAttributes = {};\r
2850                         blockElements = tinymce.extend(tinymce.makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements());\r
2851                         nonEmptyElements = schema.getNonEmptyElements();\r
2852                         children = schema.children;\r
2853                         validate = settings.validate;\r
2854                         rootBlockName = "forced_root_block" in args ? args.forced_root_block : settings.forced_root_block;\r
2855 \r
2856                         whiteSpaceElements = schema.getWhiteSpaceElements();\r
2857                         startWhiteSpaceRegExp = /^[ \t\r\n]+/;\r
2858                         endWhiteSpaceRegExp = /[ \t\r\n]+$/;\r
2859                         allWhiteSpaceRegExp = /[ \t\r\n]+/g;\r
2860 \r
2861                         function addRootBlocks() {\r
2862                                 var node = rootNode.firstChild, next, rootBlockNode;\r
2863 \r
2864                                 while (node) {\r
2865                                         next = node.next;\r
2866 \r
2867                                         if (node.type == 3 || (node.type == 1 && node.name !== 'p' && !blockElements[node.name] && !node.attr('data-mce-type'))) {\r
2868                                                 if (!rootBlockNode) {\r
2869                                                         // Create a new root block element\r
2870                                                         rootBlockNode = createNode(rootBlockName, 1);\r
2871                                                         rootNode.insert(rootBlockNode, node);\r
2872                                                         rootBlockNode.append(node);\r
2873                                                 } else\r
2874                                                         rootBlockNode.append(node);\r
2875                                         } else {\r
2876                                                 rootBlockNode = null;\r
2877                                         }\r
2878 \r
2879                                         node = next;\r
2880                                 };\r
2881                         };\r
2882 \r
2883                         function createNode(name, type) {\r
2884                                 var node = new Node(name, type), list;\r
2885 \r
2886                                 if (name in nodeFilters) {\r
2887                                         list = matchedNodes[name];\r
2888 \r
2889                                         if (list)\r
2890                                                 list.push(node);\r
2891                                         else\r
2892                                                 matchedNodes[name] = [node];\r
2893                                 }\r
2894 \r
2895                                 return node;\r
2896                         };\r
2897 \r
2898                         function removeWhitespaceBefore(node) {\r
2899                                 var textNode, textVal, sibling;\r
2900 \r
2901                                 for (textNode = node.prev; textNode && textNode.type === 3; ) {\r
2902                                         textVal = textNode.value.replace(endWhiteSpaceRegExp, '');\r
2903 \r
2904                                         if (textVal.length > 0) {\r
2905                                                 textNode.value = textVal;\r
2906                                                 textNode = textNode.prev;\r
2907                                         } else {\r
2908                                                 sibling = textNode.prev;\r
2909                                                 textNode.remove();\r
2910                                                 textNode = sibling;\r
2911                                         }\r
2912                                 }\r
2913                         };\r
2914 \r
2915                         parser = new tinymce.html.SaxParser({\r
2916                                 validate : validate,\r
2917                                 fix_self_closing : !validate, // Let the DOM parser handle <li> in <li> or <p> in <p> for better results\r
2918 \r
2919                                 cdata: function(text) {\r
2920                                         node.append(createNode('#cdata', 4)).value = text;\r
2921                                 },\r
2922 \r
2923                                 text: function(text, raw) {\r
2924                                         var textNode;\r
2925 \r
2926                                         // Trim all redundant whitespace on non white space elements\r
2927                                         if (!whiteSpaceElements[node.name]) {\r
2928                                                 text = text.replace(allWhiteSpaceRegExp, ' ');\r
2929 \r
2930                                                 if (node.lastChild && blockElements[node.lastChild.name])\r
2931                                                         text = text.replace(startWhiteSpaceRegExp, '');\r
2932                                         }\r
2933 \r
2934                                         // Do we need to create the node\r
2935                                         if (text.length !== 0) {\r
2936                                                 textNode = createNode('#text', 3);\r
2937                                                 textNode.raw = !!raw;\r
2938                                                 node.append(textNode).value = text;\r
2939                                         }\r
2940                                 },\r
2941 \r
2942                                 comment: function(text) {\r
2943                                         node.append(createNode('#comment', 8)).value = text;\r
2944                                 },\r
2945 \r
2946                                 pi: function(name, text) {\r
2947                                         node.append(createNode(name, 7)).value = text;\r
2948                                         removeWhitespaceBefore(node);\r
2949                                 },\r
2950 \r
2951                                 doctype: function(text) {\r
2952                                         var newNode;\r
2953                 \r
2954                                         newNode = node.append(createNode('#doctype', 10));\r
2955                                         newNode.value = text;\r
2956                                         removeWhitespaceBefore(node);\r
2957                                 },\r
2958 \r
2959                                 start: function(name, attrs, empty) {\r
2960                                         var newNode, attrFiltersLen, elementRule, textNode, attrName, text, sibling, parent;\r
2961 \r
2962                                         elementRule = validate ? schema.getElementRule(name) : {};\r
2963                                         if (elementRule) {\r
2964                                                 newNode = createNode(elementRule.outputName || name, 1);\r
2965                                                 newNode.attributes = attrs;\r
2966                                                 newNode.shortEnded = empty;\r
2967 \r
2968                                                 node.append(newNode);\r
2969 \r
2970                                                 // Check if node is valid child of the parent node is the child is\r
2971                                                 // unknown we don't collect it since it's probably a custom element\r
2972                                                 parent = children[node.name];\r
2973                                                 if (parent && children[newNode.name] && !parent[newNode.name])\r
2974                                                         invalidChildren.push(newNode);\r
2975 \r
2976                                                 attrFiltersLen = attributeFilters.length;\r
2977                                                 while (attrFiltersLen--) {\r
2978                                                         attrName = attributeFilters[attrFiltersLen].name;\r
2979 \r
2980                                                         if (attrName in attrs.map) {\r
2981                                                                 list = matchedAttributes[attrName];\r
2982 \r
2983                                                                 if (list)\r
2984                                                                         list.push(newNode);\r
2985                                                                 else\r
2986                                                                         matchedAttributes[attrName] = [newNode];\r
2987                                                         }\r
2988                                                 }\r
2989 \r
2990                                                 // Trim whitespace before block\r
2991                                                 if (blockElements[name])\r
2992                                                         removeWhitespaceBefore(newNode);\r
2993 \r
2994                                                 // Change current node if the element wasn't empty i.e not <br /> or <img />\r
2995                                                 if (!empty)\r
2996                                                         node = newNode;\r
2997                                         }\r
2998                                 },\r
2999 \r
3000                                 end: function(name) {\r
3001                                         var textNode, elementRule, text, sibling, tempNode;\r
3002 \r
3003                                         elementRule = validate ? schema.getElementRule(name) : {};\r
3004                                         if (elementRule) {\r
3005                                                 if (blockElements[name]) {\r
3006                                                         if (!whiteSpaceElements[node.name]) {\r
3007                                                                 // Trim whitespace at beginning of block\r
3008                                                                 for (textNode = node.firstChild; textNode && textNode.type === 3; ) {\r
3009                                                                         text = textNode.value.replace(startWhiteSpaceRegExp, '');\r
3010 \r
3011                                                                         if (text.length > 0) {\r
3012                                                                                 textNode.value = text;\r
3013                                                                                 textNode = textNode.next;\r
3014                                                                         } else {\r
3015                                                                                 sibling = textNode.next;\r
3016                                                                                 textNode.remove();\r
3017                                                                                 textNode = sibling;\r
3018                                                                         }\r
3019                                                                 }\r
3020 \r
3021                                                                 // Trim whitespace at end of block\r
3022                                                                 for (textNode = node.lastChild; textNode && textNode.type === 3; ) {\r
3023                                                                         text = textNode.value.replace(endWhiteSpaceRegExp, '');\r
3024 \r
3025                                                                         if (text.length > 0) {\r
3026                                                                                 textNode.value = text;\r
3027                                                                                 textNode = textNode.prev;\r
3028                                                                         } else {\r
3029                                                                                 sibling = textNode.prev;\r
3030                                                                                 textNode.remove();\r
3031                                                                                 textNode = sibling;\r
3032                                                                         }\r
3033                                                                 }\r
3034                                                         }\r
3035 \r
3036                                                         // Trim start white space\r
3037                                                         textNode = node.prev;\r
3038                                                         if (textNode && textNode.type === 3) {\r
3039                                                                 text = textNode.value.replace(startWhiteSpaceRegExp, '');\r
3040 \r
3041                                                                 if (text.length > 0)\r
3042                                                                         textNode.value = text;\r
3043                                                                 else\r
3044                                                                         textNode.remove();\r
3045                                                         }\r
3046                                                 }\r
3047 \r
3048                                                 // Handle empty nodes\r
3049                                                 if (elementRule.removeEmpty || elementRule.paddEmpty) {\r
3050                                                         if (node.isEmpty(nonEmptyElements)) {\r
3051                                                                 if (elementRule.paddEmpty)\r
3052                                                                         node.empty().append(new Node('#text', '3')).value = '\u00a0';\r
3053                                                                 else {\r
3054                                                                         // Leave nodes that have a name like <a name="name">\r
3055                                                                         if (!node.attributes.map.name) {\r
3056                                                                                 tempNode = node.parent;\r
3057                                                                                 node.empty().remove();\r
3058                                                                                 node = tempNode;\r
3059                                                                                 return;\r
3060                                                                         }\r
3061                                                                 }\r
3062                                                         }\r
3063                                                 }\r
3064 \r
3065                                                 node = node.parent;\r
3066                                         }\r
3067                                 }\r
3068                         }, schema);\r
3069 \r
3070                         rootNode = node = new Node(args.context || settings.root_name, 11);\r
3071 \r
3072                         parser.parse(html);\r
3073 \r
3074                         // Fix invalid children or report invalid children in a contextual parsing\r
3075                         if (validate && invalidChildren.length) {\r
3076                                 if (!args.context)\r
3077                                         fixInvalidChildren(invalidChildren);\r
3078                                 else\r
3079                                         args.invalid = true;\r
3080                         }\r
3081 \r
3082                         // Wrap nodes in the root into block elements if the root is body\r
3083                         if (rootBlockName && rootNode.name == 'body')\r
3084                                 addRootBlocks();\r
3085 \r
3086                         // Run filters only when the contents is valid\r
3087                         if (!args.invalid) {\r
3088                                 // Run node filters\r
3089                                 for (name in matchedNodes) {\r
3090                                         list = nodeFilters[name];\r
3091                                         nodes = matchedNodes[name];\r
3092 \r
3093                                         // Remove already removed children\r
3094                                         fi = nodes.length;\r
3095                                         while (fi--) {\r
3096                                                 if (!nodes[fi].parent)\r
3097                                                         nodes.splice(fi, 1);\r
3098                                         }\r
3099 \r
3100                                         for (i = 0, l = list.length; i < l; i++)\r
3101                                                 list[i](nodes, name, args);\r
3102                                 }\r
3103 \r
3104                                 // Run attribute filters\r
3105                                 for (i = 0, l = attributeFilters.length; i < l; i++) {\r
3106                                         list = attributeFilters[i];\r
3107 \r
3108                                         if (list.name in matchedAttributes) {\r
3109                                                 nodes = matchedAttributes[list.name];\r
3110 \r
3111                                                 // Remove already removed children\r
3112                                                 fi = nodes.length;\r
3113                                                 while (fi--) {\r
3114                                                         if (!nodes[fi].parent)\r
3115                                                                 nodes.splice(fi, 1);\r
3116                                                 }\r
3117 \r
3118                                                 for (fi = 0, fl = list.callbacks.length; fi < fl; fi++)\r
3119                                                         list.callbacks[fi](nodes, list.name, args);\r
3120                                         }\r
3121                                 }\r
3122                         }\r
3123 \r
3124                         return rootNode;\r
3125                 };\r
3126 \r
3127                 // Remove <br> at end of block elements Gecko and WebKit injects BR elements to\r
3128                 // make it possible to place the caret inside empty blocks. This logic tries to remove\r
3129                 // these elements and keep br elements that where intended to be there intact\r
3130                 if (settings.remove_trailing_brs) {\r
3131                         self.addNodeFilter('br', function(nodes, name) {\r
3132                                 var i, l = nodes.length, node, blockElements = schema.getBlockElements(),\r
3133                                         nonEmptyElements = schema.getNonEmptyElements(), parent, prev, prevName;\r
3134 \r
3135                                 // Remove brs from body element as well\r
3136                                 blockElements.body = 1;\r
3137 \r
3138                                 // Must loop forwards since it will otherwise remove all brs in <p>a<br><br><br></p>\r
3139                                 for (i = 0; i < l; i++) {\r
3140                                         node = nodes[i];\r
3141                                         parent = node.parent;\r
3142 \r
3143                                         if (blockElements[node.parent.name] && node === parent.lastChild) {\r
3144                                                 // Loop all nodes to the right of the current node and check for other BR elements\r
3145                                                 // excluding bookmarks since they are invisible\r
3146                                                 prev = node.prev;\r
3147                                                 while (prev) {\r
3148                                                         prevName = prev.name;\r
3149 \r
3150                                                         // Ignore bookmarks\r
3151                                                         if (prevName !== "span" || prev.attr('data-mce-type') !== 'bookmark') {\r
3152                                                                 // Found a non BR element\r
3153                                                                 if (prevName !== "br")\r
3154                                                                         break;\r
3155         \r
3156                                                                 // Found another br it's a <br><br> structure then don't remove anything\r
3157                                                                 if (prevName === 'br') {\r
3158                                                                         node = null;\r
3159                                                                         break;\r
3160                                                                 }\r
3161                                                         }\r
3162 \r
3163                                                         prev = prev.prev;\r
3164                                                 }\r
3165 \r
3166                                                 if (node) {\r
3167                                                         node.remove();\r
3168 \r
3169                                                         // Is the parent to be considered empty after we removed the BR\r
3170                                                         if (parent.isEmpty(nonEmptyElements)) {\r
3171                                                                 elementRule = schema.getElementRule(parent.name);\r
3172 \r
3173                                                                 // Remove or padd the element depending on schema rule\r
3174                                                                 if (elementRule) {\r
3175                                                                   if (elementRule.removeEmpty)\r
3176                                                                           parent.remove();\r
3177                                                                   else if (elementRule.paddEmpty)\r
3178                                                                           parent.empty().append(new tinymce.html.Node('#text', 3)).value = '\u00a0';\r
3179                                                           }\r
3180               }\r
3181                                                 }\r
3182                                         }\r
3183                                 }\r
3184                         });\r
3185                 }\r
3186         }\r
3187 })(tinymce);\r
3188 \r
3189 tinymce.html.Writer = function(settings) {\r
3190         var html = [], indent, indentBefore, indentAfter, encode, htmlOutput;\r
3191 \r
3192         settings = settings || {};\r
3193         indent = settings.indent;\r
3194         indentBefore = tinymce.makeMap(settings.indent_before || '');\r
3195         indentAfter = tinymce.makeMap(settings.indent_after || '');\r
3196         encode = tinymce.html.Entities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);\r
3197         htmlOutput = settings.element_format == "html";\r
3198 \r
3199         return {\r
3200                 start: function(name, attrs, empty) {\r
3201                         var i, l, attr, value;\r
3202 \r
3203                         if (indent && indentBefore[name] && html.length > 0) {\r
3204                                 value = html[html.length - 1];\r
3205 \r
3206                                 if (value.length > 0 && value !== '\n')\r
3207                                         html.push('\n');\r
3208                         }\r
3209 \r
3210                         html.push('<', name);\r
3211 \r
3212                         if (attrs) {\r
3213                                 for (i = 0, l = attrs.length; i < l; i++) {\r
3214                                         attr = attrs[i];\r
3215                                         html.push(' ', attr.name, '="', encode(attr.value, true), '"');\r
3216                                 }\r
3217                         }\r
3218 \r
3219                         if (!empty || htmlOutput)\r
3220                                 html[html.length] = '>';\r
3221                         else\r
3222                                 html[html.length] = ' />';\r
3223 \r
3224                         if (empty && indent && indentAfter[name] && html.length > 0) {\r
3225                                 value = html[html.length - 1];\r
3226 \r
3227                                 if (value.length > 0 && value !== '\n')\r
3228                                         html.push('\n');\r
3229                         }\r
3230                 },\r
3231 \r
3232                 end: function(name) {\r
3233                         var value;\r
3234 \r
3235                         /*if (indent && indentBefore[name] && html.length > 0) {\r
3236                                 value = html[html.length - 1];\r
3237 \r
3238                                 if (value.length > 0 && value !== '\n')\r
3239                                         html.push('\n');\r
3240                         }*/\r
3241 \r
3242                         html.push('</', name, '>');\r
3243 \r
3244                         if (indent && indentAfter[name] && html.length > 0) {\r
3245                                 value = html[html.length - 1];\r
3246 \r
3247                                 if (value.length > 0 && value !== '\n')\r
3248                                         html.push('\n');\r
3249                         }\r
3250                 },\r
3251 \r
3252                 text: function(text, raw) {\r
3253                         if (text.length > 0)\r
3254                                 html[html.length] = raw ? text : encode(text);\r
3255                 },\r
3256 \r
3257                 cdata: function(text) {\r
3258                         html.push('<![CDATA[', text, ']]>');\r
3259                 },\r
3260 \r
3261                 comment: function(text) {\r
3262                         html.push('<!--', text, '-->');\r
3263                 },\r
3264 \r
3265                 pi: function(name, text) {\r
3266                         if (text)\r
3267                                 html.push('<?', name, ' ', text, '?>');\r
3268                         else\r
3269                                 html.push('<?', name, '?>');\r
3270 \r
3271                         if (indent)\r
3272                                 html.push('\n');\r
3273                 },\r
3274 \r
3275                 doctype: function(text) {\r
3276                         html.push('<!DOCTYPE', text, '>', indent ? '\n' : '');\r
3277                 },\r
3278 \r
3279                 reset: function() {\r
3280                         html.length = 0;\r
3281                 },\r
3282 \r
3283                 getContent: function() {\r
3284                         return html.join('').replace(/\n$/, '');\r
3285                 }\r
3286         };\r
3287 };\r
3288 \r
3289 (function(tinymce) {\r
3290         tinymce.html.Serializer = function(settings, schema) {\r
3291                 var self = this, writer = new tinymce.html.Writer(settings);\r
3292 \r
3293                 settings = settings || {};\r
3294                 settings.validate = "validate" in settings ? settings.validate : true;\r
3295 \r
3296                 self.schema = schema = schema || new tinymce.html.Schema();\r
3297                 self.writer = writer;\r
3298 \r
3299                 self.serialize = function(node) {\r
3300                         var handlers, validate;\r
3301 \r
3302                         validate = settings.validate;\r
3303 \r
3304                         handlers = {\r
3305                                 // #text\r
3306                                 3: function(node, raw) {\r
3307                                         writer.text(node.value, node.raw);\r
3308                                 },\r
3309 \r
3310                                 // #comment\r
3311                                 8: function(node) {\r
3312                                         writer.comment(node.value);\r
3313                                 },\r
3314 \r
3315                                 // Processing instruction\r
3316                                 7: function(node) {\r
3317                                         writer.pi(node.name, node.value);\r
3318                                 },\r
3319 \r
3320                                 // Doctype\r
3321                                 10: function(node) {\r
3322                                         writer.doctype(node.value);\r
3323                                 },\r
3324 \r
3325                                 // CDATA\r
3326                                 4: function(node) {\r
3327                                         writer.cdata(node.value);\r
3328                                 },\r
3329 \r
3330                                 // Document fragment\r
3331                                 11: function(node) {\r
3332                                         if ((node = node.firstChild)) {\r
3333                                                 do {\r
3334                                                         walk(node);\r
3335                                                 } while (node = node.next);\r
3336                                         }\r
3337                                 }\r
3338                         };\r
3339 \r
3340                         writer.reset();\r
3341 \r
3342                         function walk(node) {\r
3343                                 var handler = handlers[node.type], name, isEmpty, attrs, attrName, attrValue, sortedAttrs, i, l, elementRule;\r
3344 \r
3345                                 if (!handler) {\r
3346                                         name = node.name;\r
3347                                         isEmpty = node.shortEnded;\r
3348                                         attrs = node.attributes;\r
3349 \r
3350                                         // Sort attributes\r
3351                                         if (validate && attrs && attrs.length > 1) {\r
3352                                                 sortedAttrs = [];\r
3353                                                 sortedAttrs.map = {};\r
3354 \r
3355                                                 elementRule = schema.getElementRule(node.name);\r
3356                                                 for (i = 0, l = elementRule.attributesOrder.length; i < l; i++) {\r
3357                                                         attrName = elementRule.attributesOrder[i];\r
3358 \r
3359                                                         if (attrName in attrs.map) {\r
3360                                                                 attrValue = attrs.map[attrName];\r
3361                                                                 sortedAttrs.map[attrName] = attrValue;\r
3362                                                                 sortedAttrs.push({name: attrName, value: attrValue});\r
3363                                                         }\r
3364                                                 }\r
3365 \r
3366                                                 for (i = 0, l = attrs.length; i < l; i++) {\r
3367                                                         attrName = attrs[i].name;\r
3368 \r
3369                                                         if (!(attrName in sortedAttrs.map)) {\r
3370                                                                 attrValue = attrs.map[attrName];\r
3371                                                                 sortedAttrs.map[attrName] = attrValue;\r
3372                                                                 sortedAttrs.push({name: attrName, value: attrValue});\r
3373                                                         }\r
3374                                                 }\r
3375 \r
3376                                                 attrs = sortedAttrs;\r
3377                                         }\r
3378 \r
3379                                         writer.start(node.name, attrs, isEmpty);\r
3380 \r
3381                                         if (!isEmpty) {\r
3382                                                 if ((node = node.firstChild)) {\r
3383                                                         do {\r
3384                                                                 walk(node);\r
3385                                                         } while (node = node.next);\r
3386                                                 }\r
3387 \r
3388                                                 writer.end(name);\r
3389                                         }\r
3390                                 } else\r
3391                                         handler(node);\r
3392                         }\r
3393 \r
3394                         // Serialize element and treat all non elements as fragments\r
3395                         if (node.type == 1 && !settings.inner)\r
3396                                 walk(node);\r
3397                         else\r
3398                                 handlers[11](node);\r
3399 \r
3400                         return writer.getContent();\r
3401                 };\r
3402         }\r
3403 })(tinymce);\r
3404 \r
3405 (function(tinymce) {\r
3406         // Shorten names\r
3407         var each = tinymce.each,\r
3408                 is = tinymce.is,\r
3409                 isWebKit = tinymce.isWebKit,\r
3410                 isIE = tinymce.isIE,\r
3411                 Entities = tinymce.html.Entities,\r
3412                 simpleSelectorRe = /^([a-z0-9],?)+$/i,\r
3413                 blockElementsMap = tinymce.html.Schema.blockElementsMap,\r
3414                 whiteSpaceRegExp = /^[ \t\r\n]*$/;\r
3415 \r
3416         tinymce.create('tinymce.dom.DOMUtils', {\r
3417                 doc : null,\r
3418                 root : null,\r
3419                 files : null,\r
3420                 pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,\r
3421                 props : {\r
3422                         "for" : "htmlFor",\r
3423                         "class" : "className",\r
3424                         className : "className",\r
3425                         checked : "checked",\r
3426                         disabled : "disabled",\r
3427                         maxlength : "maxLength",\r
3428                         readonly : "readOnly",\r
3429                         selected : "selected",\r
3430                         value : "value",\r
3431                         id : "id",\r
3432                         name : "name",\r
3433                         type : "type"\r
3434                 },\r
3435 \r
3436                 DOMUtils : function(d, s) {\r
3437                         var t = this, globalStyle, name;\r
3438 \r
3439                         t.doc = d;\r
3440                         t.win = window;\r
3441                         t.files = {};\r
3442                         t.cssFlicker = false;\r
3443                         t.counter = 0;\r
3444                         t.stdMode = !tinymce.isIE || d.documentMode >= 8;\r
3445                         t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat" || t.stdMode;\r
3446                         t.hasOuterHTML = "outerHTML" in d.createElement("a");\r
3447 \r
3448                         t.settings = s = tinymce.extend({\r
3449                                 keep_values : false,\r
3450                                 hex_colors : 1\r
3451                         }, s);\r
3452                         \r
3453                         t.schema = s.schema;\r
3454                         t.styles = new tinymce.html.Styles({\r
3455                                 url_converter : s.url_converter,\r
3456                                 url_converter_scope : s.url_converter_scope\r
3457                         }, s.schema);\r
3458 \r
3459                         // Fix IE6SP2 flicker and check it failed for pre SP2\r
3460                         if (tinymce.isIE6) {\r
3461                                 try {\r
3462                                         d.execCommand('BackgroundImageCache', false, true);\r
3463                                 } catch (e) {\r
3464                                         t.cssFlicker = true;\r
3465                                 }\r
3466                         }\r
3467 \r
3468                         if (isIE && s.schema) {\r
3469                                 // Add missing HTML 4/5 elements to IE\r
3470                                 ('abbr article aside audio canvas ' +\r
3471                                 'details figcaption figure footer ' +\r
3472                                 'header hgroup mark menu meter nav ' +\r
3473                                 'output progress section summary ' +\r
3474                                 'time video').replace(/\w+/g, function(name) {\r
3475                                         d.createElement(name);\r
3476                                 });\r
3477 \r
3478                                 // Create all custom elements\r
3479                                 for (name in s.schema.getCustomElements()) {\r
3480                                         d.createElement(name);\r
3481                                 }\r
3482                         }\r
3483 \r
3484                         tinymce.addUnload(t.destroy, t);\r
3485                 },\r
3486 \r
3487                 getRoot : function() {\r
3488                         var t = this, s = t.settings;\r
3489 \r
3490                         return (s && t.get(s.root_element)) || t.doc.body;\r
3491                 },\r
3492 \r
3493                 getViewPort : function(w) {\r
3494                         var d, b;\r
3495 \r
3496                         w = !w ? this.win : w;\r
3497                         d = w.document;\r
3498                         b = this.boxModel ? d.documentElement : d.body;\r
3499 \r
3500                         // Returns viewport size excluding scrollbars\r
3501                         return {\r
3502                                 x : w.pageXOffset || b.scrollLeft,\r
3503                                 y : w.pageYOffset || b.scrollTop,\r
3504                                 w : w.innerWidth || b.clientWidth,\r
3505                                 h : w.innerHeight || b.clientHeight\r
3506                         };\r
3507                 },\r
3508 \r
3509                 getRect : function(e) {\r
3510                         var p, t = this, sr;\r
3511 \r
3512                         e = t.get(e);\r
3513                         p = t.getPos(e);\r
3514                         sr = t.getSize(e);\r
3515 \r
3516                         return {\r
3517                                 x : p.x,\r
3518                                 y : p.y,\r
3519                                 w : sr.w,\r
3520                                 h : sr.h\r
3521                         };\r
3522                 },\r
3523 \r
3524                 getSize : function(e) {\r
3525                         var t = this, w, h;\r
3526 \r
3527                         e = t.get(e);\r
3528                         w = t.getStyle(e, 'width');\r
3529                         h = t.getStyle(e, 'height');\r
3530 \r
3531                         // Non pixel value, then force offset/clientWidth\r
3532                         if (w.indexOf('px') === -1)\r
3533                                 w = 0;\r
3534 \r
3535                         // Non pixel value, then force offset/clientWidth\r
3536                         if (h.indexOf('px') === -1)\r
3537                                 h = 0;\r
3538 \r
3539                         return {\r
3540                                 w : parseInt(w) || e.offsetWidth || e.clientWidth,\r
3541                                 h : parseInt(h) || e.offsetHeight || e.clientHeight\r
3542                         };\r
3543                 },\r
3544 \r
3545                 getParent : function(n, f, r) {\r
3546                         return this.getParents(n, f, r, false);\r
3547                 },\r
3548 \r
3549                 getParents : function(n, f, r, c) {\r
3550                         var t = this, na, se = t.settings, o = [];\r
3551 \r
3552                         n = t.get(n);\r
3553                         c = c === undefined;\r
3554 \r
3555                         if (se.strict_root)\r
3556                                 r = r || t.getRoot();\r
3557 \r
3558                         // Wrap node name as func\r
3559                         if (is(f, 'string')) {\r
3560                                 na = f;\r
3561 \r
3562                                 if (f === '*') {\r
3563                                         f = function(n) {return n.nodeType == 1;};\r
3564                                 } else {\r
3565                                         f = function(n) {\r
3566                                                 return t.is(n, na);\r
3567                                         };\r
3568                                 }\r
3569                         }\r
3570 \r
3571                         while (n) {\r
3572                                 if (n == r || !n.nodeType || n.nodeType === 9)\r
3573                                         break;\r
3574 \r
3575                                 if (!f || f(n)) {\r
3576                                         if (c)\r
3577                                                 o.push(n);\r
3578                                         else\r
3579                                                 return n;\r
3580                                 }\r
3581 \r
3582                                 n = n.parentNode;\r
3583                         }\r
3584 \r
3585                         return c ? o : null;\r
3586                 },\r
3587 \r
3588                 get : function(e) {\r
3589                         var n;\r
3590 \r
3591                         if (e && this.doc && typeof(e) == 'string') {\r
3592                                 n = e;\r
3593                                 e = this.doc.getElementById(e);\r
3594 \r
3595                                 // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick\r
3596                                 if (e && e.id !== n)\r
3597                                         return this.doc.getElementsByName(n)[1];\r
3598                         }\r
3599 \r
3600                         return e;\r
3601                 },\r
3602 \r
3603                 getNext : function(node, selector) {\r
3604                         return this._findSib(node, selector, 'nextSibling');\r
3605                 },\r
3606 \r
3607                 getPrev : function(node, selector) {\r
3608                         return this._findSib(node, selector, 'previousSibling');\r
3609                 },\r
3610 \r
3611 \r
3612                 select : function(pa, s) {\r
3613                         var t = this;\r
3614 \r
3615                         return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []);\r
3616                 },\r
3617 \r
3618                 is : function(n, selector) {\r
3619                         var i;\r
3620 \r
3621                         // If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance\r
3622                         if (n.length === undefined) {\r
3623                                 // Simple all selector\r
3624                                 if (selector === '*')\r
3625                                         return n.nodeType == 1;\r
3626 \r
3627                                 // Simple selector just elements\r
3628                                 if (simpleSelectorRe.test(selector)) {\r
3629                                         selector = selector.toLowerCase().split(/,/);\r
3630                                         n = n.nodeName.toLowerCase();\r
3631 \r
3632                                         for (i = selector.length - 1; i >= 0; i--) {\r
3633                                                 if (selector[i] == n)\r
3634                                                         return true;\r
3635                                         }\r
3636 \r
3637                                         return false;\r
3638                                 }\r
3639                         }\r
3640 \r
3641                         return tinymce.dom.Sizzle.matches(selector, n.nodeType ? [n] : n).length > 0;\r
3642                 },\r
3643 \r
3644 \r
3645                 add : function(p, n, a, h, c) {\r
3646                         var t = this;\r
3647 \r
3648                         return this.run(p, function(p) {\r
3649                                 var e, k;\r
3650 \r
3651                                 e = is(n, 'string') ? t.doc.createElement(n) : n;\r
3652                                 t.setAttribs(e, a);\r
3653 \r
3654                                 if (h) {\r
3655                                         if (h.nodeType)\r
3656                                                 e.appendChild(h);\r
3657                                         else\r
3658                                                 t.setHTML(e, h);\r
3659                                 }\r
3660 \r
3661                                 return !c ? p.appendChild(e) : e;\r
3662                         });\r
3663                 },\r
3664 \r
3665                 create : function(n, a, h) {\r
3666                         return this.add(this.doc.createElement(n), n, a, h, 1);\r
3667                 },\r
3668 \r
3669                 createHTML : function(n, a, h) {\r
3670                         var o = '', t = this, k;\r
3671 \r
3672                         o += '<' + n;\r
3673 \r
3674                         for (k in a) {\r
3675                                 if (a.hasOwnProperty(k))\r
3676                                         o += ' ' + k + '="' + t.encode(a[k]) + '"';\r
3677                         }\r
3678 \r
3679                         // A call to tinymce.is doesn't work for some odd reason on IE9 possible bug inside their JS runtime\r
3680                         if (typeof(h) != "undefined")\r
3681                                 return o + '>' + h + '</' + n + '>';\r
3682 \r
3683                         return o + ' />';\r
3684                 },\r
3685 \r
3686                 remove : function(node, keep_children) {\r
3687                         return this.run(node, function(node) {\r
3688                                 var child, parent = node.parentNode;\r
3689 \r
3690                                 if (!parent)\r
3691                                         return null;\r
3692 \r
3693                                 if (keep_children) {\r
3694                                         while (child = node.firstChild) {\r
3695                                                 // IE 8 will crash if you don't remove completely empty text nodes\r
3696                                                 if (!tinymce.isIE || child.nodeType !== 3 || child.nodeValue)\r
3697                                                         parent.insertBefore(child, node);\r
3698                                                 else\r
3699                                                         node.removeChild(child);\r
3700                                         }\r
3701                                 }\r
3702 \r
3703                                 return parent.removeChild(node);\r
3704                         });\r
3705                 },\r
3706 \r
3707                 setStyle : function(n, na, v) {\r
3708                         var t = this;\r
3709 \r
3710                         return t.run(n, function(e) {\r
3711                                 var s, i;\r
3712 \r
3713                                 s = e.style;\r
3714 \r
3715                                 // Camelcase it, if needed\r
3716                                 na = na.replace(/-(\D)/g, function(a, b){\r
3717                                         return b.toUpperCase();\r
3718                                 });\r
3719 \r
3720                                 // Default px suffix on these\r
3721                                 if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v)))\r
3722                                         v += 'px';\r
3723 \r
3724                                 switch (na) {\r
3725                                         case 'opacity':\r
3726                                                 // IE specific opacity\r
3727                                                 if (isIE) {\r
3728                                                         s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")";\r
3729 \r
3730                                                         if (!n.currentStyle || !n.currentStyle.hasLayout)\r
3731                                                                 s.display = 'inline-block';\r
3732                                                 }\r
3733 \r
3734                                                 // Fix for older browsers\r
3735                                                 s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || '';\r
3736                                                 break;\r
3737 \r
3738                                         case 'float':\r
3739                                                 isIE ? s.styleFloat = v : s.cssFloat = v;\r
3740                                                 break;\r
3741                                         \r
3742                                         default:\r
3743                                                 s[na] = v || '';\r
3744                                 }\r
3745 \r
3746                                 // Force update of the style data\r
3747                                 if (t.settings.update_styles)\r
3748                                         t.setAttrib(e, 'data-mce-style');\r
3749                         });\r
3750                 },\r
3751 \r
3752                 getStyle : function(n, na, c) {\r
3753                         n = this.get(n);\r
3754 \r
3755                         if (!n)\r
3756                                 return;\r
3757 \r
3758                         // Gecko\r
3759                         if (this.doc.defaultView && c) {\r
3760                                 // Remove camelcase\r
3761                                 na = na.replace(/[A-Z]/g, function(a){\r
3762                                         return '-' + a;\r
3763                                 });\r
3764 \r
3765                                 try {\r
3766                                         return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);\r
3767                                 } catch (ex) {\r
3768                                         // Old safari might fail\r
3769                                         return null;\r
3770                                 }\r
3771                         }\r
3772 \r
3773                         // Camelcase it, if needed\r
3774                         na = na.replace(/-(\D)/g, function(a, b){\r
3775                                 return b.toUpperCase();\r
3776                         });\r
3777 \r
3778                         if (na == 'float')\r
3779                                 na = isIE ? 'styleFloat' : 'cssFloat';\r
3780 \r
3781                         // IE & Opera\r
3782                         if (n.currentStyle && c)\r
3783                                 return n.currentStyle[na];\r
3784 \r
3785                         return n.style ? n.style[na] : undefined;\r
3786                 },\r
3787 \r
3788                 setStyles : function(e, o) {\r
3789                         var t = this, s = t.settings, ol;\r
3790 \r
3791                         ol = s.update_styles;\r
3792                         s.update_styles = 0;\r
3793 \r
3794                         each(o, function(v, n) {\r
3795                                 t.setStyle(e, n, v);\r
3796                         });\r
3797 \r
3798                         // Update style info\r
3799                         s.update_styles = ol;\r
3800                         if (s.update_styles)\r
3801                                 t.setAttrib(e, s.cssText);\r
3802                 },\r
3803 \r
3804                 removeAllAttribs: function(e) {\r
3805                         return this.run(e, function(e) {\r
3806                                 var i, attrs = e.attributes;\r
3807                                 for (i = attrs.length - 1; i >= 0; i--) {\r
3808                                         e.removeAttributeNode(attrs.item(i));\r
3809                                 }\r
3810                         });\r
3811                 },\r
3812 \r
3813                 setAttrib : function(e, n, v) {\r
3814                         var t = this;\r
3815 \r
3816                         // Whats the point\r
3817                         if (!e || !n)\r
3818                                 return;\r
3819 \r
3820                         // Strict XML mode\r
3821                         if (t.settings.strict)\r
3822                                 n = n.toLowerCase();\r
3823 \r
3824                         return this.run(e, function(e) {\r
3825                                 var s = t.settings;\r
3826 \r
3827                                 switch (n) {\r
3828                                         case "style":\r
3829                                                 if (!is(v, 'string')) {\r
3830                                                         each(v, function(v, n) {\r
3831                                                                 t.setStyle(e, n, v);\r
3832                                                         });\r
3833 \r
3834                                                         return;\r
3835                                                 }\r
3836 \r
3837                                                 // No mce_style for elements with these since they might get resized by the user\r
3838                                                 if (s.keep_values) {\r
3839                                                         if (v && !t._isRes(v))\r
3840                                                                 e.setAttribute('data-mce-style', v, 2);\r
3841                                                         else\r
3842                                                                 e.removeAttribute('data-mce-style', 2);\r
3843                                                 }\r
3844 \r
3845                                                 e.style.cssText = v;\r
3846                                                 break;\r
3847 \r
3848                                         case "class":\r
3849                                                 e.className = v || ''; // Fix IE null bug\r
3850                                                 break;\r
3851 \r
3852                                         case "src":\r
3853                                         case "href":\r
3854                                                 if (s.keep_values) {\r
3855                                                         if (s.url_converter)\r
3856                                                                 v = s.url_converter.call(s.url_converter_scope || t, v, n, e);\r
3857 \r
3858                                                         t.setAttrib(e, 'data-mce-' + n, v, 2);\r
3859                                                 }\r
3860 \r
3861                                                 break;\r
3862 \r
3863                                         case "shape":\r
3864                                                 e.setAttribute('data-mce-style', v);\r
3865                                                 break;\r
3866                                 }\r
3867 \r
3868                                 if (is(v) && v !== null && v.length !== 0)\r
3869                                         e.setAttribute(n, '' + v, 2);\r
3870                                 else\r
3871                                         e.removeAttribute(n, 2);\r
3872                         });\r
3873                 },\r
3874 \r
3875                 setAttribs : function(e, o) {\r
3876                         var t = this;\r
3877 \r
3878                         return this.run(e, function(e) {\r
3879                                 each(o, function(v, n) {\r
3880                                         t.setAttrib(e, n, v);\r
3881                                 });\r
3882                         });\r
3883                 },\r
3884 \r
3885                 getAttrib : function(e, n, dv) {\r
3886                         var v, t = this, undef;\r
3887 \r
3888                         e = t.get(e);\r
3889 \r
3890                         if (!e || e.nodeType !== 1)\r
3891                                 return dv === undef ? false : dv;\r
3892 \r
3893                         if (!is(dv))\r
3894                                 dv = '';\r
3895 \r
3896                         // Try the mce variant for these\r
3897                         if (/^(src|href|style|coords|shape)$/.test(n)) {\r
3898                                 v = e.getAttribute("data-mce-" + n);\r
3899 \r
3900                                 if (v)\r
3901                                         return v;\r
3902                         }\r
3903 \r
3904                         if (isIE && t.props[n]) {\r
3905                                 v = e[t.props[n]];\r
3906                                 v = v && v.nodeValue ? v.nodeValue : v;\r
3907                         }\r
3908 \r
3909                         if (!v)\r
3910                                 v = e.getAttribute(n, 2);\r
3911 \r
3912                         // Check boolean attribs\r
3913                         if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(n)) {\r
3914                                 if (e[t.props[n]] === true && v === '')\r
3915                                         return n;\r
3916 \r
3917                                 return v ? n : '';\r
3918                         }\r
3919 \r
3920                         // Inner input elements will override attributes on form elements\r
3921                         if (e.nodeName === "FORM" && e.getAttributeNode(n))\r
3922                                 return e.getAttributeNode(n).nodeValue;\r
3923 \r
3924                         if (n === 'style') {\r
3925                                 v = v || e.style.cssText;\r
3926 \r
3927                                 if (v) {\r
3928                                         v = t.serializeStyle(t.parseStyle(v), e.nodeName);\r
3929 \r
3930                                         if (t.settings.keep_values && !t._isRes(v))\r
3931                                                 e.setAttribute('data-mce-style', v);\r
3932                                 }\r
3933                         }\r
3934 \r
3935                         // Remove Apple and WebKit stuff\r
3936                         if (isWebKit && n === "class" && v)\r
3937                                 v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, '');\r
3938 \r
3939                         // Handle IE issues\r
3940                         if (isIE) {\r
3941                                 switch (n) {\r
3942                                         case 'rowspan':\r
3943                                         case 'colspan':\r
3944                                                 // IE returns 1 as default value\r
3945                                                 if (v === 1)\r
3946                                                         v = '';\r
3947 \r
3948                                                 break;\r
3949 \r
3950                                         case 'size':\r
3951                                                 // IE returns +0 as default value for size\r
3952                                                 if (v === '+0' || v === 20 || v === 0)\r
3953                                                         v = '';\r
3954 \r
3955                                                 break;\r
3956 \r
3957                                         case 'width':\r
3958                                         case 'height':\r
3959                                         case 'vspace':\r
3960                                         case 'checked':\r
3961                                         case 'disabled':\r
3962                                         case 'readonly':\r
3963                                                 if (v === 0)\r
3964                                                         v = '';\r
3965 \r
3966                                                 break;\r
3967 \r
3968                                         case 'hspace':\r
3969                                                 // IE returns -1 as default value\r
3970                                                 if (v === -1)\r
3971                                                         v = '';\r
3972 \r
3973                                                 break;\r
3974 \r
3975                                         case 'maxlength':\r
3976                                         case 'tabindex':\r
3977                                                 // IE returns default value\r
3978                                                 if (v === 32768 || v === 2147483647 || v === '32768')\r
3979                                                         v = '';\r
3980 \r
3981                                                 break;\r
3982 \r
3983                                         case 'multiple':\r
3984                                         case 'compact':\r
3985                                         case 'noshade':\r
3986                                         case 'nowrap':\r
3987                                                 if (v === 65535)\r
3988                                                         return n;\r
3989 \r
3990                                                 return dv;\r
3991 \r
3992                                         case 'shape':\r
3993                                                 v = v.toLowerCase();\r
3994                                                 break;\r
3995 \r
3996                                         default:\r
3997                                                 // IE has odd anonymous function for event attributes\r
3998                                                 if (n.indexOf('on') === 0 && v)\r
3999                                                         v = tinymce._replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1', '' + v);\r
4000                                 }\r
4001                         }\r
4002 \r
4003                         return (v !== undef && v !== null && v !== '') ? '' + v : dv;\r
4004                 },\r
4005 \r
4006                 getPos : function(n, ro) {\r
4007                         var t = this, x = 0, y = 0, e, d = t.doc, r;\r
4008 \r
4009                         n = t.get(n);\r
4010                         ro = ro || d.body;\r
4011 \r
4012                         if (n) {\r
4013                                 // Use getBoundingClientRect if it exists since it's faster than looping offset nodes\r
4014                                 if (n.getBoundingClientRect) {\r
4015                                         n = n.getBoundingClientRect();\r
4016                                         e = t.boxModel ? d.documentElement : d.body;\r
4017 \r
4018                                         // Add scroll offsets from documentElement or body since IE with the wrong box model will use d.body and so do WebKit\r
4019                                         // Also remove the body/documentelement clientTop/clientLeft on IE 6, 7 since they offset the position\r
4020                                         x = n.left + (d.documentElement.scrollLeft || d.body.scrollLeft) - e.clientTop;\r
4021                                         y = n.top + (d.documentElement.scrollTop || d.body.scrollTop) - e.clientLeft;\r
4022 \r
4023                                         return {x : x, y : y};\r
4024                                 }\r
4025 \r
4026                                 r = n;\r
4027                                 while (r && r != ro && r.nodeType) {\r
4028                                         x += r.offsetLeft || 0;\r
4029                                         y += r.offsetTop || 0;\r
4030                                         r = r.offsetParent;\r
4031                                 }\r
4032 \r
4033                                 r = n.parentNode;\r
4034                                 while (r && r != ro && r.nodeType) {\r
4035                                         x -= r.scrollLeft || 0;\r
4036                                         y -= r.scrollTop || 0;\r
4037                                         r = r.parentNode;\r
4038                                 }\r
4039                         }\r
4040 \r
4041                         return {x : x, y : y};\r
4042                 },\r
4043 \r
4044                 parseStyle : function(st) {\r
4045                         return this.styles.parse(st);\r
4046                 },\r
4047 \r
4048                 serializeStyle : function(o, name) {\r
4049                         return this.styles.serialize(o, name);\r
4050                 },\r
4051 \r
4052                 loadCSS : function(u) {\r
4053                         var t = this, d = t.doc, head;\r
4054 \r
4055                         if (!u)\r
4056                                 u = '';\r
4057 \r
4058                         head = t.select('head')[0];\r
4059 \r
4060                         each(u.split(','), function(u) {\r
4061                                 var link;\r
4062 \r
4063                                 if (t.files[u])\r
4064                                         return;\r
4065 \r
4066                                 t.files[u] = true;\r
4067                                 link = t.create('link', {rel : 'stylesheet', href : tinymce._addVer(u)});\r
4068 \r
4069                                 // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug\r
4070                                 // This fix seems to resolve that issue by realcing the document ones a stylesheet finishes loading\r
4071                                 // It's ugly but it seems to work fine.\r
4072                                 if (isIE && d.documentMode && d.recalc) {\r
4073                                         link.onload = function() {\r
4074                                                 if (d.recalc)\r
4075                                                         d.recalc();\r
4076 \r
4077                                                 link.onload = null;\r
4078                                         };\r
4079                                 }\r
4080 \r
4081                                 head.appendChild(link);\r
4082                         });\r
4083                 },\r
4084 \r
4085                 addClass : function(e, c) {\r
4086                         return this.run(e, function(e) {\r
4087                                 var o;\r
4088 \r
4089                                 if (!c)\r
4090                                         return 0;\r
4091 \r
4092                                 if (this.hasClass(e, c))\r
4093                                         return e.className;\r
4094 \r
4095                                 o = this.removeClass(e, c);\r
4096 \r
4097                                 return e.className = (o != '' ? (o + ' ') : '') + c;\r
4098                         });\r
4099                 },\r
4100 \r
4101                 removeClass : function(e, c) {\r
4102                         var t = this, re;\r
4103 \r
4104                         return t.run(e, function(e) {\r
4105                                 var v;\r
4106 \r
4107                                 if (t.hasClass(e, c)) {\r
4108                                         if (!re)\r
4109                                                 re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g");\r
4110 \r
4111                                         v = e.className.replace(re, ' ');\r
4112                                         v = tinymce.trim(v != ' ' ? v : '');\r
4113 \r
4114                                         e.className = v;\r
4115 \r
4116                                         // Empty class attr\r
4117                                         if (!v) {\r
4118                                                 e.removeAttribute('class');\r
4119                                                 e.removeAttribute('className');\r
4120                                         }\r
4121 \r
4122                                         return v;\r
4123                                 }\r
4124 \r
4125                                 return e.className;\r
4126                         });\r
4127                 },\r
4128 \r
4129                 hasClass : function(n, c) {\r
4130                         n = this.get(n);\r
4131 \r
4132                         if (!n || !c)\r
4133                                 return false;\r
4134 \r
4135                         return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1;\r
4136                 },\r
4137 \r
4138                 show : function(e) {\r
4139                         return this.setStyle(e, 'display', 'block');\r
4140                 },\r
4141 \r
4142                 hide : function(e) {\r
4143                         return this.setStyle(e, 'display', 'none');\r
4144                 },\r
4145 \r
4146                 isHidden : function(e) {\r
4147                         e = this.get(e);\r
4148 \r
4149                         return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none';\r
4150                 },\r
4151 \r
4152                 uniqueId : function(p) {\r
4153                         return (!p ? 'mce_' : p) + (this.counter++);\r
4154                 },\r
4155 \r
4156                 setHTML : function(element, html) {\r
4157                         var self = this;\r
4158 \r
4159                         return self.run(element, function(element) {\r
4160                                 if (isIE) {\r
4161                                         // Remove all child nodes, IE keeps empty text nodes in DOM\r
4162                                         while (element.firstChild)\r
4163                                                 element.removeChild(element.firstChild);\r
4164 \r
4165                                         try {\r
4166                                                 // IE will remove comments from the beginning\r
4167                                                 // unless you padd the contents with something\r
4168                                                 element.innerHTML = '<br />' + html;\r
4169                                                 element.removeChild(element.firstChild);\r
4170                                         } catch (ex) {\r
4171                                                 // 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
4172                                                 // This seems to fix this problem\r
4173 \r
4174                                                 // Create new div with HTML contents and a BR infront to keep comments\r
4175                                                 element = self.create('div');\r
4176                                                 element.innerHTML = '<br />' + html;\r
4177 \r
4178                                                 // Add all children from div to target\r
4179                                                 each (element.childNodes, function(node, i) {\r
4180                                                         // Skip br element\r
4181                                                         if (i)\r
4182                                                                 element.appendChild(node);\r
4183                                                 });\r
4184                                         }\r
4185                                 } else\r
4186                                         element.innerHTML = html;\r
4187 \r
4188                                 return html;\r
4189                         });\r
4190                 },\r
4191 \r
4192                 getOuterHTML : function(elm) {\r
4193                         var doc, self = this;\r
4194 \r
4195                         elm = self.get(elm);\r
4196 \r
4197                         if (!elm)\r
4198                                 return null;\r
4199 \r
4200                         if (elm.nodeType === 1 && self.hasOuterHTML)\r
4201                                 return elm.outerHTML;\r
4202 \r
4203                         doc = (elm.ownerDocument || self.doc).createElement("body");\r
4204                         doc.appendChild(elm.cloneNode(true));\r
4205 \r
4206                         return doc.innerHTML;\r
4207                 },\r
4208 \r
4209                 setOuterHTML : function(e, h, d) {\r
4210                         var t = this;\r
4211 \r
4212                         function setHTML(e, h, d) {\r
4213                                 var n, tp;\r
4214 \r
4215                                 tp = d.createElement("body");\r
4216                                 tp.innerHTML = h;\r
4217 \r
4218                                 n = tp.lastChild;\r
4219                                 while (n) {\r
4220                                         t.insertAfter(n.cloneNode(true), e);\r
4221                                         n = n.previousSibling;\r
4222                                 }\r
4223 \r
4224                                 t.remove(e);\r
4225                         };\r
4226 \r
4227                         return this.run(e, function(e) {\r
4228                                 e = t.get(e);\r
4229 \r
4230                                 // Only set HTML on elements\r
4231                                 if (e.nodeType == 1) {\r
4232                                         d = d || e.ownerDocument || t.doc;\r
4233 \r
4234                                         if (isIE) {\r
4235                                                 try {\r
4236                                                         // Try outerHTML for IE it sometimes produces an unknown runtime error\r
4237                                                         if (isIE && e.nodeType == 1)\r
4238                                                                 e.outerHTML = h;\r
4239                                                         else\r
4240                                                                 setHTML(e, h, d);\r
4241                                                 } catch (ex) {\r
4242                                                         // Fix for unknown runtime error\r
4243                                                         setHTML(e, h, d);\r
4244                                                 }\r
4245                                         } else\r
4246                                                 setHTML(e, h, d);\r
4247                                 }\r
4248                         });\r
4249                 },\r
4250 \r
4251                 decode : Entities.decode,\r
4252 \r
4253                 encode : Entities.encodeAllRaw,\r
4254 \r
4255                 insertAfter : function(node, reference_node) {\r
4256                         reference_node = this.get(reference_node);\r
4257 \r
4258                         return this.run(node, function(node) {\r
4259                                 var parent, nextSibling;\r
4260 \r
4261                                 parent = reference_node.parentNode;\r
4262                                 nextSibling = reference_node.nextSibling;\r
4263 \r
4264                                 if (nextSibling)\r
4265                                         parent.insertBefore(node, nextSibling);\r
4266                                 else\r
4267                                         parent.appendChild(node);\r
4268 \r
4269                                 return node;\r
4270                         });\r
4271                 },\r
4272 \r
4273                 isBlock : function(node) {\r
4274                         var type = node.nodeType;\r
4275 \r
4276                         // If it's a node then check the type and use the nodeName\r
4277                         if (type)\r
4278                                 return !!(type === 1 && blockElementsMap[node.nodeName]);\r
4279 \r
4280                         return !!blockElementsMap[node];\r
4281                 },\r
4282 \r
4283                 replace : function(n, o, k) {\r
4284                         var t = this;\r
4285 \r
4286                         if (is(o, 'array'))\r
4287                                 n = n.cloneNode(true);\r
4288 \r
4289                         return t.run(o, function(o) {\r
4290                                 if (k) {\r
4291                                         each(tinymce.grep(o.childNodes), function(c) {\r
4292                                                 n.appendChild(c);\r
4293                                         });\r
4294                                 }\r
4295 \r
4296                                 return o.parentNode.replaceChild(n, o);\r
4297                         });\r
4298                 },\r
4299 \r
4300                 rename : function(elm, name) {\r
4301                         var t = this, newElm;\r
4302 \r
4303                         if (elm.nodeName != name.toUpperCase()) {\r
4304                                 // Rename block element\r
4305                                 newElm = t.create(name);\r
4306 \r
4307                                 // Copy attribs to new block\r
4308                                 each(t.getAttribs(elm), function(attr_node) {\r
4309                                         t.setAttrib(newElm, attr_node.nodeName, t.getAttrib(elm, attr_node.nodeName));\r
4310                                 });\r
4311 \r
4312                                 // Replace block\r
4313                                 t.replace(newElm, elm, 1);\r
4314                         }\r
4315 \r
4316                         return newElm || elm;\r
4317                 },\r
4318 \r
4319                 findCommonAncestor : function(a, b) {\r
4320                         var ps = a, pe;\r
4321 \r
4322                         while (ps) {\r
4323                                 pe = b;\r
4324 \r
4325                                 while (pe && ps != pe)\r
4326                                         pe = pe.parentNode;\r
4327 \r
4328                                 if (ps == pe)\r
4329                                         break;\r
4330 \r
4331                                 ps = ps.parentNode;\r
4332                         }\r
4333 \r
4334                         if (!ps && a.ownerDocument)\r
4335                                 return a.ownerDocument.documentElement;\r
4336 \r
4337                         return ps;\r
4338                 },\r
4339 \r
4340                 toHex : function(s) {\r
4341                         var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);\r
4342 \r
4343                         function hex(s) {\r
4344                                 s = parseInt(s).toString(16);\r
4345 \r
4346                                 return s.length > 1 ? s : '0' + s; // 0 -> 00\r
4347                         };\r
4348 \r
4349                         if (c) {\r
4350                                 s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);\r
4351 \r
4352                                 return s;\r
4353                         }\r
4354 \r
4355                         return s;\r
4356                 },\r
4357 \r
4358                 getClasses : function() {\r
4359                         var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;\r
4360 \r
4361                         if (t.classes)\r
4362                                 return t.classes;\r
4363 \r
4364                         function addClasses(s) {\r
4365                                 // IE style imports\r
4366                                 each(s.imports, function(r) {\r
4367                                         addClasses(r);\r
4368                                 });\r
4369 \r
4370                                 each(s.cssRules || s.rules, function(r) {\r
4371                                         // Real type or fake it on IE\r
4372                                         switch (r.type || 1) {\r
4373                                                 // Rule\r
4374                                                 case 1:\r
4375                                                         if (r.selectorText) {\r
4376                                                                 each(r.selectorText.split(','), function(v) {\r
4377                                                                         v = v.replace(/^\s*|\s*$|^\s\./g, "");\r
4378 \r
4379                                                                         // Is internal or it doesn't contain a class\r
4380                                                                         if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v))\r
4381                                                                                 return;\r
4382 \r
4383                                                                         // Remove everything but class name\r
4384                                                                         ov = v;\r
4385                                                                         v = tinymce._replace(/.*\.([a-z0-9_\-]+).*/i, '$1', v);\r
4386 \r
4387                                                                         // Filter classes\r
4388                                                                         if (f && !(v = f(v, ov)))\r
4389                                                                                 return;\r
4390 \r
4391                                                                         if (!lo[v]) {\r
4392                                                                                 cl.push({'class' : v});\r
4393                                                                                 lo[v] = 1;\r
4394                                                                         }\r
4395                                                                 });\r
4396                                                         }\r
4397                                                         break;\r
4398 \r
4399                                                 // Import\r
4400                                                 case 3:\r
4401                                                         addClasses(r.styleSheet);\r
4402                                                         break;\r
4403                                         }\r
4404                                 });\r
4405                         };\r
4406 \r
4407                         try {\r
4408                                 each(t.doc.styleSheets, addClasses);\r
4409                         } catch (ex) {\r
4410                                 // Ignore\r
4411                         }\r
4412 \r
4413                         if (cl.length > 0)\r
4414                                 t.classes = cl;\r
4415 \r
4416                         return cl;\r
4417                 },\r
4418 \r
4419                 run : function(e, f, s) {\r
4420                         var t = this, o;\r
4421 \r
4422                         if (t.doc && typeof(e) === 'string')\r
4423                                 e = t.get(e);\r
4424 \r
4425                         if (!e)\r
4426                                 return false;\r
4427 \r
4428                         s = s || this;\r
4429                         if (!e.nodeType && (e.length || e.length === 0)) {\r
4430                                 o = [];\r
4431 \r
4432                                 each(e, function(e, i) {\r
4433                                         if (e) {\r
4434                                                 if (typeof(e) == 'string')\r
4435                                                         e = t.doc.getElementById(e);\r
4436 \r
4437                                                 o.push(f.call(s, e, i));\r
4438                                         }\r
4439                                 });\r
4440 \r
4441                                 return o;\r
4442                         }\r
4443 \r
4444                         return f.call(s, e);\r
4445                 },\r
4446 \r
4447                 getAttribs : function(n) {\r
4448                         var o;\r
4449 \r
4450                         n = this.get(n);\r
4451 \r
4452                         if (!n)\r
4453                                 return [];\r
4454 \r
4455                         if (isIE) {\r
4456                                 o = [];\r
4457 \r
4458                                 // Object will throw exception in IE\r
4459                                 if (n.nodeName == 'OBJECT')\r
4460                                         return n.attributes;\r
4461 \r
4462                                 // IE doesn't keep the selected attribute if you clone option elements\r
4463                                 if (n.nodeName === 'OPTION' && this.getAttrib(n, 'selected'))\r
4464                                         o.push({specified : 1, nodeName : 'selected'});\r
4465 \r
4466                                 // It's crazy that this is faster in IE but it's because it returns all attributes all the time\r
4467                                 n.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi, '').replace(/[\w:\-]+/gi, function(a) {\r
4468                                         o.push({specified : 1, nodeName : a});\r
4469                                 });\r
4470 \r
4471                                 return o;\r
4472                         }\r
4473 \r
4474                         return n.attributes;\r
4475                 },\r
4476 \r
4477                 isEmpty : function(node, elements) {\r
4478                         var self = this, i, attributes, type, walker, name, parentNode;\r
4479 \r
4480                         node = node.firstChild;\r
4481                         if (node) {\r
4482                                 walker = new tinymce.dom.TreeWalker(node);\r
4483                                 elements = elements || self.schema ? self.schema.getNonEmptyElements() : null;\r
4484 \r
4485                                 do {\r
4486                                         type = node.nodeType;\r
4487 \r
4488                                         if (type === 1) {\r
4489                                                 // Ignore bogus elements\r
4490                                                 if (node.getAttribute('data-mce-bogus'))\r
4491                                                         continue;\r
4492 \r
4493                                                 // Keep empty elements like <img />\r
4494                                                 name = node.nodeName.toLowerCase();\r
4495                                                 if (elements && elements[name]) {\r
4496                                                         // Ignore single BR elements in blocks like <p><br /></p>\r
4497                                                         parentNode = node.parentNode;\r
4498                                                         if (name === 'br' && self.isBlock(parentNode) && parentNode.firstChild === node && parentNode.lastChild === node) {\r
4499                                                                 continue;\r
4500                                                         }\r
4501 \r
4502                                                         return false;\r
4503                                                 }\r
4504 \r
4505                                                 // Keep elements with data-bookmark attributes or name attribute like <a name="1"></a>\r
4506                                                 attributes = self.getAttribs(node);\r
4507                                                 i = node.attributes.length;\r
4508                                                 while (i--) {\r
4509                                                         name = node.attributes[i].nodeName;\r
4510                                                         if (name === "name" || name === 'data-mce-bookmark')\r
4511                                                                 return false;\r
4512                                                 }\r
4513                                         }\r
4514 \r
4515                                         // Keep non whitespace text nodes\r
4516                                         if ((type === 3 && !whiteSpaceRegExp.test(node.nodeValue)))\r
4517                                                 return false;\r
4518                                 } while (node = walker.next());\r
4519                         }\r
4520 \r
4521                         return true;\r
4522                 },\r
4523 \r
4524                 destroy : function(s) {\r
4525                         var t = this;\r
4526 \r
4527                         if (t.events)\r
4528                                 t.events.destroy();\r
4529 \r
4530                         t.win = t.doc = t.root = t.events = null;\r
4531 \r
4532                         // Manual destroy then remove unload handler\r
4533                         if (!s)\r
4534                                 tinymce.removeUnload(t.destroy);\r
4535                 },\r
4536 \r
4537                 createRng : function() {\r
4538                         var d = this.doc;\r
4539 \r
4540                         return d.createRange ? d.createRange() : new tinymce.dom.Range(this);\r
4541                 },\r
4542 \r
4543                 nodeIndex : function(node, normalized) {\r
4544                         var idx = 0, lastNodeType, lastNode, nodeType;\r
4545 \r
4546                         if (node) {\r
4547                                 for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {\r
4548                                         nodeType = node.nodeType;\r
4549 \r
4550                                         // Normalize text nodes\r
4551                                         if (normalized && nodeType == 3) {\r
4552                                                 if (nodeType == lastNodeType || !node.nodeValue.length)\r
4553                                                         continue;\r
4554                                         }\r
4555                                         idx++;\r
4556                                         lastNodeType = nodeType;\r
4557                                 }\r
4558                         }\r
4559 \r
4560                         return idx;\r
4561                 },\r
4562 \r
4563                 split : function(pe, e, re) {\r
4564                         var t = this, r = t.createRng(), bef, aft, pa;\r
4565 \r
4566                         // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sense\r
4567                         // but we don't want that in our code since it serves no purpose for the end user\r
4568                         // For example if this is chopped:\r
4569                         //   <p>text 1<span><b>CHOP</b></span>text 2</p>\r
4570                         // would produce:\r
4571                         //   <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>\r
4572                         // this function will then trim of empty edges and produce:\r
4573                         //   <p>text 1</p><b>CHOP</b><p>text 2</p>\r
4574                         function trim(node) {\r
4575                                 var i, children = node.childNodes, type = node.nodeType;\r
4576 \r
4577                                 if (type == 1 && node.getAttribute('data-mce-type') == 'bookmark')\r
4578                                         return;\r
4579 \r
4580                                 for (i = children.length - 1; i >= 0; i--)\r
4581                                         trim(children[i]);\r
4582 \r
4583                                 if (type != 9) {\r
4584                                         // Keep non whitespace text nodes\r
4585                                         if (type == 3 && node.nodeValue.length > 0) {\r
4586                                                 // If parent element isn't a block or there isn't any useful contents for example "<p>   </p>"\r
4587                                                 if (!t.isBlock(node.parentNode) || tinymce.trim(node.nodeValue).length > 0)\r
4588                                                         return;\r
4589                                         } else if (type == 1) {\r
4590                                                 // If the only child is a bookmark then move it up\r
4591                                                 children = node.childNodes;\r
4592                                                 if (children.length == 1 && children[0] && children[0].nodeType == 1 && children[0].getAttribute('data-mce-type') == 'bookmark')\r
4593                                                         node.parentNode.insertBefore(children[0], node);\r
4594 \r
4595                                                 // Keep non empty elements or img, hr etc\r
4596                                                 if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName))\r
4597                                                         return;\r
4598                                         }\r
4599 \r
4600                                         t.remove(node);\r
4601                                 }\r
4602 \r
4603                                 return node;\r
4604                         };\r
4605 \r
4606                         if (pe && e) {\r
4607                                 // Get before chunk\r
4608                                 r.setStart(pe.parentNode, t.nodeIndex(pe));\r
4609                                 r.setEnd(e.parentNode, t.nodeIndex(e));\r
4610                                 bef = r.extractContents();\r
4611 \r
4612                                 // Get after chunk\r
4613                                 r = t.createRng();\r
4614                                 r.setStart(e.parentNode, t.nodeIndex(e) + 1);\r
4615                                 r.setEnd(pe.parentNode, t.nodeIndex(pe) + 1);\r
4616                                 aft = r.extractContents();\r
4617 \r
4618                                 // Insert before chunk\r
4619                                 pa = pe.parentNode;\r
4620                                 pa.insertBefore(trim(bef), pe);\r
4621 \r
4622                                 // Insert middle chunk\r
4623                                 if (re)\r
4624                                         pa.replaceChild(re, e);\r
4625                                 else\r
4626                                         pa.insertBefore(e, pe);\r
4627 \r
4628                                 // Insert after chunk\r
4629                                 pa.insertBefore(trim(aft), pe);\r
4630                                 t.remove(pe);\r
4631 \r
4632                                 return re || e;\r
4633                         }\r
4634                 },\r
4635 \r
4636                 bind : function(target, name, func, scope) {\r
4637                         var t = this;\r
4638 \r
4639                         if (!t.events)\r
4640                                 t.events = new tinymce.dom.EventUtils();\r
4641 \r
4642                         return t.events.add(target, name, func, scope || this);\r
4643                 },\r
4644 \r
4645                 unbind : function(target, name, func) {\r
4646                         var t = this;\r
4647 \r
4648                         if (!t.events)\r
4649                                 t.events = new tinymce.dom.EventUtils();\r
4650 \r
4651                         return t.events.remove(target, name, func);\r
4652                 },\r
4653 \r
4654 \r
4655                 _findSib : function(node, selector, name) {\r
4656                         var t = this, f = selector;\r
4657 \r
4658                         if (node) {\r
4659                                 // If expression make a function of it using is\r
4660                                 if (is(f, 'string')) {\r
4661                                         f = function(node) {\r
4662                                                 return t.is(node, selector);\r
4663                                         };\r
4664                                 }\r
4665 \r
4666                                 // Loop all siblings\r
4667                                 for (node = node[name]; node; node = node[name]) {\r
4668                                         if (f(node))\r
4669                                                 return node;\r
4670                                 }\r
4671                         }\r
4672 \r
4673                         return null;\r
4674                 },\r
4675 \r
4676                 _isRes : function(c) {\r
4677                         // Is live resizble element\r
4678                         return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c);\r
4679                 }\r
4680 \r
4681                 /*\r
4682                 walk : function(n, f, s) {\r
4683                         var d = this.doc, w;\r
4684 \r
4685                         if (d.createTreeWalker) {\r
4686                                 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);\r
4687 \r
4688                                 while ((n = w.nextNode()) != null)\r
4689                                         f.call(s || this, n);\r
4690                         } else\r
4691                                 tinymce.walk(n, f, 'childNodes', s);\r
4692                 }\r
4693                 */\r
4694 \r
4695                 /*\r
4696                 toRGB : function(s) {\r
4697                         var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);\r
4698 \r
4699                         if (c) {\r
4700                                 // #FFF -> #FFFFFF\r
4701                                 if (!is(c[3]))\r
4702                                         c[3] = c[2] = c[1];\r
4703 \r
4704                                 return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";\r
4705                         }\r
4706 \r
4707                         return s;\r
4708                 }\r
4709                 */\r
4710         });\r
4711 \r
4712         tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});\r
4713 })(tinymce);\r
4714 \r
4715 (function(ns) {\r
4716         // Range constructor\r
4717         function Range(dom) {\r
4718                 var t = this,\r
4719                         doc = dom.doc,\r
4720                         EXTRACT = 0,\r
4721                         CLONE = 1,\r
4722                         DELETE = 2,\r
4723                         TRUE = true,\r
4724                         FALSE = false,\r
4725                         START_OFFSET = 'startOffset',\r
4726                         START_CONTAINER = 'startContainer',\r
4727                         END_CONTAINER = 'endContainer',\r
4728                         END_OFFSET = 'endOffset',\r
4729                         extend = tinymce.extend,\r
4730                         nodeIndex = dom.nodeIndex;\r
4731 \r
4732                 extend(t, {\r
4733                         // Inital states\r
4734                         startContainer : doc,\r
4735                         startOffset : 0,\r
4736                         endContainer : doc,\r
4737                         endOffset : 0,\r
4738                         collapsed : TRUE,\r
4739                         commonAncestorContainer : doc,\r
4740 \r
4741                         // Range constants\r
4742                         START_TO_START : 0,\r
4743                         START_TO_END : 1,\r
4744                         END_TO_END : 2,\r
4745                         END_TO_START : 3,\r
4746 \r
4747                         // Public methods\r
4748                         setStart : setStart,\r
4749                         setEnd : setEnd,\r
4750                         setStartBefore : setStartBefore,\r
4751                         setStartAfter : setStartAfter,\r
4752                         setEndBefore : setEndBefore,\r
4753                         setEndAfter : setEndAfter,\r
4754                         collapse : collapse,\r
4755                         selectNode : selectNode,\r
4756                         selectNodeContents : selectNodeContents,\r
4757                         compareBoundaryPoints : compareBoundaryPoints,\r
4758                         deleteContents : deleteContents,\r
4759                         extractContents : extractContents,\r
4760                         cloneContents : cloneContents,\r
4761                         insertNode : insertNode,\r
4762                         surroundContents : surroundContents,\r
4763                         cloneRange : cloneRange\r
4764                 });\r
4765 \r
4766                 function setStart(n, o) {\r
4767                         _setEndPoint(TRUE, n, o);\r
4768                 };\r
4769 \r
4770                 function setEnd(n, o) {\r
4771                         _setEndPoint(FALSE, n, o);\r
4772                 };\r
4773 \r
4774                 function setStartBefore(n) {\r
4775                         setStart(n.parentNode, nodeIndex(n));\r
4776                 };\r
4777 \r
4778                 function setStartAfter(n) {\r
4779                         setStart(n.parentNode, nodeIndex(n) + 1);\r
4780                 };\r
4781 \r
4782                 function setEndBefore(n) {\r
4783                         setEnd(n.parentNode, nodeIndex(n));\r
4784                 };\r
4785 \r
4786                 function setEndAfter(n) {\r
4787                         setEnd(n.parentNode, nodeIndex(n) + 1);\r
4788                 };\r
4789 \r
4790                 function collapse(ts) {\r
4791                         if (ts) {\r
4792                                 t[END_CONTAINER] = t[START_CONTAINER];\r
4793                                 t[END_OFFSET] = t[START_OFFSET];\r
4794                         } else {\r
4795                                 t[START_CONTAINER] = t[END_CONTAINER];\r
4796                                 t[START_OFFSET] = t[END_OFFSET];\r
4797                         }\r
4798 \r
4799                         t.collapsed = TRUE;\r
4800                 };\r
4801 \r
4802                 function selectNode(n) {\r
4803                         setStartBefore(n);\r
4804                         setEndAfter(n);\r
4805                 };\r
4806 \r
4807                 function selectNodeContents(n) {\r
4808                         setStart(n, 0);\r
4809                         setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);\r
4810                 };\r
4811 \r
4812                 function compareBoundaryPoints(h, r) {\r
4813                         var sc = t[START_CONTAINER], so = t[START_OFFSET], ec = t[END_CONTAINER], eo = t[END_OFFSET],\r
4814                         rsc = r.startContainer, rso = r.startOffset, rec = r.endContainer, reo = r.endOffset;\r
4815 \r
4816                         // Check START_TO_START\r
4817                         if (h === 0)\r
4818                                 return _compareBoundaryPoints(sc, so, rsc, rso);\r
4819         \r
4820                         // Check START_TO_END\r
4821                         if (h === 1)\r
4822                                 return _compareBoundaryPoints(ec, eo, rsc, rso);\r
4823         \r
4824                         // Check END_TO_END\r
4825                         if (h === 2)\r
4826                                 return _compareBoundaryPoints(ec, eo, rec, reo);\r
4827         \r
4828                         // Check END_TO_START\r
4829                         if (h === 3) \r
4830                                 return _compareBoundaryPoints(sc, so, rec, reo);\r
4831                 };\r
4832 \r
4833                 function deleteContents() {\r
4834                         _traverse(DELETE);\r
4835                 };\r
4836 \r
4837                 function extractContents() {\r
4838                         return _traverse(EXTRACT);\r
4839                 };\r
4840 \r
4841                 function cloneContents() {\r
4842                         return _traverse(CLONE);\r
4843                 };\r
4844 \r
4845                 function insertNode(n) {\r
4846                         var startContainer = this[START_CONTAINER],\r
4847                                 startOffset = this[START_OFFSET], nn, o;\r
4848 \r
4849                         // Node is TEXT_NODE or CDATA\r
4850                         if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {\r
4851                                 if (!startOffset) {\r
4852                                         // At the start of text\r
4853                                         startContainer.parentNode.insertBefore(n, startContainer);\r
4854                                 } else if (startOffset >= startContainer.nodeValue.length) {\r
4855                                         // At the end of text\r
4856                                         dom.insertAfter(n, startContainer);\r
4857                                 } else {\r
4858                                         // Middle, need to split\r
4859                                         nn = startContainer.splitText(startOffset);\r
4860                                         startContainer.parentNode.insertBefore(n, nn);\r
4861                                 }\r
4862                         } else {\r
4863                                 // Insert element node\r
4864                                 if (startContainer.childNodes.length > 0)\r
4865                                         o = startContainer.childNodes[startOffset];\r
4866 \r
4867                                 if (o)\r
4868                                         startContainer.insertBefore(n, o);\r
4869                                 else\r
4870                                         startContainer.appendChild(n);\r
4871                         }\r
4872                 };\r
4873 \r
4874                 function surroundContents(n) {\r
4875                         var f = t.extractContents();\r
4876 \r
4877                         t.insertNode(n);\r
4878                         n.appendChild(f);\r
4879                         t.selectNode(n);\r
4880                 };\r
4881 \r
4882                 function cloneRange() {\r
4883                         return extend(new Range(dom), {\r
4884                                 startContainer : t[START_CONTAINER],\r
4885                                 startOffset : t[START_OFFSET],\r
4886                                 endContainer : t[END_CONTAINER],\r
4887                                 endOffset : t[END_OFFSET],\r
4888                                 collapsed : t.collapsed,\r
4889                                 commonAncestorContainer : t.commonAncestorContainer\r
4890                         });\r
4891                 };\r
4892 \r
4893                 // Private methods\r
4894 \r
4895                 function _getSelectedNode(container, offset) {\r
4896                         var child;\r
4897 \r
4898                         if (container.nodeType == 3 /* TEXT_NODE */)\r
4899                                 return container;\r
4900 \r
4901                         if (offset < 0)\r
4902                                 return container;\r
4903 \r
4904                         child = container.firstChild;\r
4905                         while (child && offset > 0) {\r
4906                                 --offset;\r
4907                                 child = child.nextSibling;\r
4908                         }\r
4909 \r
4910                         if (child)\r
4911                                 return child;\r
4912 \r
4913                         return container;\r
4914                 };\r
4915 \r
4916                 function _isCollapsed() {\r
4917                         return (t[START_CONTAINER] == t[END_CONTAINER] && t[START_OFFSET] == t[END_OFFSET]);\r
4918                 };\r
4919 \r
4920                 function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {\r
4921                         var c, offsetC, n, cmnRoot, childA, childB;\r
4922                         \r
4923                         // In the first case the boundary-points have the same container. A is before B\r
4924                         // if its offset is less than the offset of B, A is equal to B if its offset is\r
4925                         // equal to the offset of B, and A is after B if its offset is greater than the\r
4926                         // offset of B.\r
4927                         if (containerA == containerB) {\r
4928                                 if (offsetA == offsetB)\r
4929                                         return 0; // equal\r
4930 \r
4931                                 if (offsetA < offsetB)\r
4932                                         return -1; // before\r
4933 \r
4934                                 return 1; // after\r
4935                         }\r
4936 \r
4937                         // In the second case a child node C of the container of A is an ancestor\r
4938                         // container of B. In this case, A is before B if the offset of A is less than or\r
4939                         // equal to the index of the child node C and A is after B otherwise.\r
4940                         c = containerB;\r
4941                         while (c && c.parentNode != containerA)\r
4942                                 c = c.parentNode;\r
4943 \r
4944                         if (c) {\r
4945                                 offsetC = 0;\r
4946                                 n = containerA.firstChild;\r
4947 \r
4948                                 while (n != c && offsetC < offsetA) {\r
4949                                         offsetC++;\r
4950                                         n = n.nextSibling;\r
4951                                 }\r
4952 \r
4953                                 if (offsetA <= offsetC)\r
4954                                         return -1; // before\r
4955 \r
4956                                 return 1; // after\r
4957                         }\r
4958 \r
4959                         // In the third case a child node C of the container of B is an ancestor container\r
4960                         // of A. In this case, A is before B if the index of the child node C is less than\r
4961                         // the offset of B and A is after B otherwise.\r
4962                         c = containerA;\r
4963                         while (c && c.parentNode != containerB) {\r
4964                                 c = c.parentNode;\r
4965                         }\r
4966 \r
4967                         if (c) {\r
4968                                 offsetC = 0;\r
4969                                 n = containerB.firstChild;\r
4970 \r
4971                                 while (n != c && offsetC < offsetB) {\r
4972                                         offsetC++;\r
4973                                         n = n.nextSibling;\r
4974                                 }\r
4975 \r
4976                                 if (offsetC < offsetB)\r
4977                                         return -1; // before\r
4978 \r
4979                                 return 1; // after\r
4980                         }\r
4981 \r
4982                         // In the fourth case, none of three other cases hold: the containers of A and B\r
4983                         // are siblings or descendants of sibling nodes. In this case, A is before B if\r
4984                         // the container of A is before the container of B in a pre-order traversal of the\r
4985                         // Ranges' context tree and A is after B otherwise.\r
4986                         cmnRoot = dom.findCommonAncestor(containerA, containerB);\r
4987                         childA = containerA;\r
4988 \r
4989                         while (childA && childA.parentNode != cmnRoot)\r
4990                                 childA = childA.parentNode;\r
4991 \r
4992                         if (!childA)\r
4993                                 childA = cmnRoot;\r
4994 \r
4995                         childB = containerB;\r
4996                         while (childB && childB.parentNode != cmnRoot)\r
4997                                 childB = childB.parentNode;\r
4998 \r
4999                         if (!childB)\r
5000                                 childB = cmnRoot;\r
5001 \r
5002                         if (childA == childB)\r
5003                                 return 0; // equal\r
5004 \r
5005                         n = cmnRoot.firstChild;\r
5006                         while (n) {\r
5007                                 if (n == childA)\r
5008                                         return -1; // before\r
5009 \r
5010                                 if (n == childB)\r
5011                                         return 1; // after\r
5012 \r
5013                                 n = n.nextSibling;\r
5014                         }\r
5015                 };\r
5016 \r
5017                 function _setEndPoint(st, n, o) {\r
5018                         var ec, sc;\r
5019 \r
5020                         if (st) {\r
5021                                 t[START_CONTAINER] = n;\r
5022                                 t[START_OFFSET] = o;\r
5023                         } else {\r
5024                                 t[END_CONTAINER] = n;\r
5025                                 t[END_OFFSET] = o;\r
5026                         }\r
5027 \r
5028                         // If one boundary-point of a Range is set to have a root container\r
5029                         // other than the current one for the Range, the Range is collapsed to\r
5030                         // the new position. This enforces the restriction that both boundary-\r
5031                         // points of a Range must have the same root container.\r
5032                         ec = t[END_CONTAINER];\r
5033                         while (ec.parentNode)\r
5034                                 ec = ec.parentNode;\r
5035 \r
5036                         sc = t[START_CONTAINER];\r
5037                         while (sc.parentNode)\r
5038                                 sc = sc.parentNode;\r
5039 \r
5040                         if (sc == ec) {\r
5041                                 // The start position of a Range is guaranteed to never be after the\r
5042                                 // end position. To enforce this restriction, if the start is set to\r
5043                                 // be at a position after the end, the Range is collapsed to that\r
5044                                 // position.\r
5045                                 if (_compareBoundaryPoints(t[START_CONTAINER], t[START_OFFSET], t[END_CONTAINER], t[END_OFFSET]) > 0)\r
5046                                         t.collapse(st);\r
5047                         } else\r
5048                                 t.collapse(st);\r
5049 \r
5050                         t.collapsed = _isCollapsed();\r
5051                         t.commonAncestorContainer = dom.findCommonAncestor(t[START_CONTAINER], t[END_CONTAINER]);\r
5052                 };\r
5053 \r
5054                 function _traverse(how) {\r
5055                         var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;\r
5056 \r
5057                         if (t[START_CONTAINER] == t[END_CONTAINER])\r
5058                                 return _traverseSameContainer(how);\r
5059 \r
5060                         for (c = t[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
5061                                 if (p == t[START_CONTAINER])\r
5062                                         return _traverseCommonStartContainer(c, how);\r
5063 \r
5064                                 ++endContainerDepth;\r
5065                         }\r
5066 \r
5067                         for (c = t[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
5068                                 if (p == t[END_CONTAINER])\r
5069                                         return _traverseCommonEndContainer(c, how);\r
5070 \r
5071                                 ++startContainerDepth;\r
5072                         }\r
5073 \r
5074                         depthDiff = startContainerDepth - endContainerDepth;\r
5075 \r
5076                         startNode = t[START_CONTAINER];\r
5077                         while (depthDiff > 0) {\r
5078                                 startNode = startNode.parentNode;\r
5079                                 depthDiff--;\r
5080                         }\r
5081 \r
5082                         endNode = t[END_CONTAINER];\r
5083                         while (depthDiff < 0) {\r
5084                                 endNode = endNode.parentNode;\r
5085                                 depthDiff++;\r
5086                         }\r
5087 \r
5088                         // ascend the ancestor hierarchy until we have a common parent.\r
5089                         for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {\r
5090                                 startNode = sp;\r
5091                                 endNode = ep;\r
5092                         }\r
5093 \r
5094                         return _traverseCommonAncestors(startNode, endNode, how);\r
5095                 };\r
5096 \r
5097                  function _traverseSameContainer(how) {\r
5098                         var frag, s, sub, n, cnt, sibling, xferNode;\r
5099 \r
5100                         if (how != DELETE)\r
5101                                 frag = doc.createDocumentFragment();\r
5102 \r
5103                         // If selection is empty, just return the fragment\r
5104                         if (t[START_OFFSET] == t[END_OFFSET])\r
5105                                 return frag;\r
5106 \r
5107                         // Text node needs special case handling\r
5108                         if (t[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {\r
5109                                 // get the substring\r
5110                                 s = t[START_CONTAINER].nodeValue;\r
5111                                 sub = s.substring(t[START_OFFSET], t[END_OFFSET]);\r
5112 \r
5113                                 // set the original text node to its new value\r
5114                                 if (how != CLONE) {\r
5115                                         t[START_CONTAINER].deleteData(t[START_OFFSET], t[END_OFFSET] - t[START_OFFSET]);\r
5116 \r
5117                                         // Nothing is partially selected, so collapse to start point\r
5118                                         t.collapse(TRUE);\r
5119                                 }\r
5120 \r
5121                                 if (how == DELETE)\r
5122                                         return;\r
5123 \r
5124                                 frag.appendChild(doc.createTextNode(sub));\r
5125                                 return frag;\r
5126                         }\r
5127 \r
5128                         // Copy nodes between the start/end offsets.\r
5129                         n = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]);\r
5130                         cnt = t[END_OFFSET] - t[START_OFFSET];\r
5131 \r
5132                         while (cnt > 0) {\r
5133                                 sibling = n.nextSibling;\r
5134                                 xferNode = _traverseFullySelected(n, how);\r
5135 \r
5136                                 if (frag)\r
5137                                         frag.appendChild( xferNode );\r
5138 \r
5139                                 --cnt;\r
5140                                 n = sibling;\r
5141                         }\r
5142 \r
5143                         // Nothing is partially selected, so collapse to start point\r
5144                         if (how != CLONE)\r
5145                                 t.collapse(TRUE);\r
5146 \r
5147                         return frag;\r
5148                 };\r
5149 \r
5150                 function _traverseCommonStartContainer(endAncestor, how) {\r
5151                         var frag, n, endIdx, cnt, sibling, xferNode;\r
5152 \r
5153                         if (how != DELETE)\r
5154                                 frag = doc.createDocumentFragment();\r
5155 \r
5156                         n = _traverseRightBoundary(endAncestor, how);\r
5157 \r
5158                         if (frag)\r
5159                                 frag.appendChild(n);\r
5160 \r
5161                         endIdx = nodeIndex(endAncestor);\r
5162                         cnt = endIdx - t[START_OFFSET];\r
5163 \r
5164                         if (cnt <= 0) {\r
5165                                 // Collapse to just before the endAncestor, which\r
5166                                 // is partially selected.\r
5167                                 if (how != CLONE) {\r
5168                                         t.setEndBefore(endAncestor);\r
5169                                         t.collapse(FALSE);\r
5170                                 }\r
5171 \r
5172                                 return frag;\r
5173                         }\r
5174 \r
5175                         n = endAncestor.previousSibling;\r
5176                         while (cnt > 0) {\r
5177                                 sibling = n.previousSibling;\r
5178                                 xferNode = _traverseFullySelected(n, how);\r
5179 \r
5180                                 if (frag)\r
5181                                         frag.insertBefore(xferNode, frag.firstChild);\r
5182 \r
5183                                 --cnt;\r
5184                                 n = sibling;\r
5185                         }\r
5186 \r
5187                         // Collapse to just before the endAncestor, which\r
5188                         // is partially selected.\r
5189                         if (how != CLONE) {\r
5190                                 t.setEndBefore(endAncestor);\r
5191                                 t.collapse(FALSE);\r
5192                         }\r
5193 \r
5194                         return frag;\r
5195                 };\r
5196 \r
5197                 function _traverseCommonEndContainer(startAncestor, how) {\r
5198                         var frag, startIdx, n, cnt, sibling, xferNode;\r
5199 \r
5200                         if (how != DELETE)\r
5201                                 frag = doc.createDocumentFragment();\r
5202 \r
5203                         n = _traverseLeftBoundary(startAncestor, how);\r
5204                         if (frag)\r
5205                                 frag.appendChild(n);\r
5206 \r
5207                         startIdx = nodeIndex(startAncestor);\r
5208                         ++startIdx; // Because we already traversed it\r
5209 \r
5210                         cnt = t[END_OFFSET] - startIdx;\r
5211                         n = startAncestor.nextSibling;\r
5212                         while (cnt > 0) {\r
5213                                 sibling = n.nextSibling;\r
5214                                 xferNode = _traverseFullySelected(n, how);\r
5215 \r
5216                                 if (frag)\r
5217                                         frag.appendChild(xferNode);\r
5218 \r
5219                                 --cnt;\r
5220                                 n = sibling;\r
5221                         }\r
5222 \r
5223                         if (how != CLONE) {\r
5224                                 t.setStartAfter(startAncestor);\r
5225                                 t.collapse(TRUE);\r
5226                         }\r
5227 \r
5228                         return frag;\r
5229                 };\r
5230 \r
5231                 function _traverseCommonAncestors(startAncestor, endAncestor, how) {\r
5232                         var n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;\r
5233 \r
5234                         if (how != DELETE)\r
5235                                 frag = doc.createDocumentFragment();\r
5236 \r
5237                         n = _traverseLeftBoundary(startAncestor, how);\r
5238                         if (frag)\r
5239                                 frag.appendChild(n);\r
5240 \r
5241                         commonParent = startAncestor.parentNode;\r
5242                         startOffset = nodeIndex(startAncestor);\r
5243                         endOffset = nodeIndex(endAncestor);\r
5244                         ++startOffset;\r
5245 \r
5246                         cnt = endOffset - startOffset;\r
5247                         sibling = startAncestor.nextSibling;\r
5248 \r
5249                         while (cnt > 0) {\r
5250                                 nextSibling = sibling.nextSibling;\r
5251                                 n = _traverseFullySelected(sibling, how);\r
5252 \r
5253                                 if (frag)\r
5254                                         frag.appendChild(n);\r
5255 \r
5256                                 sibling = nextSibling;\r
5257                                 --cnt;\r
5258                         }\r
5259 \r
5260                         n = _traverseRightBoundary(endAncestor, how);\r
5261 \r
5262                         if (frag)\r
5263                                 frag.appendChild(n);\r
5264 \r
5265                         if (how != CLONE) {\r
5266                                 t.setStartAfter(startAncestor);\r
5267                                 t.collapse(TRUE);\r
5268                         }\r
5269 \r
5270                         return frag;\r
5271                 };\r
5272 \r
5273                 function _traverseRightBoundary(root, how) {\r
5274                         var next = _getSelectedNode(t[END_CONTAINER], t[END_OFFSET] - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != t[END_CONTAINER];\r
5275 \r
5276                         if (next == root)\r
5277                                 return _traverseNode(next, isFullySelected, FALSE, how);\r
5278 \r
5279                         parent = next.parentNode;\r
5280                         clonedParent = _traverseNode(parent, FALSE, FALSE, how);\r
5281 \r
5282                         while (parent) {\r
5283                                 while (next) {\r
5284                                         prevSibling = next.previousSibling;\r
5285                                         clonedChild = _traverseNode(next, isFullySelected, FALSE, how);\r
5286 \r
5287                                         if (how != DELETE)\r
5288                                                 clonedParent.insertBefore(clonedChild, clonedParent.firstChild);\r
5289 \r
5290                                         isFullySelected = TRUE;\r
5291                                         next = prevSibling;\r
5292                                 }\r
5293 \r
5294                                 if (parent == root)\r
5295                                         return clonedParent;\r
5296 \r
5297                                 next = parent.previousSibling;\r
5298                                 parent = parent.parentNode;\r
5299 \r
5300                                 clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);\r
5301 \r
5302                                 if (how != DELETE)\r
5303                                         clonedGrandParent.appendChild(clonedParent);\r
5304 \r
5305                                 clonedParent = clonedGrandParent;\r
5306                         }\r
5307                 };\r
5308 \r
5309                 function _traverseLeftBoundary(root, how) {\r
5310                         var next = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]), isFullySelected = next != t[START_CONTAINER], parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;\r
5311 \r
5312                         if (next == root)\r
5313                                 return _traverseNode(next, isFullySelected, TRUE, how);\r
5314 \r
5315                         parent = next.parentNode;\r
5316                         clonedParent = _traverseNode(parent, FALSE, TRUE, how);\r
5317 \r
5318                         while (parent) {\r
5319                                 while (next) {\r
5320                                         nextSibling = next.nextSibling;\r
5321                                         clonedChild = _traverseNode(next, isFullySelected, TRUE, how);\r
5322 \r
5323                                         if (how != DELETE)\r
5324                                                 clonedParent.appendChild(clonedChild);\r
5325 \r
5326                                         isFullySelected = TRUE;\r
5327                                         next = nextSibling;\r
5328                                 }\r
5329 \r
5330                                 if (parent == root)\r
5331                                         return clonedParent;\r
5332 \r
5333                                 next = parent.nextSibling;\r
5334                                 parent = parent.parentNode;\r
5335 \r
5336                                 clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);\r
5337 \r
5338                                 if (how != DELETE)\r
5339                                         clonedGrandParent.appendChild(clonedParent);\r
5340 \r
5341                                 clonedParent = clonedGrandParent;\r
5342                         }\r
5343                 };\r
5344 \r
5345                 function _traverseNode(n, isFullySelected, isLeft, how) {\r
5346                         var txtValue, newNodeValue, oldNodeValue, offset, newNode;\r
5347 \r
5348                         if (isFullySelected)\r
5349                                 return _traverseFullySelected(n, how);\r
5350 \r
5351                         if (n.nodeType == 3 /* TEXT_NODE */) {\r
5352                                 txtValue = n.nodeValue;\r
5353 \r
5354                                 if (isLeft) {\r
5355                                         offset = t[START_OFFSET];\r
5356                                         newNodeValue = txtValue.substring(offset);\r
5357                                         oldNodeValue = txtValue.substring(0, offset);\r
5358                                 } else {\r
5359                                         offset = t[END_OFFSET];\r
5360                                         newNodeValue = txtValue.substring(0, offset);\r
5361                                         oldNodeValue = txtValue.substring(offset);\r
5362                                 }\r
5363 \r
5364                                 if (how != CLONE)\r
5365                                         n.nodeValue = oldNodeValue;\r
5366 \r
5367                                 if (how == DELETE)\r
5368                                         return;\r
5369 \r
5370                                 newNode = n.cloneNode(FALSE);\r
5371                                 newNode.nodeValue = newNodeValue;\r
5372 \r
5373                                 return newNode;\r
5374                         }\r
5375 \r
5376                         if (how == DELETE)\r
5377                                 return;\r
5378 \r
5379                         return n.cloneNode(FALSE);\r
5380                 };\r
5381 \r
5382                 function _traverseFullySelected(n, how) {\r
5383                         if (how != DELETE)\r
5384                                 return how == CLONE ? n.cloneNode(TRUE) : n;\r
5385 \r
5386                         n.parentNode.removeChild(n);\r
5387                 };\r
5388         };\r
5389 \r
5390         ns.Range = Range;\r
5391 })(tinymce.dom);\r
5392 \r
5393 (function() {\r
5394         function Selection(selection) {\r
5395                 var self = this, dom = selection.dom, TRUE = true, FALSE = false;\r
5396 \r
5397                 function getPosition(rng, start) {\r
5398                         var checkRng, startIndex = 0, endIndex, inside,\r
5399                                 children, child, offset, index, position = -1, parent;\r
5400 \r
5401                         // Setup test range, collapse it and get the parent\r
5402                         checkRng = rng.duplicate();\r
5403                         checkRng.collapse(start);\r
5404                         parent = checkRng.parentElement();\r
5405 \r
5406                         // Check if the selection is within the right document\r
5407                         if (parent.ownerDocument !== selection.dom.doc)\r
5408                                 return;\r
5409 \r
5410                         // IE will report non editable elements as it's parent so look for an editable one\r
5411                         while (parent.contentEditable === "false") {\r
5412                                 parent = parent.parentNode;\r
5413                         }\r
5414 \r
5415                         // If parent doesn't have any children then return that we are inside the element\r
5416                         if (!parent.hasChildNodes()) {\r
5417                                 return {node : parent, inside : 1};\r
5418                         }\r
5419 \r
5420                         // Setup node list and endIndex\r
5421                         children = parent.children;\r
5422                         endIndex = children.length - 1;\r
5423 \r
5424                         // Perform a binary search for the position\r
5425                         while (startIndex <= endIndex) {\r
5426                                 index = Math.floor((startIndex + endIndex) / 2);\r
5427 \r
5428                                 // Move selection to node and compare the ranges\r
5429                                 child = children[index];\r
5430                                 checkRng.moveToElementText(child);\r
5431                                 position = checkRng.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', rng);\r
5432 \r
5433                                 // Before/after or an exact match\r
5434                                 if (position > 0) {\r
5435                                         endIndex = index - 1;\r
5436                                 } else if (position < 0) {\r
5437                                         startIndex = index + 1;\r
5438                                 } else {\r
5439                                         return {node : child};\r
5440                                 }\r
5441                         }\r
5442 \r
5443                         // Check if child position is before or we didn't find a position\r
5444                         if (position < 0) {\r
5445                                 // No element child was found use the parent element and the offset inside that\r
5446                                 if (!child) {\r
5447                                         checkRng.moveToElementText(parent);\r
5448                                         checkRng.collapse(true);\r
5449                                         child = parent;\r
5450                                         inside = true;\r
5451                                 } else\r
5452                                         checkRng.collapse(false);\r
5453 \r
5454                                 checkRng.setEndPoint(start ? 'EndToStart' : 'EndToEnd', rng);\r
5455 \r
5456                                 // Fix for edge case: <div style="width: 100px; height:100px;"><table>..</table>ab|c</div>\r
5457                                 if (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) > 0) {\r
5458                                         checkRng = rng.duplicate();\r
5459                                         checkRng.collapse(start);\r
5460 \r
5461                                         offset = -1;\r
5462                                         while (parent == checkRng.parentElement()) {\r
5463                                                 if (checkRng.move('character', -1) == 0)\r
5464                                                         break;\r
5465 \r
5466                                                 offset++;\r
5467                                         }\r
5468                                 }\r
5469 \r
5470                                 offset = offset || checkRng.text.replace('\r\n', ' ').length;\r
5471                         } else {\r
5472                                 // Child position is after the selection endpoint\r
5473                                 checkRng.collapse(true);\r
5474                                 checkRng.setEndPoint(start ? 'StartToStart' : 'StartToEnd', rng);\r
5475 \r
5476                                 // Get the length of the text to find where the endpoint is relative to it's container\r
5477                                 offset = checkRng.text.replace('\r\n', ' ').length;\r
5478                         }\r
5479 \r
5480                         return {node : child, position : position, offset : offset, inside : inside};\r
5481                 };\r
5482 \r
5483                 // Returns a W3C DOM compatible range object by using the IE Range API\r
5484                 function getRange() {\r
5485                         var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed, tmpRange, element2, bookmark, fail;\r
5486 \r
5487                         // If selection is outside the current document just return an empty range\r
5488                         element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();\r
5489                         if (element.ownerDocument != dom.doc)\r
5490                                 return domRange;\r
5491 \r
5492                         collapsed = selection.isCollapsed();\r
5493 \r
5494                         // Handle control selection\r
5495                         if (ieRange.item) {\r
5496                                 domRange.setStart(element.parentNode, dom.nodeIndex(element));\r
5497                                 domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);\r
5498 \r
5499                                 return domRange;\r
5500                         }\r
5501 \r
5502                         function findEndPoint(start) {\r
5503                                 var endPoint = getPosition(ieRange, start), container, offset, textNodeOffset = 0, sibling, undef, nodeValue;\r
5504 \r
5505                                 container = endPoint.node;\r
5506                                 offset = endPoint.offset;\r
5507 \r
5508                                 if (endPoint.inside && !container.hasChildNodes()) {\r
5509                                         domRange[start ? 'setStart' : 'setEnd'](container, 0);\r
5510                                         return;\r
5511                                 }\r
5512 \r
5513                                 if (offset === undef) {\r
5514                                         domRange[start ? 'setStartBefore' : 'setEndAfter'](container);\r
5515                                         return;\r
5516                                 }\r
5517 \r
5518                                 if (endPoint.position < 0) {\r
5519                                         sibling = endPoint.inside ? container.firstChild : container.nextSibling;\r
5520 \r
5521                                         if (!sibling) {\r
5522                                                 domRange[start ? 'setStartAfter' : 'setEndAfter'](container);\r
5523                                                 return;\r
5524                                         }\r
5525 \r
5526                                         if (!offset) {\r
5527                                                 if (sibling.nodeType == 3)\r
5528                                                         domRange[start ? 'setStart' : 'setEnd'](sibling, 0);\r
5529                                                 else\r
5530                                                         domRange[start ? 'setStartBefore' : 'setEndBefore'](sibling);\r
5531 \r
5532                                                 return;\r
5533                                         }\r
5534 \r
5535                                         // Find the text node and offset\r
5536                                         while (sibling) {\r
5537                                                 nodeValue = sibling.nodeValue;\r
5538                                                 textNodeOffset += nodeValue.length;\r
5539 \r
5540                                                 // We are at or passed the position we where looking for\r
5541                                                 if (textNodeOffset >= offset) {\r
5542                                                         container = sibling;\r
5543                                                         textNodeOffset -= offset;\r
5544                                                         textNodeOffset = nodeValue.length - textNodeOffset;\r
5545                                                         break;\r
5546                                                 }\r
5547 \r
5548                                                 sibling = sibling.nextSibling;\r
5549                                         }\r
5550                                 } else {\r
5551                                         // Find the text node and offset\r
5552                                         sibling = container.previousSibling;\r
5553 \r
5554                                         if (!sibling)\r
5555                                                 return domRange[start ? 'setStartBefore' : 'setEndBefore'](container);\r
5556 \r
5557                                         // If there isn't any text to loop then use the first position\r
5558                                         if (!offset) {\r
5559                                                 if (container.nodeType == 3)\r
5560                                                         domRange[start ? 'setStart' : 'setEnd'](sibling, container.nodeValue.length);\r
5561                                                 else\r
5562                                                         domRange[start ? 'setStartAfter' : 'setEndAfter'](sibling);\r
5563 \r
5564                                                 return;\r
5565                                         }\r
5566 \r
5567                                         while (sibling) {\r
5568                                                 textNodeOffset += sibling.nodeValue.length;\r
5569 \r
5570                                                 // We are at or passed the position we where looking for\r
5571                                                 if (textNodeOffset >= offset) {\r
5572                                                         container = sibling;\r
5573                                                         textNodeOffset -= offset;\r
5574                                                         break;\r
5575                                                 }\r
5576 \r
5577                                                 sibling = sibling.previousSibling;\r
5578                                         }\r
5579                                 }\r
5580 \r
5581                                 domRange[start ? 'setStart' : 'setEnd'](container, textNodeOffset);\r
5582                         };\r
5583 \r
5584                         try {\r
5585                                 // Find start point\r
5586                                 findEndPoint(true);\r
5587 \r
5588                                 // Find end point if needed\r
5589                                 if (!collapsed)\r
5590                                         findEndPoint();\r
5591                         } catch (ex) {\r
5592                                 // IE has a nasty bug where text nodes might throw "invalid argument" when you\r
5593                                 // access the nodeValue or other properties of text nodes. This seems to happend when\r
5594                                 // text nodes are split into two nodes by a delete/backspace call. So lets detect it and try to fix it.\r
5595                                 if (ex.number == -2147024809) {\r
5596                                         // Get the current selection\r
5597                                         bookmark = self.getBookmark(2);\r
5598 \r
5599                                         // Get start element\r
5600                                         tmpRange = ieRange.duplicate();\r
5601                                         tmpRange.collapse(true);\r
5602                                         element = tmpRange.parentElement();\r
5603 \r
5604                                         // Get end element\r
5605                                         if (!collapsed) {\r
5606                                                 tmpRange = ieRange.duplicate();\r
5607                                                 tmpRange.collapse(false);\r
5608                                                 element2 = tmpRange.parentElement();\r
5609                                                 element2.innerHTML = element2.innerHTML;\r
5610                                         }\r
5611 \r
5612                                         // Remove the broken elements\r
5613                                         element.innerHTML = element.innerHTML;\r
5614 \r
5615                                         // Restore the selection\r
5616                                         self.moveToBookmark(bookmark);\r
5617 \r
5618                                         // Since the range has moved we need to re-get it\r
5619                                         ieRange = selection.getRng();\r
5620 \r
5621                                         // Find start point\r
5622                                         findEndPoint(true);\r
5623 \r
5624                                         // Find end point if needed\r
5625                                         if (!collapsed)\r
5626                                                 findEndPoint();\r
5627                                 } else\r
5628                                         throw ex; // Throw other errors\r
5629                         }\r
5630 \r
5631                         return domRange;\r
5632                 };\r
5633 \r
5634                 this.getBookmark = function(type) {\r
5635                         var rng = selection.getRng(), start, end, bookmark = {};\r
5636 \r
5637                         function getIndexes(node) {\r
5638                                 var node, parent, root, children, i, indexes = [];\r
5639 \r
5640                                 parent = node.parentNode;\r
5641                                 root = dom.getRoot().parentNode;\r
5642 \r
5643                                 while (parent != root) {\r
5644                                         children = parent.children;\r
5645 \r
5646                                         i = children.length;\r
5647                                         while (i--) {\r
5648                                                 if (node === children[i]) {\r
5649                                                         indexes.push(i);\r
5650                                                         break;\r
5651                                                 }\r
5652                                         }\r
5653 \r
5654                                         node = parent;\r
5655                                         parent = parent.parentNode;\r
5656                                 }\r
5657 \r
5658                                 return indexes;\r
5659                         };\r
5660 \r
5661                         function getBookmarkEndPoint(start) {\r
5662                                 var position;\r
5663 \r
5664                                 position = getPosition(rng, start);\r
5665                                 if (position) {\r
5666                                         return {\r
5667                                                 position : position.position,\r
5668                                                 offset : position.offset,\r
5669                                                 indexes : getIndexes(position.node),\r
5670                                                 inside : position.inside\r
5671                                         };\r
5672                                 }\r
5673                         };\r
5674 \r
5675                         // Non ubstructive bookmark\r
5676                         if (type === 2) {\r
5677                                 // Handle text selection\r
5678                                 if (!rng.item) {\r
5679                                         bookmark.start = getBookmarkEndPoint(true);\r
5680 \r
5681                                         if (!selection.isCollapsed())\r
5682                                                 bookmark.end = getBookmarkEndPoint();\r
5683                                 } else\r
5684                                         bookmark.start = {ctrl : true, indexes : getIndexes(rng.item(0))};\r
5685                         }\r
5686 \r
5687                         return bookmark;\r
5688                 };\r
5689 \r
5690                 this.moveToBookmark = function(bookmark) {\r
5691                         var rng, body = dom.doc.body;\r
5692 \r
5693                         function resolveIndexes(indexes) {\r
5694                                 var node, i, idx, children;\r
5695 \r
5696                                 node = dom.getRoot();\r
5697                                 for (i = indexes.length - 1; i >= 0; i--) {\r
5698                                         children = node.children;\r
5699                                         idx = indexes[i];\r
5700 \r
5701                                         if (idx <= children.length - 1) {\r
5702                                                 node = children[idx];\r
5703                                         }\r
5704                                 }\r
5705 \r
5706                                 return node;\r
5707                         };\r
5708                         \r
5709                         function setBookmarkEndPoint(start) {\r
5710                                 var endPoint = bookmark[start ? 'start' : 'end'], moveLeft, moveRng, undef;\r
5711 \r
5712                                 if (endPoint) {\r
5713                                         moveLeft = endPoint.position > 0;\r
5714 \r
5715                                         moveRng = body.createTextRange();\r
5716                                         moveRng.moveToElementText(resolveIndexes(endPoint.indexes));\r
5717 \r
5718                                         offset = endPoint.offset;\r
5719                                         if (offset !== undef) {\r
5720                                                 moveRng.collapse(endPoint.inside || moveLeft);\r
5721                                                 moveRng.moveStart('character', moveLeft ? -offset : offset);\r
5722                                         } else\r
5723                                                 moveRng.collapse(start);\r
5724 \r
5725                                         rng.setEndPoint(start ? 'StartToStart' : 'EndToStart', moveRng);\r
5726 \r
5727                                         if (start)\r
5728                                                 rng.collapse(true);\r
5729                                 }\r
5730                         };\r
5731 \r
5732                         if (bookmark.start) {\r
5733                                 if (bookmark.start.ctrl) {\r
5734                                         rng = body.createControlRange();\r
5735                                         rng.addElement(resolveIndexes(bookmark.start.indexes));\r
5736                                         rng.select();\r
5737                                 } else {\r
5738                                         rng = body.createTextRange();\r
5739                                         setBookmarkEndPoint(true);\r
5740                                         setBookmarkEndPoint();\r
5741                                         rng.select();\r
5742                                 }\r
5743                         }\r
5744                 };\r
5745 \r
5746                 this.addRange = function(rng) {\r
5747                         var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, doc = selection.dom.doc, body = doc.body;\r
5748 \r
5749                         function setEndPoint(start) {\r
5750                                 var container, offset, marker, tmpRng, nodes;\r
5751 \r
5752                                 marker = dom.create('a');\r
5753                                 container = start ? startContainer : endContainer;\r
5754                                 offset = start ? startOffset : endOffset;\r
5755                                 tmpRng = ieRng.duplicate();\r
5756 \r
5757                                 if (container == doc || container == doc.documentElement) {\r
5758                                         container = body;\r
5759                                         offset = 0;\r
5760                                 }\r
5761 \r
5762                                 if (container.nodeType == 3) {\r
5763                                         container.parentNode.insertBefore(marker, container);\r
5764                                         tmpRng.moveToElementText(marker);\r
5765                                         tmpRng.moveStart('character', offset);\r
5766                                         dom.remove(marker);\r
5767                                         ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);\r
5768                                 } else {\r
5769                                         nodes = container.childNodes;\r
5770 \r
5771                                         if (nodes.length) {\r
5772                                                 if (offset >= nodes.length) {\r
5773                                                         dom.insertAfter(marker, nodes[nodes.length - 1]);\r
5774                                                 } else {\r
5775                                                         container.insertBefore(marker, nodes[offset]);\r
5776                                                 }\r
5777 \r
5778                                                 tmpRng.moveToElementText(marker);\r
5779                                         } else {\r
5780                                                 // Empty node selection for example <div>|</div>\r
5781                                                 marker = doc.createTextNode('\uFEFF');\r
5782                                                 container.appendChild(marker);\r
5783                                                 tmpRng.moveToElementText(marker.parentNode);\r
5784                                                 tmpRng.collapse(TRUE);\r
5785                                         }\r
5786 \r
5787                                         ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);\r
5788                                         dom.remove(marker);\r
5789                                 }\r
5790                         }\r
5791 \r
5792                         // Setup some shorter versions\r
5793                         startContainer = rng.startContainer;\r
5794                         startOffset = rng.startOffset;\r
5795                         endContainer = rng.endContainer;\r
5796                         endOffset = rng.endOffset;\r
5797                         ieRng = body.createTextRange();\r
5798 \r
5799                         // If single element selection then try making a control selection out of it\r
5800                         if (startContainer == endContainer && startContainer.nodeType == 1 && startOffset == endOffset - 1) {\r
5801                                 if (startOffset == endOffset - 1) {\r
5802                                         try {\r
5803                                                 ctrlRng = body.createControlRange();\r
5804                                                 ctrlRng.addElement(startContainer.childNodes[startOffset]);\r
5805                                                 ctrlRng.select();\r
5806                                                 return;\r
5807                                         } catch (ex) {\r
5808                                                 // Ignore\r
5809                                         }\r
5810                                 }\r
5811                         }\r
5812 \r
5813                         // Set start/end point of selection\r
5814                         setEndPoint(true);\r
5815                         setEndPoint();\r
5816 \r
5817                         // Select the new range and scroll it into view\r
5818                         ieRng.select();\r
5819                 };\r
5820 \r
5821                 // Expose range method\r
5822                 this.getRangeAt = getRange;\r
5823         };\r
5824 \r
5825         // Expose the selection object\r
5826         tinymce.dom.TridentSelection = Selection;\r
5827 })();\r
5828 \r
5829 \r
5830 /*\r
5831  * Sizzle CSS Selector Engine - v1.0\r
5832  *  Copyright 2009, The Dojo Foundation\r
5833  *  Released under the MIT, BSD, and GPL Licenses.\r
5834  *  More information: http://sizzlejs.com/\r
5835  */\r
5836 (function(){\r
5837 \r
5838 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,\r
5839         done = 0,\r
5840         toString = Object.prototype.toString,\r
5841         hasDuplicate = false,\r
5842         baseHasDuplicate = true;\r
5843 \r
5844 // Here we check if the JavaScript engine is using some sort of\r
5845 // optimization where it does not always call our comparision\r
5846 // function. If that is the case, discard the hasDuplicate value.\r
5847 //   Thus far that includes Google Chrome.\r
5848 [0, 0].sort(function(){\r
5849         baseHasDuplicate = false;\r
5850         return 0;\r
5851 });\r
5852 \r
5853 var Sizzle = function(selector, context, results, seed) {\r
5854         results = results || [];\r
5855         context = context || document;\r
5856 \r
5857         var origContext = context;\r
5858 \r
5859         if ( context.nodeType !== 1 && context.nodeType !== 9 ) {\r
5860                 return [];\r
5861         }\r
5862         \r
5863         if ( !selector || typeof selector !== "string" ) {\r
5864                 return results;\r
5865         }\r
5866 \r
5867         var parts = [], m, set, checkSet, extra, prune = true, contextXML = Sizzle.isXML(context),\r
5868                 soFar = selector, ret, cur, pop, i;\r
5869         \r
5870         // Reset the position of the chunker regexp (start from head)\r
5871         do {\r
5872                 chunker.exec("");\r
5873                 m = chunker.exec(soFar);\r
5874 \r
5875                 if ( m ) {\r
5876                         soFar = m[3];\r
5877                 \r
5878                         parts.push( m[1] );\r
5879                 \r
5880                         if ( m[2] ) {\r
5881                                 extra = m[3];\r
5882                                 break;\r
5883                         }\r
5884                 }\r
5885         } while ( m );\r
5886 \r
5887         if ( parts.length > 1 && origPOS.exec( selector ) ) {\r
5888                 if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {\r
5889                         set = posProcess( parts[0] + parts[1], context );\r
5890                 } else {\r
5891                         set = Expr.relative[ parts[0] ] ?\r
5892                                 [ context ] :\r
5893                                 Sizzle( parts.shift(), context );\r
5894 \r
5895                         while ( parts.length ) {\r
5896                                 selector = parts.shift();\r
5897 \r
5898                                 if ( Expr.relative[ selector ] ) {\r
5899                                         selector += parts.shift();\r
5900                                 }\r
5901                                 \r
5902                                 set = posProcess( selector, set );\r
5903                         }\r
5904                 }\r
5905         } else {\r
5906                 // Take a shortcut and set the context if the root selector is an ID\r
5907                 // (but not if it'll be faster if the inner selector is an ID)\r
5908                 if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&\r
5909                                 Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {\r
5910                         ret = Sizzle.find( parts.shift(), context, contextXML );\r
5911                         context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];\r
5912                 }\r
5913 \r
5914                 if ( context ) {\r
5915                         ret = seed ?\r
5916                                 { expr: parts.pop(), set: makeArray(seed) } :\r
5917                                 Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );\r
5918                         set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;\r
5919 \r
5920                         if ( parts.length > 0 ) {\r
5921                                 checkSet = makeArray(set);\r
5922                         } else {\r
5923                                 prune = false;\r
5924                         }\r
5925 \r
5926                         while ( parts.length ) {\r
5927                                 cur = parts.pop();\r
5928                                 pop = cur;\r
5929 \r
5930                                 if ( !Expr.relative[ cur ] ) {\r
5931                                         cur = "";\r
5932                                 } else {\r
5933                                         pop = parts.pop();\r
5934                                 }\r
5935 \r
5936                                 if ( pop == null ) {\r
5937                                         pop = context;\r
5938                                 }\r
5939 \r
5940                                 Expr.relative[ cur ]( checkSet, pop, contextXML );\r
5941                         }\r
5942                 } else {\r
5943                         checkSet = parts = [];\r
5944                 }\r
5945         }\r
5946 \r
5947         if ( !checkSet ) {\r
5948                 checkSet = set;\r
5949         }\r
5950 \r
5951         if ( !checkSet ) {\r
5952                 Sizzle.error( cur || selector );\r
5953         }\r
5954 \r
5955         if ( toString.call(checkSet) === "[object Array]" ) {\r
5956                 if ( !prune ) {\r
5957                         results.push.apply( results, checkSet );\r
5958                 } else if ( context && context.nodeType === 1 ) {\r
5959                         for ( i = 0; checkSet[i] != null; i++ ) {\r
5960                                 if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {\r
5961                                         results.push( set[i] );\r
5962                                 }\r
5963                         }\r
5964                 } else {\r
5965                         for ( i = 0; checkSet[i] != null; i++ ) {\r
5966                                 if ( checkSet[i] && checkSet[i].nodeType === 1 ) {\r
5967                                         results.push( set[i] );\r
5968                                 }\r
5969                         }\r
5970                 }\r
5971         } else {\r
5972                 makeArray( checkSet, results );\r
5973         }\r
5974 \r
5975         if ( extra ) {\r
5976                 Sizzle( extra, origContext, results, seed );\r
5977                 Sizzle.uniqueSort( results );\r
5978         }\r
5979 \r
5980         return results;\r
5981 };\r
5982 \r
5983 Sizzle.uniqueSort = function(results){\r
5984         if ( sortOrder ) {\r
5985                 hasDuplicate = baseHasDuplicate;\r
5986                 results.sort(sortOrder);\r
5987 \r
5988                 if ( hasDuplicate ) {\r
5989                         for ( var i = 1; i < results.length; i++ ) {\r
5990                                 if ( results[i] === results[i-1] ) {\r
5991                                         results.splice(i--, 1);\r
5992                                 }\r
5993                         }\r
5994                 }\r
5995         }\r
5996 \r
5997         return results;\r
5998 };\r
5999 \r
6000 Sizzle.matches = function(expr, set){\r
6001         return Sizzle(expr, null, null, set);\r
6002 };\r
6003 \r
6004 Sizzle.find = function(expr, context, isXML){\r
6005         var set;\r
6006 \r
6007         if ( !expr ) {\r
6008                 return [];\r
6009         }\r
6010 \r
6011         for ( var i = 0, l = Expr.order.length; i < l; i++ ) {\r
6012                 var type = Expr.order[i], match;\r
6013                 \r
6014                 if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {\r
6015                         var left = match[1];\r
6016                         match.splice(1,1);\r
6017 \r
6018                         if ( left.substr( left.length - 1 ) !== "\\" ) {\r
6019                                 match[1] = (match[1] || "").replace(/\\/g, "");\r
6020                                 set = Expr.find[ type ]( match, context, isXML );\r
6021                                 if ( set != null ) {\r
6022                                         expr = expr.replace( Expr.match[ type ], "" );\r
6023                                         break;\r
6024                                 }\r
6025                         }\r
6026                 }\r
6027         }\r
6028 \r
6029         if ( !set ) {\r
6030                 set = context.getElementsByTagName("*");\r
6031         }\r
6032 \r
6033         return {set: set, expr: expr};\r
6034 };\r
6035 \r
6036 Sizzle.filter = function(expr, set, inplace, not){\r
6037         var old = expr, result = [], curLoop = set, match, anyFound,\r
6038                 isXMLFilter = set && set[0] && Sizzle.isXML(set[0]);\r
6039 \r
6040         while ( expr && set.length ) {\r
6041                 for ( var type in Expr.filter ) {\r
6042                         if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {\r
6043                                 var filter = Expr.filter[ type ], found, item, left = match[1];\r
6044                                 anyFound = false;\r
6045 \r
6046                                 match.splice(1,1);\r
6047 \r
6048                                 if ( left.substr( left.length - 1 ) === "\\" ) {\r
6049                                         continue;\r
6050                                 }\r
6051 \r
6052                                 if ( curLoop === result ) {\r
6053                                         result = [];\r
6054                                 }\r
6055 \r
6056                                 if ( Expr.preFilter[ type ] ) {\r
6057                                         match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );\r
6058 \r
6059                                         if ( !match ) {\r
6060                                                 anyFound = found = true;\r
6061                                         } else if ( match === true ) {\r
6062                                                 continue;\r
6063                                         }\r
6064                                 }\r
6065 \r
6066                                 if ( match ) {\r
6067                                         for ( var i = 0; (item = curLoop[i]) != null; i++ ) {\r
6068                                                 if ( item ) {\r
6069                                                         found = filter( item, match, i, curLoop );\r
6070                                                         var pass = not ^ !!found;\r
6071 \r
6072                                                         if ( inplace && found != null ) {\r
6073                                                                 if ( pass ) {\r
6074                                                                         anyFound = true;\r
6075                                                                 } else {\r
6076                                                                         curLoop[i] = false;\r
6077                                                                 }\r
6078                                                         } else if ( pass ) {\r
6079                                                                 result.push( item );\r
6080                                                                 anyFound = true;\r
6081                                                         }\r
6082                                                 }\r
6083                                         }\r
6084                                 }\r
6085 \r
6086                                 if ( found !== undefined ) {\r
6087                                         if ( !inplace ) {\r
6088                                                 curLoop = result;\r
6089                                         }\r
6090 \r
6091                                         expr = expr.replace( Expr.match[ type ], "" );\r
6092 \r
6093                                         if ( !anyFound ) {\r
6094                                                 return [];\r
6095                                         }\r
6096 \r
6097                                         break;\r
6098                                 }\r
6099                         }\r
6100                 }\r
6101 \r
6102                 // Improper expression\r
6103                 if ( expr === old ) {\r
6104                         if ( anyFound == null ) {\r
6105                                 Sizzle.error( expr );\r
6106                         } else {\r
6107                                 break;\r
6108                         }\r
6109                 }\r
6110 \r
6111                 old = expr;\r
6112         }\r
6113 \r
6114         return curLoop;\r
6115 };\r
6116 \r
6117 Sizzle.error = function( msg ) {\r
6118         throw "Syntax error, unrecognized expression: " + msg;\r
6119 };\r
6120 \r
6121 var Expr = Sizzle.selectors = {\r
6122         order: [ "ID", "NAME", "TAG" ],\r
6123         match: {\r
6124                 ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,\r
6125                 CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,\r
6126                 NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,\r
6127                 ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,\r
6128                 TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,\r
6129                 CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,\r
6130                 POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,\r
6131                 PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/\r
6132         },\r
6133         leftMatch: {},\r
6134         attrMap: {\r
6135                 "class": "className",\r
6136                 "for": "htmlFor"\r
6137         },\r
6138         attrHandle: {\r
6139                 href: function(elem){\r
6140                         return elem.getAttribute("href");\r
6141                 }\r
6142         },\r
6143         relative: {\r
6144                 "+": function(checkSet, part){\r
6145                         var isPartStr = typeof part === "string",\r
6146                                 isTag = isPartStr && !/\W/.test(part),\r
6147                                 isPartStrNotTag = isPartStr && !isTag;\r
6148 \r
6149                         if ( isTag ) {\r
6150                                 part = part.toLowerCase();\r
6151                         }\r
6152 \r
6153                         for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {\r
6154                                 if ( (elem = checkSet[i]) ) {\r
6155                                         while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}\r
6156 \r
6157                                         checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?\r
6158                                                 elem || false :\r
6159                                                 elem === part;\r
6160                                 }\r
6161                         }\r
6162 \r
6163                         if ( isPartStrNotTag ) {\r
6164                                 Sizzle.filter( part, checkSet, true );\r
6165                         }\r
6166                 },\r
6167                 ">": function(checkSet, part){\r
6168                         var isPartStr = typeof part === "string",\r
6169                                 elem, i = 0, l = checkSet.length;\r
6170 \r
6171                         if ( isPartStr && !/\W/.test(part) ) {\r
6172                                 part = part.toLowerCase();\r
6173 \r
6174                                 for ( ; i < l; i++ ) {\r
6175                                         elem = checkSet[i];\r
6176                                         if ( elem ) {\r
6177                                                 var parent = elem.parentNode;\r
6178                                                 checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;\r
6179                                         }\r
6180                                 }\r
6181                         } else {\r
6182                                 for ( ; i < l; i++ ) {\r
6183                                         elem = checkSet[i];\r
6184                                         if ( elem ) {\r
6185                                                 checkSet[i] = isPartStr ?\r
6186                                                         elem.parentNode :\r
6187                                                         elem.parentNode === part;\r
6188                                         }\r
6189                                 }\r
6190 \r
6191                                 if ( isPartStr ) {\r
6192                                         Sizzle.filter( part, checkSet, true );\r
6193                                 }\r
6194                         }\r
6195                 },\r
6196                 "": function(checkSet, part, isXML){\r
6197                         var doneName = done++, checkFn = dirCheck, nodeCheck;\r
6198 \r
6199                         if ( typeof part === "string" && !/\W/.test(part) ) {\r
6200                                 part = part.toLowerCase();\r
6201                                 nodeCheck = part;\r
6202                                 checkFn = dirNodeCheck;\r
6203                         }\r
6204 \r
6205                         checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);\r
6206                 },\r
6207                 "~": function(checkSet, part, isXML){\r
6208                         var doneName = done++, checkFn = dirCheck, nodeCheck;\r
6209 \r
6210                         if ( typeof part === "string" && !/\W/.test(part) ) {\r
6211                                 part = part.toLowerCase();\r
6212                                 nodeCheck = part;\r
6213                                 checkFn = dirNodeCheck;\r
6214                         }\r
6215 \r
6216                         checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);\r
6217                 }\r
6218         },\r
6219         find: {\r
6220                 ID: function(match, context, isXML){\r
6221                         if ( typeof context.getElementById !== "undefined" && !isXML ) {\r
6222                                 var m = context.getElementById(match[1]);\r
6223                                 return m ? [m] : [];\r
6224                         }\r
6225                 },\r
6226                 NAME: function(match, context){\r
6227                         if ( typeof context.getElementsByName !== "undefined" ) {\r
6228                                 var ret = [], results = context.getElementsByName(match[1]);\r
6229 \r
6230                                 for ( var i = 0, l = results.length; i < l; i++ ) {\r
6231                                         if ( results[i].getAttribute("name") === match[1] ) {\r
6232                                                 ret.push( results[i] );\r
6233                                         }\r
6234                                 }\r
6235 \r
6236                                 return ret.length === 0 ? null : ret;\r
6237                         }\r
6238                 },\r
6239                 TAG: function(match, context){\r
6240                         return context.getElementsByTagName(match[1]);\r
6241                 }\r
6242         },\r
6243         preFilter: {\r
6244                 CLASS: function(match, curLoop, inplace, result, not, isXML){\r
6245                         match = " " + match[1].replace(/\\/g, "") + " ";\r
6246 \r
6247                         if ( isXML ) {\r
6248                                 return match;\r
6249                         }\r
6250 \r
6251                         for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {\r
6252                                 if ( elem ) {\r
6253                                         if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {\r
6254                                                 if ( !inplace ) {\r
6255                                                         result.push( elem );\r
6256                                                 }\r
6257                                         } else if ( inplace ) {\r
6258                                                 curLoop[i] = false;\r
6259                                         }\r
6260                                 }\r
6261                         }\r
6262 \r
6263                         return false;\r
6264                 },\r
6265                 ID: function(match){\r
6266                         return match[1].replace(/\\/g, "");\r
6267                 },\r
6268                 TAG: function(match, curLoop){\r
6269                         return match[1].toLowerCase();\r
6270                 },\r
6271                 CHILD: function(match){\r
6272                         if ( match[1] === "nth" ) {\r
6273                                 // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'\r
6274                                 var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(\r
6275                                         match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||\r
6276                                         !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);\r
6277 \r
6278                                 // calculate the numbers (first)n+(last) including if they are negative\r
6279                                 match[2] = (test[1] + (test[2] || 1)) - 0;\r
6280                                 match[3] = test[3] - 0;\r
6281                         }\r
6282 \r
6283                         // TODO: Move to normal caching system\r
6284                         match[0] = done++;\r
6285 \r
6286                         return match;\r
6287                 },\r
6288                 ATTR: function(match, curLoop, inplace, result, not, isXML){\r
6289                         var name = match[1].replace(/\\/g, "");\r
6290                         \r
6291                         if ( !isXML && Expr.attrMap[name] ) {\r
6292                                 match[1] = Expr.attrMap[name];\r
6293                         }\r
6294 \r
6295                         if ( match[2] === "~=" ) {\r
6296                                 match[4] = " " + match[4] + " ";\r
6297                         }\r
6298 \r
6299                         return match;\r
6300                 },\r
6301                 PSEUDO: function(match, curLoop, inplace, result, not){\r
6302                         if ( match[1] === "not" ) {\r
6303                                 // If we're dealing with a complex expression, or a simple one\r
6304                                 if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {\r
6305                                         match[3] = Sizzle(match[3], null, null, curLoop);\r
6306                                 } else {\r
6307                                         var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);\r
6308                                         if ( !inplace ) {\r
6309                                                 result.push.apply( result, ret );\r
6310                                         }\r
6311                                         return false;\r
6312                                 }\r
6313                         } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {\r
6314                                 return true;\r
6315                         }\r
6316                         \r
6317                         return match;\r
6318                 },\r
6319                 POS: function(match){\r
6320                         match.unshift( true );\r
6321                         return match;\r
6322                 }\r
6323         },\r
6324         filters: {\r
6325                 enabled: function(elem){\r
6326                         return elem.disabled === false && elem.type !== "hidden";\r
6327                 },\r
6328                 disabled: function(elem){\r
6329                         return elem.disabled === true;\r
6330                 },\r
6331                 checked: function(elem){\r
6332                         return elem.checked === true;\r
6333                 },\r
6334                 selected: function(elem){\r
6335                         // Accessing this property makes selected-by-default\r
6336                         // options in Safari work properly\r
6337                         elem.parentNode.selectedIndex;\r
6338                         return elem.selected === true;\r
6339                 },\r
6340                 parent: function(elem){\r
6341                         return !!elem.firstChild;\r
6342                 },\r
6343                 empty: function(elem){\r
6344                         return !elem.firstChild;\r
6345                 },\r
6346                 has: function(elem, i, match){\r
6347                         return !!Sizzle( match[3], elem ).length;\r
6348                 },\r
6349                 header: function(elem){\r
6350                         return (/h\d/i).test( elem.nodeName );\r
6351                 },\r
6352                 text: function(elem){\r
6353                         return "text" === elem.type;\r
6354                 },\r
6355                 radio: function(elem){\r
6356                         return "radio" === elem.type;\r
6357                 },\r
6358                 checkbox: function(elem){\r
6359                         return "checkbox" === elem.type;\r
6360                 },\r
6361                 file: function(elem){\r
6362                         return "file" === elem.type;\r
6363                 },\r
6364                 password: function(elem){\r
6365                         return "password" === elem.type;\r
6366                 },\r
6367                 submit: function(elem){\r
6368                         return "submit" === elem.type;\r
6369                 },\r
6370                 image: function(elem){\r
6371                         return "image" === elem.type;\r
6372                 },\r
6373                 reset: function(elem){\r
6374                         return "reset" === elem.type;\r
6375                 },\r
6376                 button: function(elem){\r
6377                         return "button" === elem.type || elem.nodeName.toLowerCase() === "button";\r
6378                 },\r
6379                 input: function(elem){\r
6380                         return (/input|select|textarea|button/i).test(elem.nodeName);\r
6381                 }\r
6382         },\r
6383         setFilters: {\r
6384                 first: function(elem, i){\r
6385                         return i === 0;\r
6386                 },\r
6387                 last: function(elem, i, match, array){\r
6388                         return i === array.length - 1;\r
6389                 },\r
6390                 even: function(elem, i){\r
6391                         return i % 2 === 0;\r
6392                 },\r
6393                 odd: function(elem, i){\r
6394                         return i % 2 === 1;\r
6395                 },\r
6396                 lt: function(elem, i, match){\r
6397                         return i < match[3] - 0;\r
6398                 },\r
6399                 gt: function(elem, i, match){\r
6400                         return i > match[3] - 0;\r
6401                 },\r
6402                 nth: function(elem, i, match){\r
6403                         return match[3] - 0 === i;\r
6404                 },\r
6405                 eq: function(elem, i, match){\r
6406                         return match[3] - 0 === i;\r
6407                 }\r
6408         },\r
6409         filter: {\r
6410                 PSEUDO: function(elem, match, i, array){\r
6411                         var name = match[1], filter = Expr.filters[ name ];\r
6412 \r
6413                         if ( filter ) {\r
6414                                 return filter( elem, i, match, array );\r
6415                         } else if ( name === "contains" ) {\r
6416                                 return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;\r
6417                         } else if ( name === "not" ) {\r
6418                                 var not = match[3];\r
6419 \r
6420                                 for ( var j = 0, l = not.length; j < l; j++ ) {\r
6421                                         if ( not[j] === elem ) {\r
6422                                                 return false;\r
6423                                         }\r
6424                                 }\r
6425 \r
6426                                 return true;\r
6427                         } else {\r
6428                                 Sizzle.error( "Syntax error, unrecognized expression: " + name );\r
6429                         }\r
6430                 },\r
6431                 CHILD: function(elem, match){\r
6432                         var type = match[1], node = elem;\r
6433                         switch (type) {\r
6434                                 case 'only':\r
6435                                 case 'first':\r
6436                                         while ( (node = node.previousSibling) )  {\r
6437                                                 if ( node.nodeType === 1 ) { \r
6438                                                         return false; \r
6439                                                 }\r
6440                                         }\r
6441                                         if ( type === "first" ) { \r
6442                                                 return true; \r
6443                                         }\r
6444                                         node = elem;\r
6445                                 case 'last':\r
6446                                         while ( (node = node.nextSibling) )      {\r
6447                                                 if ( node.nodeType === 1 ) { \r
6448                                                         return false; \r
6449                                                 }\r
6450                                         }\r
6451                                         return true;\r
6452                                 case 'nth':\r
6453                                         var first = match[2], last = match[3];\r
6454 \r
6455                                         if ( first === 1 && last === 0 ) {\r
6456                                                 return true;\r
6457                                         }\r
6458                                         \r
6459                                         var doneName = match[0],\r
6460                                                 parent = elem.parentNode;\r
6461         \r
6462                                         if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {\r
6463                                                 var count = 0;\r
6464                                                 for ( node = parent.firstChild; node; node = node.nextSibling ) {\r
6465                                                         if ( node.nodeType === 1 ) {\r
6466                                                                 node.nodeIndex = ++count;\r
6467                                                         }\r
6468                                                 } \r
6469                                                 parent.sizcache = doneName;\r
6470                                         }\r
6471                                         \r
6472                                         var diff = elem.nodeIndex - last;\r
6473                                         if ( first === 0 ) {\r
6474                                                 return diff === 0;\r
6475                                         } else {\r
6476                                                 return ( diff % first === 0 && diff / first >= 0 );\r
6477                                         }\r
6478                         }\r
6479                 },\r
6480                 ID: function(elem, match){\r
6481                         return elem.nodeType === 1 && elem.getAttribute("id") === match;\r
6482                 },\r
6483                 TAG: function(elem, match){\r
6484                         return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;\r
6485                 },\r
6486                 CLASS: function(elem, match){\r
6487                         return (" " + (elem.className || elem.getAttribute("class")) + " ")\r
6488                                 .indexOf( match ) > -1;\r
6489                 },\r
6490                 ATTR: function(elem, match){\r
6491                         var name = match[1],\r
6492                                 result = Expr.attrHandle[ name ] ?\r
6493                                         Expr.attrHandle[ name ]( elem ) :\r
6494                                         elem[ name ] != null ?\r
6495                                                 elem[ name ] :\r
6496                                                 elem.getAttribute( name ),\r
6497                                 value = result + "",\r
6498                                 type = match[2],\r
6499                                 check = match[4];\r
6500 \r
6501                         return result == null ?\r
6502                                 type === "!=" :\r
6503                                 type === "=" ?\r
6504                                 value === check :\r
6505                                 type === "*=" ?\r
6506                                 value.indexOf(check) >= 0 :\r
6507                                 type === "~=" ?\r
6508                                 (" " + value + " ").indexOf(check) >= 0 :\r
6509                                 !check ?\r
6510                                 value && result !== false :\r
6511                                 type === "!=" ?\r
6512                                 value !== check :\r
6513                                 type === "^=" ?\r
6514                                 value.indexOf(check) === 0 :\r
6515                                 type === "$=" ?\r
6516                                 value.substr(value.length - check.length) === check :\r
6517                                 type === "|=" ?\r
6518                                 value === check || value.substr(0, check.length + 1) === check + "-" :\r
6519                                 false;\r
6520                 },\r
6521                 POS: function(elem, match, i, array){\r
6522                         var name = match[2], filter = Expr.setFilters[ name ];\r
6523 \r
6524                         if ( filter ) {\r
6525                                 return filter( elem, i, match, array );\r
6526                         }\r
6527                 }\r
6528         }\r
6529 };\r
6530 \r
6531 var origPOS = Expr.match.POS,\r
6532         fescape = function(all, num){\r
6533                 return "\\" + (num - 0 + 1);\r
6534         };\r
6535 \r
6536 for ( var type in Expr.match ) {\r
6537         Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );\r
6538         Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );\r
6539 }\r
6540 \r
6541 var makeArray = function(array, results) {\r
6542         array = Array.prototype.slice.call( array, 0 );\r
6543 \r
6544         if ( results ) {\r
6545                 results.push.apply( results, array );\r
6546                 return results;\r
6547         }\r
6548         \r
6549         return array;\r
6550 };\r
6551 \r
6552 // Perform a simple check to determine if the browser is capable of\r
6553 // converting a NodeList to an array using builtin methods.\r
6554 // Also verifies that the returned array holds DOM nodes\r
6555 // (which is not the case in the Blackberry browser)\r
6556 try {\r
6557         Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;\r
6558 \r
6559 // Provide a fallback method if it does not work\r
6560 } catch(e){\r
6561         makeArray = function(array, results) {\r
6562                 var ret = results || [], i = 0;\r
6563 \r
6564                 if ( toString.call(array) === "[object Array]" ) {\r
6565                         Array.prototype.push.apply( ret, array );\r
6566                 } else {\r
6567                         if ( typeof array.length === "number" ) {\r
6568                                 for ( var l = array.length; i < l; i++ ) {\r
6569                                         ret.push( array[i] );\r
6570                                 }\r
6571                         } else {\r
6572                                 for ( ; array[i]; i++ ) {\r
6573                                         ret.push( array[i] );\r
6574                                 }\r
6575                         }\r
6576                 }\r
6577 \r
6578                 return ret;\r
6579         };\r
6580 }\r
6581 \r
6582 var sortOrder;\r
6583 \r
6584 if ( document.documentElement.compareDocumentPosition ) {\r
6585         sortOrder = function( a, b ) {\r
6586                 if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {\r
6587                         if ( a == b ) {\r
6588                                 hasDuplicate = true;\r
6589                         }\r
6590                         return a.compareDocumentPosition ? -1 : 1;\r
6591                 }\r
6592 \r
6593                 var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;\r
6594                 if ( ret === 0 ) {\r
6595                         hasDuplicate = true;\r
6596                 }\r
6597                 return ret;\r
6598         };\r
6599 } else if ( "sourceIndex" in document.documentElement ) {\r
6600         sortOrder = function( a, b ) {\r
6601                 if ( !a.sourceIndex || !b.sourceIndex ) {\r
6602                         if ( a == b ) {\r
6603                                 hasDuplicate = true;\r
6604                         }\r
6605                         return a.sourceIndex ? -1 : 1;\r
6606                 }\r
6607 \r
6608                 var ret = a.sourceIndex - b.sourceIndex;\r
6609                 if ( ret === 0 ) {\r
6610                         hasDuplicate = true;\r
6611                 }\r
6612                 return ret;\r
6613         };\r
6614 } else if ( document.createRange ) {\r
6615         sortOrder = function( a, b ) {\r
6616                 if ( !a.ownerDocument || !b.ownerDocument ) {\r
6617                         if ( a == b ) {\r
6618                                 hasDuplicate = true;\r
6619                         }\r
6620                         return a.ownerDocument ? -1 : 1;\r
6621                 }\r
6622 \r
6623                 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();\r
6624                 aRange.setStart(a, 0);\r
6625                 aRange.setEnd(a, 0);\r
6626                 bRange.setStart(b, 0);\r
6627                 bRange.setEnd(b, 0);\r
6628                 var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);\r
6629                 if ( ret === 0 ) {\r
6630                         hasDuplicate = true;\r
6631                 }\r
6632                 return ret;\r
6633         };\r
6634 }\r
6635 \r
6636 // Utility function for retreiving the text value of an array of DOM nodes\r
6637 Sizzle.getText = function( elems ) {\r
6638         var ret = "", elem;\r
6639 \r
6640         for ( var i = 0; elems[i]; i++ ) {\r
6641                 elem = elems[i];\r
6642 \r
6643                 // Get the text from text nodes and CDATA nodes\r
6644                 if ( elem.nodeType === 3 || elem.nodeType === 4 ) {\r
6645                         ret += elem.nodeValue;\r
6646 \r
6647                 // Traverse everything else, except comment nodes\r
6648                 } else if ( elem.nodeType !== 8 ) {\r
6649                         ret += Sizzle.getText( elem.childNodes );\r
6650                 }\r
6651         }\r
6652 \r
6653         return ret;\r
6654 };\r
6655 \r
6656 // Check to see if the browser returns elements by name when\r
6657 // querying by getElementById (and provide a workaround)\r
6658 (function(){\r
6659         // We're going to inject a fake input element with a specified name\r
6660         var form = document.createElement("div"),\r
6661                 id = "script" + (new Date()).getTime();\r
6662         form.innerHTML = "<a name='" + id + "'/>";\r
6663 \r
6664         // Inject it into the root element, check its status, and remove it quickly\r
6665         var root = document.documentElement;\r
6666         root.insertBefore( form, root.firstChild );\r
6667 \r
6668         // The workaround has to do additional checks after a getElementById\r
6669         // Which slows things down for other browsers (hence the branching)\r
6670         if ( document.getElementById( id ) ) {\r
6671                 Expr.find.ID = function(match, context, isXML){\r
6672                         if ( typeof context.getElementById !== "undefined" && !isXML ) {\r
6673                                 var m = context.getElementById(match[1]);\r
6674                                 return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];\r
6675                         }\r
6676                 };\r
6677 \r
6678                 Expr.filter.ID = function(elem, match){\r
6679                         var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");\r
6680                         return elem.nodeType === 1 && node && node.nodeValue === match;\r
6681                 };\r
6682         }\r
6683 \r
6684         root.removeChild( form );\r
6685         root = form = null; // release memory in IE\r
6686 })();\r
6687 \r
6688 (function(){\r
6689         // Check to see if the browser returns only elements\r
6690         // when doing getElementsByTagName("*")\r
6691 \r
6692         // Create a fake element\r
6693         var div = document.createElement("div");\r
6694         div.appendChild( document.createComment("") );\r
6695 \r
6696         // Make sure no comments are found\r
6697         if ( div.getElementsByTagName("*").length > 0 ) {\r
6698                 Expr.find.TAG = function(match, context){\r
6699                         var results = context.getElementsByTagName(match[1]);\r
6700 \r
6701                         // Filter out possible comments\r
6702                         if ( match[1] === "*" ) {\r
6703                                 var tmp = [];\r
6704 \r
6705                                 for ( var i = 0; results[i]; i++ ) {\r
6706                                         if ( results[i].nodeType === 1 ) {\r
6707                                                 tmp.push( results[i] );\r
6708                                         }\r
6709                                 }\r
6710 \r
6711                                 results = tmp;\r
6712                         }\r
6713 \r
6714                         return results;\r
6715                 };\r
6716         }\r
6717 \r
6718         // Check to see if an attribute returns normalized href attributes\r
6719         div.innerHTML = "<a href='#'></a>";\r
6720         if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&\r
6721                         div.firstChild.getAttribute("href") !== "#" ) {\r
6722                 Expr.attrHandle.href = function(elem){\r
6723                         return elem.getAttribute("href", 2);\r
6724                 };\r
6725         }\r
6726 \r
6727         div = null; // release memory in IE\r
6728 })();\r
6729 \r
6730 if ( document.querySelectorAll ) {\r
6731         (function(){\r
6732                 var oldSizzle = Sizzle, div = document.createElement("div");\r
6733                 div.innerHTML = "<p class='TEST'></p>";\r
6734 \r
6735                 // Safari can't handle uppercase or unicode characters when\r
6736                 // in quirks mode.\r
6737                 if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {\r
6738                         return;\r
6739                 }\r
6740         \r
6741                 Sizzle = function(query, context, extra, seed){\r
6742                         context = context || document;\r
6743 \r
6744                         // Only use querySelectorAll on non-XML documents\r
6745                         // (ID selectors don't work in non-HTML documents)\r
6746                         if ( !seed && context.nodeType === 9 && !Sizzle.isXML(context) ) {\r
6747                                 try {\r
6748                                         return makeArray( context.querySelectorAll(query), extra );\r
6749                                 } catch(e){}\r
6750                         }\r
6751                 \r
6752                         return oldSizzle(query, context, extra, seed);\r
6753                 };\r
6754 \r
6755                 for ( var prop in oldSizzle ) {\r
6756                         Sizzle[ prop ] = oldSizzle[ prop ];\r
6757                 }\r
6758 \r
6759                 div = null; // release memory in IE\r
6760         })();\r
6761 }\r
6762 \r
6763 (function(){\r
6764         var div = document.createElement("div");\r
6765 \r
6766         div.innerHTML = "<div class='test e'></div><div class='test'></div>";\r
6767 \r
6768         // Opera can't find a second classname (in 9.6)\r
6769         // Also, make sure that getElementsByClassName actually exists\r
6770         if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {\r
6771                 return;\r
6772         }\r
6773 \r
6774         // Safari caches class attributes, doesn't catch changes (in 3.2)\r
6775         div.lastChild.className = "e";\r
6776 \r
6777         if ( div.getElementsByClassName("e").length === 1 ) {\r
6778                 return;\r
6779         }\r
6780         \r
6781         Expr.order.splice(1, 0, "CLASS");\r
6782         Expr.find.CLASS = function(match, context, isXML) {\r
6783                 if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {\r
6784                         return context.getElementsByClassName(match[1]);\r
6785                 }\r
6786         };\r
6787 \r
6788         div = null; // release memory in IE\r
6789 })();\r
6790 \r
6791 function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\r
6792         for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
6793                 var elem = checkSet[i];\r
6794                 if ( elem ) {\r
6795                         elem = elem[dir];\r
6796                         var match = false;\r
6797 \r
6798                         while ( elem ) {\r
6799                                 if ( elem.sizcache === doneName ) {\r
6800                                         match = checkSet[elem.sizset];\r
6801                                         break;\r
6802                                 }\r
6803 \r
6804                                 if ( elem.nodeType === 1 && !isXML ){\r
6805                                         elem.sizcache = doneName;\r
6806                                         elem.sizset = i;\r
6807                                 }\r
6808 \r
6809                                 if ( elem.nodeName.toLowerCase() === cur ) {\r
6810                                         match = elem;\r
6811                                         break;\r
6812                                 }\r
6813 \r
6814                                 elem = elem[dir];\r
6815                         }\r
6816 \r
6817                         checkSet[i] = match;\r
6818                 }\r
6819         }\r
6820 }\r
6821 \r
6822 function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\r
6823         for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
6824                 var elem = checkSet[i];\r
6825                 if ( elem ) {\r
6826                         elem = elem[dir];\r
6827                         var match = false;\r
6828 \r
6829                         while ( elem ) {\r
6830                                 if ( elem.sizcache === doneName ) {\r
6831                                         match = checkSet[elem.sizset];\r
6832                                         break;\r
6833                                 }\r
6834 \r
6835                                 if ( elem.nodeType === 1 ) {\r
6836                                         if ( !isXML ) {\r
6837                                                 elem.sizcache = doneName;\r
6838                                                 elem.sizset = i;\r
6839                                         }\r
6840                                         if ( typeof cur !== "string" ) {\r
6841                                                 if ( elem === cur ) {\r
6842                                                         match = true;\r
6843                                                         break;\r
6844                                                 }\r
6845 \r
6846                                         } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {\r
6847                                                 match = elem;\r
6848                                                 break;\r
6849                                         }\r
6850                                 }\r
6851 \r
6852                                 elem = elem[dir];\r
6853                         }\r
6854 \r
6855                         checkSet[i] = match;\r
6856                 }\r
6857         }\r
6858 }\r
6859 \r
6860 Sizzle.contains = document.compareDocumentPosition ? function(a, b){\r
6861         return !!(a.compareDocumentPosition(b) & 16);\r
6862 } : function(a, b){\r
6863         return a !== b && (a.contains ? a.contains(b) : true);\r
6864 };\r
6865 \r
6866 Sizzle.isXML = function(elem){\r
6867         // documentElement is verified for cases where it doesn't yet exist\r
6868         // (such as loading iframes in IE - #4833) \r
6869         var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;\r
6870         return documentElement ? documentElement.nodeName !== "HTML" : false;\r
6871 };\r
6872 \r
6873 var posProcess = function(selector, context){\r
6874         var tmpSet = [], later = "", match,\r
6875                 root = context.nodeType ? [context] : context;\r
6876 \r
6877         // Position selectors must be done after the filter\r
6878         // And so must :not(positional) so we move all PSEUDOs to the end\r
6879         while ( (match = Expr.match.PSEUDO.exec( selector )) ) {\r
6880                 later += match[0];\r
6881                 selector = selector.replace( Expr.match.PSEUDO, "" );\r
6882         }\r
6883 \r
6884         selector = Expr.relative[selector] ? selector + "*" : selector;\r
6885 \r
6886         for ( var i = 0, l = root.length; i < l; i++ ) {\r
6887                 Sizzle( selector, root[i], tmpSet );\r
6888         }\r
6889 \r
6890         return Sizzle.filter( later, tmpSet );\r
6891 };\r
6892 \r
6893 // EXPOSE\r
6894 \r
6895 window.tinymce.dom.Sizzle = Sizzle;\r
6896 \r
6897 })();\r
6898 \r
6899 \r
6900 (function(tinymce) {\r
6901         // Shorten names\r
6902         var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event;\r
6903 \r
6904         tinymce.create('tinymce.dom.EventUtils', {\r
6905                 EventUtils : function() {\r
6906                         this.inits = [];\r
6907                         this.events = [];\r
6908                 },\r
6909 \r
6910                 add : function(o, n, f, s) {\r
6911                         var cb, t = this, el = t.events, r;\r
6912 \r
6913                         if (n instanceof Array) {\r
6914                                 r = [];\r
6915 \r
6916                                 each(n, function(n) {\r
6917                                         r.push(t.add(o, n, f, s));\r
6918                                 });\r
6919 \r
6920                                 return r;\r
6921                         }\r
6922 \r
6923                         // Handle array\r
6924                         if (o && o.hasOwnProperty && o instanceof Array) {\r
6925                                 r = [];\r
6926 \r
6927                                 each(o, function(o) {\r
6928                                         o = DOM.get(o);\r
6929                                         r.push(t.add(o, n, f, s));\r
6930                                 });\r
6931 \r
6932                                 return r;\r
6933                         }\r
6934 \r
6935                         o = DOM.get(o);\r
6936 \r
6937                         if (!o)\r
6938                                 return;\r
6939 \r
6940                         // Setup event callback\r
6941                         cb = function(e) {\r
6942                                 // Is all events disabled\r
6943                                 if (t.disabled)\r
6944                                         return;\r
6945 \r
6946                                 e = e || window.event;\r
6947 \r
6948                                 // Patch in target, preventDefault and stopPropagation in IE it's W3C valid\r
6949                                 if (e && isIE) {\r
6950                                         if (!e.target)\r
6951                                                 e.target = e.srcElement;\r
6952 \r
6953                                         // Patch in preventDefault, stopPropagation methods for W3C compatibility\r
6954                                         tinymce.extend(e, t._stoppers);\r
6955                                 }\r
6956 \r
6957                                 if (!s)\r
6958                                         return f(e);\r
6959 \r
6960                                 return f.call(s, e);\r
6961                         };\r
6962 \r
6963                         if (n == 'unload') {\r
6964                                 tinymce.unloads.unshift({func : cb});\r
6965                                 return cb;\r
6966                         }\r
6967 \r
6968                         if (n == 'init') {\r
6969                                 if (t.domLoaded)\r
6970                                         cb();\r
6971                                 else\r
6972                                         t.inits.push(cb);\r
6973 \r
6974                                 return cb;\r
6975                         }\r
6976 \r
6977                         // Store away listener reference\r
6978                         el.push({\r
6979                                 obj : o,\r
6980                                 name : n,\r
6981                                 func : f,\r
6982                                 cfunc : cb,\r
6983                                 scope : s\r
6984                         });\r
6985 \r
6986                         t._add(o, n, cb);\r
6987 \r
6988                         return f;\r
6989                 },\r
6990 \r
6991                 remove : function(o, n, f) {\r
6992                         var t = this, a = t.events, s = false, r;\r
6993 \r
6994                         // Handle array\r
6995                         if (o && o.hasOwnProperty && o instanceof Array) {\r
6996                                 r = [];\r
6997 \r
6998                                 each(o, function(o) {\r
6999                                         o = DOM.get(o);\r
7000                                         r.push(t.remove(o, n, f));\r
7001                                 });\r
7002 \r
7003                                 return r;\r
7004                         }\r
7005 \r
7006                         o = DOM.get(o);\r
7007 \r
7008                         each(a, function(e, i) {\r
7009                                 if (e.obj == o && e.name == n && (!f || (e.func == f || e.cfunc == f))) {\r
7010                                         a.splice(i, 1);\r
7011                                         t._remove(o, n, e.cfunc);\r
7012                                         s = true;\r
7013                                         return false;\r
7014                                 }\r
7015                         });\r
7016 \r
7017                         return s;\r
7018                 },\r
7019 \r
7020                 clear : function(o) {\r
7021                         var t = this, a = t.events, i, e;\r
7022 \r
7023                         if (o) {\r
7024                                 o = DOM.get(o);\r
7025 \r
7026                                 for (i = a.length - 1; i >= 0; i--) {\r
7027                                         e = a[i];\r
7028 \r
7029                                         if (e.obj === o) {\r
7030                                                 t._remove(e.obj, e.name, e.cfunc);\r
7031                                                 e.obj = e.cfunc = null;\r
7032                                                 a.splice(i, 1);\r
7033                                         }\r
7034                                 }\r
7035                         }\r
7036                 },\r
7037 \r
7038                 cancel : function(e) {\r
7039                         if (!e)\r
7040                                 return false;\r
7041 \r
7042                         this.stop(e);\r
7043 \r
7044                         return this.prevent(e);\r
7045                 },\r
7046 \r
7047                 stop : function(e) {\r
7048                         if (e.stopPropagation)\r
7049                                 e.stopPropagation();\r
7050                         else\r
7051                                 e.cancelBubble = true;\r
7052 \r
7053                         return false;\r
7054                 },\r
7055 \r
7056                 prevent : function(e) {\r
7057                         if (e.preventDefault)\r
7058                                 e.preventDefault();\r
7059                         else\r
7060                                 e.returnValue = false;\r
7061 \r
7062                         return false;\r
7063                 },\r
7064 \r
7065                 destroy : function() {\r
7066                         var t = this;\r
7067 \r
7068                         each(t.events, function(e, i) {\r
7069                                 t._remove(e.obj, e.name, e.cfunc);\r
7070                                 e.obj = e.cfunc = null;\r
7071                         });\r
7072 \r
7073                         t.events = [];\r
7074                         t = null;\r
7075                 },\r
7076 \r
7077                 _add : function(o, n, f) {\r
7078                         if (o.attachEvent)\r
7079                                 o.attachEvent('on' + n, f);\r
7080                         else if (o.addEventListener)\r
7081                                 o.addEventListener(n, f, false);\r
7082                         else\r
7083                                 o['on' + n] = f;\r
7084                 },\r
7085 \r
7086                 _remove : function(o, n, f) {\r
7087                         if (o) {\r
7088                                 try {\r
7089                                         if (o.detachEvent)\r
7090                                                 o.detachEvent('on' + n, f);\r
7091                                         else if (o.removeEventListener)\r
7092                                                 o.removeEventListener(n, f, false);\r
7093                                         else\r
7094                                                 o['on' + n] = null;\r
7095                                 } catch (ex) {\r
7096                                         // Might fail with permission denined on IE so we just ignore that\r
7097                                 }\r
7098                         }\r
7099                 },\r
7100 \r
7101                 _pageInit : function(win) {\r
7102                         var t = this;\r
7103 \r
7104                         // Keep it from running more than once\r
7105                         if (t.domLoaded)\r
7106                                 return;\r
7107 \r
7108                         t.domLoaded = true;\r
7109 \r
7110                         each(t.inits, function(c) {\r
7111                                 c();\r
7112                         });\r
7113 \r
7114                         t.inits = [];\r
7115                 },\r
7116 \r
7117                 _wait : function(win) {\r
7118                         var t = this, doc = win.document;\r
7119 \r
7120                         // No need since the document is already loaded\r
7121                         if (win.tinyMCE_GZ && tinyMCE_GZ.loaded) {\r
7122                                 t.domLoaded = 1;\r
7123                                 return;\r
7124                         }\r
7125 \r
7126                         // Use IE method\r
7127                         if (doc.attachEvent) {\r
7128                                 doc.attachEvent("onreadystatechange", function() {\r
7129                                         if (doc.readyState === "complete") {\r
7130                                                 doc.detachEvent("onreadystatechange", arguments.callee);\r
7131                                                 t._pageInit(win);\r
7132                                         }\r
7133                                 });\r
7134 \r
7135                                 if (doc.documentElement.doScroll && win == win.top) {\r
7136                                         (function() {\r
7137                                                 if (t.domLoaded)\r
7138                                                         return;\r
7139 \r
7140                                                 try {\r
7141                                                         // If IE is used, use the trick by Diego Perini licensed under MIT by request to the author.\r
7142                                                         // http://javascript.nwbox.com/IEContentLoaded/\r
7143                                                         doc.documentElement.doScroll("left");\r
7144                                                 } catch (ex) {\r
7145                                                         setTimeout(arguments.callee, 0);\r
7146                                                         return;\r
7147                                                 }\r
7148 \r
7149                                                 t._pageInit(win);\r
7150                                         })();\r
7151                                 }\r
7152                         } else if (doc.addEventListener) {\r
7153                                 t._add(win, 'DOMContentLoaded', function() {\r
7154                                         t._pageInit(win);\r
7155                                 });\r
7156                         }\r
7157 \r
7158                         t._add(win, 'load', function() {\r
7159                                 t._pageInit(win);\r
7160                         });\r
7161                 },\r
7162 \r
7163                 _stoppers : {\r
7164                         preventDefault : function() {\r
7165                                 this.returnValue = false;\r
7166                         },\r
7167 \r
7168                         stopPropagation : function() {\r
7169                                 this.cancelBubble = true;\r
7170                         }\r
7171                 }\r
7172         });\r
7173 \r
7174         Event = tinymce.dom.Event = new tinymce.dom.EventUtils();\r
7175 \r
7176         // Dispatch DOM content loaded event for IE and Safari\r
7177         Event._wait(window);\r
7178 \r
7179         tinymce.addUnload(function() {\r
7180                 Event.destroy();\r
7181         });\r
7182 })(tinymce);\r
7183 \r
7184 (function(tinymce) {\r
7185         tinymce.dom.Element = function(id, settings) {\r
7186                 var t = this, dom, el;\r
7187 \r
7188                 t.settings = settings = settings || {};\r
7189                 t.id = id;\r
7190                 t.dom = dom = settings.dom || tinymce.DOM;\r
7191 \r
7192                 // Only IE leaks DOM references, this is a lot faster\r
7193                 if (!tinymce.isIE)\r
7194                         el = dom.get(t.id);\r
7195 \r
7196                 tinymce.each(\r
7197                                 ('getPos,getRect,getParent,add,setStyle,getStyle,setStyles,' + \r
7198                                 'setAttrib,setAttribs,getAttrib,addClass,removeClass,' + \r
7199                                 'hasClass,getOuterHTML,setOuterHTML,remove,show,hide,' + \r
7200                                 'isHidden,setHTML,get').split(/,/)\r
7201                         , function(k) {\r
7202                                 t[k] = function() {\r
7203                                         var a = [id], i;\r
7204 \r
7205                                         for (i = 0; i < arguments.length; i++)\r
7206                                                 a.push(arguments[i]);\r
7207 \r
7208                                         a = dom[k].apply(dom, a);\r
7209                                         t.update(k);\r
7210 \r
7211                                         return a;\r
7212                                 };\r
7213                 });\r
7214 \r
7215                 tinymce.extend(t, {\r
7216                         on : function(n, f, s) {\r
7217                                 return tinymce.dom.Event.add(t.id, n, f, s);\r
7218                         },\r
7219 \r
7220                         getXY : function() {\r
7221                                 return {\r
7222                                         x : parseInt(t.getStyle('left')),\r
7223                                         y : parseInt(t.getStyle('top'))\r
7224                                 };\r
7225                         },\r
7226 \r
7227                         getSize : function() {\r
7228                                 var n = dom.get(t.id);\r
7229 \r
7230                                 return {\r
7231                                         w : parseInt(t.getStyle('width') || n.clientWidth),\r
7232                                         h : parseInt(t.getStyle('height') || n.clientHeight)\r
7233                                 };\r
7234                         },\r
7235 \r
7236                         moveTo : function(x, y) {\r
7237                                 t.setStyles({left : x, top : y});\r
7238                         },\r
7239 \r
7240                         moveBy : function(x, y) {\r
7241                                 var p = t.getXY();\r
7242 \r
7243                                 t.moveTo(p.x + x, p.y + y);\r
7244                         },\r
7245 \r
7246                         resizeTo : function(w, h) {\r
7247                                 t.setStyles({width : w, height : h});\r
7248                         },\r
7249 \r
7250                         resizeBy : function(w, h) {\r
7251                                 var s = t.getSize();\r
7252 \r
7253                                 t.resizeTo(s.w + w, s.h + h);\r
7254                         },\r
7255 \r
7256                         update : function(k) {\r
7257                                 var b;\r
7258 \r
7259                                 if (tinymce.isIE6 && settings.blocker) {\r
7260                                         k = k || '';\r
7261 \r
7262                                         // Ignore getters\r
7263                                         if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0)\r
7264                                                 return;\r
7265 \r
7266                                         // Remove blocker on remove\r
7267                                         if (k == 'remove') {\r
7268                                                 dom.remove(t.blocker);\r
7269                                                 return;\r
7270                                         }\r
7271 \r
7272                                         if (!t.blocker) {\r
7273                                                 t.blocker = dom.uniqueId();\r
7274                                                 b = dom.add(settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'});\r
7275                                                 dom.setStyle(b, 'opacity', 0);\r
7276                                         } else\r
7277                                                 b = dom.get(t.blocker);\r
7278 \r
7279                                         dom.setStyles(b, {\r
7280                                                 left : t.getStyle('left', 1),\r
7281                                                 top : t.getStyle('top', 1),\r
7282                                                 width : t.getStyle('width', 1),\r
7283                                                 height : t.getStyle('height', 1),\r
7284                                                 display : t.getStyle('display', 1),\r
7285                                                 zIndex : parseInt(t.getStyle('zIndex', 1) || 0) - 1\r
7286                                         });\r
7287                                 }\r
7288                         }\r
7289                 });\r
7290         };\r
7291 })(tinymce);\r
7292 \r
7293 (function(tinymce) {\r
7294         function trimNl(s) {\r
7295                 return s.replace(/[\n\r]+/g, '');\r
7296         };\r
7297 \r
7298         // Shorten names\r
7299         var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each;\r
7300 \r
7301         tinymce.create('tinymce.dom.Selection', {\r
7302                 Selection : function(dom, win, serializer) {\r
7303                         var t = this;\r
7304 \r
7305                         t.dom = dom;\r
7306                         t.win = win;\r
7307                         t.serializer = serializer;\r
7308 \r
7309                         // Add events\r
7310                         each([\r
7311                                 'onBeforeSetContent',\r
7312 \r
7313                                 'onBeforeGetContent',\r
7314 \r
7315                                 'onSetContent',\r
7316 \r
7317                                 'onGetContent'\r
7318                         ], function(e) {\r
7319                                 t[e] = new tinymce.util.Dispatcher(t);\r
7320                         });\r
7321 \r
7322                         // No W3C Range support\r
7323                         if (!t.win.getSelection)\r
7324                                 t.tridentSel = new tinymce.dom.TridentSelection(t);\r
7325 \r
7326                         if (tinymce.isIE && dom.boxModel)\r
7327                                 this._fixIESelection();\r
7328 \r
7329                         // Prevent leaks\r
7330                         tinymce.addUnload(t.destroy, t);\r
7331                 },\r
7332 \r
7333                 setCursorLocation: function(node, offset) {\r
7334                         var t = this; var r = t.dom.createRng();\r
7335                         r.setStart(node, offset);\r
7336                         r.setEnd(node, offset);\r
7337                         t.setRng(r);\r
7338                         t.collapse(false);\r
7339                 },\r
7340                 getContent : function(s) {\r
7341                         var t = this, r = t.getRng(), e = t.dom.create("body"), se = t.getSel(), wb, wa, n;\r
7342 \r
7343                         s = s || {};\r
7344                         wb = wa = '';\r
7345                         s.get = true;\r
7346                         s.format = s.format || 'html';\r
7347                         s.forced_root_block = '';\r
7348                         t.onBeforeGetContent.dispatch(t, s);\r
7349 \r
7350                         if (s.format == 'text')\r
7351                                 return t.isCollapsed() ? '' : (r.text || (se.toString ? se.toString() : ''));\r
7352 \r
7353                         if (r.cloneContents) {\r
7354                                 n = r.cloneContents();\r
7355 \r
7356                                 if (n)\r
7357                                         e.appendChild(n);\r
7358                         } else if (is(r.item) || is(r.htmlText)) {\r
7359                                 // IE will produce invalid markup if elements are present that\r
7360                                 // it doesn't understand like custom elements or HTML5 elements.\r
7361                                 // Adding a BR in front of the contents and then remoiving it seems to fix it though.\r
7362                                 e.innerHTML = '<br>' + (r.item ? r.item(0).outerHTML : r.htmlText);\r
7363                                 e.removeChild(e.firstChild);\r
7364                         } else\r
7365                                 e.innerHTML = r.toString();\r
7366 \r
7367                         // Keep whitespace before and after\r
7368                         if (/^\s/.test(e.innerHTML))\r
7369                                 wb = ' ';\r
7370 \r
7371                         if (/\s+$/.test(e.innerHTML))\r
7372                                 wa = ' ';\r
7373 \r
7374                         s.getInner = true;\r
7375 \r
7376                         s.content = t.isCollapsed() ? '' : wb + t.serializer.serialize(e, s) + wa;\r
7377                         t.onGetContent.dispatch(t, s);\r
7378 \r
7379                         return s.content;\r
7380                 },\r
7381 \r
7382                 setContent : function(content, args) {\r
7383                         var self = this, rng = self.getRng(), caretNode, doc = self.win.document, frag, temp;\r
7384 \r
7385                         args = args || {format : 'html'};\r
7386                         args.set = true;\r
7387                         content = args.content = content;\r
7388 \r
7389                         // Dispatch before set content event\r
7390                         if (!args.no_events)\r
7391                                 self.onBeforeSetContent.dispatch(self, args);\r
7392 \r
7393                         content = args.content;\r
7394 \r
7395                         if (rng.insertNode) {\r
7396                                 // Make caret marker since insertNode places the caret in the beginning of text after insert\r
7397                                 content += '<span id="__caret">_</span>';\r
7398 \r
7399                                 // Delete and insert new node\r
7400                                 if (rng.startContainer == doc && rng.endContainer == doc) {\r
7401                                         // WebKit will fail if the body is empty since the range is then invalid and it can't insert contents\r
7402                                         doc.body.innerHTML = content;\r
7403                                 } else {\r
7404                                         rng.deleteContents();\r
7405 \r
7406                                         if (doc.body.childNodes.length == 0) {\r
7407                                                 doc.body.innerHTML = content;\r
7408                                         } else {\r
7409                                                 // createContextualFragment doesn't exists in IE 9 DOMRanges\r
7410                                                 if (rng.createContextualFragment) {\r
7411                                                         rng.insertNode(rng.createContextualFragment(content));\r
7412                                                 } else {\r
7413                                                         // Fake createContextualFragment call in IE 9\r
7414                                                         frag = doc.createDocumentFragment();\r
7415                                                         temp = doc.createElement('div');\r
7416 \r
7417                                                         frag.appendChild(temp);\r
7418                                                         temp.outerHTML = content;\r
7419 \r
7420                                                         rng.insertNode(frag);\r
7421                                                 }\r
7422                                         }\r
7423                                 }\r
7424 \r
7425                                 // Move to caret marker\r
7426                                 caretNode = self.dom.get('__caret');\r
7427 \r
7428                                 // Make sure we wrap it compleatly, Opera fails with a simple select call\r
7429                                 rng = doc.createRange();\r
7430                                 rng.setStartBefore(caretNode);\r
7431                                 rng.setEndBefore(caretNode);\r
7432                                 self.setRng(rng);\r
7433 \r
7434                                 // Remove the caret position\r
7435                                 self.dom.remove('__caret');\r
7436 \r
7437                                 try {\r
7438                                         self.setRng(rng);\r
7439                                 } catch (ex) {\r
7440                                         // Might fail on Opera for some odd reason\r
7441                                 }\r
7442                         } else {\r
7443                                 if (rng.item) {\r
7444                                         // Delete content and get caret text selection\r
7445                                         doc.execCommand('Delete', false, null);\r
7446                                         rng = self.getRng();\r
7447                                 }\r
7448 \r
7449                                 // Explorer removes spaces from the beginning of pasted contents\r
7450                                 if (/^\s+/.test(content)) {\r
7451                                         rng.pasteHTML('<span id="__mce_tmp">_</span>' + content);\r
7452                                         self.dom.remove('__mce_tmp');\r
7453                                 } else\r
7454                                         rng.pasteHTML(content);\r
7455                         }\r
7456 \r
7457                         // Dispatch set content event\r
7458                         if (!args.no_events)\r
7459                                 self.onSetContent.dispatch(self, args);\r
7460                 },\r
7461 \r
7462                 getStart : function() {\r
7463                         var rng = this.getRng(), startElement, parentElement, checkRng, node;\r
7464 \r
7465                         if (rng.duplicate || rng.item) {\r
7466                                 // Control selection, return first item\r
7467                                 if (rng.item)\r
7468                                         return rng.item(0);\r
7469 \r
7470                                 // Get start element\r
7471                                 checkRng = rng.duplicate();\r
7472                                 checkRng.collapse(1);\r
7473                                 startElement = checkRng.parentElement();\r
7474 \r
7475                                 // Check if range parent is inside the start element, then return the inner parent element\r
7476                                 // This will fix issues when a single element is selected, IE would otherwise return the wrong start element\r
7477                                 parentElement = node = rng.parentElement();\r
7478                                 while (node = node.parentNode) {\r
7479                                         if (node == startElement) {\r
7480                                                 startElement = parentElement;\r
7481                                                 break;\r
7482                                         }\r
7483                                 }\r
7484 \r
7485                                 return startElement;\r
7486                         } else {\r
7487                                 startElement = rng.startContainer;\r
7488 \r
7489                                 if (startElement.nodeType == 1 && startElement.hasChildNodes())\r
7490                                         startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];\r
7491 \r
7492                                 if (startElement && startElement.nodeType == 3)\r
7493                                         return startElement.parentNode;\r
7494 \r
7495                                 return startElement;\r
7496                         }\r
7497                 },\r
7498 \r
7499                 getEnd : function() {\r
7500                         var t = this, r = t.getRng(), e, eo;\r
7501 \r
7502                         if (r.duplicate || r.item) {\r
7503                                 if (r.item)\r
7504                                         return r.item(0);\r
7505 \r
7506                                 r = r.duplicate();\r
7507                                 r.collapse(0);\r
7508                                 e = r.parentElement();\r
7509 \r
7510                                 if (e && e.nodeName == 'BODY')\r
7511                                         return e.lastChild || e;\r
7512 \r
7513                                 return e;\r
7514                         } else {\r
7515                                 e = r.endContainer;\r
7516                                 eo = r.endOffset;\r
7517 \r
7518                                 if (e.nodeType == 1 && e.hasChildNodes())\r
7519                                         e = e.childNodes[eo > 0 ? eo - 1 : eo];\r
7520 \r
7521                                 if (e && e.nodeType == 3)\r
7522                                         return e.parentNode;\r
7523 \r
7524                                 return e;\r
7525                         }\r
7526                 },\r
7527 \r
7528                 getBookmark : function(type, normalized) {\r
7529                         var t = this, dom = t.dom, rng, rng2, id, collapsed, name, element, index, chr = '\uFEFF', styles;\r
7530 \r
7531                         function findIndex(name, element) {\r
7532                                 var index = 0;\r
7533 \r
7534                                 each(dom.select(name), function(node, i) {\r
7535                                         if (node == element)\r
7536                                                 index = i;\r
7537                                 });\r
7538 \r
7539                                 return index;\r
7540                         };\r
7541 \r
7542                         if (type == 2) {\r
7543                                 function getLocation() {\r
7544                                         var rng = t.getRng(true), root = dom.getRoot(), bookmark = {};\r
7545 \r
7546                                         function getPoint(rng, start) {\r
7547                                                 var container = rng[start ? 'startContainer' : 'endContainer'],\r
7548                                                         offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;\r
7549 \r
7550                                                 if (container.nodeType == 3) {\r
7551                                                         if (normalized) {\r
7552                                                                 for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling)\r
7553                                                                         offset += node.nodeValue.length;\r
7554                                                         }\r
7555 \r
7556                                                         point.push(offset);\r
7557                                                 } else {\r
7558                                                         childNodes = container.childNodes;\r
7559 \r
7560                                                         if (offset >= childNodes.length && childNodes.length) {\r
7561                                                                 after = 1;\r
7562                                                                 offset = Math.max(0, childNodes.length - 1);\r
7563                                                         }\r
7564 \r
7565                                                         point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after);\r
7566                                                 }\r
7567 \r
7568                                                 for (; container && container != root; container = container.parentNode)\r
7569                                                         point.push(t.dom.nodeIndex(container, normalized));\r
7570 \r
7571                                                 return point;\r
7572                                         };\r
7573 \r
7574                                         bookmark.start = getPoint(rng, true);\r
7575 \r
7576                                         if (!t.isCollapsed())\r
7577                                                 bookmark.end = getPoint(rng);\r
7578 \r
7579                                         return bookmark;\r
7580                                 };\r
7581 \r
7582                                 if (t.tridentSel)\r
7583                                         return t.tridentSel.getBookmark(type);\r
7584 \r
7585                                 return getLocation();\r
7586                         }\r
7587 \r
7588                         // Handle simple range\r
7589                         if (type)\r
7590                                 return {rng : t.getRng()};\r
7591 \r
7592                         rng = t.getRng();\r
7593                         id = dom.uniqueId();\r
7594                         collapsed = tinyMCE.activeEditor.selection.isCollapsed();\r
7595                         styles = 'overflow:hidden;line-height:0px';\r
7596 \r
7597                         // Explorer method\r
7598                         if (rng.duplicate || rng.item) {\r
7599                                 // Text selection\r
7600                                 if (!rng.item) {\r
7601                                         rng2 = rng.duplicate();\r
7602 \r
7603                                         try {\r
7604                                                 // Insert start marker\r
7605                                                 rng.collapse();\r
7606                                                 rng.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');\r
7607 \r
7608                                                 // Insert end marker\r
7609                                                 if (!collapsed) {\r
7610                                                         rng2.collapse(false);\r
7611 \r
7612                                                         // Detect the empty space after block elements in IE and move the end back one character <p></p>] becomes <p>]</p>\r
7613                                                         rng.moveToElementText(rng2.parentElement());\r
7614                                                         if (rng.compareEndPoints('StartToEnd', rng2) == 0)\r
7615                                                                 rng2.move('character', -1);\r
7616 \r
7617                                                         rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');\r
7618                                                 }\r
7619                                         } catch (ex) {\r
7620                                                 // IE might throw unspecified error so lets ignore it\r
7621                                                 return null;\r
7622                                         }\r
7623                                 } else {\r
7624                                         // Control selection\r
7625                                         element = rng.item(0);\r
7626                                         name = element.nodeName;\r
7627 \r
7628                                         return {name : name, index : findIndex(name, element)};\r
7629                                 }\r
7630                         } else {\r
7631                                 element = t.getNode();\r
7632                                 name = element.nodeName;\r
7633                                 if (name == 'IMG')\r
7634                                         return {name : name, index : findIndex(name, element)};\r
7635 \r
7636                                 // W3C method\r
7637                                 rng2 = rng.cloneRange();\r
7638 \r
7639                                 // Insert end marker\r
7640                                 if (!collapsed) {\r
7641                                         rng2.collapse(false);\r
7642                                         rng2.insertNode(dom.create('span', {'data-mce-type' : "bookmark", id : id + '_end', style : styles}, chr));\r
7643                                 }\r
7644 \r
7645                                 rng.collapse(true);\r
7646                                 rng.insertNode(dom.create('span', {'data-mce-type' : "bookmark", id : id + '_start', style : styles}, chr));\r
7647                         }\r
7648 \r
7649                         t.moveToBookmark({id : id, keep : 1});\r
7650 \r
7651                         return {id : id};\r
7652                 },\r
7653 \r
7654                 moveToBookmark : function(bookmark) {\r
7655                         var t = this, dom = t.dom, marker1, marker2, rng, root, startContainer, endContainer, startOffset, endOffset;\r
7656 \r
7657                         if (bookmark) {\r
7658                                 if (bookmark.start) {\r
7659                                         rng = dom.createRng();\r
7660                                         root = dom.getRoot();\r
7661 \r
7662                                         function setEndPoint(start) {\r
7663                                                 var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;\r
7664 \r
7665                                                 if (point) {\r
7666                                                         offset = point[0];\r
7667 \r
7668                                                         // Find container node\r
7669                                                         for (node = root, i = point.length - 1; i >= 1; i--) {\r
7670                                                                 children = node.childNodes;\r
7671 \r
7672                                                                 if (point[i] > children.length - 1)\r
7673                                                                         return;\r
7674 \r
7675                                                                 node = children[point[i]];\r
7676                                                         }\r
7677 \r
7678                                                         // Move text offset to best suitable location\r
7679                                                         if (node.nodeType === 3)\r
7680                                                                 offset = Math.min(point[0], node.nodeValue.length);\r
7681 \r
7682                                                         // Move element offset to best suitable location\r
7683                                                         if (node.nodeType === 1)\r
7684                                                                 offset = Math.min(point[0], node.childNodes.length);\r
7685 \r
7686                                                         // Set offset within container node\r
7687                                                         if (start)\r
7688                                                                 rng.setStart(node, offset);\r
7689                                                         else\r
7690                                                                 rng.setEnd(node, offset);\r
7691                                                 }\r
7692 \r
7693                                                 return true;\r
7694                                         };\r
7695 \r
7696                                         if (t.tridentSel)\r
7697                                                 return t.tridentSel.moveToBookmark(bookmark);\r
7698 \r
7699                                         if (setEndPoint(true) && setEndPoint()) {\r
7700                                                 t.setRng(rng);\r
7701                                         }\r
7702                                 } else if (bookmark.id) {\r
7703                                         function restoreEndPoint(suffix) {\r
7704                                                 var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;\r
7705 \r
7706                                                 if (marker) {\r
7707                                                         node = marker.parentNode;\r
7708 \r
7709                                                         if (suffix == 'start') {\r
7710                                                                 if (!keep) {\r
7711                                                                         idx = dom.nodeIndex(marker);\r
7712                                                                 } else {\r
7713                                                                         node = marker.firstChild;\r
7714                                                                         idx = 1;\r
7715                                                                 }\r
7716 \r
7717                                                                 startContainer = endContainer = node;\r
7718                                                                 startOffset = endOffset = idx;\r
7719                                                         } else {\r
7720                                                                 if (!keep) {\r
7721                                                                         idx = dom.nodeIndex(marker);\r
7722                                                                 } else {\r
7723                                                                         node = marker.firstChild;\r
7724                                                                         idx = 1;\r
7725                                                                 }\r
7726 \r
7727                                                                 endContainer = node;\r
7728                                                                 endOffset = idx;\r
7729                                                         }\r
7730 \r
7731                                                         if (!keep) {\r
7732                                                                 prev = marker.previousSibling;\r
7733                                                                 next = marker.nextSibling;\r
7734 \r
7735                                                                 // Remove all marker text nodes\r
7736                                                                 each(tinymce.grep(marker.childNodes), function(node) {\r
7737                                                                         if (node.nodeType == 3)\r
7738                                                                                 node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');\r
7739                                                                 });\r
7740 \r
7741                                                                 // Remove marker but keep children if for example contents where inserted into the marker\r
7742                                                                 // Also remove duplicated instances of the marker for example by a split operation or by WebKit auto split on paste feature\r
7743                                                                 while (marker = dom.get(bookmark.id + '_' + suffix))\r
7744                                                                         dom.remove(marker, 1);\r
7745 \r
7746                                                                 // If siblings are text nodes then merge them unless it's Opera since it some how removes the node\r
7747                                                                 // 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
7748                                                                 if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !tinymce.isOpera) {\r
7749                                                                         idx = prev.nodeValue.length;\r
7750                                                                         prev.appendData(next.nodeValue);\r
7751                                                                         dom.remove(next);\r
7752 \r
7753                                                                         if (suffix == 'start') {\r
7754                                                                                 startContainer = endContainer = prev;\r
7755                                                                                 startOffset = endOffset = idx;\r
7756                                                                         } else {\r
7757                                                                                 endContainer = prev;\r
7758                                                                                 endOffset = idx;\r
7759                                                                         }\r
7760                                                                 }\r
7761                                                         }\r
7762                                                 }\r
7763                                         };\r
7764 \r
7765                                         function addBogus(node) {\r
7766                                                 // Adds a bogus BR element for empty block elements or just a space on IE since it renders BR elements incorrectly\r
7767                                                 if (dom.isBlock(node) && !node.innerHTML)\r
7768                                                         node.innerHTML = !isIE ? '<br data-mce-bogus="1" />' : ' ';\r
7769 \r
7770                                                 return node;\r
7771                                         };\r
7772 \r
7773                                         // Restore start/end points\r
7774                                         restoreEndPoint('start');\r
7775                                         restoreEndPoint('end');\r
7776 \r
7777                                         if (startContainer) {\r
7778                                                 rng = dom.createRng();\r
7779                                                 rng.setStart(addBogus(startContainer), startOffset);\r
7780                                                 rng.setEnd(addBogus(endContainer), endOffset);\r
7781                                                 t.setRng(rng);\r
7782                                         }\r
7783                                 } else if (bookmark.name) {\r
7784                                         t.select(dom.select(bookmark.name)[bookmark.index]);\r
7785                                 } else if (bookmark.rng)\r
7786                                         t.setRng(bookmark.rng);\r
7787                         }\r
7788                 },\r
7789 \r
7790                 select : function(node, content) {\r
7791                         var t = this, dom = t.dom, rng = dom.createRng(), idx;\r
7792 \r
7793                         if (node) {\r
7794                                 idx = dom.nodeIndex(node);\r
7795                                 rng.setStart(node.parentNode, idx);\r
7796                                 rng.setEnd(node.parentNode, idx + 1);\r
7797 \r
7798                                 // Find first/last text node or BR element\r
7799                                 if (content) {\r
7800                                         function setPoint(node, start) {\r
7801                                                 var walker = new tinymce.dom.TreeWalker(node, node);\r
7802 \r
7803                                                 do {\r
7804                                                         // Text node\r
7805                                                         if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length != 0) {\r
7806                                                                 if (start)\r
7807                                                                         rng.setStart(node, 0);\r
7808                                                                 else\r
7809                                                                         rng.setEnd(node, node.nodeValue.length);\r
7810 \r
7811                                                                 return;\r
7812                                                         }\r
7813 \r
7814                                                         // BR element\r
7815                                                         if (node.nodeName == 'BR') {\r
7816                                                                 if (start)\r
7817                                                                         rng.setStartBefore(node);\r
7818                                                                 else\r
7819                                                                         rng.setEndBefore(node);\r
7820 \r
7821                                                                 return;\r
7822                                                         }\r
7823                                                 } while (node = (start ? walker.next() : walker.prev()));\r
7824                                         };\r
7825 \r
7826                                         setPoint(node, 1);\r
7827                                         setPoint(node);\r
7828                                 }\r
7829 \r
7830                                 t.setRng(rng);\r
7831                         }\r
7832 \r
7833                         return node;\r
7834                 },\r
7835 \r
7836                 isCollapsed : function() {\r
7837                         var t = this, r = t.getRng(), s = t.getSel();\r
7838 \r
7839                         if (!r || r.item)\r
7840                                 return false;\r
7841 \r
7842                         if (r.compareEndPoints)\r
7843                                 return r.compareEndPoints('StartToEnd', r) === 0;\r
7844 \r
7845                         return !s || r.collapsed;\r
7846                 },\r
7847 \r
7848                 collapse : function(to_start) {\r
7849                         var self = this, rng = self.getRng(), node;\r
7850 \r
7851                         // Control range on IE\r
7852                         if (rng.item) {\r
7853                                 node = rng.item(0);\r
7854                                 rng = self.win.document.body.createTextRange();\r
7855                                 rng.moveToElementText(node);\r
7856                         }\r
7857 \r
7858                         rng.collapse(!!to_start);\r
7859                         self.setRng(rng);\r
7860                 },\r
7861 \r
7862                 getSel : function() {\r
7863                         var t = this, w = this.win;\r
7864 \r
7865                         return w.getSelection ? w.getSelection() : w.document.selection;\r
7866                 },\r
7867 \r
7868                 getRng : function(w3c) {\r
7869                         var t = this, s, r, elm, doc = t.win.document;\r
7870 \r
7871                         // Found tridentSel object then we need to use that one\r
7872                         if (w3c && t.tridentSel)\r
7873                                 return t.tridentSel.getRangeAt(0);\r
7874 \r
7875                         try {\r
7876                                 if (s = t.getSel())\r
7877                                         r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : doc.createRange());\r
7878                         } catch (ex) {\r
7879                                 // IE throws unspecified error here if TinyMCE is placed in a frame/iframe\r
7880                         }\r
7881 \r
7882                         // We have W3C ranges and it's IE then fake control selection since IE9 doesn't handle that correctly yet\r
7883                         if (tinymce.isIE && r && r.setStart && doc.selection.createRange().item) {\r
7884                                 elm = doc.selection.createRange().item(0);\r
7885                                 r = doc.createRange();\r
7886                                 r.setStartBefore(elm);\r
7887                                 r.setEndAfter(elm);\r
7888                         }\r
7889 \r
7890                         // No range found then create an empty one\r
7891                         // This can occur when the editor is placed in a hidden container element on Gecko\r
7892                         // Or on IE when there was an exception\r
7893                         if (!r)\r
7894                                 r = doc.createRange ? doc.createRange() : doc.body.createTextRange();\r
7895 \r
7896                         if (t.selectedRange && t.explicitRange) {\r
7897                                 if (r.compareBoundaryPoints(r.START_TO_START, t.selectedRange) === 0 && r.compareBoundaryPoints(r.END_TO_END, t.selectedRange) === 0) {\r
7898                                         // Safari, Opera and Chrome only ever select text which causes the range to change.\r
7899                                         // This lets us use the originally set range if the selection hasn't been changed by the user.\r
7900                                         r = t.explicitRange;\r
7901                                 } else {\r
7902                                         t.selectedRange = null;\r
7903                                         t.explicitRange = null;\r
7904                                 }\r
7905                         }\r
7906 \r
7907                         return r;\r
7908                 },\r
7909 \r
7910                 setRng : function(r) {\r
7911                         var s, t = this;\r
7912                         \r
7913                         if (!t.tridentSel) {\r
7914                                 s = t.getSel();\r
7915 \r
7916                                 if (s) {\r
7917                                         t.explicitRange = r;\r
7918 \r
7919                                         try {\r
7920                                                 s.removeAllRanges();\r
7921                                         } catch (ex) {\r
7922                                                 // IE9 might throw errors here don't know why\r
7923                                         }\r
7924 \r
7925                                         s.addRange(r);\r
7926                                         t.selectedRange = s.getRangeAt(0);\r
7927                                 }\r
7928                         } else {\r
7929                                 // Is W3C Range\r
7930                                 if (r.cloneRange) {\r
7931                                         t.tridentSel.addRange(r);\r
7932                                         return;\r
7933                                 }\r
7934 \r
7935                                 // Is IE specific range\r
7936                                 try {\r
7937                                         r.select();\r
7938                                 } catch (ex) {\r
7939                                         // Needed for some odd IE bug #1843306\r
7940                                 }\r
7941                         }\r
7942                 },\r
7943 \r
7944                 setNode : function(n) {\r
7945                         var t = this;\r
7946 \r
7947                         t.setContent(t.dom.getOuterHTML(n));\r
7948 \r
7949                         return n;\r
7950                 },\r
7951 \r
7952                 getNode : function() {\r
7953                         var t = this, rng = t.getRng(), sel = t.getSel(), elm, start = rng.startContainer, end = rng.endContainer;\r
7954 \r
7955                         // Range maybe lost after the editor is made visible again\r
7956                         if (!rng)\r
7957                                 return t.dom.getRoot();\r
7958 \r
7959                         if (rng.setStart) {\r
7960                                 elm = rng.commonAncestorContainer;\r
7961 \r
7962                                 // Handle selection a image or other control like element such as anchors\r
7963                                 if (!rng.collapsed) {\r
7964                                         if (rng.startContainer == rng.endContainer) {\r
7965                                                 if (rng.endOffset - rng.startOffset < 2) {\r
7966                                                         if (rng.startContainer.hasChildNodes())\r
7967                                                                 elm = rng.startContainer.childNodes[rng.startOffset];\r
7968                                                 }\r
7969                                         }\r
7970 \r
7971                                         // If the anchor node is a element instead of a text node then return this element\r
7972                                         //if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1) \r
7973                                         //      return sel.anchorNode.childNodes[sel.anchorOffset];\r
7974 \r
7975                                         // Handle cases where the selection is immediately wrapped around a node and return that node instead of it's parent.\r
7976                                         // This happens when you double click an underlined word in FireFox.\r
7977                                         if (start.nodeType === 3 && end.nodeType === 3) {\r
7978                                                 function skipEmptyTextNodes(n, forwards) {\r
7979                                                         var orig = n;\r
7980                                                         while (n && n.nodeType === 3 && n.length === 0) {\r
7981                                                                 n = forwards ? n.nextSibling : n.previousSibling;\r
7982                                                         }\r
7983                                                         return n || orig;\r
7984                                                 }\r
7985                                                 if (start.length === rng.startOffset) {\r
7986                                                         start = skipEmptyTextNodes(start.nextSibling, true);\r
7987                                                 } else {\r
7988                                                         start = start.parentNode;\r
7989                                                 }\r
7990                                                 if (rng.endOffset === 0) {\r
7991                                                         end = skipEmptyTextNodes(end.previousSibling, false);\r
7992                                                 } else {\r
7993                                                         end = end.parentNode;\r
7994                                                 }\r
7995 \r
7996                                                 if (start && start === end)\r
7997                                                         return start;\r
7998                                         }\r
7999                                 }\r
8000 \r
8001                                 if (elm && elm.nodeType == 3)\r
8002                                         return elm.parentNode;\r
8003 \r
8004                                 return elm;\r
8005                         }\r
8006 \r
8007                         return rng.item ? rng.item(0) : rng.parentElement();\r
8008                 },\r
8009 \r
8010                 getSelectedBlocks : function(st, en) {\r
8011                         var t = this, dom = t.dom, sb, eb, n, bl = [];\r
8012 \r
8013                         sb = dom.getParent(st || t.getStart(), dom.isBlock);\r
8014                         eb = dom.getParent(en || t.getEnd(), dom.isBlock);\r
8015 \r
8016                         if (sb)\r
8017                                 bl.push(sb);\r
8018 \r
8019                         if (sb && eb && sb != eb) {\r
8020                                 n = sb;\r
8021 \r
8022                                 while ((n = n.nextSibling) && n != eb) {\r
8023                                         if (dom.isBlock(n))\r
8024                                                 bl.push(n);\r
8025                                 }\r
8026                         }\r
8027 \r
8028                         if (eb && sb != eb)\r
8029                                 bl.push(eb);\r
8030 \r
8031                         return bl;\r
8032                 },\r
8033 \r
8034                 normalize : function() {\r
8035                         var self = this, rng, normalized;\r
8036 \r
8037                         // Normalize only on non IE browsers for now\r
8038                         if (tinymce.isIE)\r
8039                                 return;\r
8040 \r
8041                         function normalizeEndPoint(start) {\r
8042                                 var container, offset, walker, dom = self.dom, body = dom.getRoot(), node;\r
8043 \r
8044                                 container = rng[(start ? 'start' : 'end') + 'Container'];\r
8045                                 offset = rng[(start ? 'start' : 'end') + 'Offset'];\r
8046 \r
8047                                 // If the container is a document move it to the body element\r
8048                                 if (container.nodeType === 9) {\r
8049                                         container = container.body;\r
8050                                         offset = 0;\r
8051                                 }\r
8052 \r
8053                                 // If the container is body try move it into the closest text node or position\r
8054                                 // TODO: Add more logic here to handle element selection cases\r
8055                                 if (container === body) {\r
8056                                         // Resolve the index\r
8057                                         if (container.hasChildNodes()) {\r
8058                                                 container = container.childNodes[Math.min(!start && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1)];\r
8059                                                 offset = 0;\r
8060 \r
8061                                                 // Don't walk into elements that doesn't have any child nodes like a IMG\r
8062                                                 if (container.hasChildNodes()) {\r
8063                                                         // Walk the DOM to find a text node to place the caret at or a BR\r
8064                                                         node = container;\r
8065                                                         walker = new tinymce.dom.TreeWalker(container, body);\r
8066                                                         do {\r
8067                                                                 // Found a text node use that position\r
8068                                                                 if (node.nodeType === 3) {\r
8069                                                                         offset = start ? 0 : node.nodeValue.length - 1;\r
8070                                                                         container = node;\r
8071                                                                         break;\r
8072                                                                 }\r
8073 \r
8074                                                                 // Found a BR element that we can place the caret before\r
8075                                                                 if (node.nodeName === 'BR') {\r
8076                                                                         offset = dom.nodeIndex(node);\r
8077                                                                         container = node.parentNode;\r
8078                                                                         break;\r
8079                                                                 }\r
8080                                                         } while (node = (start ? walker.next() : walker.prev()));\r
8081 \r
8082                                                         normalized = true;\r
8083                                                 }\r
8084                                         }\r
8085                                 }\r
8086 \r
8087                                 // Set endpoint if it was normalized\r
8088                                 if (normalized)\r
8089                                         rng['set' + (start ? 'Start' : 'End')](container, offset);\r
8090                         };\r
8091 \r
8092                         rng = self.getRng();\r
8093 \r
8094                         // Normalize the end points\r
8095                         normalizeEndPoint(true);\r
8096                         \r
8097                         if (rng.collapsed)\r
8098                                 normalizeEndPoint();\r
8099 \r
8100                         // Set the selection if it was normalized\r
8101                         if (normalized) {\r
8102                                 //console.log(self.dom.dumpRng(rng));\r
8103                                 self.setRng(rng);\r
8104                         }\r
8105                 },\r
8106 \r
8107                 destroy : function(s) {\r
8108                         var t = this;\r
8109 \r
8110                         t.win = null;\r
8111 \r
8112                         // Manual destroy then remove unload handler\r
8113                         if (!s)\r
8114                                 tinymce.removeUnload(t.destroy);\r
8115                 },\r
8116 \r
8117                 // 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
8118                 _fixIESelection : function() {\r
8119                         var dom = this.dom, doc = dom.doc, body = doc.body, started, startRng, htmlElm;\r
8120 \r
8121                         // Make HTML element unselectable since we are going to handle selection by hand\r
8122                         doc.documentElement.unselectable = true;\r
8123 \r
8124                         // Return range from point or null if it failed\r
8125                         function rngFromPoint(x, y) {\r
8126                                 var rng = body.createTextRange();\r
8127 \r
8128                                 try {\r
8129                                         rng.moveToPoint(x, y);\r
8130                                 } catch (ex) {\r
8131                                         // IE sometimes throws and exception, so lets just ignore it\r
8132                                         rng = null;\r
8133                                 }\r
8134 \r
8135                                 return rng;\r
8136                         };\r
8137 \r
8138                         // Fires while the selection is changing\r
8139                         function selectionChange(e) {\r
8140                                 var pointRng;\r
8141 \r
8142                                 // Check if the button is down or not\r
8143                                 if (e.button) {\r
8144                                         // Create range from mouse position\r
8145                                         pointRng = rngFromPoint(e.x, e.y);\r
8146 \r
8147                                         if (pointRng) {\r
8148                                                 // Check if pointRange is before/after selection then change the endPoint\r
8149                                                 if (pointRng.compareEndPoints('StartToStart', startRng) > 0)\r
8150                                                         pointRng.setEndPoint('StartToStart', startRng);\r
8151                                                 else\r
8152                                                         pointRng.setEndPoint('EndToEnd', startRng);\r
8153 \r
8154                                                 pointRng.select();\r
8155                                         }\r
8156                                 } else\r
8157                                         endSelection();\r
8158                         }\r
8159 \r
8160                         // Removes listeners\r
8161                         function endSelection() {\r
8162                                 var rng = doc.selection.createRange();\r
8163 \r
8164                                 // If the range is collapsed then use the last start range\r
8165                                 if (startRng && !rng.item && rng.compareEndPoints('StartToEnd', rng) === 0)\r
8166                                         startRng.select();\r
8167 \r
8168                                 dom.unbind(doc, 'mouseup', endSelection);\r
8169                                 dom.unbind(doc, 'mousemove', selectionChange);\r
8170                                 startRng = started = 0;\r
8171                         };\r
8172 \r
8173                         // Detect when user selects outside BODY\r
8174                         dom.bind(doc, ['mousedown', 'contextmenu'], function(e) {\r
8175                                 if (e.target.nodeName === 'HTML') {\r
8176                                         if (started)\r
8177                                                 endSelection();\r
8178 \r
8179                                         // Detect vertical scrollbar, since IE will fire a mousedown on the scrollbar and have target set as HTML\r
8180                                         htmlElm = doc.documentElement;\r
8181                                         if (htmlElm.scrollHeight > htmlElm.clientHeight)\r
8182                                                 return;\r
8183 \r
8184                                         started = 1;\r
8185                                         // Setup start position\r
8186                                         startRng = rngFromPoint(e.x, e.y);\r
8187                                         if (startRng) {\r
8188                                                 // Listen for selection change events\r
8189                                                 dom.bind(doc, 'mouseup', endSelection);\r
8190                                                 dom.bind(doc, 'mousemove', selectionChange);\r
8191 \r
8192                                                 dom.win.focus();\r
8193                                                 startRng.select();\r
8194                                         }\r
8195                                 }\r
8196                         });\r
8197                 }\r
8198         });\r
8199 })(tinymce);\r
8200 \r
8201 (function(tinymce) {\r
8202         tinymce.dom.Serializer = function(settings, dom, schema) {\r
8203                 var onPreProcess, onPostProcess, isIE = tinymce.isIE, each = tinymce.each, htmlParser;\r
8204 \r
8205                 // Support the old apply_source_formatting option\r
8206                 if (!settings.apply_source_formatting)\r
8207                         settings.indent = false;\r
8208 \r
8209                 settings.remove_trailing_brs = true;\r
8210 \r
8211                 // Default DOM and Schema if they are undefined\r
8212                 dom = dom || tinymce.DOM;\r
8213                 schema = schema || new tinymce.html.Schema(settings);\r
8214                 settings.entity_encoding = settings.entity_encoding || 'named';\r
8215 \r
8216                 onPreProcess = new tinymce.util.Dispatcher(self);\r
8217 \r
8218                 onPostProcess = new tinymce.util.Dispatcher(self);\r
8219 \r
8220                 htmlParser = new tinymce.html.DomParser(settings, schema);\r
8221 \r
8222                 // Convert move data-mce-src, data-mce-href and data-mce-style into nodes or process them if needed\r
8223                 htmlParser.addAttributeFilter('src,href,style', function(nodes, name) {\r
8224                         var i = nodes.length, node, value, internalName = 'data-mce-' + name, urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope, undef;\r
8225 \r
8226                         while (i--) {\r
8227                                 node = nodes[i];\r
8228 \r
8229                                 value = node.attributes.map[internalName];\r
8230                                 if (value !== undef) {\r
8231                                         // Set external name to internal value and remove internal\r
8232                                         node.attr(name, value.length > 0 ? value : null);\r
8233                                         node.attr(internalName, null);\r
8234                                 } else {\r
8235                                         // No internal attribute found then convert the value we have in the DOM\r
8236                                         value = node.attributes.map[name];\r
8237 \r
8238                                         if (name === "style")\r
8239                                                 value = dom.serializeStyle(dom.parseStyle(value), node.name);\r
8240                                         else if (urlConverter)\r
8241                                                 value = urlConverter.call(urlConverterScope, value, name, node.name);\r
8242 \r
8243                                         node.attr(name, value.length > 0 ? value : null);\r
8244                                 }\r
8245                         }\r
8246                 });\r
8247 \r
8248                 // Remove internal classes mceItem<..>\r
8249                 htmlParser.addAttributeFilter('class', function(nodes, name) {\r
8250                         var i = nodes.length, node, value;\r
8251 \r
8252                         while (i--) {\r
8253                                 node = nodes[i];\r
8254                                 value = node.attr('class').replace(/\s*mce(Item\w+|Selected)\s*/g, '');\r
8255                                 node.attr('class', value.length > 0 ? value : null);\r
8256                         }\r
8257                 });\r
8258 \r
8259                 // Remove bookmark elements\r
8260                 htmlParser.addAttributeFilter('data-mce-type', function(nodes, name, args) {\r
8261                         var i = nodes.length, node;\r
8262 \r
8263                         while (i--) {\r
8264                                 node = nodes[i];\r
8265 \r
8266                                 if (node.attributes.map['data-mce-type'] === 'bookmark' && !args.cleanup)\r
8267                                         node.remove();\r
8268                         }\r
8269                 });\r
8270 \r
8271                 // Force script into CDATA sections and remove the mce- prefix also add comments around styles\r
8272                 htmlParser.addNodeFilter('script,style', function(nodes, name) {\r
8273                         var i = nodes.length, node, value;\r
8274 \r
8275                         function trim(value) {\r
8276                                 return value.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n')\r
8277                                                 .replace(/^[\r\n]*|[\r\n]*$/g, '')\r
8278                                                 .replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, '')\r
8279                                                 .replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, '');\r
8280                         };\r
8281 \r
8282                         while (i--) {\r
8283                                 node = nodes[i];\r
8284                                 value = node.firstChild ? node.firstChild.value : '';\r
8285 \r
8286                                 if (name === "script") {\r
8287                                         // Remove mce- prefix from script elements\r
8288                                         node.attr('type', (node.attr('type') || 'text/javascript').replace(/^mce\-/, ''));\r
8289 \r
8290                                         if (value.length > 0)\r
8291                                                 node.firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>';\r
8292                                 } else {\r
8293                                         if (value.length > 0)\r
8294                                                 node.firstChild.value = '<!--\n' + trim(value) + '\n-->';\r
8295                                 }\r
8296                         }\r
8297                 });\r
8298 \r
8299                 // Convert comments to cdata and handle protected comments\r
8300                 htmlParser.addNodeFilter('#comment', function(nodes, name) {\r
8301                         var i = nodes.length, node;\r
8302 \r
8303                         while (i--) {\r
8304                                 node = nodes[i];\r
8305 \r
8306                                 if (node.value.indexOf('[CDATA[') === 0) {\r
8307                                         node.name = '#cdata';\r
8308                                         node.type = 4;\r
8309                                         node.value = node.value.replace(/^\[CDATA\[|\]\]$/g, '');\r
8310                                 } else if (node.value.indexOf('mce:protected ') === 0) {\r
8311                                         node.name = "#text";\r
8312                                         node.type = 3;\r
8313                                         node.raw = true;\r
8314                                         node.value = unescape(node.value).substr(14);\r
8315                                 }\r
8316                         }\r
8317                 });\r
8318 \r
8319                 htmlParser.addNodeFilter('xml:namespace,input', function(nodes, name) {\r
8320                         var i = nodes.length, node;\r
8321 \r
8322                         while (i--) {\r
8323                                 node = nodes[i];\r
8324                                 if (node.type === 7)\r
8325                                         node.remove();\r
8326                                 else if (node.type === 1) {\r
8327                                         if (name === "input" && !("type" in node.attributes.map))\r
8328                                                 node.attr('type', 'text');\r
8329                                 }\r
8330                         }\r
8331                 });\r
8332 \r
8333                 // Fix list elements, TODO: Replace this later\r
8334                 if (settings.fix_list_elements) {\r
8335                         htmlParser.addNodeFilter('ul,ol', function(nodes, name) {\r
8336                                 var i = nodes.length, node, parentNode;\r
8337 \r
8338                                 while (i--) {\r
8339                                         node = nodes[i];\r
8340                                         parentNode = node.parent;\r
8341 \r
8342                                         if (parentNode.name === 'ul' || parentNode.name === 'ol') {\r
8343                                                 if (node.prev && node.prev.name === 'li') {\r
8344                                                         node.prev.append(node);\r
8345                                                 }\r
8346                                         }\r
8347                                 }\r
8348                         });\r
8349                 }\r
8350 \r
8351                 // Remove internal data attributes\r
8352                 htmlParser.addAttributeFilter('data-mce-src,data-mce-href,data-mce-style', function(nodes, name) {\r
8353                         var i = nodes.length;\r
8354 \r
8355                         while (i--) {\r
8356                                 nodes[i].attr(name, null);\r
8357                         }\r
8358                 });\r
8359 \r
8360                 // Return public methods\r
8361                 return {\r
8362                         schema : schema,\r
8363 \r
8364                         addNodeFilter : htmlParser.addNodeFilter,\r
8365 \r
8366                         addAttributeFilter : htmlParser.addAttributeFilter,\r
8367 \r
8368                         onPreProcess : onPreProcess,\r
8369 \r
8370                         onPostProcess : onPostProcess,\r
8371 \r
8372                         serialize : function(node, args) {\r
8373                                 var impl, doc, oldDoc, htmlSerializer, content;\r
8374 \r
8375                                 // Explorer won't clone contents of script and style and the\r
8376                                 // selected index of select elements are cleared on a clone operation.\r
8377                                 if (isIE && dom.select('script,style,select,map').length > 0) {\r
8378                                         content = node.innerHTML;\r
8379                                         node = node.cloneNode(false);\r
8380                                         dom.setHTML(node, content);\r
8381                                 } else\r
8382                                         node = node.cloneNode(true);\r
8383 \r
8384                                 // Nodes needs to be attached to something in WebKit/Opera\r
8385                                 // Older builds of Opera crashes if you attach the node to an document created dynamically\r
8386                                 // and since we can't feature detect a crash we need to sniff the acutal build number\r
8387                                 // This fix will make DOM ranges and make Sizzle happy!\r
8388                                 impl = node.ownerDocument.implementation;\r
8389                                 if (impl.createHTMLDocument) {\r
8390                                         // Create an empty HTML document\r
8391                                         doc = impl.createHTMLDocument("");\r
8392 \r
8393                                         // Add the element or it's children if it's a body element to the new document\r
8394                                         each(node.nodeName == 'BODY' ? node.childNodes : [node], function(node) {\r
8395                                                 doc.body.appendChild(doc.importNode(node, true));\r
8396                                         });\r
8397 \r
8398                                         // Grab first child or body element for serialization\r
8399                                         if (node.nodeName != 'BODY')\r
8400                                                 node = doc.body.firstChild;\r
8401                                         else\r
8402                                                 node = doc.body;\r
8403 \r
8404                                         // set the new document in DOMUtils so createElement etc works\r
8405                                         oldDoc = dom.doc;\r
8406                                         dom.doc = doc;\r
8407                                 }\r
8408 \r
8409                                 args = args || {};\r
8410                                 args.format = args.format || 'html';\r
8411 \r
8412                                 // Pre process\r
8413                                 if (!args.no_events) {\r
8414                                         args.node = node;\r
8415                                         onPreProcess.dispatch(self, args);\r
8416                                 }\r
8417 \r
8418                                 // Setup serializer\r
8419                                 htmlSerializer = new tinymce.html.Serializer(settings, schema);\r
8420 \r
8421                                 // Parse and serialize HTML\r
8422                                 args.content = htmlSerializer.serialize(\r
8423                                         htmlParser.parse(args.getInner ? node.innerHTML : tinymce.trim(dom.getOuterHTML(node), args), args)\r
8424                                 );\r
8425 \r
8426                                 // Replace all BOM characters for now until we can find a better solution\r
8427                                 if (!args.cleanup)\r
8428                                         args.content = args.content.replace(/\uFEFF/g, '');\r
8429 \r
8430                                 // Post process\r
8431                                 if (!args.no_events)\r
8432                                         onPostProcess.dispatch(self, args);\r
8433 \r
8434                                 // Restore the old document if it was changed\r
8435                                 if (oldDoc)\r
8436                                         dom.doc = oldDoc;\r
8437 \r
8438                                 args.node = null;\r
8439 \r
8440                                 return args.content;\r
8441                         },\r
8442 \r
8443                         addRules : function(rules) {\r
8444                                 schema.addValidElements(rules);\r
8445                         },\r
8446 \r
8447                         setRules : function(rules) {\r
8448                                 schema.setValidElements(rules);\r
8449                         }\r
8450                 };\r
8451         };\r
8452 })(tinymce);\r
8453 (function(tinymce) {\r
8454         tinymce.dom.ScriptLoader = function(settings) {\r
8455                 var QUEUED = 0,\r
8456                         LOADING = 1,\r
8457                         LOADED = 2,\r
8458                         states = {},\r
8459                         queue = [],\r
8460                         scriptLoadedCallbacks = {},\r
8461                         queueLoadedCallbacks = [],\r
8462                         loading = 0,\r
8463                         undefined;\r
8464 \r
8465                 function loadScript(url, callback) {\r
8466                         var t = this, dom = tinymce.DOM, elm, uri, loc, id;\r
8467 \r
8468                         // Execute callback when script is loaded\r
8469                         function done() {\r
8470                                 dom.remove(id);\r
8471 \r
8472                                 if (elm)\r
8473                                         elm.onreadystatechange = elm.onload = elm = null;\r
8474 \r
8475                                 callback();\r
8476                         };\r
8477                         \r
8478                         function error() {\r
8479                                 // Report the error so it's easier for people to spot loading errors\r
8480                                 if (typeof(console) !== "undefined" && console.log)\r
8481                                         console.log("Failed to load: " + url);\r
8482 \r
8483                                 // We can't mark it as done if there is a load error since\r
8484                                 // A) We don't want to produce 404 errors on the server and\r
8485                                 // B) the onerror event won't fire on all browsers.\r
8486                                 // done();\r
8487                         };\r
8488 \r
8489                         id = dom.uniqueId();\r
8490 \r
8491                         if (tinymce.isIE6) {\r
8492                                 uri = new tinymce.util.URI(url);\r
8493                                 loc = location;\r
8494 \r
8495                                 // If script is from same domain and we\r
8496                                 // use IE 6 then use XHR since it's more reliable\r
8497                                 if (uri.host == loc.hostname && uri.port == loc.port && (uri.protocol + ':') == loc.protocol && uri.protocol.toLowerCase() != 'file') {\r
8498                                         tinymce.util.XHR.send({\r
8499                                                 url : tinymce._addVer(uri.getURI()),\r
8500                                                 success : function(content) {\r
8501                                                         // Create new temp script element\r
8502                                                         var script = dom.create('script', {\r
8503                                                                 type : 'text/javascript'\r
8504                                                         });\r
8505 \r
8506                                                         // Evaluate script in global scope\r
8507                                                         script.text = content;\r
8508                                                         document.getElementsByTagName('head')[0].appendChild(script);\r
8509                                                         dom.remove(script);\r
8510 \r
8511                                                         done();\r
8512                                                 },\r
8513                                                 \r
8514                                                 error : error\r
8515                                         });\r
8516 \r
8517                                         return;\r
8518                                 }\r
8519                         }\r
8520 \r
8521                         // Create new script element\r
8522                         elm = dom.create('script', {\r
8523                                 id : id,\r
8524                                 type : 'text/javascript',\r
8525                                 src : tinymce._addVer(url)\r
8526                         });\r
8527 \r
8528                         // Add onload listener for non IE browsers since IE9\r
8529                         // fires onload event before the script is parsed and executed\r
8530                         if (!tinymce.isIE)\r
8531                                 elm.onload = done;\r
8532 \r
8533                         // Add onerror event will get fired on some browsers but not all of them\r
8534                         elm.onerror = error;\r
8535 \r
8536                         // Opera 9.60 doesn't seem to fire the onreadystate event at correctly\r
8537                         if (!tinymce.isOpera) {\r
8538                                 elm.onreadystatechange = function() {\r
8539                                         var state = elm.readyState;\r
8540 \r
8541                                         // Loaded state is passed on IE 6 however there\r
8542                                         // are known issues with this method but we can't use\r
8543                                         // XHR in a cross domain loading\r
8544                                         if (state == 'complete' || state == 'loaded')\r
8545                                                 done();\r
8546                                 };\r
8547                         }\r
8548 \r
8549                         // Most browsers support this feature so we report errors\r
8550                         // for those at least to help users track their missing plugins etc\r
8551                         // todo: Removed since it produced error if the document is unloaded by navigating away, re-add it as an option\r
8552                         /*elm.onerror = function() {\r
8553                                 alert('Failed to load: ' + url);\r
8554                         };*/\r
8555 \r
8556                         // Add script to document\r
8557                         (document.getElementsByTagName('head')[0] || document.body).appendChild(elm);\r
8558                 };\r
8559 \r
8560                 this.isDone = function(url) {\r
8561                         return states[url] == LOADED;\r
8562                 };\r
8563 \r
8564                 this.markDone = function(url) {\r
8565                         states[url] = LOADED;\r
8566                 };\r
8567 \r
8568                 this.add = this.load = function(url, callback, scope) {\r
8569                         var item, state = states[url];\r
8570 \r
8571                         // Add url to load queue\r
8572                         if (state == undefined) {\r
8573                                 queue.push(url);\r
8574                                 states[url] = QUEUED;\r
8575                         }\r
8576 \r
8577                         if (callback) {\r
8578                                 // Store away callback for later execution\r
8579                                 if (!scriptLoadedCallbacks[url])\r
8580                                         scriptLoadedCallbacks[url] = [];\r
8581 \r
8582                                 scriptLoadedCallbacks[url].push({\r
8583                                         func : callback,\r
8584                                         scope : scope || this\r
8585                                 });\r
8586                         }\r
8587                 };\r
8588 \r
8589                 this.loadQueue = function(callback, scope) {\r
8590                         this.loadScripts(queue, callback, scope);\r
8591                 };\r
8592 \r
8593                 this.loadScripts = function(scripts, callback, scope) {\r
8594                         var loadScripts;\r
8595 \r
8596                         function execScriptLoadedCallbacks(url) {\r
8597                                 // Execute URL callback functions\r
8598                                 tinymce.each(scriptLoadedCallbacks[url], function(callback) {\r
8599                                         callback.func.call(callback.scope);\r
8600                                 });\r
8601 \r
8602                                 scriptLoadedCallbacks[url] = undefined;\r
8603                         };\r
8604 \r
8605                         queueLoadedCallbacks.push({\r
8606                                 func : callback,\r
8607                                 scope : scope || this\r
8608                         });\r
8609 \r
8610                         loadScripts = function() {\r
8611                                 var loadingScripts = tinymce.grep(scripts);\r
8612 \r
8613                                 // Current scripts has been handled\r
8614                                 scripts.length = 0;\r
8615 \r
8616                                 // Load scripts that needs to be loaded\r
8617                                 tinymce.each(loadingScripts, function(url) {\r
8618                                         // Script is already loaded then execute script callbacks directly\r
8619                                         if (states[url] == LOADED) {\r
8620                                                 execScriptLoadedCallbacks(url);\r
8621                                                 return;\r
8622                                         }\r
8623 \r
8624                                         // Is script not loading then start loading it\r
8625                                         if (states[url] != LOADING) {\r
8626                                                 states[url] = LOADING;\r
8627                                                 loading++;\r
8628 \r
8629                                                 loadScript(url, function() {\r
8630                                                         states[url] = LOADED;\r
8631                                                         loading--;\r
8632 \r
8633                                                         execScriptLoadedCallbacks(url);\r
8634 \r
8635                                                         // Load more scripts if they where added by the recently loaded script\r
8636                                                         loadScripts();\r
8637                                                 });\r
8638                                         }\r
8639                                 });\r
8640 \r
8641                                 // No scripts are currently loading then execute all pending queue loaded callbacks\r
8642                                 if (!loading) {\r
8643                                         tinymce.each(queueLoadedCallbacks, function(callback) {\r
8644                                                 callback.func.call(callback.scope);\r
8645                                         });\r
8646 \r
8647                                         queueLoadedCallbacks.length = 0;\r
8648                                 }\r
8649                         };\r
8650 \r
8651                         loadScripts();\r
8652                 };\r
8653         };\r
8654 \r
8655         // Global script loader\r
8656         tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();\r
8657 })(tinymce);\r
8658 \r
8659 tinymce.dom.TreeWalker = function(start_node, root_node) {\r
8660         var node = start_node;\r
8661 \r
8662         function findSibling(node, start_name, sibling_name, shallow) {\r
8663                 var sibling, parent;\r
8664 \r
8665                 if (node) {\r
8666                         // Walk into nodes if it has a start\r
8667                         if (!shallow && node[start_name])\r
8668                                 return node[start_name];\r
8669 \r
8670                         // Return the sibling if it has one\r
8671                         if (node != root_node) {\r
8672                                 sibling = node[sibling_name];\r
8673                                 if (sibling)\r
8674                                         return sibling;\r
8675 \r
8676                                 // Walk up the parents to look for siblings\r
8677                                 for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) {\r
8678                                         sibling = parent[sibling_name];\r
8679                                         if (sibling)\r
8680                                                 return sibling;\r
8681                                 }\r
8682                         }\r
8683                 }\r
8684         };\r
8685 \r
8686         this.current = function() {\r
8687                 return node;\r
8688         };\r
8689 \r
8690         this.next = function(shallow) {\r
8691                 return (node = findSibling(node, 'firstChild', 'nextSibling', shallow));\r
8692         };\r
8693 \r
8694         this.prev = function(shallow) {\r
8695                 return (node = findSibling(node, 'lastChild', 'previousSibling', shallow));\r
8696         };\r
8697 };\r
8698 \r
8699 (function(tinymce) {\r
8700         tinymce.dom.RangeUtils = function(dom) {\r
8701                 var INVISIBLE_CHAR = '\uFEFF';\r
8702 \r
8703                 this.walk = function(rng, callback) {\r
8704                         var startContainer = rng.startContainer,\r
8705                                 startOffset = rng.startOffset,\r
8706                                 endContainer = rng.endContainer,\r
8707                                 endOffset = rng.endOffset,\r
8708                                 ancestor, startPoint,\r
8709                                 endPoint, node, parent, siblings, nodes;\r
8710 \r
8711                         // Handle table cell selection the table plugin enables\r
8712                         // you to fake select table cells and perform formatting actions on them\r
8713                         nodes = dom.select('td.mceSelected,th.mceSelected');\r
8714                         if (nodes.length > 0) {\r
8715                                 tinymce.each(nodes, function(node) {\r
8716                                         callback([node]);\r
8717                                 });\r
8718 \r
8719                                 return;\r
8720                         }\r
8721 \r
8722                         function collectSiblings(node, name, end_node) {\r
8723                                 var siblings = [];\r
8724 \r
8725                                 for (; node && node != end_node; node = node[name])\r
8726                                         siblings.push(node);\r
8727 \r
8728                                 return siblings;\r
8729                         };\r
8730 \r
8731                         function findEndPoint(node, root) {\r
8732                                 do {\r
8733                                         if (node.parentNode == root)\r
8734                                                 return node;\r
8735 \r
8736                                         node = node.parentNode;\r
8737                                 } while(node);\r
8738                         };\r
8739 \r
8740                         function walkBoundary(start_node, end_node, next) {\r
8741                                 var siblingName = next ? 'nextSibling' : 'previousSibling';\r
8742 \r
8743                                 for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) {\r
8744                                         parent = node.parentNode;\r
8745                                         siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName);\r
8746 \r
8747                                         if (siblings.length) {\r
8748                                                 if (!next)\r
8749                                                         siblings.reverse();\r
8750 \r
8751                                                 callback(siblings);\r
8752                                         }\r
8753                                 }\r
8754                         };\r
8755 \r
8756                         // If index based start position then resolve it\r
8757                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes())\r
8758                                 startContainer = startContainer.childNodes[startOffset];\r
8759 \r
8760                         // If index based end position then resolve it\r
8761                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes())\r
8762                                 endContainer = endContainer.childNodes[Math.min(endOffset - 1, endContainer.childNodes.length - 1)];\r
8763 \r
8764                         // Find common ancestor and end points\r
8765                         ancestor = dom.findCommonAncestor(startContainer, endContainer);\r
8766 \r
8767                         // Same container\r
8768                         if (startContainer == endContainer)\r
8769                                 return callback([startContainer]);\r
8770 \r
8771                         // Process left side\r
8772                         for (node = startContainer; node; node = node.parentNode) {\r
8773                                 if (node == endContainer)\r
8774                                         return walkBoundary(startContainer, ancestor, true);\r
8775 \r
8776                                 if (node == ancestor)\r
8777                                         break;\r
8778                         }\r
8779 \r
8780                         // Process right side\r
8781                         for (node = endContainer; node; node = node.parentNode) {\r
8782                                 if (node == startContainer)\r
8783                                         return walkBoundary(endContainer, ancestor);\r
8784 \r
8785                                 if (node == ancestor)\r
8786                                         break;\r
8787                         }\r
8788 \r
8789                         // Find start/end point\r
8790                         startPoint = findEndPoint(startContainer, ancestor) || startContainer;\r
8791                         endPoint = findEndPoint(endContainer, ancestor) || endContainer;\r
8792 \r
8793                         // Walk left leaf\r
8794                         walkBoundary(startContainer, startPoint, true);\r
8795 \r
8796                         // Walk the middle from start to end point\r
8797                         siblings = collectSiblings(\r
8798                                 startPoint == startContainer ? startPoint : startPoint.nextSibling,\r
8799                                 'nextSibling',\r
8800                                 endPoint == endContainer ? endPoint.nextSibling : endPoint\r
8801                         );\r
8802 \r
8803                         if (siblings.length)\r
8804                                 callback(siblings);\r
8805 \r
8806                         // Walk right leaf\r
8807                         walkBoundary(endContainer, endPoint);\r
8808                 };\r
8809 \r
8810                 /*              this.split = function(rng) {\r
8811                         var startContainer = rng.startContainer,\r
8812                                 startOffset = rng.startOffset,\r
8813                                 endContainer = rng.endContainer,\r
8814                                 endOffset = rng.endOffset;\r
8815 \r
8816                         function splitText(node, offset) {\r
8817                                 if (offset == node.nodeValue.length)\r
8818                                         node.appendData(INVISIBLE_CHAR);\r
8819 \r
8820                                 node = node.splitText(offset);\r
8821 \r
8822                                 if (node.nodeValue === INVISIBLE_CHAR)\r
8823                                         node.nodeValue = '';\r
8824 \r
8825                                 return node;\r
8826                         };\r
8827 \r
8828                         // Handle single text node\r
8829                         if (startContainer == endContainer) {\r
8830                                 if (startContainer.nodeType == 3) {\r
8831                                         if (startOffset != 0)\r
8832                                                 startContainer = endContainer = splitText(startContainer, startOffset);\r
8833 \r
8834                                         if (endOffset - startOffset != startContainer.nodeValue.length)\r
8835                                                 splitText(startContainer, endOffset - startOffset);\r
8836                                 }\r
8837                         } else {\r
8838                                 // Split startContainer text node if needed\r
8839                                 if (startContainer.nodeType == 3 && startOffset != 0) {\r
8840                                         startContainer = splitText(startContainer, startOffset);\r
8841                                         startOffset = 0;\r
8842                                 }\r
8843 \r
8844                                 // Split endContainer text node if needed\r
8845                                 if (endContainer.nodeType == 3 && endOffset != endContainer.nodeValue.length) {\r
8846                                         endContainer = splitText(endContainer, endOffset).previousSibling;\r
8847                                         endOffset = endContainer.nodeValue.length;\r
8848                                 }\r
8849                         }\r
8850 \r
8851                         return {\r
8852                                 startContainer : startContainer,\r
8853                                 startOffset : startOffset,\r
8854                                 endContainer : endContainer,\r
8855                                 endOffset : endOffset\r
8856                         };\r
8857                 };\r
8858 */\r
8859         };\r
8860 \r
8861         tinymce.dom.RangeUtils.compareRanges = function(rng1, rng2) {\r
8862                 if (rng1 && rng2) {\r
8863                         // Compare native IE ranges\r
8864                         if (rng1.item || rng1.duplicate) {\r
8865                                 // Both are control ranges and the selected element matches\r
8866                                 if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))\r
8867                                         return true;\r
8868 \r
8869                                 // Both are text ranges and the range matches\r
8870                                 if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1))\r
8871                                         return true;\r
8872                         } else {\r
8873                                 // Compare w3c ranges\r
8874                                 return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;\r
8875                         }\r
8876                 }\r
8877 \r
8878                 return false;\r
8879         };\r
8880 })(tinymce);\r
8881 \r
8882 (function(tinymce) {\r
8883         var Event = tinymce.dom.Event, each = tinymce.each;\r
8884 \r
8885         tinymce.create('tinymce.ui.KeyboardNavigation', {\r
8886                 KeyboardNavigation: function(settings, dom) {\r
8887                         var t = this, root = settings.root, items = settings.items,\r
8888                                         enableUpDown = settings.enableUpDown, enableLeftRight = settings.enableLeftRight || !settings.enableUpDown,\r
8889                                         excludeFromTabOrder = settings.excludeFromTabOrder,\r
8890                                         itemFocussed, itemBlurred, rootKeydown, rootFocussed, focussedId;\r
8891 \r
8892                         dom = dom || tinymce.DOM;\r
8893 \r
8894                         itemFocussed = function(evt) {\r
8895                                 focussedId = evt.target.id;\r
8896                         };\r
8897                         \r
8898                         itemBlurred = function(evt) {\r
8899                                 dom.setAttrib(evt.target.id, 'tabindex', '-1');\r
8900                         };\r
8901                         \r
8902                         rootFocussed = function(evt) {\r
8903                                 var item = dom.get(focussedId);\r
8904                                 dom.setAttrib(item, 'tabindex', '0');\r
8905                                 item.focus();\r
8906                         };\r
8907                         \r
8908                         t.focus = function() {\r
8909                                 dom.get(focussedId).focus();\r
8910                         };\r
8911 \r
8912                         t.destroy = function() {\r
8913                                 each(items, function(item) {\r
8914                                         dom.unbind(dom.get(item.id), 'focus', itemFocussed);\r
8915                                         dom.unbind(dom.get(item.id), 'blur', itemBlurred);\r
8916                                 });\r
8917 \r
8918                                 dom.unbind(dom.get(root), 'focus', rootFocussed);\r
8919                                 dom.unbind(dom.get(root), 'keydown', rootKeydown);\r
8920 \r
8921                                 items = dom = root = t.focus = itemFocussed = itemBlurred = rootKeydown = rootFocussed = null;\r
8922                                 t.destroy = function() {};\r
8923                         };\r
8924                         \r
8925                         t.moveFocus = function(dir, evt) {\r
8926                                 var idx = -1, controls = t.controls, newFocus;\r
8927 \r
8928                                 if (!focussedId)\r
8929                                         return;\r
8930 \r
8931                                 each(items, function(item, index) {\r
8932                                         if (item.id === focussedId) {\r
8933                                                 idx = index;\r
8934                                                 return false;\r
8935                                         }\r
8936                                 });\r
8937 \r
8938                                 idx += dir;\r
8939                                 if (idx < 0) {\r
8940                                         idx = items.length - 1;\r
8941                                 } else if (idx >= items.length) {\r
8942                                         idx = 0;\r
8943                                 }\r
8944                                 \r
8945                                 newFocus = items[idx];\r
8946                                 dom.setAttrib(focussedId, 'tabindex', '-1');\r
8947                                 dom.setAttrib(newFocus.id, 'tabindex', '0');\r
8948                                 dom.get(newFocus.id).focus();\r
8949 \r
8950                                 if (settings.actOnFocus) {\r
8951                                         settings.onAction(newFocus.id);\r
8952                                 }\r
8953 \r
8954                                 if (evt)\r
8955                                         Event.cancel(evt);\r
8956                         };\r
8957                         \r
8958                         rootKeydown = function(evt) {\r
8959                                 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
8960                                 \r
8961                                 switch (evt.keyCode) {\r
8962                                         case DOM_VK_LEFT:\r
8963                                                 if (enableLeftRight) t.moveFocus(-1);\r
8964                                                 break;\r
8965         \r
8966                                         case DOM_VK_RIGHT:\r
8967                                                 if (enableLeftRight) t.moveFocus(1);\r
8968                                                 break;\r
8969         \r
8970                                         case DOM_VK_UP:\r
8971                                                 if (enableUpDown) t.moveFocus(-1);\r
8972                                                 break;\r
8973 \r
8974                                         case DOM_VK_DOWN:\r
8975                                                 if (enableUpDown) t.moveFocus(1);\r
8976                                                 break;\r
8977 \r
8978                                         case DOM_VK_ESCAPE:\r
8979                                                 if (settings.onCancel) {\r
8980                                                         settings.onCancel();\r
8981                                                         Event.cancel(evt);\r
8982                                                 }\r
8983                                                 break;\r
8984 \r
8985                                         case DOM_VK_ENTER:\r
8986                                         case DOM_VK_RETURN:\r
8987                                         case DOM_VK_SPACE:\r
8988                                                 if (settings.onAction) {\r
8989                                                         settings.onAction(focussedId);\r
8990                                                         Event.cancel(evt);\r
8991                                                 }\r
8992                                                 break;\r
8993                                 }\r
8994                         };\r
8995 \r
8996                         // Set up state and listeners for each item.\r
8997                         each(items, function(item, idx) {\r
8998                                 var tabindex;\r
8999 \r
9000                                 if (!item.id) {\r
9001                                         item.id = dom.uniqueId('_mce_item_');\r
9002                                 }\r
9003 \r
9004                                 if (excludeFromTabOrder) {\r
9005                                         dom.bind(item.id, 'blur', itemBlurred);\r
9006                                         tabindex = '-1';\r
9007                                 } else {\r
9008                                         tabindex = (idx === 0 ? '0' : '-1');\r
9009                                 }\r
9010 \r
9011                                 dom.setAttrib(item.id, 'tabindex', tabindex);\r
9012                                 dom.bind(dom.get(item.id), 'focus', itemFocussed);\r
9013                         });\r
9014                         \r
9015                         // Setup initial state for root element.\r
9016                         if (items[0]){\r
9017                                 focussedId = items[0].id;\r
9018                         }\r
9019 \r
9020                         dom.setAttrib(root, 'tabindex', '-1');\r
9021                         \r
9022                         // Setup listeners for root element.\r
9023                         dom.bind(dom.get(root), 'focus', rootFocussed);\r
9024                         dom.bind(dom.get(root), 'keydown', rootKeydown);\r
9025                 }\r
9026         });\r
9027 })(tinymce);\r
9028 (function(tinymce) {\r
9029         // Shorten class names\r
9030         var DOM = tinymce.DOM, is = tinymce.is;\r
9031 \r
9032         tinymce.create('tinymce.ui.Control', {\r
9033                 Control : function(id, s, editor) {\r
9034                         this.id = id;\r
9035                         this.settings = s = s || {};\r
9036                         this.rendered = false;\r
9037                         this.onRender = new tinymce.util.Dispatcher(this);\r
9038                         this.classPrefix = '';\r
9039                         this.scope = s.scope || this;\r
9040                         this.disabled = 0;\r
9041                         this.active = 0;\r
9042                         this.editor = editor;\r
9043                 },\r
9044                 \r
9045                 setAriaProperty : function(property, value) {\r
9046                         var element = DOM.get(this.id + '_aria') || DOM.get(this.id);\r
9047                         if (element) {\r
9048                                 DOM.setAttrib(element, 'aria-' + property, !!value);\r
9049                         }\r
9050                 },\r
9051                 \r
9052                 focus : function() {\r
9053                         DOM.get(this.id).focus();\r
9054                 },\r
9055 \r
9056                 setDisabled : function(s) {\r
9057                         if (s != this.disabled) {\r
9058                                 this.setAriaProperty('disabled', s);\r
9059 \r
9060                                 this.setState('Disabled', s);\r
9061                                 this.setState('Enabled', !s);\r
9062                                 this.disabled = s;\r
9063                         }\r
9064                 },\r
9065 \r
9066                 isDisabled : function() {\r
9067                         return this.disabled;\r
9068                 },\r
9069 \r
9070                 setActive : function(s) {\r
9071                         if (s != this.active) {\r
9072                                 this.setState('Active', s);\r
9073                                 this.active = s;\r
9074                                 this.setAriaProperty('pressed', s);\r
9075                         }\r
9076                 },\r
9077 \r
9078                 isActive : function() {\r
9079                         return this.active;\r
9080                 },\r
9081 \r
9082                 setState : function(c, s) {\r
9083                         var n = DOM.get(this.id);\r
9084 \r
9085                         c = this.classPrefix + c;\r
9086 \r
9087                         if (s)\r
9088                                 DOM.addClass(n, c);\r
9089                         else\r
9090                                 DOM.removeClass(n, c);\r
9091                 },\r
9092 \r
9093                 isRendered : function() {\r
9094                         return this.rendered;\r
9095                 },\r
9096 \r
9097                 renderHTML : function() {\r
9098                 },\r
9099 \r
9100                 renderTo : function(n) {\r
9101                         DOM.setHTML(n, this.renderHTML());\r
9102                 },\r
9103 \r
9104                 postRender : function() {\r
9105                         var t = this, b;\r
9106 \r
9107                         // Set pending states\r
9108                         if (is(t.disabled)) {\r
9109                                 b = t.disabled;\r
9110                                 t.disabled = -1;\r
9111                                 t.setDisabled(b);\r
9112                         }\r
9113 \r
9114                         if (is(t.active)) {\r
9115                                 b = t.active;\r
9116                                 t.active = -1;\r
9117                                 t.setActive(b);\r
9118                         }\r
9119                 },\r
9120 \r
9121                 remove : function() {\r
9122                         DOM.remove(this.id);\r
9123                         this.destroy();\r
9124                 },\r
9125 \r
9126                 destroy : function() {\r
9127                         tinymce.dom.Event.clear(this.id);\r
9128                 }\r
9129         });\r
9130 })(tinymce);\r
9131 tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {\r
9132         Container : function(id, s, editor) {\r
9133                 this.parent(id, s, editor);\r
9134 \r
9135                 this.controls = [];\r
9136 \r
9137                 this.lookup = {};\r
9138         },\r
9139 \r
9140         add : function(c) {\r
9141                 this.lookup[c.id] = c;\r
9142                 this.controls.push(c);\r
9143 \r
9144                 return c;\r
9145         },\r
9146 \r
9147         get : function(n) {\r
9148                 return this.lookup[n];\r
9149         }\r
9150 });\r
9151 \r
9152 \r
9153 tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {\r
9154         Separator : function(id, s) {\r
9155                 this.parent(id, s);\r
9156                 this.classPrefix = 'mceSeparator';\r
9157                 this.setDisabled(true);\r
9158         },\r
9159 \r
9160         renderHTML : function() {\r
9161                 return tinymce.DOM.createHTML('span', {'class' : this.classPrefix, role : 'separator', 'aria-orientation' : 'vertical', tabindex : '-1'});\r
9162         }\r
9163 });\r
9164 \r
9165 (function(tinymce) {\r
9166         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
9167 \r
9168         tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', {\r
9169                 MenuItem : function(id, s) {\r
9170                         this.parent(id, s);\r
9171                         this.classPrefix = 'mceMenuItem';\r
9172                 },\r
9173 \r
9174                 setSelected : function(s) {\r
9175                         this.setState('Selected', s);\r
9176                         this.setAriaProperty('checked', !!s);\r
9177                         this.selected = s;\r
9178                 },\r
9179 \r
9180                 isSelected : function() {\r
9181                         return this.selected;\r
9182                 },\r
9183 \r
9184                 postRender : function() {\r
9185                         var t = this;\r
9186                         \r
9187                         t.parent();\r
9188 \r
9189                         // Set pending state\r
9190                         if (is(t.selected))\r
9191                                 t.setSelected(t.selected);\r
9192                 }\r
9193         });\r
9194 })(tinymce);\r
9195 \r
9196 (function(tinymce) {\r
9197         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
9198 \r
9199         tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', {\r
9200                 Menu : function(id, s) {\r
9201                         var t = this;\r
9202 \r
9203                         t.parent(id, s);\r
9204                         t.items = {};\r
9205                         t.collapsed = false;\r
9206                         t.menuCount = 0;\r
9207                         t.onAddItem = new tinymce.util.Dispatcher(this);\r
9208                 },\r
9209 \r
9210                 expand : function(d) {\r
9211                         var t = this;\r
9212 \r
9213                         if (d) {\r
9214                                 walk(t, function(o) {\r
9215                                         if (o.expand)\r
9216                                                 o.expand();\r
9217                                 }, 'items', t);\r
9218                         }\r
9219 \r
9220                         t.collapsed = false;\r
9221                 },\r
9222 \r
9223                 collapse : function(d) {\r
9224                         var t = this;\r
9225 \r
9226                         if (d) {\r
9227                                 walk(t, function(o) {\r
9228                                         if (o.collapse)\r
9229                                                 o.collapse();\r
9230                                 }, 'items', t);\r
9231                         }\r
9232 \r
9233                         t.collapsed = true;\r
9234                 },\r
9235 \r
9236                 isCollapsed : function() {\r
9237                         return this.collapsed;\r
9238                 },\r
9239 \r
9240                 add : function(o) {\r
9241                         if (!o.settings)\r
9242                                 o = new tinymce.ui.MenuItem(o.id || DOM.uniqueId(), o);\r
9243 \r
9244                         this.onAddItem.dispatch(this, o);\r
9245 \r
9246                         return this.items[o.id] = o;\r
9247                 },\r
9248 \r
9249                 addSeparator : function() {\r
9250                         return this.add({separator : true});\r
9251                 },\r
9252 \r
9253                 addMenu : function(o) {\r
9254                         if (!o.collapse)\r
9255                                 o = this.createMenu(o);\r
9256 \r
9257                         this.menuCount++;\r
9258 \r
9259                         return this.add(o);\r
9260                 },\r
9261 \r
9262                 hasMenus : function() {\r
9263                         return this.menuCount !== 0;\r
9264                 },\r
9265 \r
9266                 remove : function(o) {\r
9267                         delete this.items[o.id];\r
9268                 },\r
9269 \r
9270                 removeAll : function() {\r
9271                         var t = this;\r
9272 \r
9273                         walk(t, function(o) {\r
9274                                 if (o.removeAll)\r
9275                                         o.removeAll();\r
9276                                 else\r
9277                                         o.remove();\r
9278 \r
9279                                 o.destroy();\r
9280                         }, 'items', t);\r
9281 \r
9282                         t.items = {};\r
9283                 },\r
9284 \r
9285                 createMenu : function(o) {\r
9286                         var m = new tinymce.ui.Menu(o.id || DOM.uniqueId(), o);\r
9287 \r
9288                         m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem);\r
9289 \r
9290                         return m;\r
9291                 }\r
9292         });\r
9293 })(tinymce);\r
9294 (function(tinymce) {\r
9295         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element;\r
9296 \r
9297         tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', {\r
9298                 DropMenu : function(id, s) {\r
9299                         s = s || {};\r
9300                         s.container = s.container || DOM.doc.body;\r
9301                         s.offset_x = s.offset_x || 0;\r
9302                         s.offset_y = s.offset_y || 0;\r
9303                         s.vp_offset_x = s.vp_offset_x || 0;\r
9304                         s.vp_offset_y = s.vp_offset_y || 0;\r
9305 \r
9306                         if (is(s.icons) && !s.icons)\r
9307                                 s['class'] += ' mceNoIcons';\r
9308 \r
9309                         this.parent(id, s);\r
9310                         this.onShowMenu = new tinymce.util.Dispatcher(this);\r
9311                         this.onHideMenu = new tinymce.util.Dispatcher(this);\r
9312                         this.classPrefix = 'mceMenu';\r
9313                 },\r
9314 \r
9315                 createMenu : function(s) {\r
9316                         var t = this, cs = t.settings, m;\r
9317 \r
9318                         s.container = s.container || cs.container;\r
9319                         s.parent = t;\r
9320                         s.constrain = s.constrain || cs.constrain;\r
9321                         s['class'] = s['class'] || cs['class'];\r
9322                         s.vp_offset_x = s.vp_offset_x || cs.vp_offset_x;\r
9323                         s.vp_offset_y = s.vp_offset_y || cs.vp_offset_y;\r
9324                         s.keyboard_focus = cs.keyboard_focus;\r
9325                         m = new tinymce.ui.DropMenu(s.id || DOM.uniqueId(), s);\r
9326 \r
9327                         m.onAddItem.add(t.onAddItem.dispatch, t.onAddItem);\r
9328 \r
9329                         return m;\r
9330                 },\r
9331                 \r
9332                 focus : function() {\r
9333                         var t = this;\r
9334                         if (t.keyboardNav) {\r
9335                                 t.keyboardNav.focus();\r
9336                         }\r
9337                 },\r
9338 \r
9339                 update : function() {\r
9340                         var t = this, s = t.settings, tb = DOM.get('menu_' + t.id + '_tbl'), co = DOM.get('menu_' + t.id + '_co'), tw, th;\r
9341 \r
9342                         tw = s.max_width ? Math.min(tb.clientWidth, s.max_width) : tb.clientWidth;\r
9343                         th = s.max_height ? Math.min(tb.clientHeight, s.max_height) : tb.clientHeight;\r
9344 \r
9345                         if (!DOM.boxModel)\r
9346                                 t.element.setStyles({width : tw + 2, height : th + 2});\r
9347                         else\r
9348                                 t.element.setStyles({width : tw, height : th});\r
9349 \r
9350                         if (s.max_width)\r
9351                                 DOM.setStyle(co, 'width', tw);\r
9352 \r
9353                         if (s.max_height) {\r
9354                                 DOM.setStyle(co, 'height', th);\r
9355 \r
9356                                 if (tb.clientHeight < s.max_height)\r
9357                                         DOM.setStyle(co, 'overflow', 'hidden');\r
9358                         }\r
9359                 },\r
9360 \r
9361                 showMenu : function(x, y, px) {\r
9362                         var t = this, s = t.settings, co, vp = DOM.getViewPort(), w, h, mx, my, ot = 2, dm, tb, cp = t.classPrefix;\r
9363 \r
9364                         t.collapse(1);\r
9365 \r
9366                         if (t.isMenuVisible)\r
9367                                 return;\r
9368 \r
9369                         if (!t.rendered) {\r
9370                                 co = DOM.add(t.settings.container, t.renderNode());\r
9371 \r
9372                                 each(t.items, function(o) {\r
9373                                         o.postRender();\r
9374                                 });\r
9375 \r
9376                                 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
9377                         } else\r
9378                                 co = DOM.get('menu_' + t.id);\r
9379 \r
9380                         // Move layer out of sight unless it's Opera since it scrolls to top of page due to an bug\r
9381                         if (!tinymce.isOpera)\r
9382                                 DOM.setStyles(co, {left : -0xFFFF , top : -0xFFFF});\r
9383 \r
9384                         DOM.show(co);\r
9385                         t.update();\r
9386 \r
9387                         x += s.offset_x || 0;\r
9388                         y += s.offset_y || 0;\r
9389                         vp.w -= 4;\r
9390                         vp.h -= 4;\r
9391 \r
9392                         // Move inside viewport if not submenu\r
9393                         if (s.constrain) {\r
9394                                 w = co.clientWidth - ot;\r
9395                                 h = co.clientHeight - ot;\r
9396                                 mx = vp.x + vp.w;\r
9397                                 my = vp.y + vp.h;\r
9398 \r
9399                                 if ((x + s.vp_offset_x + w) > mx)\r
9400                                         x = px ? px - w : Math.max(0, (mx - s.vp_offset_x) - w);\r
9401 \r
9402                                 if ((y + s.vp_offset_y + h) > my)\r
9403                                         y = Math.max(0, (my - s.vp_offset_y) - h);\r
9404                         }\r
9405 \r
9406                         DOM.setStyles(co, {left : x , top : y});\r
9407                         t.element.update();\r
9408 \r
9409                         t.isMenuVisible = 1;\r
9410                         t.mouseClickFunc = Event.add(co, 'click', function(e) {\r
9411                                 var m;\r
9412 \r
9413                                 e = e.target;\r
9414 \r
9415                                 if (e && (e = DOM.getParent(e, 'tr')) && !DOM.hasClass(e, cp + 'ItemSub')) {\r
9416                                         m = t.items[e.id];\r
9417 \r
9418                                         if (m.isDisabled())\r
9419                                                 return;\r
9420 \r
9421                                         dm = t;\r
9422 \r
9423                                         while (dm) {\r
9424                                                 if (dm.hideMenu)\r
9425                                                         dm.hideMenu();\r
9426 \r
9427                                                 dm = dm.settings.parent;\r
9428                                         }\r
9429 \r
9430                                         if (m.settings.onclick)\r
9431                                                 m.settings.onclick(e);\r
9432 \r
9433                                         return Event.cancel(e); // Cancel to fix onbeforeunload problem\r
9434                                 }\r
9435                         });\r
9436 \r
9437                         if (t.hasMenus()) {\r
9438                                 t.mouseOverFunc = Event.add(co, 'mouseover', function(e) {\r
9439                                         var m, r, mi;\r
9440 \r
9441                                         e = e.target;\r
9442                                         if (e && (e = DOM.getParent(e, 'tr'))) {\r
9443                                                 m = t.items[e.id];\r
9444 \r
9445                                                 if (t.lastMenu)\r
9446                                                         t.lastMenu.collapse(1);\r
9447 \r
9448                                                 if (m.isDisabled())\r
9449                                                         return;\r
9450 \r
9451                                                 if (e && DOM.hasClass(e, cp + 'ItemSub')) {\r
9452                                                         //p = DOM.getPos(s.container);\r
9453                                                         r = DOM.getRect(e);\r
9454                                                         m.showMenu((r.x + r.w - ot), r.y - ot, r.x);\r
9455                                                         t.lastMenu = m;\r
9456                                                         DOM.addClass(DOM.get(m.id).firstChild, cp + 'ItemActive');\r
9457                                                 }\r
9458                                         }\r
9459                                 });\r
9460                         }\r
9461                         \r
9462                         Event.add(co, 'keydown', t._keyHandler, t);\r
9463 \r
9464                         t.onShowMenu.dispatch(t);\r
9465 \r
9466                         if (s.keyboard_focus) { \r
9467                                 t._setupKeyboardNav(); \r
9468                         }\r
9469                 },\r
9470 \r
9471                 hideMenu : function(c) {\r
9472                         var t = this, co = DOM.get('menu_' + t.id), e;\r
9473 \r
9474                         if (!t.isMenuVisible)\r
9475                                 return;\r
9476 \r
9477                         if (t.keyboardNav) t.keyboardNav.destroy();\r
9478                         Event.remove(co, 'mouseover', t.mouseOverFunc);\r
9479                         Event.remove(co, 'click', t.mouseClickFunc);\r
9480                         Event.remove(co, 'keydown', t._keyHandler);\r
9481                         DOM.hide(co);\r
9482                         t.isMenuVisible = 0;\r
9483 \r
9484                         if (!c)\r
9485                                 t.collapse(1);\r
9486 \r
9487                         if (t.element)\r
9488                                 t.element.hide();\r
9489 \r
9490                         if (e = DOM.get(t.id))\r
9491                                 DOM.removeClass(e.firstChild, t.classPrefix + 'ItemActive');\r
9492 \r
9493                         t.onHideMenu.dispatch(t);\r
9494                 },\r
9495 \r
9496                 add : function(o) {\r
9497                         var t = this, co;\r
9498 \r
9499                         o = t.parent(o);\r
9500 \r
9501                         if (t.isRendered && (co = DOM.get('menu_' + t.id)))\r
9502                                 t._add(DOM.select('tbody', co)[0], o);\r
9503 \r
9504                         return o;\r
9505                 },\r
9506 \r
9507                 collapse : function(d) {\r
9508                         this.parent(d);\r
9509                         this.hideMenu(1);\r
9510                 },\r
9511 \r
9512                 remove : function(o) {\r
9513                         DOM.remove(o.id);\r
9514                         this.destroy();\r
9515 \r
9516                         return this.parent(o);\r
9517                 },\r
9518 \r
9519                 destroy : function() {\r
9520                         var t = this, co = DOM.get('menu_' + t.id);\r
9521 \r
9522                         if (t.keyboardNav) t.keyboardNav.destroy();\r
9523                         Event.remove(co, 'mouseover', t.mouseOverFunc);\r
9524                         Event.remove(DOM.select('a', co), 'focus', t.mouseOverFunc);\r
9525                         Event.remove(co, 'click', t.mouseClickFunc);\r
9526                         Event.remove(co, 'keydown', t._keyHandler);\r
9527 \r
9528                         if (t.element)\r
9529                                 t.element.remove();\r
9530 \r
9531                         DOM.remove(co);\r
9532                 },\r
9533 \r
9534                 renderNode : function() {\r
9535                         var t = this, s = t.settings, n, tb, co, w;\r
9536 \r
9537                         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
9538                         if (t.settings.parent) {\r
9539                                 DOM.setAttrib(w, 'aria-parent', 'menu_' + t.settings.parent.id);\r
9540                         }\r
9541                         co = DOM.add(w, 'div', {role: 'presentation', id : 'menu_' + t.id + '_co', 'class' : t.classPrefix + (s['class'] ? ' ' + s['class'] : '')});\r
9542                         t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
9543 \r
9544                         if (s.menu_line)\r
9545                                 DOM.add(co, 'span', {'class' : t.classPrefix + 'Line'});\r
9546 \r
9547 //                      n = DOM.add(co, 'div', {id : 'menu_' + t.id + '_co', 'class' : 'mceMenuContainer'});\r
9548                         n = DOM.add(co, 'table', {role: 'presentation', id : 'menu_' + t.id + '_tbl', border : 0, cellPadding : 0, cellSpacing : 0});\r
9549                         tb = DOM.add(n, 'tbody');\r
9550 \r
9551                         each(t.items, function(o) {\r
9552                                 t._add(tb, o);\r
9553                         });\r
9554 \r
9555                         t.rendered = true;\r
9556 \r
9557                         return w;\r
9558                 },\r
9559 \r
9560                 // Internal functions\r
9561                 _setupKeyboardNav : function(){\r
9562                         var contextMenu, menuItems, t=this; \r
9563                         contextMenu = DOM.select('#menu_' + t.id)[0];\r
9564                         menuItems = DOM.select('a[role=option]', 'menu_' + t.id);\r
9565                         menuItems.splice(0,0,contextMenu);\r
9566                         t.keyboardNav = new tinymce.ui.KeyboardNavigation({\r
9567                                 root: 'menu_' + t.id,\r
9568                                 items: menuItems,\r
9569                                 onCancel: function() {\r
9570                                         t.hideMenu();\r
9571                                 },\r
9572                                 enableUpDown: true\r
9573                         });\r
9574                         contextMenu.focus();\r
9575                 },\r
9576 \r
9577                 _keyHandler : function(evt) {\r
9578                         var t = this, e;\r
9579                         switch (evt.keyCode) {\r
9580                                 case 37: // Left\r
9581                                         if (t.settings.parent) {\r
9582                                                 t.hideMenu();\r
9583                                                 t.settings.parent.focus();\r
9584                                                 Event.cancel(evt);\r
9585                                         }\r
9586                                         break;\r
9587                                 case 39: // Right\r
9588                                         if (t.mouseOverFunc)\r
9589                                                 t.mouseOverFunc(evt);\r
9590                                         break;\r
9591                         }\r
9592                 },\r
9593 \r
9594                 _add : function(tb, o) {\r
9595                         var n, s = o.settings, a, ro, it, cp = this.classPrefix, ic;\r
9596 \r
9597                         if (s.separator) {\r
9598                                 ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'ItemSeparator'});\r
9599                                 DOM.add(ro, 'td', {'class' : cp + 'ItemSeparator'});\r
9600 \r
9601                                 if (n = ro.previousSibling)\r
9602                                         DOM.addClass(n, 'mceLast');\r
9603 \r
9604                                 return;\r
9605                         }\r
9606 \r
9607                         n = ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'Item ' + cp + 'ItemEnabled'});\r
9608                         n = it = DOM.add(n, s.titleItem ? 'th' : 'td');\r
9609                         n = a = DOM.add(n, 'a', {id: o.id + '_aria',  role: s.titleItem ? 'presentation' : 'option', href : 'javascript:;', onclick : "return false;", onmousedown : 'return false;'});\r
9610 \r
9611                         if (s.parent) {\r
9612                                 DOM.setAttrib(a, 'aria-haspopup', 'true');\r
9613                                 DOM.setAttrib(a, 'aria-owns', 'menu_' + o.id);\r
9614                         }\r
9615 \r
9616                         DOM.addClass(it, s['class']);\r
9617 //                      n = DOM.add(n, 'span', {'class' : 'item'});\r
9618 \r
9619                         ic = DOM.add(n, 'span', {'class' : 'mceIcon' + (s.icon ? ' mce_' + s.icon : '')});\r
9620 \r
9621                         if (s.icon_src)\r
9622                                 DOM.add(ic, 'img', {src : s.icon_src});\r
9623 \r
9624                         n = DOM.add(n, s.element || 'span', {'class' : 'mceText', title : o.settings.title}, o.settings.title);\r
9625 \r
9626                         if (o.settings.style)\r
9627                                 DOM.setAttrib(n, 'style', o.settings.style);\r
9628 \r
9629                         if (tb.childNodes.length == 1)\r
9630                                 DOM.addClass(ro, 'mceFirst');\r
9631 \r
9632                         if ((n = ro.previousSibling) && DOM.hasClass(n, cp + 'ItemSeparator'))\r
9633                                 DOM.addClass(ro, 'mceFirst');\r
9634 \r
9635                         if (o.collapse)\r
9636                                 DOM.addClass(ro, cp + 'ItemSub');\r
9637 \r
9638                         if (n = ro.previousSibling)\r
9639                                 DOM.removeClass(n, 'mceLast');\r
9640 \r
9641                         DOM.addClass(ro, 'mceLast');\r
9642                 }\r
9643         });\r
9644 })(tinymce);\r
9645 (function(tinymce) {\r
9646         var DOM = tinymce.DOM;\r
9647 \r
9648         tinymce.create('tinymce.ui.Button:tinymce.ui.Control', {\r
9649                 Button : function(id, s, ed) {\r
9650                         this.parent(id, s, ed);\r
9651                         this.classPrefix = 'mceButton';\r
9652                 },\r
9653 \r
9654                 renderHTML : function() {\r
9655                         var cp = this.classPrefix, s = this.settings, h, l;\r
9656 \r
9657                         l = DOM.encode(s.label || '');\r
9658                         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
9659                         if (s.image && !(this.editor  &&this.editor.forcedHighContrastMode) )\r
9660                                 h += '<img class="mceIcon" src="' + s.image + '" alt="' + DOM.encode(s.title) + '" />' + l;\r
9661                         else\r
9662                                 h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '');\r
9663 \r
9664                         h += '<span class="mceVoiceLabel mceIconOnly" style="display: none;" id="' + this.id + '_voice">' + s.title + '</span>'; \r
9665                         h += '</a>';\r
9666                         return h;\r
9667                 },\r
9668 \r
9669                 postRender : function() {\r
9670                         var t = this, s = t.settings;\r
9671 \r
9672                         tinymce.dom.Event.add(t.id, 'click', function(e) {\r
9673                                 if (!t.isDisabled())\r
9674                                         return s.onclick.call(s.scope, e);\r
9675                         });\r
9676                 }\r
9677         });\r
9678 })(tinymce);\r
9679 \r
9680 (function(tinymce) {\r
9681         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
9682 \r
9683         tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {\r
9684                 ListBox : function(id, s, ed) {\r
9685                         var t = this;\r
9686 \r
9687                         t.parent(id, s, ed);\r
9688 \r
9689                         t.items = [];\r
9690 \r
9691                         t.onChange = new Dispatcher(t);\r
9692 \r
9693                         t.onPostRender = new Dispatcher(t);\r
9694 \r
9695                         t.onAdd = new Dispatcher(t);\r
9696 \r
9697                         t.onRenderMenu = new tinymce.util.Dispatcher(this);\r
9698 \r
9699                         t.classPrefix = 'mceListBox';\r
9700                 },\r
9701 \r
9702                 select : function(va) {\r
9703                         var t = this, fv, f;\r
9704 \r
9705                         if (va == undefined)\r
9706                                 return t.selectByIndex(-1);\r
9707 \r
9708                         // Is string or number make function selector\r
9709                         if (va && va.call)\r
9710                                 f = va;\r
9711                         else {\r
9712                                 f = function(v) {\r
9713                                         return v == va;\r
9714                                 };\r
9715                         }\r
9716 \r
9717                         // Do we need to do something?\r
9718                         if (va != t.selectedValue) {\r
9719                                 // Find item\r
9720                                 each(t.items, function(o, i) {\r
9721                                         if (f(o.value)) {\r
9722                                                 fv = 1;\r
9723                                                 t.selectByIndex(i);\r
9724                                                 return false;\r
9725                                         }\r
9726                                 });\r
9727 \r
9728                                 if (!fv)\r
9729                                         t.selectByIndex(-1);\r
9730                         }\r
9731                 },\r
9732 \r
9733                 selectByIndex : function(idx) {\r
9734                         var t = this, e, o;\r
9735 \r
9736                         if (idx != t.selectedIndex) {\r
9737                                 e = DOM.get(t.id + '_text');\r
9738                                 o = t.items[idx];\r
9739 \r
9740                                 if (o) {\r
9741                                         t.selectedValue = o.value;\r
9742                                         t.selectedIndex = idx;\r
9743                                         DOM.setHTML(e, DOM.encode(o.title));\r
9744                                         DOM.removeClass(e, 'mceTitle');\r
9745                                         DOM.setAttrib(t.id, 'aria-valuenow', o.title);\r
9746                                 } else {\r
9747                                         DOM.setHTML(e, DOM.encode(t.settings.title));\r
9748                                         DOM.addClass(e, 'mceTitle');\r
9749                                         t.selectedValue = t.selectedIndex = null;\r
9750                                         DOM.setAttrib(t.id, 'aria-valuenow', t.settings.title);\r
9751                                 }\r
9752                                 e = 0;\r
9753                         }\r
9754                 },\r
9755 \r
9756                 add : function(n, v, o) {\r
9757                         var t = this;\r
9758 \r
9759                         o = o || {};\r
9760                         o = tinymce.extend(o, {\r
9761                                 title : n,\r
9762                                 value : v\r
9763                         });\r
9764 \r
9765                         t.items.push(o);\r
9766                         t.onAdd.dispatch(t, o);\r
9767                 },\r
9768 \r
9769                 getLength : function() {\r
9770                         return this.items.length;\r
9771                 },\r
9772 \r
9773                 renderHTML : function() {\r
9774                         var h = '', t = this, s = t.settings, cp = t.classPrefix;\r
9775 \r
9776                         h = '<span role="button" aria-haspopup="true" aria-labelledby="' + t.id +'_text" aria-describedby="' + t.id + '_voiceDesc"><table role="presentation" tabindex="0" id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>';\r
9777                         h += '<td>' + DOM.createHTML('span', {id: t.id + '_voiceDesc', 'class': 'voiceLabel', style:'display:none;'}, t.settings.title); \r
9778                         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
9779                         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
9780                         h += '</tr></tbody></table></span>';\r
9781 \r
9782                         return h;\r
9783                 },\r
9784 \r
9785                 showMenu : function() {\r
9786                         var t = this, p2, e = DOM.get(this.id), m;\r
9787 \r
9788                         if (t.isDisabled() || t.items.length == 0)\r
9789                                 return;\r
9790 \r
9791                         if (t.menu && t.menu.isMenuVisible)\r
9792                                 return t.hideMenu();\r
9793 \r
9794                         if (!t.isMenuRendered) {\r
9795                                 t.renderMenu();\r
9796                                 t.isMenuRendered = true;\r
9797                         }\r
9798 \r
9799                         p2 = DOM.getPos(e);\r
9800 \r
9801                         m = t.menu;\r
9802                         m.settings.offset_x = p2.x;\r
9803                         m.settings.offset_y = p2.y;\r
9804                         m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus\r
9805 \r
9806                         // Select in menu\r
9807                         if (t.oldID)\r
9808                                 m.items[t.oldID].setSelected(0);\r
9809 \r
9810                         each(t.items, function(o) {\r
9811                                 if (o.value === t.selectedValue) {\r
9812                                         m.items[o.id].setSelected(1);\r
9813                                         t.oldID = o.id;\r
9814                                 }\r
9815                         });\r
9816 \r
9817                         m.showMenu(0, e.clientHeight);\r
9818 \r
9819                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
9820                         DOM.addClass(t.id, t.classPrefix + 'Selected');\r
9821 \r
9822                         //DOM.get(t.id + '_text').focus();\r
9823                 },\r
9824 \r
9825                 hideMenu : function(e) {\r
9826                         var t = this;\r
9827 \r
9828                         if (t.menu && t.menu.isMenuVisible) {\r
9829                                 DOM.removeClass(t.id, t.classPrefix + 'Selected');\r
9830 \r
9831                                 // Prevent double toogles by canceling the mouse click event to the button\r
9832                                 if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open'))\r
9833                                         return;\r
9834 \r
9835                                 if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
9836                                         DOM.removeClass(t.id, t.classPrefix + 'Selected');\r
9837                                         Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
9838                                         t.menu.hideMenu();\r
9839                                 }\r
9840                         }\r
9841                 },\r
9842 \r
9843                 renderMenu : function() {\r
9844                         var t = this, m;\r
9845 \r
9846                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
9847                                 menu_line : 1,\r
9848                                 'class' : t.classPrefix + 'Menu mceNoIcons',\r
9849                                 max_width : 150,\r
9850                                 max_height : 150\r
9851                         });\r
9852 \r
9853                         m.onHideMenu.add(function() {\r
9854                                 t.hideMenu();\r
9855                                 t.focus();\r
9856                         });\r
9857 \r
9858                         m.add({\r
9859                                 title : t.settings.title,\r
9860                                 'class' : 'mceMenuItemTitle',\r
9861                                 onclick : function() {\r
9862                                         if (t.settings.onselect('') !== false)\r
9863                                                 t.select(''); // Must be runned after\r
9864                                 }\r
9865                         });\r
9866 \r
9867                         each(t.items, function(o) {\r
9868                                 // No value then treat it as a title\r
9869                                 if (o.value === undefined) {\r
9870                                         m.add({\r
9871                                                 title : o.title,\r
9872                                                 'class' : 'mceMenuItemTitle',\r
9873                                                 onclick : function() {\r
9874                                                         if (t.settings.onselect('') !== false)\r
9875                                                                 t.select(''); // Must be runned after\r
9876                                                 }\r
9877                                         });\r
9878                                 } else {\r
9879                                         o.id = DOM.uniqueId();\r
9880                                         o.onclick = function() {\r
9881                                                 if (t.settings.onselect(o.value) !== false)\r
9882                                                         t.select(o.value); // Must be runned after\r
9883                                         };\r
9884 \r
9885                                         m.add(o);\r
9886                                 }\r
9887                         });\r
9888 \r
9889                         t.onRenderMenu.dispatch(t, m);\r
9890                         t.menu = m;\r
9891                 },\r
9892 \r
9893                 postRender : function() {\r
9894                         var t = this, cp = t.classPrefix;\r
9895 \r
9896                         Event.add(t.id, 'click', t.showMenu, t);\r
9897                         Event.add(t.id, 'keydown', function(evt) {\r
9898                                 if (evt.keyCode == 32) { // Space\r
9899                                         t.showMenu(evt);\r
9900                                         Event.cancel(evt);\r
9901                                 }\r
9902                         });\r
9903                         Event.add(t.id, 'focus', function() {\r
9904                                 if (!t._focused) {\r
9905                                         t.keyDownHandler = Event.add(t.id, 'keydown', function(e) {\r
9906                                                 if (e.keyCode == 40) {\r
9907                                                         t.showMenu();\r
9908                                                         Event.cancel(e);\r
9909                                                 }\r
9910                                         });\r
9911                                         t.keyPressHandler = Event.add(t.id, 'keypress', function(e) {\r
9912                                                 var v;\r
9913                                                 if (e.keyCode == 13) {\r
9914                                                         // Fake select on enter\r
9915                                                         v = t.selectedValue;\r
9916                                                         t.selectedValue = null; // Needs to be null to fake change\r
9917                                                         Event.cancel(e);\r
9918                                                         t.settings.onselect(v);\r
9919                                                 }\r
9920                                         });\r
9921                                 }\r
9922 \r
9923                                 t._focused = 1;\r
9924                         });\r
9925                         Event.add(t.id, 'blur', function() {\r
9926                                 Event.remove(t.id, 'keydown', t.keyDownHandler);\r
9927                                 Event.remove(t.id, 'keypress', t.keyPressHandler);\r
9928                                 t._focused = 0;\r
9929                         });\r
9930 \r
9931                         // Old IE doesn't have hover on all elements\r
9932                         if (tinymce.isIE6 || !DOM.boxModel) {\r
9933                                 Event.add(t.id, 'mouseover', function() {\r
9934                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
9935                                                 DOM.addClass(t.id, cp + 'Hover');\r
9936                                 });\r
9937 \r
9938                                 Event.add(t.id, 'mouseout', function() {\r
9939                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
9940                                                 DOM.removeClass(t.id, cp + 'Hover');\r
9941                                 });\r
9942                         }\r
9943 \r
9944                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
9945                 },\r
9946 \r
9947                 destroy : function() {\r
9948                         this.parent();\r
9949 \r
9950                         Event.clear(this.id + '_text');\r
9951                         Event.clear(this.id + '_open');\r
9952                 }\r
9953         });\r
9954 })(tinymce);\r
9955 (function(tinymce) {\r
9956         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
9957 \r
9958         tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {\r
9959                 NativeListBox : function(id, s) {\r
9960                         this.parent(id, s);\r
9961                         this.classPrefix = 'mceNativeListBox';\r
9962                 },\r
9963 \r
9964                 setDisabled : function(s) {\r
9965                         DOM.get(this.id).disabled = s;\r
9966                         this.setAriaProperty('disabled', s);\r
9967                 },\r
9968 \r
9969                 isDisabled : function() {\r
9970                         return DOM.get(this.id).disabled;\r
9971                 },\r
9972 \r
9973                 select : function(va) {\r
9974                         var t = this, fv, f;\r
9975 \r
9976                         if (va == undefined)\r
9977                                 return t.selectByIndex(-1);\r
9978 \r
9979                         // Is string or number make function selector\r
9980                         if (va && va.call)\r
9981                                 f = va;\r
9982                         else {\r
9983                                 f = function(v) {\r
9984                                         return v == va;\r
9985                                 };\r
9986                         }\r
9987 \r
9988                         // Do we need to do something?\r
9989                         if (va != t.selectedValue) {\r
9990                                 // Find item\r
9991                                 each(t.items, function(o, i) {\r
9992                                         if (f(o.value)) {\r
9993                                                 fv = 1;\r
9994                                                 t.selectByIndex(i);\r
9995                                                 return false;\r
9996                                         }\r
9997                                 });\r
9998 \r
9999                                 if (!fv)\r
10000                                         t.selectByIndex(-1);\r
10001                         }\r
10002                 },\r
10003 \r
10004                 selectByIndex : function(idx) {\r
10005                         DOM.get(this.id).selectedIndex = idx + 1;\r
10006                         this.selectedValue = this.items[idx] ? this.items[idx].value : null;\r
10007                 },\r
10008 \r
10009                 add : function(n, v, a) {\r
10010                         var o, t = this;\r
10011 \r
10012                         a = a || {};\r
10013                         a.value = v;\r
10014 \r
10015                         if (t.isRendered())\r
10016                                 DOM.add(DOM.get(this.id), 'option', a, n);\r
10017 \r
10018                         o = {\r
10019                                 title : n,\r
10020                                 value : v,\r
10021                                 attribs : a\r
10022                         };\r
10023 \r
10024                         t.items.push(o);\r
10025                         t.onAdd.dispatch(t, o);\r
10026                 },\r
10027 \r
10028                 getLength : function() {\r
10029                         return this.items.length;\r
10030                 },\r
10031 \r
10032                 renderHTML : function() {\r
10033                         var h, t = this;\r
10034 \r
10035                         h = DOM.createHTML('option', {value : ''}, '-- ' + t.settings.title + ' --');\r
10036 \r
10037                         each(t.items, function(it) {\r
10038                                 h += DOM.createHTML('option', {value : it.value}, it.title);\r
10039                         });\r
10040 \r
10041                         h = DOM.createHTML('select', {id : t.id, 'class' : 'mceNativeListBox', 'aria-labelledby': t.id + '_aria'}, h);\r
10042                         h += DOM.createHTML('span', {id : t.id + '_aria', 'style': 'display: none'}, t.settings.title);\r
10043                         return h;\r
10044                 },\r
10045 \r
10046                 postRender : function() {\r
10047                         var t = this, ch, changeListenerAdded = true;\r
10048 \r
10049                         t.rendered = true;\r
10050 \r
10051                         function onChange(e) {\r
10052                                 var v = t.items[e.target.selectedIndex - 1];\r
10053 \r
10054                                 if (v && (v = v.value)) {\r
10055                                         t.onChange.dispatch(t, v);\r
10056 \r
10057                                         if (t.settings.onselect)\r
10058                                                 t.settings.onselect(v);\r
10059                                 }\r
10060                         };\r
10061 \r
10062                         Event.add(t.id, 'change', onChange);\r
10063 \r
10064                         // Accessibility keyhandler\r
10065                         Event.add(t.id, 'keydown', function(e) {\r
10066                                 var bf;\r
10067 \r
10068                                 Event.remove(t.id, 'change', ch);\r
10069                                 changeListenerAdded = false;\r
10070 \r
10071                                 bf = Event.add(t.id, 'blur', function() {\r
10072                                         if (changeListenerAdded) return;\r
10073                                         changeListenerAdded = true;\r
10074                                         Event.add(t.id, 'change', onChange);\r
10075                                         Event.remove(t.id, 'blur', bf);\r
10076                                 });\r
10077 \r
10078                                 //prevent default left and right keys on chrome - so that the keyboard navigation is used.\r
10079                                 if (tinymce.isWebKit && (e.keyCode==37 ||e.keyCode==39)) {\r
10080                                         return Event.prevent(e);\r
10081                                 }\r
10082                                 \r
10083                                 if (e.keyCode == 13 || e.keyCode == 32) {\r
10084                                         onChange(e);\r
10085                                         return Event.cancel(e);\r
10086                                 }\r
10087                         });\r
10088 \r
10089                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
10090                 }\r
10091         });\r
10092 })(tinymce);\r
10093 \r
10094 (function(tinymce) {\r
10095         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
10096 \r
10097         tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', {\r
10098                 MenuButton : function(id, s, ed) {\r
10099                         this.parent(id, s, ed);\r
10100 \r
10101                         this.onRenderMenu = new tinymce.util.Dispatcher(this);\r
10102 \r
10103                         s.menu_container = s.menu_container || DOM.doc.body;\r
10104                 },\r
10105 \r
10106                 showMenu : function() {\r
10107                         var t = this, p1, p2, e = DOM.get(t.id), m;\r
10108 \r
10109                         if (t.isDisabled())\r
10110                                 return;\r
10111 \r
10112                         if (!t.isMenuRendered) {\r
10113                                 t.renderMenu();\r
10114                                 t.isMenuRendered = true;\r
10115                         }\r
10116 \r
10117                         if (t.isMenuVisible)\r
10118                                 return t.hideMenu();\r
10119 \r
10120                         p1 = DOM.getPos(t.settings.menu_container);\r
10121                         p2 = DOM.getPos(e);\r
10122 \r
10123                         m = t.menu;\r
10124                         m.settings.offset_x = p2.x;\r
10125                         m.settings.offset_y = p2.y;\r
10126                         m.settings.vp_offset_x = p2.x;\r
10127                         m.settings.vp_offset_y = p2.y;\r
10128                         m.settings.keyboard_focus = t._focused;\r
10129                         m.showMenu(0, e.clientHeight);\r
10130 \r
10131                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
10132                         t.setState('Selected', 1);\r
10133 \r
10134                         t.isMenuVisible = 1;\r
10135                 },\r
10136 \r
10137                 renderMenu : function() {\r
10138                         var t = this, m;\r
10139 \r
10140                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
10141                                 menu_line : 1,\r
10142                                 'class' : this.classPrefix + 'Menu',\r
10143                                 icons : t.settings.icons\r
10144                         });\r
10145 \r
10146                         m.onHideMenu.add(function() {\r
10147                                 t.hideMenu();\r
10148                                 t.focus();\r
10149                         });\r
10150 \r
10151                         t.onRenderMenu.dispatch(t, m);\r
10152                         t.menu = m;\r
10153                 },\r
10154 \r
10155                 hideMenu : function(e) {\r
10156                         var t = this;\r
10157 \r
10158                         // Prevent double toogles by canceling the mouse click event to the button\r
10159                         if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id || e.id === t.id + '_open';}))\r
10160                                 return;\r
10161 \r
10162                         if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
10163                                 t.setState('Selected', 0);\r
10164                                 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
10165                                 if (t.menu)\r
10166                                         t.menu.hideMenu();\r
10167                         }\r
10168 \r
10169                         t.isMenuVisible = 0;\r
10170                 },\r
10171 \r
10172                 postRender : function() {\r
10173                         var t = this, s = t.settings;\r
10174 \r
10175                         Event.add(t.id, 'click', function() {\r
10176                                 if (!t.isDisabled()) {\r
10177                                         if (s.onclick)\r
10178                                                 s.onclick(t.value);\r
10179 \r
10180                                         t.showMenu();\r
10181                                 }\r
10182                         });\r
10183                 }\r
10184         });\r
10185 })(tinymce);\r
10186 \r
10187 (function(tinymce) {\r
10188         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
10189 \r
10190         tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', {\r
10191                 SplitButton : function(id, s, ed) {\r
10192                         this.parent(id, s, ed);\r
10193                         this.classPrefix = 'mceSplitButton';\r
10194                 },\r
10195 \r
10196                 renderHTML : function() {\r
10197                         var h, t = this, s = t.settings, h1;\r
10198 \r
10199                         h = '<tbody><tr>';\r
10200 \r
10201                         if (s.image)\r
10202                                 h1 = DOM.createHTML('img ', {src : s.image, role: 'presentation', 'class' : 'mceAction ' + s['class']});\r
10203                         else\r
10204                                 h1 = DOM.createHTML('span', {'class' : 'mceAction ' + s['class']}, '');\r
10205 \r
10206                         h1 += DOM.createHTML('span', {'class': 'mceVoiceLabel mceIconOnly', id: t.id + '_voice', style: 'display:none;'}, s.title);\r
10207                         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
10208         \r
10209                         h1 = DOM.createHTML('span', {'class' : 'mceOpen ' + s['class']}, '<span style="display:none;" class="mceIconOnly" aria-hidden="true">\u25BC</span>');\r
10210                         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
10211 \r
10212                         h += '</tr></tbody>';\r
10213                         h = DOM.createHTML('table', {id : t.id, role: 'presentation', tabindex: '0',  'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', title : s.title}, h);\r
10214                         return DOM.createHTML('span', {role: 'button', 'aria-labelledby': t.id + '_voice', 'aria-haspopup': 'true'}, h);\r
10215                 },\r
10216 \r
10217                 postRender : function() {\r
10218                         var t = this, s = t.settings, activate;\r
10219 \r
10220                         if (s.onclick) {\r
10221                                 activate = function(evt) {\r
10222                                         if (!t.isDisabled()) {\r
10223                                                 s.onclick(t.value);\r
10224                                                 Event.cancel(evt);\r
10225                                         }\r
10226                                 };\r
10227                                 Event.add(t.id + '_action', 'click', activate);\r
10228                                 Event.add(t.id, ['click', 'keydown'], function(evt) {\r
10229                                         var DOM_VK_SPACE = 32, DOM_VK_ENTER = 14, DOM_VK_RETURN = 13, DOM_VK_UP = 38, DOM_VK_DOWN = 40;\r
10230                                         if ((evt.keyCode === 32 || evt.keyCode === 13 || evt.keyCode === 14) && !evt.altKey && !evt.ctrlKey && !evt.metaKey) {\r
10231                                                 activate();\r
10232                                                 Event.cancel(evt);\r
10233                                         } else if (evt.type === 'click' || evt.keyCode === DOM_VK_DOWN) {\r
10234                                                 t.showMenu();\r
10235                                                 Event.cancel(evt);\r
10236                                         }\r
10237                                 });\r
10238                         }\r
10239 \r
10240                         Event.add(t.id + '_open', 'click', function (evt) {\r
10241                                 t.showMenu();\r
10242                                 Event.cancel(evt);\r
10243                         });\r
10244                         Event.add([t.id, t.id + '_open'], 'focus', function() {t._focused = 1;});\r
10245                         Event.add([t.id, t.id + '_open'], 'blur', function() {t._focused = 0;});\r
10246 \r
10247                         // Old IE doesn't have hover on all elements\r
10248                         if (tinymce.isIE6 || !DOM.boxModel) {\r
10249                                 Event.add(t.id, 'mouseover', function() {\r
10250                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
10251                                                 DOM.addClass(t.id, 'mceSplitButtonHover');\r
10252                                 });\r
10253 \r
10254                                 Event.add(t.id, 'mouseout', function() {\r
10255                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
10256                                                 DOM.removeClass(t.id, 'mceSplitButtonHover');\r
10257                                 });\r
10258                         }\r
10259                 },\r
10260 \r
10261                 destroy : function() {\r
10262                         this.parent();\r
10263 \r
10264                         Event.clear(this.id + '_action');\r
10265                         Event.clear(this.id + '_open');\r
10266                         Event.clear(this.id);\r
10267                 }\r
10268         });\r
10269 })(tinymce);\r
10270 \r
10271 (function(tinymce) {\r
10272         var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each;\r
10273 \r
10274         tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', {\r
10275                 ColorSplitButton : function(id, s, ed) {\r
10276                         var t = this;\r
10277 \r
10278                         t.parent(id, s, ed);\r
10279 \r
10280                         t.settings = s = tinymce.extend({\r
10281                                 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
10282                                 grid_width : 8,\r
10283                                 default_color : '#888888'\r
10284                         }, t.settings);\r
10285 \r
10286                         t.onShowMenu = new tinymce.util.Dispatcher(t);\r
10287 \r
10288                         t.onHideMenu = new tinymce.util.Dispatcher(t);\r
10289 \r
10290                         t.value = s.default_color;\r
10291                 },\r
10292 \r
10293                 showMenu : function() {\r
10294                         var t = this, r, p, e, p2;\r
10295 \r
10296                         if (t.isDisabled())\r
10297                                 return;\r
10298 \r
10299                         if (!t.isMenuRendered) {\r
10300                                 t.renderMenu();\r
10301                                 t.isMenuRendered = true;\r
10302                         }\r
10303 \r
10304                         if (t.isMenuVisible)\r
10305                                 return t.hideMenu();\r
10306 \r
10307                         e = DOM.get(t.id);\r
10308                         DOM.show(t.id + '_menu');\r
10309                         DOM.addClass(e, 'mceSplitButtonSelected');\r
10310                         p2 = DOM.getPos(e);\r
10311                         DOM.setStyles(t.id + '_menu', {\r
10312                                 left : p2.x,\r
10313                                 top : p2.y + e.clientHeight,\r
10314                                 zIndex : 200000\r
10315                         });\r
10316                         e = 0;\r
10317 \r
10318                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
10319                         t.onShowMenu.dispatch(t);\r
10320 \r
10321                         if (t._focused) {\r
10322                                 t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) {\r
10323                                         if (e.keyCode == 27)\r
10324                                                 t.hideMenu();\r
10325                                 });\r
10326 \r
10327                                 DOM.select('a', t.id + '_menu')[0].focus(); // Select first link\r
10328                         }\r
10329 \r
10330                         t.isMenuVisible = 1;\r
10331                 },\r
10332 \r
10333                 hideMenu : function(e) {\r
10334                         var t = this;\r
10335 \r
10336                         if (t.isMenuVisible) {\r
10337                                 // Prevent double toogles by canceling the mouse click event to the button\r
10338                                 if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';}))\r
10339                                         return;\r
10340 \r
10341                                 if (!e || !DOM.getParent(e.target, '.mceSplitButtonMenu')) {\r
10342                                         DOM.removeClass(t.id, 'mceSplitButtonSelected');\r
10343                                         Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
10344                                         Event.remove(t.id + '_menu', 'keydown', t._keyHandler);\r
10345                                         DOM.hide(t.id + '_menu');\r
10346                                 }\r
10347 \r
10348                                 t.isMenuVisible = 0;\r
10349                                 t.onHideMenu.dispatch();\r
10350                         }\r
10351                 },\r
10352 \r
10353                 renderMenu : function() {\r
10354                         var t = this, m, i = 0, s = t.settings, n, tb, tr, w, context;\r
10355 \r
10356                         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
10357                         m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'});\r
10358                         DOM.add(m, 'span', {'class' : 'mceMenuLine'});\r
10359 \r
10360                         n = DOM.add(m, 'table', {role: 'presentation', 'class' : 'mceColorSplitMenu'});\r
10361                         tb = DOM.add(n, 'tbody');\r
10362 \r
10363                         // Generate color grid\r
10364                         i = 0;\r
10365                         each(is(s.colors, 'array') ? s.colors : s.colors.split(','), function(c) {\r
10366                                 c = c.replace(/^#/, '');\r
10367 \r
10368                                 if (!i--) {\r
10369                                         tr = DOM.add(tb, 'tr');\r
10370                                         i = s.grid_width - 1;\r
10371                                 }\r
10372 \r
10373                                 n = DOM.add(tr, 'td');\r
10374                                 n = DOM.add(n, 'a', {\r
10375                                         role : 'option',\r
10376                                         href : 'javascript:;',\r
10377                                         style : {\r
10378                                                 backgroundColor : '#' + c\r
10379                                         },\r
10380                                         'title': t.editor.getLang('colors.' + c, c),\r
10381                                         'data-mce-color' : '#' + c\r
10382                                 });\r
10383 \r
10384                                 if (t.editor.forcedHighContrastMode) {\r
10385                                         n = DOM.add(n, 'canvas', { width: 16, height: 16, 'aria-hidden': 'true' });\r
10386                                         if (n.getContext && (context = n.getContext("2d"))) {\r
10387                                                 context.fillStyle = '#' + c;\r
10388                                                 context.fillRect(0, 0, 16, 16);\r
10389                                         } else {\r
10390                                                 // No point leaving a canvas element around if it's not supported for drawing on anyway.\r
10391                                                 DOM.remove(n);\r
10392                                         }\r
10393                                 }\r
10394                         });\r
10395 \r
10396                         if (s.more_colors_func) {\r
10397                                 n = DOM.add(tb, 'tr');\r
10398                                 n = DOM.add(n, 'td', {colspan : s.grid_width, 'class' : 'mceMoreColors'});\r
10399                                 n = DOM.add(n, 'a', {role: 'option', id : t.id + '_more', href : 'javascript:;', onclick : 'return false;', 'class' : 'mceMoreColors'}, s.more_colors_title);\r
10400 \r
10401                                 Event.add(n, 'click', function(e) {\r
10402                                         s.more_colors_func.call(s.more_colors_scope || this);\r
10403                                         return Event.cancel(e); // Cancel to fix onbeforeunload problem\r
10404                                 });\r
10405                         }\r
10406 \r
10407                         DOM.addClass(m, 'mceColorSplitMenu');\r
10408                         \r
10409                         new tinymce.ui.KeyboardNavigation({\r
10410                                 root: t.id + '_menu',\r
10411                                 items: DOM.select('a', t.id + '_menu'),\r
10412                                 onCancel: function() {\r
10413                                         t.hideMenu();\r
10414                                         t.focus();\r
10415                                 }\r
10416                         });\r
10417 \r
10418                         // Prevent IE from scrolling and hindering click to occur #4019\r
10419                         Event.add(t.id + '_menu', 'mousedown', function(e) {return Event.cancel(e);});\r
10420 \r
10421                         Event.add(t.id + '_menu', 'click', function(e) {\r
10422                                 var c;\r
10423 \r
10424                                 e = DOM.getParent(e.target, 'a', tb);\r
10425 \r
10426                                 if (e && e.nodeName.toLowerCase() == 'a' && (c = e.getAttribute('data-mce-color')))\r
10427                                         t.setColor(c);\r
10428 \r
10429                                 return Event.cancel(e); // Prevent IE auto save warning\r
10430                         });\r
10431 \r
10432                         return w;\r
10433                 },\r
10434 \r
10435                 setColor : function(c) {\r
10436                         this.displayColor(c);\r
10437                         this.hideMenu();\r
10438                         this.settings.onselect(c);\r
10439                 },\r
10440                 \r
10441                 displayColor : function(c) {\r
10442                         var t = this;\r
10443 \r
10444                         DOM.setStyle(t.id + '_preview', 'backgroundColor', c);\r
10445 \r
10446                         t.value = c;\r
10447                 },\r
10448 \r
10449                 postRender : function() {\r
10450                         var t = this, id = t.id;\r
10451 \r
10452                         t.parent();\r
10453                         DOM.add(id + '_action', 'div', {id : id + '_preview', 'class' : 'mceColorPreview'});\r
10454                         DOM.setStyle(t.id + '_preview', 'backgroundColor', t.value);\r
10455                 },\r
10456 \r
10457                 destroy : function() {\r
10458                         this.parent();\r
10459 \r
10460                         Event.clear(this.id + '_menu');\r
10461                         Event.clear(this.id + '_more');\r
10462                         DOM.remove(this.id + '_menu');\r
10463                 }\r
10464         });\r
10465 })(tinymce);\r
10466 \r
10467 (function(tinymce) {\r
10468 // Shorten class names\r
10469 var dom = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event;\r
10470 tinymce.create('tinymce.ui.ToolbarGroup:tinymce.ui.Container', {\r
10471         renderHTML : function() {\r
10472                 var t = this, h = [], controls = t.controls, each = tinymce.each, settings = t.settings;\r
10473 \r
10474                 h.push('<div id="' + t.id + '" role="group" aria-labelledby="' + t.id + '_voice">');\r
10475                 //TODO: ACC test this out - adding a role = application for getting the landmarks working well.\r
10476                 h.push("<span role='application'>");\r
10477                 h.push('<span id="' + t.id + '_voice" class="mceVoiceLabel" style="display:none;">' + dom.encode(settings.name) + '</span>');\r
10478                 each(controls, function(toolbar) {\r
10479                         h.push(toolbar.renderHTML());\r
10480                 });\r
10481                 h.push("</span>");\r
10482                 h.push('</div>');\r
10483 \r
10484                 return h.join('');\r
10485         },\r
10486         \r
10487         focus : function() {\r
10488                 this.keyNav.focus();\r
10489         },\r
10490         \r
10491         postRender : function() {\r
10492                 var t = this, items = [];\r
10493 \r
10494                 each(t.controls, function(toolbar) {\r
10495                         each (toolbar.controls, function(control) {\r
10496                                 if (control.id) {\r
10497                                         items.push(control);\r
10498                                 }\r
10499                         });\r
10500                 });\r
10501 \r
10502                 t.keyNav = new tinymce.ui.KeyboardNavigation({\r
10503                         root: t.id,\r
10504                         items: items,\r
10505                         onCancel: function() {\r
10506                                 t.editor.focus();\r
10507                         },\r
10508                         excludeFromTabOrder: !t.settings.tab_focus_toolbar\r
10509                 });\r
10510         },\r
10511         \r
10512         destroy : function() {\r
10513                 var self = this;\r
10514 \r
10515                 self.parent();\r
10516                 self.keyNav.destroy();\r
10517                 Event.clear(self.id);\r
10518         }\r
10519 });\r
10520 })(tinymce);\r
10521 \r
10522 (function(tinymce) {\r
10523 // Shorten class names\r
10524 var dom = tinymce.DOM, each = tinymce.each;\r
10525 tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {\r
10526         renderHTML : function() {\r
10527                 var t = this, h = '', c, co, s = t.settings, i, pr, nx, cl;\r
10528 \r
10529                 cl = t.controls;\r
10530                 for (i=0; i<cl.length; i++) {\r
10531                         // Get current control, prev control, next control and if the control is a list box or not\r
10532                         co = cl[i];\r
10533                         pr = cl[i - 1];\r
10534                         nx = cl[i + 1];\r
10535 \r
10536                         // Add toolbar start\r
10537                         if (i === 0) {\r
10538                                 c = 'mceToolbarStart';\r
10539 \r
10540                                 if (co.Button)\r
10541                                         c += ' mceToolbarStartButton';\r
10542                                 else if (co.SplitButton)\r
10543                                         c += ' mceToolbarStartSplitButton';\r
10544                                 else if (co.ListBox)\r
10545                                         c += ' mceToolbarStartListBox';\r
10546 \r
10547                                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
10548                         }\r
10549 \r
10550                         // Add toolbar end before list box and after the previous button\r
10551                         // This is to fix the o2k7 editor skins\r
10552                         if (pr && co.ListBox) {\r
10553                                 if (pr.Button || pr.SplitButton)\r
10554                                         h += dom.createHTML('td', {'class' : 'mceToolbarEnd'}, dom.createHTML('span', null, '<!-- IE -->'));\r
10555                         }\r
10556 \r
10557                         // Render control HTML\r
10558 \r
10559                         // IE 8 quick fix, needed to propertly generate a hit area for anchors\r
10560                         if (dom.stdMode)\r
10561                                 h += '<td style="position: relative">' + co.renderHTML() + '</td>';\r
10562                         else\r
10563                                 h += '<td>' + co.renderHTML() + '</td>';\r
10564 \r
10565                         // Add toolbar start after list box and before the next button\r
10566                         // This is to fix the o2k7 editor skins\r
10567                         if (nx && co.ListBox) {\r
10568                                 if (nx.Button || nx.SplitButton)\r
10569                                         h += dom.createHTML('td', {'class' : 'mceToolbarStart'}, dom.createHTML('span', null, '<!-- IE -->'));\r
10570                         }\r
10571                 }\r
10572 \r
10573                 c = 'mceToolbarEnd';\r
10574 \r
10575                 if (co.Button)\r
10576                         c += ' mceToolbarEndButton';\r
10577                 else if (co.SplitButton)\r
10578                         c += ' mceToolbarEndSplitButton';\r
10579                 else if (co.ListBox)\r
10580                         c += ' mceToolbarEndListBox';\r
10581 \r
10582                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
10583 \r
10584                 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
10585         }\r
10586 });\r
10587 })(tinymce);\r
10588 \r
10589 (function(tinymce) {\r
10590         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;\r
10591 \r
10592         tinymce.create('tinymce.AddOnManager', {\r
10593                 AddOnManager : function() {\r
10594                         var self = this;\r
10595 \r
10596                         self.items = [];\r
10597                         self.urls = {};\r
10598                         self.lookup = {};\r
10599                         self.onAdd = new Dispatcher(self);\r
10600                 },\r
10601 \r
10602                 get : function(n) {\r
10603                         if (this.lookup[n]) {\r
10604                                 return this.lookup[n].instance;\r
10605                         } else {\r
10606                                 return undefined;\r
10607                         }\r
10608                 },\r
10609 \r
10610                 dependencies : function(n) {\r
10611                         var result;\r
10612                         if (this.lookup[n]) {\r
10613                                 result = this.lookup[n].dependencies;\r
10614                         }\r
10615                         return result || [];\r
10616                 },\r
10617 \r
10618                 requireLangPack : function(n) {\r
10619                         var s = tinymce.settings;\r
10620 \r
10621                         if (s && s.language && s.language_load !== false)\r
10622                                 tinymce.ScriptLoader.add(this.urls[n] + '/langs/' + s.language + '.js');\r
10623                 },\r
10624 \r
10625                 add : function(id, o, dependencies) {\r
10626                         this.items.push(o);\r
10627                         this.lookup[id] = {instance:o, dependencies:dependencies};\r
10628                         this.onAdd.dispatch(this, id, o);\r
10629 \r
10630                         return o;\r
10631                 },\r
10632                 createUrl: function(baseUrl, dep) {\r
10633                         if (typeof dep === "object") {\r
10634                                 return dep\r
10635                         } else {\r
10636                                 return {prefix: baseUrl.prefix, resource: dep, suffix: baseUrl.suffix};\r
10637                         }\r
10638                 },\r
10639 \r
10640                 addComponents: function(pluginName, scripts) {\r
10641                         var pluginUrl = this.urls[pluginName];\r
10642                         tinymce.each(scripts, function(script){\r
10643                                 tinymce.ScriptLoader.add(pluginUrl+"/"+script); \r
10644                         });\r
10645                 },\r
10646 \r
10647                 load : function(n, u, cb, s) {\r
10648                         var t = this, url = u;\r
10649 \r
10650                         function loadDependencies() {\r
10651                                 var dependencies = t.dependencies(n);\r
10652                                 tinymce.each(dependencies, function(dep) {\r
10653                                         var newUrl = t.createUrl(u, dep);\r
10654                                         t.load(newUrl.resource, newUrl, undefined, undefined);\r
10655                                 });\r
10656                                 if (cb) {\r
10657                                         if (s) {\r
10658                                                 cb.call(s);\r
10659                                         } else {\r
10660                                                 cb.call(tinymce.ScriptLoader);\r
10661                                         }\r
10662                                 }\r
10663                         }\r
10664 \r
10665                         if (t.urls[n])\r
10666                                 return;\r
10667                         if (typeof u === "object")\r
10668                                 url = u.prefix + u.resource + u.suffix;\r
10669 \r
10670                         if (url.indexOf('/') != 0 && url.indexOf('://') == -1)\r
10671                                 url = tinymce.baseURL + '/' + url;\r
10672 \r
10673                         t.urls[n] = url.substring(0, url.lastIndexOf('/'));\r
10674 \r
10675                         if (t.lookup[n]) {\r
10676                                 loadDependencies();\r
10677                         } else {\r
10678                                 tinymce.ScriptLoader.add(url, loadDependencies, s);\r
10679                         }\r
10680                 }\r
10681         });\r
10682 \r
10683         // Create plugin and theme managers\r
10684         tinymce.PluginManager = new tinymce.AddOnManager();\r
10685         tinymce.ThemeManager = new tinymce.AddOnManager();\r
10686 }(tinymce));\r
10687 \r
10688 (function(tinymce) {\r
10689         // Shorten names\r
10690         var each = tinymce.each, extend = tinymce.extend,\r
10691                 DOM = tinymce.DOM, Event = tinymce.dom.Event,\r
10692                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
10693                 explode = tinymce.explode,\r
10694                 Dispatcher = tinymce.util.Dispatcher, undefined, instanceCounter = 0;\r
10695 \r
10696         // Setup some URLs where the editor API is located and where the document is\r
10697         tinymce.documentBaseURL = window.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');\r
10698         if (!/[\/\\]$/.test(tinymce.documentBaseURL))\r
10699                 tinymce.documentBaseURL += '/';\r
10700 \r
10701         tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL);\r
10702 \r
10703         tinymce.baseURI = new tinymce.util.URI(tinymce.baseURL);\r
10704 \r
10705         // Add before unload listener\r
10706         // This was required since IE was leaking memory if you added and removed beforeunload listeners\r
10707         // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event\r
10708         tinymce.onBeforeUnload = new Dispatcher(tinymce);\r
10709 \r
10710         // Must be on window or IE will leak if the editor is placed in frame or iframe\r
10711         Event.add(window, 'beforeunload', function(e) {\r
10712                 tinymce.onBeforeUnload.dispatch(tinymce, e);\r
10713         });\r
10714 \r
10715         tinymce.onAddEditor = new Dispatcher(tinymce);\r
10716 \r
10717         tinymce.onRemoveEditor = new Dispatcher(tinymce);\r
10718 \r
10719         tinymce.EditorManager = extend(tinymce, {\r
10720                 editors : [],\r
10721 \r
10722                 i18n : {},\r
10723 \r
10724                 activeEditor : null,\r
10725 \r
10726                 init : function(s) {\r
10727                         var t = this, pl, sl = tinymce.ScriptLoader, e, el = [], ed;\r
10728 \r
10729                         function execCallback(se, n, s) {\r
10730                                 var f = se[n];\r
10731 \r
10732                                 if (!f)\r
10733                                         return;\r
10734 \r
10735                                 if (tinymce.is(f, 'string')) {\r
10736                                         s = f.replace(/\.\w+$/, '');\r
10737                                         s = s ? tinymce.resolve(s) : 0;\r
10738                                         f = tinymce.resolve(f);\r
10739                                 }\r
10740 \r
10741                                 return f.apply(s || this, Array.prototype.slice.call(arguments, 2));\r
10742                         };\r
10743 \r
10744                         s = extend({\r
10745                                 theme : "simple",\r
10746                                 language : "en"\r
10747                         }, s);\r
10748 \r
10749                         t.settings = s;\r
10750 \r
10751                         // Legacy call\r
10752                         Event.add(document, 'init', function() {\r
10753                                 var l, co;\r
10754 \r
10755                                 execCallback(s, 'onpageload');\r
10756 \r
10757                                 switch (s.mode) {\r
10758                                         case "exact":\r
10759                                                 l = s.elements || '';\r
10760 \r
10761                                                 if(l.length > 0) {\r
10762                                                         each(explode(l), function(v) {\r
10763                                                                 if (DOM.get(v)) {\r
10764                                                                         ed = new tinymce.Editor(v, s);\r
10765                                                                         el.push(ed);\r
10766                                                                         ed.render(1);\r
10767                                                                 } else {\r
10768                                                                         each(document.forms, function(f) {\r
10769                                                                                 each(f.elements, function(e) {\r
10770                                                                                         if (e.name === v) {\r
10771                                                                                                 v = 'mce_editor_' + instanceCounter++;\r
10772                                                                                                 DOM.setAttrib(e, 'id', v);\r
10773 \r
10774                                                                                                 ed = new tinymce.Editor(v, s);\r
10775                                                                                                 el.push(ed);\r
10776                                                                                                 ed.render(1);\r
10777                                                                                         }\r
10778                                                                                 });\r
10779                                                                         });\r
10780                                                                 }\r
10781                                                         });\r
10782                                                 }\r
10783                                                 break;\r
10784 \r
10785                                         case "textareas":\r
10786                                         case "specific_textareas":\r
10787                                                 function hasClass(n, c) {\r
10788                                                         return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);\r
10789                                                 };\r
10790 \r
10791                                                 each(DOM.select('textarea'), function(v) {\r
10792                                                         if (s.editor_deselector && hasClass(v, s.editor_deselector))\r
10793                                                                 return;\r
10794 \r
10795                                                         if (!s.editor_selector || hasClass(v, s.editor_selector)) {\r
10796                                                                 // Can we use the name\r
10797                                                                 e = DOM.get(v.name);\r
10798                                                                 if (!v.id && !e)\r
10799                                                                         v.id = v.name;\r
10800 \r
10801                                                                 // Generate unique name if missing or already exists\r
10802                                                                 if (!v.id || t.get(v.id))\r
10803                                                                         v.id = DOM.uniqueId();\r
10804 \r
10805                                                                 ed = new tinymce.Editor(v.id, s);\r
10806                                                                 el.push(ed);\r
10807                                                                 ed.render(1);\r
10808                                                         }\r
10809                                                 });\r
10810                                                 break;\r
10811                                 }\r
10812 \r
10813                                 // Call onInit when all editors are initialized\r
10814                                 if (s.oninit) {\r
10815                                         l = co = 0;\r
10816 \r
10817                                         each(el, function(ed) {\r
10818                                                 co++;\r
10819 \r
10820                                                 if (!ed.initialized) {\r
10821                                                         // Wait for it\r
10822                                                         ed.onInit.add(function() {\r
10823                                                                 l++;\r
10824 \r
10825                                                                 // All done\r
10826                                                                 if (l == co)\r
10827                                                                         execCallback(s, 'oninit');\r
10828                                                         });\r
10829                                                 } else\r
10830                                                         l++;\r
10831 \r
10832                                                 // All done\r
10833                                                 if (l == co)\r
10834                                                         execCallback(s, 'oninit');                                      \r
10835                                         });\r
10836                                 }\r
10837                         });\r
10838                 },\r
10839 \r
10840                 get : function(id) {\r
10841                         if (id === undefined)\r
10842                                 return this.editors;\r
10843 \r
10844                         return this.editors[id];\r
10845                 },\r
10846 \r
10847                 getInstanceById : function(id) {\r
10848                         return this.get(id);\r
10849                 },\r
10850 \r
10851                 add : function(editor) {\r
10852                         var self = this, editors = self.editors;\r
10853 \r
10854                         // Add named and index editor instance\r
10855                         editors[editor.id] = editor;\r
10856                         editors.push(editor);\r
10857 \r
10858                         self._setActive(editor);\r
10859                         self.onAddEditor.dispatch(self, editor);\r
10860 \r
10861 \r
10862                         return editor;\r
10863                 },\r
10864 \r
10865                 remove : function(editor) {\r
10866                         var t = this, i, editors = t.editors;\r
10867 \r
10868                         // Not in the collection\r
10869                         if (!editors[editor.id])\r
10870                                 return null;\r
10871 \r
10872                         delete editors[editor.id];\r
10873 \r
10874                         for (i = 0; i < editors.length; i++) {\r
10875                                 if (editors[i] == editor) {\r
10876                                         editors.splice(i, 1);\r
10877                                         break;\r
10878                                 }\r
10879                         }\r
10880 \r
10881                         // Select another editor since the active one was removed\r
10882                         if (t.activeEditor == editor)\r
10883                                 t._setActive(editors[0]);\r
10884 \r
10885                         editor.destroy();\r
10886                         t.onRemoveEditor.dispatch(t, editor);\r
10887 \r
10888                         return editor;\r
10889                 },\r
10890 \r
10891                 execCommand : function(c, u, v) {\r
10892                         var t = this, ed = t.get(v), w;\r
10893 \r
10894                         // Manager commands\r
10895                         switch (c) {\r
10896                                 case "mceFocus":\r
10897                                         ed.focus();\r
10898                                         return true;\r
10899 \r
10900                                 case "mceAddEditor":\r
10901                                 case "mceAddControl":\r
10902                                         if (!t.get(v))\r
10903                                                 new tinymce.Editor(v, t.settings).render();\r
10904 \r
10905                                         return true;\r
10906 \r
10907                                 case "mceAddFrameControl":\r
10908                                         w = v.window;\r
10909 \r
10910                                         // Add tinyMCE global instance and tinymce namespace to specified window\r
10911                                         w.tinyMCE = tinyMCE;\r
10912                                         w.tinymce = tinymce;\r
10913 \r
10914                                         tinymce.DOM.doc = w.document;\r
10915                                         tinymce.DOM.win = w;\r
10916 \r
10917                                         ed = new tinymce.Editor(v.element_id, v);\r
10918                                         ed.render();\r
10919 \r
10920                                         // Fix IE memory leaks\r
10921                                         if (tinymce.isIE) {\r
10922                                                 function clr() {\r
10923                                                         ed.destroy();\r
10924                                                         w.detachEvent('onunload', clr);\r
10925                                                         w = w.tinyMCE = w.tinymce = null; // IE leak\r
10926                                                 };\r
10927 \r
10928                                                 w.attachEvent('onunload', clr);\r
10929                                         }\r
10930 \r
10931                                         v.page_window = null;\r
10932 \r
10933                                         return true;\r
10934 \r
10935                                 case "mceRemoveEditor":\r
10936                                 case "mceRemoveControl":\r
10937                                         if (ed)\r
10938                                                 ed.remove();\r
10939 \r
10940                                         return true;\r
10941 \r
10942                                 case 'mceToggleEditor':\r
10943                                         if (!ed) {\r
10944                                                 t.execCommand('mceAddControl', 0, v);\r
10945                                                 return true;\r
10946                                         }\r
10947 \r
10948                                         if (ed.isHidden())\r
10949                                                 ed.show();\r
10950                                         else\r
10951                                                 ed.hide();\r
10952 \r
10953                                         return true;\r
10954                         }\r
10955 \r
10956                         // Run command on active editor\r
10957                         if (t.activeEditor)\r
10958                                 return t.activeEditor.execCommand(c, u, v);\r
10959 \r
10960                         return false;\r
10961                 },\r
10962 \r
10963                 execInstanceCommand : function(id, c, u, v) {\r
10964                         var ed = this.get(id);\r
10965 \r
10966                         if (ed)\r
10967                                 return ed.execCommand(c, u, v);\r
10968 \r
10969                         return false;\r
10970                 },\r
10971 \r
10972                 triggerSave : function() {\r
10973                         each(this.editors, function(e) {\r
10974                                 e.save();\r
10975                         });\r
10976                 },\r
10977 \r
10978                 addI18n : function(p, o) {\r
10979                         var lo, i18n = this.i18n;\r
10980 \r
10981                         if (!tinymce.is(p, 'string')) {\r
10982                                 each(p, function(o, lc) {\r
10983                                         each(o, function(o, g) {\r
10984                                                 each(o, function(o, k) {\r
10985                                                         if (g === 'common')\r
10986                                                                 i18n[lc + '.' + k] = o;\r
10987                                                         else\r
10988                                                                 i18n[lc + '.' + g + '.' + k] = o;\r
10989                                                 });\r
10990                                         });\r
10991                                 });\r
10992                         } else {\r
10993                                 each(o, function(o, k) {\r
10994                                         i18n[p + '.' + k] = o;\r
10995                                 });\r
10996                         }\r
10997                 },\r
10998 \r
10999                 // Private methods\r
11000 \r
11001                 _setActive : function(editor) {\r
11002                         this.selectedInstance = this.activeEditor = editor;\r
11003                 }\r
11004         });\r
11005 })(tinymce);\r
11006 \r
11007 (function(tinymce) {\r
11008         // Shorten these names\r
11009         var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend,\r
11010                 Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isGecko = tinymce.isGecko,\r
11011                 isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, is = tinymce.is,\r
11012                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
11013                 inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;\r
11014 \r
11015         tinymce.create('tinymce.Editor', {\r
11016                 Editor : function(id, s) {\r
11017                         var t = this;\r
11018 \r
11019                         t.id = t.editorId = id;\r
11020 \r
11021                         t.execCommands = {};\r
11022                         t.queryStateCommands = {};\r
11023                         t.queryValueCommands = {};\r
11024 \r
11025                         t.isNotDirty = false;\r
11026 \r
11027                         t.plugins = {};\r
11028 \r
11029                         // Add events to the editor\r
11030                         each([\r
11031                                 'onPreInit',\r
11032 \r
11033                                 'onBeforeRenderUI',\r
11034 \r
11035                                 'onPostRender',\r
11036 \r
11037                                 'onInit',\r
11038 \r
11039                                 'onRemove',\r
11040 \r
11041                                 'onActivate',\r
11042 \r
11043                                 'onDeactivate',\r
11044 \r
11045                                 'onClick',\r
11046 \r
11047                                 'onEvent',\r
11048 \r
11049                                 'onMouseUp',\r
11050 \r
11051                                 'onMouseDown',\r
11052 \r
11053                                 'onDblClick',\r
11054 \r
11055                                 'onKeyDown',\r
11056 \r
11057                                 'onKeyUp',\r
11058 \r
11059                                 'onKeyPress',\r
11060 \r
11061                                 'onContextMenu',\r
11062 \r
11063                                 'onSubmit',\r
11064 \r
11065                                 'onReset',\r
11066 \r
11067                                 'onPaste',\r
11068 \r
11069                                 'onPreProcess',\r
11070 \r
11071                                 'onPostProcess',\r
11072 \r
11073                                 'onBeforeSetContent',\r
11074 \r
11075                                 'onBeforeGetContent',\r
11076 \r
11077                                 'onSetContent',\r
11078 \r
11079                                 'onGetContent',\r
11080 \r
11081                                 'onLoadContent',\r
11082 \r
11083                                 'onSaveContent',\r
11084 \r
11085                                 'onNodeChange',\r
11086 \r
11087                                 'onChange',\r
11088 \r
11089                                 'onBeforeExecCommand',\r
11090 \r
11091                                 'onExecCommand',\r
11092 \r
11093                                 'onUndo',\r
11094 \r
11095                                 'onRedo',\r
11096 \r
11097                                 'onVisualAid',\r
11098 \r
11099                                 'onSetProgressState'\r
11100                         ], function(e) {\r
11101                                 t[e] = new Dispatcher(t);\r
11102                         });\r
11103 \r
11104                         t.settings = s = extend({\r
11105                                 id : id,\r
11106                                 language : 'en',\r
11107                                 docs_language : 'en',\r
11108                                 theme : 'simple',\r
11109                                 skin : 'default',\r
11110                                 delta_width : 0,\r
11111                                 delta_height : 0,\r
11112                                 popup_css : '',\r
11113                                 plugins : '',\r
11114                                 document_base_url : tinymce.documentBaseURL,\r
11115                                 add_form_submit_trigger : 1,\r
11116                                 submit_patch : 1,\r
11117                                 add_unload_trigger : 1,\r
11118                                 convert_urls : 1,\r
11119                                 relative_urls : 1,\r
11120                                 remove_script_host : 1,\r
11121                                 table_inline_editing : 0,\r
11122                                 object_resizing : 1,\r
11123                                 cleanup : 1,\r
11124                                 accessibility_focus : 1,\r
11125                                 custom_shortcuts : 1,\r
11126                                 custom_undo_redo_keyboard_shortcuts : 1,\r
11127                                 custom_undo_redo_restore_selection : 1,\r
11128                                 custom_undo_redo : 1,\r
11129                                 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
11130                                 visual_table_class : 'mceItemTable',\r
11131                                 visual : 1,\r
11132                                 font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',\r
11133                                 apply_source_formatting : 1,\r
11134                                 directionality : 'ltr',\r
11135                                 forced_root_block : 'p',\r
11136                                 hidden_input : 1,\r
11137                                 padd_empty_editor : 1,\r
11138                                 render_ui : 1,\r
11139                                 init_theme : 1,\r
11140                                 force_p_newlines : 1,\r
11141                                 indentation : '30px',\r
11142                                 keep_styles : 1,\r
11143                                 fix_table_elements : 1,\r
11144                                 inline_styles : 1,\r
11145                                 convert_fonts_to_spans : true,\r
11146                                 indent : 'simple',\r
11147                                 indent_before : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr',\r
11148                                 indent_after : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr',\r
11149                                 validate : true,\r
11150                                 entity_encoding : 'named',\r
11151                                 url_converter : t.convertURL,\r
11152                                 url_converter_scope : t,\r
11153                                 ie7_compat : true\r
11154                         }, s);\r
11155 \r
11156                         t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {\r
11157                                 base_uri : tinyMCE.baseURI\r
11158                         });\r
11159 \r
11160                         t.baseURI = tinymce.baseURI;\r
11161 \r
11162                         t.contentCSS = [];\r
11163 \r
11164                         // Call setup\r
11165                         t.execCallback('setup', t);\r
11166                 },\r
11167 \r
11168                 render : function(nst) {\r
11169                         var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader;\r
11170 \r
11171                         // Page is not loaded yet, wait for it\r
11172                         if (!Event.domLoaded) {\r
11173                                 Event.add(document, 'init', function() {\r
11174                                         t.render();\r
11175                                 });\r
11176                                 return;\r
11177                         }\r
11178 \r
11179                         tinyMCE.settings = s;\r
11180 \r
11181                         // Element not found, then skip initialization\r
11182                         if (!t.getElement())\r
11183                                 return;\r
11184 \r
11185                         // Is a iPad/iPhone and not on iOS5, then skip initialization. We need to sniff \r
11186                         // here since the browser says it has contentEditable support but there is no visible\r
11187                         // caret We will remove this check ones Apple implements full contentEditable support\r
11188                         if (tinymce.isIDevice && !tinymce.isIOS5)\r
11189                                 return;\r
11190 \r
11191                         // Add hidden input for non input elements inside form elements\r
11192                         if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))\r
11193                                 DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);\r
11194 \r
11195                         if (tinymce.WindowManager)\r
11196                                 t.windowManager = new tinymce.WindowManager(t);\r
11197 \r
11198                         if (s.encoding == 'xml') {\r
11199                                 t.onGetContent.add(function(ed, o) {\r
11200                                         if (o.save)\r
11201                                                 o.content = DOM.encode(o.content);\r
11202                                 });\r
11203                         }\r
11204 \r
11205                         if (s.add_form_submit_trigger) {\r
11206                                 t.onSubmit.addToTop(function() {\r
11207                                         if (t.initialized) {\r
11208                                                 t.save();\r
11209                                                 t.isNotDirty = 1;\r
11210                                         }\r
11211                                 });\r
11212                         }\r
11213 \r
11214                         if (s.add_unload_trigger) {\r
11215                                 t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {\r
11216                                         if (t.initialized && !t.destroyed && !t.isHidden())\r
11217                                                 t.save({format : 'raw', no_events : true});\r
11218                                 });\r
11219                         }\r
11220 \r
11221                         tinymce.addUnload(t.destroy, t);\r
11222 \r
11223                         if (s.submit_patch) {\r
11224                                 t.onBeforeRenderUI.add(function() {\r
11225                                         var n = t.getElement().form;\r
11226 \r
11227                                         if (!n)\r
11228                                                 return;\r
11229 \r
11230                                         // Already patched\r
11231                                         if (n._mceOldSubmit)\r
11232                                                 return;\r
11233 \r
11234                                         // Check page uses id="submit" or name="submit" for it's submit button\r
11235                                         if (!n.submit.nodeType && !n.submit.length) {\r
11236                                                 t.formElement = n;\r
11237                                                 n._mceOldSubmit = n.submit;\r
11238                                                 n.submit = function() {\r
11239                                                         // Save all instances\r
11240                                                         tinymce.triggerSave();\r
11241                                                         t.isNotDirty = 1;\r
11242 \r
11243                                                         return t.formElement._mceOldSubmit(t.formElement);\r
11244                                                 };\r
11245                                         }\r
11246 \r
11247                                         n = null;\r
11248                                 });\r
11249                         }\r
11250 \r
11251                         // Load scripts\r
11252                         function loadScripts() {\r
11253                                 if (s.language && s.language_load !== false)\r
11254                                         sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');\r
11255 \r
11256                                 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])\r
11257                                         ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');\r
11258 \r
11259                                 each(explode(s.plugins), function(p) {\r
11260                                         if (p &&!PluginManager.urls[p]) {\r
11261                                                 if (p.charAt(0) == '-') {\r
11262                                                         p = p.substr(1, p.length);\r
11263                                                         var dependencies = PluginManager.dependencies(p);\r
11264                                                         each(dependencies, function(dep) {\r
11265                                                                 var defaultSettings = {prefix:'plugins/', resource: dep, suffix:'/editor_plugin' + tinymce.suffix + '.js'};\r
11266                                                                 var dep = PluginManager.createUrl(defaultSettings, dep);\r
11267                                                                 PluginManager.load(dep.resource, dep);\r
11268                                                                 \r
11269                                                         });\r
11270                                                 } else {\r
11271                                                         // Skip safari plugin, since it is removed as of 3.3b1\r
11272                                                         if (p == 'safari') {\r
11273                                                                 return;\r
11274                                                         }\r
11275                                                         PluginManager.load(p, {prefix:'plugins/', resource: p, suffix:'/editor_plugin' + tinymce.suffix + '.js'});\r
11276                                                 }\r
11277                                         }\r
11278                                 });\r
11279 \r
11280                                 // Init when que is loaded\r
11281                                 sl.loadQueue(function() {\r
11282                                         if (!t.removed)\r
11283                                                 t.init();\r
11284                                 });\r
11285                         };\r
11286 \r
11287                         loadScripts();\r
11288                 },\r
11289 \r
11290                 init : function() {\r
11291                         var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re, i, initializedPlugins = [];\r
11292 \r
11293                         tinymce.add(t);\r
11294 \r
11295                         s.aria_label = s.aria_label || DOM.getAttrib(e, 'aria-label', t.getLang('aria.rich_text_area'));\r
11296 \r
11297                         if (s.theme) {\r
11298                                 s.theme = s.theme.replace(/-/, '');\r
11299                                 o = ThemeManager.get(s.theme);\r
11300                                 t.theme = new o();\r
11301 \r
11302                                 if (t.theme.init && s.init_theme)\r
11303                                         t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));\r
11304                         }\r
11305                         function initPlugin(p) {\r
11306                                 var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;\r
11307                                 if (c && tinymce.inArray(initializedPlugins,p) === -1) {\r
11308                                         each(PluginManager.dependencies(p), function(dep){\r
11309                                                 initPlugin(dep);\r
11310                                         });\r
11311                                         po = new c(t, u);\r
11312 \r
11313                                         t.plugins[p] = po;\r
11314 \r
11315                                         if (po.init) {\r
11316                                                 po.init(t, u);\r
11317                                                 initializedPlugins.push(p);\r
11318                                         }\r
11319                                 }\r
11320                         }\r
11321                         \r
11322                         // Create all plugins\r
11323                         each(explode(s.plugins.replace(/\-/g, '')), initPlugin);\r
11324 \r
11325                         // Setup popup CSS path(s)\r
11326                         if (s.popup_css !== false) {\r
11327                                 if (s.popup_css)\r
11328                                         s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);\r
11329                                 else\r
11330                                         s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");\r
11331                         }\r
11332 \r
11333                         if (s.popup_css_add)\r
11334                                 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);\r
11335 \r
11336                         t.controlManager = new tinymce.ControlManager(t);\r
11337 \r
11338                         if (s.custom_undo_redo) {\r
11339                                 t.onBeforeExecCommand.add(function(ed, cmd, ui, val, a) {\r
11340                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))\r
11341                                                 t.undoManager.beforeChange();\r
11342                                 });\r
11343 \r
11344                                 t.onExecCommand.add(function(ed, cmd, ui, val, a) {\r
11345                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))\r
11346                                                 t.undoManager.add();\r
11347                                 });\r
11348                         }\r
11349 \r
11350                         t.onExecCommand.add(function(ed, c) {\r
11351                                 // Don't refresh the select lists until caret move\r
11352                                 if (!/^(FontName|FontSize)$/.test(c))\r
11353                                         t.nodeChanged();\r
11354                         });\r
11355 \r
11356                         // Remove ghost selections on images and tables in Gecko\r
11357                         if (isGecko) {\r
11358                                 function repaint(a, o) {\r
11359                                         if (!o || !o.initial)\r
11360                                                 t.execCommand('mceRepaint');\r
11361                                 };\r
11362 \r
11363                                 t.onUndo.add(repaint);\r
11364                                 t.onRedo.add(repaint);\r
11365                                 t.onSetContent.add(repaint);\r
11366                         }\r
11367 \r
11368                         // Enables users to override the control factory\r
11369                         t.onBeforeRenderUI.dispatch(t, t.controlManager);\r
11370 \r
11371                         // Measure box\r
11372                         if (s.render_ui) {\r
11373                                 w = s.width || e.style.width || e.offsetWidth;\r
11374                                 h = s.height || e.style.height || e.offsetHeight;\r
11375                                 t.orgDisplay = e.style.display;\r
11376                                 re = /^[0-9\.]+(|px)$/i;\r
11377 \r
11378                                 if (re.test('' + w))\r
11379                                         w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);\r
11380 \r
11381                                 if (re.test('' + h))\r
11382                                         h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);\r
11383 \r
11384                                 // Render UI\r
11385                                 o = t.theme.renderUI({\r
11386                                         targetNode : e,\r
11387                                         width : w,\r
11388                                         height : h,\r
11389                                         deltaWidth : s.delta_width,\r
11390                                         deltaHeight : s.delta_height\r
11391                                 });\r
11392 \r
11393                                 t.editorContainer = o.editorContainer;\r
11394                         }\r
11395 \r
11396 \r
11397                         // User specified a document.domain value\r
11398                         if (document.domain && location.hostname != document.domain)\r
11399                                 tinymce.relaxedDomain = document.domain;\r
11400 \r
11401                         // Resize editor\r
11402                         DOM.setStyles(o.sizeContainer || o.editorContainer, {\r
11403                                 width : w,\r
11404                                 height : h\r
11405                         });\r
11406 \r
11407                         // Load specified content CSS last\r
11408                         if (s.content_css) {\r
11409                                 tinymce.each(explode(s.content_css), function(u) {\r
11410                                         t.contentCSS.push(t.documentBaseURI.toAbsolute(u));\r
11411                                 });\r
11412                         }\r
11413 \r
11414                         h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');\r
11415                         if (h < 100)\r
11416                                 h = 100;\r
11417 \r
11418                         t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">';\r
11419 \r
11420                         // We only need to override paths if we have to\r
11421                         // IE has a bug where it remove site absolute urls to relative ones if this is specified\r
11422                         if (s.document_base_url != tinymce.documentBaseURL)\r
11423                                 t.iframeHTML += '<base href="' + t.documentBaseURI.getURI() + '" />';\r
11424 \r
11425                         // IE8 doesn't support carets behind images setting ie7_compat would force IE8+ to run in IE7 compat mode.\r
11426                         if (s.ie7_compat)\r
11427                                 t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" />';\r
11428                         else\r
11429                                 t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=edge" />';\r
11430 \r
11431                         t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';\r
11432 \r
11433                         bi = s.body_id || 'tinymce';\r
11434                         if (bi.indexOf('=') != -1) {\r
11435                                 bi = t.getParam('body_id', '', 'hash');\r
11436                                 bi = bi[t.id] || bi;\r
11437                         }\r
11438 \r
11439                         bc = s.body_class || '';\r
11440                         if (bc.indexOf('=') != -1) {\r
11441                                 bc = t.getParam('body_class', '', 'hash');\r
11442                                 bc = bc[t.id] || '';\r
11443                         }\r
11444 \r
11445                         t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"><br></body></html>';\r
11446 \r
11447                         // Domain relaxing enabled, then set document domain\r
11448                         if (tinymce.relaxedDomain && (isIE || (tinymce.isOpera && parseFloat(opera.version()) < 11))) {\r
11449                                 // We need to write the contents here in IE since multiple writes messes up refresh button and back button\r
11450                                 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
11451                         }\r
11452 \r
11453                         // Create iframe\r
11454                         // TODO: ACC add the appropriate description on this.\r
11455                         n = DOM.add(o.iframeContainer, 'iframe', { \r
11456                                 id : t.id + "_ifr",\r
11457                                 src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7\r
11458                                 frameBorder : '0',\r
11459                                 allowTransparency : "true",\r
11460                                 title : s.aria_label,\r
11461                                 style : {\r
11462                                         width : '100%',\r
11463                                         height : h,\r
11464                                         display : 'block' // Important for Gecko to render the iframe correctly\r
11465                                 }\r
11466                         });\r
11467 \r
11468                         t.contentAreaContainer = o.iframeContainer;\r
11469                         DOM.get(o.editorContainer).style.display = t.orgDisplay;\r
11470                         DOM.get(t.id).style.display = 'none';\r
11471                         DOM.setAttrib(t.id, 'aria-hidden', true);\r
11472 \r
11473                         if (!tinymce.relaxedDomain || !u)\r
11474                                 t.setupIframe();\r
11475 \r
11476                         e = n = o = null; // Cleanup\r
11477                 },\r
11478 \r
11479                 setupIframe : function() {\r
11480                         var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;\r
11481 \r
11482                         // Setup iframe body\r
11483                         if (!isIE || !tinymce.relaxedDomain) {\r
11484                                 // Fix for a focus bug in FF 3.x where the body element\r
11485                                 // wouldn't get proper focus if the user clicked on the HTML element\r
11486                                 if (isGecko && !Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4\r
11487                                         t.onMouseDown.add(function(ed, e) {\r
11488                                                 if (e.target.nodeName === "HTML") {\r
11489                                                         var body = t.getBody();\r
11490 \r
11491                                                         // Blur the body it's focused but not correctly focused\r
11492                                                         body.blur();\r
11493 \r
11494                                                         // Refocus the body after a little while\r
11495                                                         setTimeout(function() {\r
11496                                                                 body.focus();\r
11497                                                         }, 0);\r
11498                                                 }\r
11499                                         });\r
11500                                 }\r
11501 \r
11502                                 d.open();\r
11503                                 d.write(t.iframeHTML);\r
11504                                 d.close();\r
11505 \r
11506                                 if (tinymce.relaxedDomain)\r
11507                                         d.domain = tinymce.relaxedDomain;\r
11508                         }\r
11509 \r
11510                         // It will not steal focus while setting contentEditable\r
11511                         b = t.getBody();\r
11512                         b.disabled = true;\r
11513 \r
11514                         if (!s.readonly)\r
11515                                 b.contentEditable = true;\r
11516 \r
11517                         b.disabled = false;\r
11518 \r
11519                         t.schema = new tinymce.html.Schema(s);\r
11520 \r
11521                         t.dom = new tinymce.dom.DOMUtils(t.getDoc(), {\r
11522                                 keep_values : true,\r
11523                                 url_converter : t.convertURL,\r
11524                                 url_converter_scope : t,\r
11525                                 hex_colors : s.force_hex_style_colors,\r
11526                                 class_filter : s.class_filter,\r
11527                                 update_styles : 1,\r
11528                                 fix_ie_paragraphs : 1,\r
11529                                 schema : t.schema\r
11530                         });\r
11531 \r
11532                         t.parser = new tinymce.html.DomParser(s, t.schema);\r
11533 \r
11534                         // Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.\r
11535                         if (!t.settings.allow_html_in_named_anchor) {\r
11536                                 t.parser.addAttributeFilter('name', function(nodes, name) {\r
11537                                         var i = nodes.length, sibling, prevSibling, parent, node;\r
11538         \r
11539                                         while (i--) {\r
11540                                                 node = nodes[i];\r
11541                                                 if (node.name === 'a' && node.firstChild) {\r
11542                                                         parent = node.parent;\r
11543         \r
11544                                                         // Move children after current node\r
11545                                                         sibling = node.lastChild;\r
11546                                                         do {\r
11547                                                                 prevSibling = sibling.prev;\r
11548                                                                 parent.insert(sibling, node);\r
11549                                                                 sibling = prevSibling;\r
11550                                                         } while (sibling);\r
11551                                                 }\r
11552                                         }\r
11553                                 });\r
11554                         }\r
11555 \r
11556                         // Convert src and href into data-mce-src, data-mce-href and data-mce-style\r
11557                         t.parser.addAttributeFilter('src,href,style', function(nodes, name) {\r
11558                                 var i = nodes.length, node, dom = t.dom, value, internalName;\r
11559 \r
11560                                 while (i--) {\r
11561                                         node = nodes[i];\r
11562                                         value = node.attr(name);\r
11563                                         internalName = 'data-mce-' + name;\r
11564 \r
11565                                         // Add internal attribute if we need to we don't on a refresh of the document\r
11566                                         if (!node.attributes.map[internalName]) {       \r
11567                                                 if (name === "style")\r
11568                                                         node.attr(internalName, dom.serializeStyle(dom.parseStyle(value), node.name));\r
11569                                                 else\r
11570                                                         node.attr(internalName, t.convertURL(value, name, node.name));\r
11571                                         }\r
11572                                 }\r
11573                         });\r
11574 \r
11575                         // Keep scripts from executing\r
11576                         t.parser.addNodeFilter('script', function(nodes, name) {\r
11577                                 var i = nodes.length;\r
11578 \r
11579                                 while (i--)\r
11580                                         nodes[i].attr('type', 'mce-text/javascript');\r
11581                         });\r
11582 \r
11583                         t.parser.addNodeFilter('#cdata', function(nodes, name) {\r
11584                                 var i = nodes.length, node;\r
11585 \r
11586                                 while (i--) {\r
11587                                         node = nodes[i];\r
11588                                         node.type = 8;\r
11589                                         node.name = '#comment';\r
11590                                         node.value = '[CDATA[' + node.value + ']]';\r
11591                                 }\r
11592                         });\r
11593 \r
11594                         t.parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes, name) {\r
11595                                 var i = nodes.length, node, nonEmptyElements = t.schema.getNonEmptyElements();\r
11596 \r
11597                                 while (i--) {\r
11598                                         node = nodes[i];\r
11599 \r
11600                                         if (node.isEmpty(nonEmptyElements))\r
11601                                                 node.empty().append(new tinymce.html.Node('br', 1)).shortEnded = true;\r
11602                                 }\r
11603                         });\r
11604 \r
11605                         t.serializer = new tinymce.dom.Serializer(s, t.dom, t.schema);\r
11606 \r
11607                         t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);\r
11608 \r
11609                         t.formatter = new tinymce.Formatter(this);\r
11610 \r
11611                         // Register default formats\r
11612                         t.formatter.register({\r
11613                                 alignleft : [\r
11614                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}},\r
11615                                         {selector : 'img,table', collapsed : false, styles : {'float' : 'left'}}\r
11616                                 ],\r
11617 \r
11618                                 aligncenter : [\r
11619                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}},\r
11620                                         {selector : 'img', collapsed : false, styles : {display : 'block', marginLeft : 'auto', marginRight : 'auto'}},\r
11621                                         {selector : 'table', collapsed : false, styles : {marginLeft : 'auto', marginRight : 'auto'}}\r
11622                                 ],\r
11623 \r
11624                                 alignright : [\r
11625                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}},\r
11626                                         {selector : 'img,table', collapsed : false, styles : {'float' : 'right'}}\r
11627                                 ],\r
11628 \r
11629                                 alignfull : [\r
11630                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'justify'}}\r
11631                                 ],\r
11632 \r
11633                                 bold : [\r
11634                                         {inline : 'strong', remove : 'all'},\r
11635                                         {inline : 'span', styles : {fontWeight : 'bold'}},\r
11636                                         {inline : 'b', remove : 'all'}\r
11637                                 ],\r
11638 \r
11639                                 italic : [\r
11640                                         {inline : 'em', remove : 'all'},\r
11641                                         {inline : 'span', styles : {fontStyle : 'italic'}},\r
11642                                         {inline : 'i', remove : 'all'}\r
11643                                 ],\r
11644 \r
11645                                 underline : [\r
11646                                         {inline : 'span', styles : {textDecoration : 'underline'}, exact : true},\r
11647                                         {inline : 'u', remove : 'all'}\r
11648                                 ],\r
11649 \r
11650                                 strikethrough : [\r
11651                                         {inline : 'span', styles : {textDecoration : 'line-through'}, exact : true},\r
11652                                         {inline : 'strike', remove : 'all'}\r
11653                                 ],\r
11654 \r
11655                                 forecolor : {inline : 'span', styles : {color : '%value'}, wrap_links : false},\r
11656                                 hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}, wrap_links : false},\r
11657                                 fontname : {inline : 'span', styles : {fontFamily : '%value'}},\r
11658                                 fontsize : {inline : 'span', styles : {fontSize : '%value'}},\r
11659                                 fontsize_class : {inline : 'span', attributes : {'class' : '%value'}},\r
11660                                 blockquote : {block : 'blockquote', wrapper : 1, remove : 'all'},\r
11661                                 subscript : {inline : 'sub'},\r
11662                                 superscript : {inline : 'sup'},\r
11663 \r
11664                                 link : {inline : 'a', selector : 'a', remove : 'all', split : true, deep : true,\r
11665                                         onmatch : function(node) {\r
11666                                                 return true;\r
11667                                         },\r
11668 \r
11669                                         onformat : function(elm, fmt, vars) {\r
11670                                                 each(vars, function(value, key) {\r
11671                                                         t.dom.setAttrib(elm, key, value);\r
11672                                                 });\r
11673                                         }\r
11674                                 },\r
11675 \r
11676                                 removeformat : [\r
11677                                         {selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},\r
11678                                         {selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},\r
11679                                         {selector : '*', attributes : ['style', 'class'], split : false, expand : false, deep : true}\r
11680                                 ]\r
11681                         });\r
11682 \r
11683                         // Register default block formats\r
11684                         each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) {\r
11685                                 t.formatter.register(name, {block : name, remove : 'all'});\r
11686                         });\r
11687 \r
11688                         // Register user defined formats\r
11689                         t.formatter.register(t.settings.formats);\r
11690 \r
11691                         t.undoManager = new tinymce.UndoManager(t);\r
11692 \r
11693                         // Pass through\r
11694                         t.undoManager.onAdd.add(function(um, l) {\r
11695                                 if (um.hasUndo())\r
11696                                         return t.onChange.dispatch(t, l, um);\r
11697                         });\r
11698 \r
11699                         t.undoManager.onUndo.add(function(um, l) {\r
11700                                 return t.onUndo.dispatch(t, l, um);\r
11701                         });\r
11702 \r
11703                         t.undoManager.onRedo.add(function(um, l) {\r
11704                                 return t.onRedo.dispatch(t, l, um);\r
11705                         });\r
11706 \r
11707                         t.forceBlocks = new tinymce.ForceBlocks(t, {\r
11708                                 forced_root_block : s.forced_root_block\r
11709                         });\r
11710 \r
11711                         t.editorCommands = new tinymce.EditorCommands(t);\r
11712 \r
11713                         // Pass through\r
11714                         t.serializer.onPreProcess.add(function(se, o) {\r
11715                                 return t.onPreProcess.dispatch(t, o, se);\r
11716                         });\r
11717 \r
11718                         t.serializer.onPostProcess.add(function(se, o) {\r
11719                                 return t.onPostProcess.dispatch(t, o, se);\r
11720                         });\r
11721 \r
11722                         t.onPreInit.dispatch(t);\r
11723 \r
11724                         if (!s.gecko_spellcheck)\r
11725                                 t.getBody().spellcheck = 0;\r
11726 \r
11727                         if (!s.readonly)\r
11728                                 t._addEvents();\r
11729 \r
11730                         t.controlManager.onPostRender.dispatch(t, t.controlManager);\r
11731                         t.onPostRender.dispatch(t);\r
11732 \r
11733                         t.quirks = new tinymce.util.Quirks(this);\r
11734 \r
11735                         if (s.directionality)\r
11736                                 t.getBody().dir = s.directionality;\r
11737 \r
11738                         if (s.nowrap)\r
11739                                 t.getBody().style.whiteSpace = "nowrap";\r
11740 \r
11741                         if (s.handle_node_change_callback) {\r
11742                                 t.onNodeChange.add(function(ed, cm, n) {\r
11743                                         t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());\r
11744                                 });\r
11745                         }\r
11746 \r
11747                         if (s.save_callback) {\r
11748                                 t.onSaveContent.add(function(ed, o) {\r
11749                                         var h = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
11750 \r
11751                                         if (h)\r
11752                                                 o.content = h;\r
11753                                 });\r
11754                         }\r
11755 \r
11756                         if (s.onchange_callback) {\r
11757                                 t.onChange.add(function(ed, l) {\r
11758                                         t.execCallback('onchange_callback', t, l);\r
11759                                 });\r
11760                         }\r
11761 \r
11762                         if (s.protect) {\r
11763                                 t.onBeforeSetContent.add(function(ed, o) {\r
11764                                         if (s.protect) {\r
11765                                                 each(s.protect, function(pattern) {\r
11766                                                         o.content = o.content.replace(pattern, function(str) {\r
11767                                                                 return '<!--mce:protected ' + escape(str) + '-->';\r
11768                                                         });\r
11769                                                 });\r
11770                                         }\r
11771                                 });\r
11772                         }\r
11773 \r
11774                         if (s.convert_newlines_to_brs) {\r
11775                                 t.onBeforeSetContent.add(function(ed, o) {\r
11776                                         if (o.initial)\r
11777                                                 o.content = o.content.replace(/\r?\n/g, '<br />');\r
11778                                 });\r
11779                         }\r
11780 \r
11781                         if (s.preformatted) {\r
11782                                 t.onPostProcess.add(function(ed, o) {\r
11783                                         o.content = o.content.replace(/^\s*<pre.*?>/, '');\r
11784                                         o.content = o.content.replace(/<\/pre>\s*$/, '');\r
11785 \r
11786                                         if (o.set)\r
11787                                                 o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';\r
11788                                 });\r
11789                         }\r
11790 \r
11791                         if (s.verify_css_classes) {\r
11792                                 t.serializer.attribValueFilter = function(n, v) {\r
11793                                         var s, cl;\r
11794 \r
11795                                         if (n == 'class') {\r
11796                                                 // Build regexp for classes\r
11797                                                 if (!t.classesRE) {\r
11798                                                         cl = t.dom.getClasses();\r
11799 \r
11800                                                         if (cl.length > 0) {\r
11801                                                                 s = '';\r
11802 \r
11803                                                                 each (cl, function(o) {\r
11804                                                                         s += (s ? '|' : '') + o['class'];\r
11805                                                                 });\r
11806 \r
11807                                                                 t.classesRE = new RegExp('(' + s + ')', 'gi');\r
11808                                                         }\r
11809                                                 }\r
11810 \r
11811                                                 return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';\r
11812                                         }\r
11813 \r
11814                                         return v;\r
11815                                 };\r
11816                         }\r
11817 \r
11818                         if (s.cleanup_callback) {\r
11819                                 t.onBeforeSetContent.add(function(ed, o) {\r
11820                                         o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
11821                                 });\r
11822 \r
11823                                 t.onPreProcess.add(function(ed, o) {\r
11824                                         if (o.set)\r
11825                                                 t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);\r
11826 \r
11827                                         if (o.get)\r
11828                                                 t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);\r
11829                                 });\r
11830 \r
11831                                 t.onPostProcess.add(function(ed, o) {\r
11832                                         if (o.set)\r
11833                                                 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
11834 \r
11835                                         if (o.get)                                              \r
11836                                                 o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);\r
11837                                 });\r
11838                         }\r
11839 \r
11840                         if (s.save_callback) {\r
11841                                 t.onGetContent.add(function(ed, o) {\r
11842                                         if (o.save)\r
11843                                                 o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
11844                                 });\r
11845                         }\r
11846 \r
11847                         if (s.handle_event_callback) {\r
11848                                 t.onEvent.add(function(ed, e, o) {\r
11849                                         if (t.execCallback('handle_event_callback', e, ed, o) === false)\r
11850                                                 Event.cancel(e);\r
11851                                 });\r
11852                         }\r
11853 \r
11854                         // Add visual aids when new contents is added\r
11855                         t.onSetContent.add(function() {\r
11856                                 t.addVisual(t.getBody());\r
11857                         });\r
11858 \r
11859                         // Remove empty contents\r
11860                         if (s.padd_empty_editor) {\r
11861                                 t.onPostProcess.add(function(ed, o) {\r
11862                                         o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');\r
11863                                 });\r
11864                         }\r
11865 \r
11866                         if (isGecko) {\r
11867                                 // Fix gecko link bug, when a link is placed at the end of block elements there is\r
11868                                 // no way to move the caret behind the link. This fix adds a bogus br element after the link\r
11869                                 function fixLinks(ed, o) {\r
11870                                         each(ed.dom.select('a'), function(n) {\r
11871                                                 var pn = n.parentNode;\r
11872 \r
11873                                                 if (ed.dom.isBlock(pn) && pn.lastChild === n)\r
11874                                                         ed.dom.add(pn, 'br', {'data-mce-bogus' : 1});\r
11875                                         });\r
11876                                 };\r
11877 \r
11878                                 t.onExecCommand.add(function(ed, cmd) {\r
11879                                         if (cmd === 'CreateLink')\r
11880                                                 fixLinks(ed);\r
11881                                 });\r
11882 \r
11883                                 t.onSetContent.add(t.selection.onSetContent.add(fixLinks));\r
11884                         }\r
11885 \r
11886                         t.load({initial : true, format : 'html'});\r
11887                         t.startContent = t.getContent({format : 'raw'});\r
11888                         t.undoManager.add();\r
11889                         t.initialized = true;\r
11890 \r
11891                         t.onInit.dispatch(t);\r
11892                         t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());\r
11893                         t.execCallback('init_instance_callback', t);\r
11894                         t.focus(true);\r
11895                         t.nodeChanged({initial : 1});\r
11896 \r
11897                         // Load specified content CSS last\r
11898                         each(t.contentCSS, function(u) {\r
11899                                 t.dom.loadCSS(u);\r
11900                         });\r
11901 \r
11902                         // Handle auto focus\r
11903                         if (s.auto_focus) {\r
11904                                 setTimeout(function () {\r
11905                                         var ed = tinymce.get(s.auto_focus);\r
11906 \r
11907                                         ed.selection.select(ed.getBody(), 1);\r
11908                                         ed.selection.collapse(1);\r
11909                                         ed.getBody().focus();\r
11910                                         ed.getWin().focus();\r
11911                                 }, 100);\r
11912                         }\r
11913 \r
11914                         e = null;\r
11915                 },\r
11916 \r
11917 \r
11918                 focus : function(sf) {\r
11919                         var oed, t = this, selection = t.selection, ce = t.settings.content_editable, ieRng, controlElm, doc = t.getDoc();\r
11920 \r
11921                         if (!sf) {\r
11922                                 // Get selected control element\r
11923                                 ieRng = selection.getRng();\r
11924                                 if (ieRng.item) {\r
11925                                         controlElm = ieRng.item(0);\r
11926                                 }\r
11927 \r
11928                                 t._refreshContentEditable();\r
11929                                 selection.normalize();\r
11930 \r
11931                                 // Is not content editable\r
11932                                 if (!ce)\r
11933                                         t.getWin().focus();\r
11934 \r
11935                                 // Focus the body as well since it's contentEditable\r
11936                                 if (tinymce.isGecko) {\r
11937                                         t.getBody().focus();\r
11938                                 }\r
11939 \r
11940                                 // Restore selected control element\r
11941                                 // This is needed when for example an image is selected within a\r
11942                                 // layer a call to focus will then remove the control selection\r
11943                                 if (controlElm && controlElm.ownerDocument == doc) {\r
11944                                         ieRng = doc.body.createControlRange();\r
11945                                         ieRng.addElement(controlElm);\r
11946                                         ieRng.select();\r
11947                                 }\r
11948 \r
11949                         }\r
11950 \r
11951                         if (tinymce.activeEditor != t) {\r
11952                                 if ((oed = tinymce.activeEditor) != null)\r
11953                                         oed.onDeactivate.dispatch(oed, t);\r
11954 \r
11955                                 t.onActivate.dispatch(t, oed);\r
11956                         }\r
11957 \r
11958                         tinymce._setActive(t);\r
11959                 },\r
11960 \r
11961                 execCallback : function(n) {\r
11962                         var t = this, f = t.settings[n], s;\r
11963 \r
11964                         if (!f)\r
11965                                 return;\r
11966 \r
11967                         // Look through lookup\r
11968                         if (t.callbackLookup && (s = t.callbackLookup[n])) {\r
11969                                 f = s.func;\r
11970                                 s = s.scope;\r
11971                         }\r
11972 \r
11973                         if (is(f, 'string')) {\r
11974                                 s = f.replace(/\.\w+$/, '');\r
11975                                 s = s ? tinymce.resolve(s) : 0;\r
11976                                 f = tinymce.resolve(f);\r
11977                                 t.callbackLookup = t.callbackLookup || {};\r
11978                                 t.callbackLookup[n] = {func : f, scope : s};\r
11979                         }\r
11980 \r
11981                         return f.apply(s || t, Array.prototype.slice.call(arguments, 1));\r
11982                 },\r
11983 \r
11984                 translate : function(s) {\r
11985                         var c = this.settings.language || 'en', i18n = tinymce.i18n;\r
11986 \r
11987                         if (!s)\r
11988                                 return '';\r
11989 \r
11990                         return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {\r
11991                                 return i18n[c + '.' + b] || '{#' + b + '}';\r
11992                         });\r
11993                 },\r
11994 \r
11995                 getLang : function(n, dv) {\r
11996                         return tinymce.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}');\r
11997                 },\r
11998 \r
11999                 getParam : function(n, dv, ty) {\r
12000                         var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;\r
12001 \r
12002                         if (ty === 'hash') {\r
12003                                 o = {};\r
12004 \r
12005                                 if (is(v, 'string')) {\r
12006                                         each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {\r
12007                                                 v = v.split('=');\r
12008 \r
12009                                                 if (v.length > 1)\r
12010                                                         o[tr(v[0])] = tr(v[1]);\r
12011                                                 else\r
12012                                                         o[tr(v[0])] = tr(v);\r
12013                                         });\r
12014                                 } else\r
12015                                         o = v;\r
12016 \r
12017                                 return o;\r
12018                         }\r
12019 \r
12020                         return v;\r
12021                 },\r
12022 \r
12023                 nodeChanged : function(o) {\r
12024                         var t = this, s = t.selection, n = s.getStart() || t.getBody();\r
12025 \r
12026                         // Fix for bug #1896577 it seems that this can not be fired while the editor is loading\r
12027                         if (t.initialized) {\r
12028                                 o = o || {};\r
12029                                 n = isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n; // Fix for IE initial state\r
12030 \r
12031                                 // Get parents and add them to object\r
12032                                 o.parents = [];\r
12033                                 t.dom.getParent(n, function(node) {\r
12034                                         if (node.nodeName == 'BODY')\r
12035                                                 return true;\r
12036 \r
12037                                         o.parents.push(node);\r
12038                                 });\r
12039 \r
12040                                 t.onNodeChange.dispatch(\r
12041                                         t,\r
12042                                         o ? o.controlManager || t.controlManager : t.controlManager,\r
12043                                         n,\r
12044                                         s.isCollapsed(),\r
12045                                         o\r
12046                                 );\r
12047                         }\r
12048                 },\r
12049 \r
12050                 addButton : function(n, s) {\r
12051                         var t = this;\r
12052 \r
12053                         t.buttons = t.buttons || {};\r
12054                         t.buttons[n] = s;\r
12055                 },\r
12056 \r
12057                 addCommand : function(name, callback, scope) {\r
12058                         this.execCommands[name] = {func : callback, scope : scope || this};\r
12059                 },\r
12060 \r
12061                 addQueryStateHandler : function(name, callback, scope) {\r
12062                         this.queryStateCommands[name] = {func : callback, scope : scope || this};\r
12063                 },\r
12064 \r
12065                 addQueryValueHandler : function(name, callback, scope) {\r
12066                         this.queryValueCommands[name] = {func : callback, scope : scope || this};\r
12067                 },\r
12068 \r
12069                 addShortcut : function(pa, desc, cmd_func, sc) {\r
12070                         var t = this, c;\r
12071 \r
12072                         if (!t.settings.custom_shortcuts)\r
12073                                 return false;\r
12074 \r
12075                         t.shortcuts = t.shortcuts || {};\r
12076 \r
12077                         if (is(cmd_func, 'string')) {\r
12078                                 c = cmd_func;\r
12079 \r
12080                                 cmd_func = function() {\r
12081                                         t.execCommand(c, false, null);\r
12082                                 };\r
12083                         }\r
12084 \r
12085                         if (is(cmd_func, 'object')) {\r
12086                                 c = cmd_func;\r
12087 \r
12088                                 cmd_func = function() {\r
12089                                         t.execCommand(c[0], c[1], c[2]);\r
12090                                 };\r
12091                         }\r
12092 \r
12093                         each(explode(pa), function(pa) {\r
12094                                 var o = {\r
12095                                         func : cmd_func,\r
12096                                         scope : sc || this,\r
12097                                         desc : desc,\r
12098                                         alt : false,\r
12099                                         ctrl : false,\r
12100                                         shift : false\r
12101                                 };\r
12102 \r
12103                                 each(explode(pa, '+'), function(v) {\r
12104                                         switch (v) {\r
12105                                                 case 'alt':\r
12106                                                 case 'ctrl':\r
12107                                                 case 'shift':\r
12108                                                         o[v] = true;\r
12109                                                         break;\r
12110 \r
12111                                                 default:\r
12112                                                         o.charCode = v.charCodeAt(0);\r
12113                                                         o.keyCode = v.toUpperCase().charCodeAt(0);\r
12114                                         }\r
12115                                 });\r
12116 \r
12117                                 t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;\r
12118                         });\r
12119 \r
12120                         return true;\r
12121                 },\r
12122 \r
12123                 execCommand : function(cmd, ui, val, a) {\r
12124                         var t = this, s = 0, o, st;\r
12125 \r
12126                         if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))\r
12127                                 t.focus();\r
12128 \r
12129                         o = {};\r
12130                         t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);\r
12131                         if (o.terminate)\r
12132                                 return false;\r
12133 \r
12134                         // Command callback\r
12135                         if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {\r
12136                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
12137                                 return true;\r
12138                         }\r
12139 \r
12140                         // Registred commands\r
12141                         if (o = t.execCommands[cmd]) {\r
12142                                 st = o.func.call(o.scope, ui, val);\r
12143 \r
12144                                 // Fall through on true\r
12145                                 if (st !== true) {\r
12146                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
12147                                         return st;\r
12148                                 }\r
12149                         }\r
12150 \r
12151                         // Plugin commands\r
12152                         each(t.plugins, function(p) {\r
12153                                 if (p.execCommand && p.execCommand(cmd, ui, val)) {\r
12154                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
12155                                         s = 1;\r
12156                                         return false;\r
12157                                 }\r
12158                         });\r
12159 \r
12160                         if (s)\r
12161                                 return true;\r
12162 \r
12163                         // Theme commands\r
12164                         if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {\r
12165                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
12166                                 return true;\r
12167                         }\r
12168 \r
12169                         // Editor commands\r
12170                         if (t.editorCommands.execCommand(cmd, ui, val)) {\r
12171                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
12172                                 return true;\r
12173                         }\r
12174 \r
12175                         // Browser commands\r
12176                         t.getDoc().execCommand(cmd, ui, val);\r
12177                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
12178                 },\r
12179 \r
12180                 queryCommandState : function(cmd) {\r
12181                         var t = this, o, s;\r
12182 \r
12183                         // Is hidden then return undefined\r
12184                         if (t._isHidden())\r
12185                                 return;\r
12186 \r
12187                         // Registred commands\r
12188                         if (o = t.queryStateCommands[cmd]) {\r
12189                                 s = o.func.call(o.scope);\r
12190 \r
12191                                 // Fall though on true\r
12192                                 if (s !== true)\r
12193                                         return s;\r
12194                         }\r
12195 \r
12196                         // Registred commands\r
12197                         o = t.editorCommands.queryCommandState(cmd);\r
12198                         if (o !== -1)\r
12199                                 return o;\r
12200 \r
12201                         // Browser commands\r
12202                         try {\r
12203                                 return this.getDoc().queryCommandState(cmd);\r
12204                         } catch (ex) {\r
12205                                 // Fails sometimes see bug: 1896577\r
12206                         }\r
12207                 },\r
12208 \r
12209                 queryCommandValue : function(c) {\r
12210                         var t = this, o, s;\r
12211 \r
12212                         // Is hidden then return undefined\r
12213                         if (t._isHidden())\r
12214                                 return;\r
12215 \r
12216                         // Registred commands\r
12217                         if (o = t.queryValueCommands[c]) {\r
12218                                 s = o.func.call(o.scope);\r
12219 \r
12220                                 // Fall though on true\r
12221                                 if (s !== true)\r
12222                                         return s;\r
12223                         }\r
12224 \r
12225                         // Registred commands\r
12226                         o = t.editorCommands.queryCommandValue(c);\r
12227                         if (is(o))\r
12228                                 return o;\r
12229 \r
12230                         // Browser commands\r
12231                         try {\r
12232                                 return this.getDoc().queryCommandValue(c);\r
12233                         } catch (ex) {\r
12234                                 // Fails sometimes see bug: 1896577\r
12235                         }\r
12236                 },\r
12237 \r
12238                 show : function() {\r
12239                         var t = this;\r
12240 \r
12241                         DOM.show(t.getContainer());\r
12242                         DOM.hide(t.id);\r
12243                         t.load();\r
12244                 },\r
12245 \r
12246                 hide : function() {\r
12247                         var t = this, d = t.getDoc();\r
12248 \r
12249                         // Fixed bug where IE has a blinking cursor left from the editor\r
12250                         if (isIE && d)\r
12251                                 d.execCommand('SelectAll');\r
12252 \r
12253                         // We must save before we hide so Safari doesn't crash\r
12254                         t.save();\r
12255                         DOM.hide(t.getContainer());\r
12256                         DOM.setStyle(t.id, 'display', t.orgDisplay);\r
12257                 },\r
12258 \r
12259                 isHidden : function() {\r
12260                         return !DOM.isHidden(this.id);\r
12261                 },\r
12262 \r
12263                 setProgressState : function(b, ti, o) {\r
12264                         this.onSetProgressState.dispatch(this, b, ti, o);\r
12265 \r
12266                         return b;\r
12267                 },\r
12268 \r
12269                 load : function(o) {\r
12270                         var t = this, e = t.getElement(), h;\r
12271 \r
12272                         if (e) {\r
12273                                 o = o || {};\r
12274                                 o.load = true;\r
12275 \r
12276                                 // Double encode existing entities in the value\r
12277                                 h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);\r
12278                                 o.element = e;\r
12279 \r
12280                                 if (!o.no_events)\r
12281                                         t.onLoadContent.dispatch(t, o);\r
12282 \r
12283                                 o.element = e = null;\r
12284 \r
12285                                 return h;\r
12286                         }\r
12287                 },\r
12288 \r
12289                 save : function(o) {\r
12290                         var t = this, e = t.getElement(), h, f;\r
12291 \r
12292                         if (!e || !t.initialized)\r
12293                                 return;\r
12294 \r
12295                         o = o || {};\r
12296                         o.save = true;\r
12297 \r
12298                         // Add undo level will trigger onchange event\r
12299                         if (!o.no_events) {\r
12300                                 t.undoManager.typing = false;\r
12301                                 t.undoManager.add();\r
12302                         }\r
12303 \r
12304                         o.element = e;\r
12305                         h = o.content = t.getContent(o);\r
12306 \r
12307                         if (!o.no_events)\r
12308                                 t.onSaveContent.dispatch(t, o);\r
12309 \r
12310                         h = o.content;\r
12311 \r
12312                         if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {\r
12313                                 e.innerHTML = h;\r
12314 \r
12315                                 // Update hidden form element\r
12316                                 if (f = DOM.getParent(t.id, 'form')) {\r
12317                                         each(f.elements, function(e) {\r
12318                                                 if (e.name == t.id) {\r
12319                                                         e.value = h;\r
12320                                                         return false;\r
12321                                                 }\r
12322                                         });\r
12323                                 }\r
12324                         } else\r
12325                                 e.value = h;\r
12326 \r
12327                         o.element = e = null;\r
12328 \r
12329                         return h;\r
12330                 },\r
12331 \r
12332                 setContent : function(content, args) {\r
12333                         var self = this, rootNode, body = self.getBody(), forcedRootBlockName;\r
12334 \r
12335                         // Setup args object\r
12336                         args = args || {};\r
12337                         args.format = args.format || 'html';\r
12338                         args.set = true;\r
12339                         args.content = content;\r
12340 \r
12341                         // Do preprocessing\r
12342                         if (!args.no_events)\r
12343                                 self.onBeforeSetContent.dispatch(self, args);\r
12344 \r
12345                         content = args.content;\r
12346 \r
12347                         // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content\r
12348                         // It will also be impossible to place the caret in the editor unless there is a BR element present\r
12349                         if (!tinymce.isIE && (content.length === 0 || /^\s+$/.test(content))) {\r
12350                                 forcedRootBlockName = self.settings.forced_root_block;\r
12351                                 if (forcedRootBlockName)\r
12352                                         content = '<' + forcedRootBlockName + '><br data-mce-bogus="1"></' + forcedRootBlockName + '>';\r
12353                                 else\r
12354                                         content = '<br data-mce-bogus="1">';\r
12355 \r
12356                                 body.innerHTML = content;\r
12357                                 self.selection.select(body, true);\r
12358                                 self.selection.collapse(true);\r
12359                                 return;\r
12360                         }\r
12361 \r
12362                         // Parse and serialize the html\r
12363                         if (args.format !== 'raw') {\r
12364                                 content = new tinymce.html.Serializer({}, self.schema).serialize(\r
12365                                         self.parser.parse(content)\r
12366                                 );\r
12367                         }\r
12368 \r
12369                         // Set the new cleaned contents to the editor\r
12370                         args.content = tinymce.trim(content);\r
12371                         self.dom.setHTML(body, args.content);\r
12372 \r
12373                         // Do post processing\r
12374                         if (!args.no_events)\r
12375                                 self.onSetContent.dispatch(self, args);\r
12376 \r
12377                         self.selection.normalize();\r
12378 \r
12379                         return args.content;\r
12380                 },\r
12381 \r
12382                 getContent : function(args) {\r
12383                         var self = this, content;\r
12384 \r
12385                         // Setup args object\r
12386                         args = args || {};\r
12387                         args.format = args.format || 'html';\r
12388                         args.get = true;\r
12389 \r
12390                         // Do preprocessing\r
12391                         if (!args.no_events)\r
12392                                 self.onBeforeGetContent.dispatch(self, args);\r
12393 \r
12394                         // Get raw contents or by default the cleaned contents\r
12395                         if (args.format == 'raw')\r
12396                                 content = self.getBody().innerHTML;\r
12397                         else\r
12398                                 content = self.serializer.serialize(self.getBody(), args);\r
12399 \r
12400                         args.content = tinymce.trim(content);\r
12401 \r
12402                         // Do post processing\r
12403                         if (!args.no_events)\r
12404                                 self.onGetContent.dispatch(self, args);\r
12405 \r
12406                         return args.content;\r
12407                 },\r
12408 \r
12409                 isDirty : function() {\r
12410                         var self = this;\r
12411 \r
12412                         return tinymce.trim(self.startContent) != tinymce.trim(self.getContent({format : 'raw', no_events : 1})) && !self.isNotDirty;\r
12413                 },\r
12414 \r
12415                 getContainer : function() {\r
12416                         var t = this;\r
12417 \r
12418                         if (!t.container)\r
12419                                 t.container = DOM.get(t.editorContainer || t.id + '_parent');\r
12420 \r
12421                         return t.container;\r
12422                 },\r
12423 \r
12424                 getContentAreaContainer : function() {\r
12425                         return this.contentAreaContainer;\r
12426                 },\r
12427 \r
12428                 getElement : function() {\r
12429                         return DOM.get(this.settings.content_element || this.id);\r
12430                 },\r
12431 \r
12432                 getWin : function() {\r
12433                         var t = this, e;\r
12434 \r
12435                         if (!t.contentWindow) {\r
12436                                 e = DOM.get(t.id + "_ifr");\r
12437 \r
12438                                 if (e)\r
12439                                         t.contentWindow = e.contentWindow;\r
12440                         }\r
12441 \r
12442                         return t.contentWindow;\r
12443                 },\r
12444 \r
12445                 getDoc : function() {\r
12446                         var t = this, w;\r
12447 \r
12448                         if (!t.contentDocument) {\r
12449                                 w = t.getWin();\r
12450 \r
12451                                 if (w)\r
12452                                         t.contentDocument = w.document;\r
12453                         }\r
12454 \r
12455                         return t.contentDocument;\r
12456                 },\r
12457 \r
12458                 getBody : function() {\r
12459                         return this.bodyElement || this.getDoc().body;\r
12460                 },\r
12461 \r
12462                 convertURL : function(u, n, e) {\r
12463                         var t = this, s = t.settings;\r
12464 \r
12465                         // Use callback instead\r
12466                         if (s.urlconverter_callback)\r
12467                                 return t.execCallback('urlconverter_callback', u, e, true, n);\r
12468 \r
12469                         // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs\r
12470                         if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)\r
12471                                 return u;\r
12472 \r
12473                         // Convert to relative\r
12474                         if (s.relative_urls)\r
12475                                 return t.documentBaseURI.toRelative(u);\r
12476 \r
12477                         // Convert to absolute\r
12478                         u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);\r
12479 \r
12480                         return u;\r
12481                 },\r
12482 \r
12483                 addVisual : function(e) {\r
12484                         var t = this, s = t.settings;\r
12485 \r
12486                         e = e || t.getBody();\r
12487 \r
12488                         if (!is(t.hasVisual))\r
12489                                 t.hasVisual = s.visual;\r
12490 \r
12491                         each(t.dom.select('table,a', e), function(e) {\r
12492                                 var v;\r
12493 \r
12494                                 switch (e.nodeName) {\r
12495                                         case 'TABLE':\r
12496                                                 v = t.dom.getAttrib(e, 'border');\r
12497 \r
12498                                                 if (!v || v == '0') {\r
12499                                                         if (t.hasVisual)\r
12500                                                                 t.dom.addClass(e, s.visual_table_class);\r
12501                                                         else\r
12502                                                                 t.dom.removeClass(e, s.visual_table_class);\r
12503                                                 }\r
12504 \r
12505                                                 return;\r
12506 \r
12507                                         case 'A':\r
12508                                                 v = t.dom.getAttrib(e, 'name');\r
12509 \r
12510                                                 if (v) {\r
12511                                                         if (t.hasVisual)\r
12512                                                                 t.dom.addClass(e, 'mceItemAnchor');\r
12513                                                         else\r
12514                                                                 t.dom.removeClass(e, 'mceItemAnchor');\r
12515                                                 }\r
12516 \r
12517                                                 return;\r
12518                                 }\r
12519                         });\r
12520 \r
12521                         t.onVisualAid.dispatch(t, e, t.hasVisual);\r
12522                 },\r
12523 \r
12524                 remove : function() {\r
12525                         var t = this, e = t.getContainer();\r
12526 \r
12527                         t.removed = 1; // Cancels post remove event execution\r
12528                         t.hide();\r
12529 \r
12530                         t.execCallback('remove_instance_callback', t);\r
12531                         t.onRemove.dispatch(t);\r
12532 \r
12533                         // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command\r
12534                         t.onExecCommand.listeners = [];\r
12535 \r
12536                         tinymce.remove(t);\r
12537                         DOM.remove(e);\r
12538                 },\r
12539 \r
12540                 destroy : function(s) {\r
12541                         var t = this;\r
12542 \r
12543                         // One time is enough\r
12544                         if (t.destroyed)\r
12545                                 return;\r
12546 \r
12547                         if (!s) {\r
12548                                 tinymce.removeUnload(t.destroy);\r
12549                                 tinyMCE.onBeforeUnload.remove(t._beforeUnload);\r
12550 \r
12551                                 // Manual destroy\r
12552                                 if (t.theme && t.theme.destroy)\r
12553                                         t.theme.destroy();\r
12554 \r
12555                                 // Destroy controls, selection and dom\r
12556                                 t.controlManager.destroy();\r
12557                                 t.selection.destroy();\r
12558                                 t.dom.destroy();\r
12559 \r
12560                                 // Remove all events\r
12561 \r
12562                                 // Don't clear the window or document if content editable\r
12563                                 // is enabled since other instances might still be present\r
12564                                 if (!t.settings.content_editable) {\r
12565                                         Event.clear(t.getWin());\r
12566                                         Event.clear(t.getDoc());\r
12567                                 }\r
12568 \r
12569                                 Event.clear(t.getBody());\r
12570                                 Event.clear(t.formElement);\r
12571                         }\r
12572 \r
12573                         if (t.formElement) {\r
12574                                 t.formElement.submit = t.formElement._mceOldSubmit;\r
12575                                 t.formElement._mceOldSubmit = null;\r
12576                         }\r
12577 \r
12578                         t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;\r
12579 \r
12580                         if (t.selection)\r
12581                                 t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;\r
12582 \r
12583                         t.destroyed = 1;\r
12584                 },\r
12585 \r
12586                 // Internal functions\r
12587 \r
12588                 _addEvents : function() {\r
12589                         // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset\r
12590                         var t = this, i, s = t.settings, dom = t.dom, lo = {\r
12591                                 mouseup : 'onMouseUp',\r
12592                                 mousedown : 'onMouseDown',\r
12593                                 click : 'onClick',\r
12594                                 keyup : 'onKeyUp',\r
12595                                 keydown : 'onKeyDown',\r
12596                                 keypress : 'onKeyPress',\r
12597                                 submit : 'onSubmit',\r
12598                                 reset : 'onReset',\r
12599                                 contextmenu : 'onContextMenu',\r
12600                                 dblclick : 'onDblClick',\r
12601                                 paste : 'onPaste' // Doesn't work in all browsers yet\r
12602                         };\r
12603 \r
12604                         function eventHandler(e, o) {\r
12605                                 var ty = e.type;\r
12606 \r
12607                                 // Don't fire events when it's removed\r
12608                                 if (t.removed)\r
12609                                         return;\r
12610 \r
12611                                 // Generic event handler\r
12612                                 if (t.onEvent.dispatch(t, e, o) !== false) {\r
12613                                         // Specific event handler\r
12614                                         t[lo[e.fakeType || e.type]].dispatch(t, e, o);\r
12615                                 }\r
12616                         };\r
12617 \r
12618                         // Add DOM events\r
12619                         each(lo, function(v, k) {\r
12620                                 switch (k) {\r
12621                                         case 'contextmenu':\r
12622                                                 dom.bind(t.getDoc(), k, eventHandler);\r
12623                                                 break;\r
12624 \r
12625                                         case 'paste':\r
12626                                                 dom.bind(t.getBody(), k, function(e) {\r
12627                                                         eventHandler(e);\r
12628                                                 });\r
12629                                                 break;\r
12630 \r
12631                                         case 'submit':\r
12632                                         case 'reset':\r
12633                                                 dom.bind(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);\r
12634                                                 break;\r
12635 \r
12636                                         default:\r
12637                                                 dom.bind(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler);\r
12638                                 }\r
12639                         });\r
12640 \r
12641                         dom.bind(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) {\r
12642                                 t.focus(true);\r
12643                         });\r
12644 \r
12645 \r
12646                         // Fixes bug where a specified document_base_uri could result in broken images\r
12647                         // This will also fix drag drop of images in Gecko\r
12648                         if (tinymce.isGecko) {\r
12649                                 dom.bind(t.getDoc(), 'DOMNodeInserted', function(e) {\r
12650                                         var v;\r
12651 \r
12652                                         e = e.target;\r
12653 \r
12654                                         if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('data-mce-src')))\r
12655                                                 e.src = t.documentBaseURI.toAbsolute(v);\r
12656                                 });\r
12657                         }\r
12658 \r
12659                         // Set various midas options in Gecko\r
12660                         if (isGecko) {\r
12661                                 function setOpts() {\r
12662                                         var t = this, d = t.getDoc(), s = t.settings;\r
12663 \r
12664                                         if (isGecko && !s.readonly) {\r
12665                                                 t._refreshContentEditable();\r
12666 \r
12667                                                 try {\r
12668                                                         // Try new Gecko method\r
12669                                                         d.execCommand("styleWithCSS", 0, false);\r
12670                                                 } catch (ex) {\r
12671                                                         // Use old method\r
12672                                                         if (!t._isHidden())\r
12673                                                                 try {d.execCommand("useCSS", 0, true);} catch (ex) {}\r
12674                                                 }\r
12675 \r
12676                                                 if (!s.table_inline_editing)\r
12677                                                         try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {}\r
12678 \r
12679                                                 if (!s.object_resizing)\r
12680                                                         try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {}\r
12681                                         }\r
12682                                 };\r
12683 \r
12684                                 t.onBeforeExecCommand.add(setOpts);\r
12685                                 t.onMouseDown.add(setOpts);\r
12686                         }\r
12687 \r
12688                         t.onClick.add(function(ed, e) {\r
12689                                 e = e.target;\r
12690 \r
12691                                 // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250\r
12692                                 // WebKit can't even do simple things like selecting an image\r
12693                                 // Needs tobe the setBaseAndExtend or it will fail to select floated images\r
12694                                 if (tinymce.isWebKit && e.nodeName == 'IMG')\r
12695                                         t.selection.getSel().setBaseAndExtent(e, 0, e, 1);\r
12696 \r
12697                                 if (e.nodeName == 'A' && dom.hasClass(e, 'mceItemAnchor'))\r
12698                                         t.selection.select(e);\r
12699 \r
12700                                 t.nodeChanged();\r
12701                         });\r
12702 \r
12703                         // Add node change handlers\r
12704                         t.onMouseUp.add(t.nodeChanged);\r
12705                         //t.onClick.add(t.nodeChanged);\r
12706                         t.onKeyUp.add(function(ed, e) {\r
12707                                 var c = e.keyCode;\r
12708 \r
12709                                 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
12710                                         t.nodeChanged();\r
12711                         });\r
12712 \r
12713 \r
12714                         // Add block quote deletion handler\r
12715                         t.onKeyDown.add(function(ed, e) {\r
12716                                 // Was the BACKSPACE key pressed?\r
12717                                 if (e.keyCode != 8)\r
12718                                         return;\r
12719 \r
12720                                 var n = ed.selection.getRng().startContainer;\r
12721                                 var offset = ed.selection.getRng().startOffset;\r
12722 \r
12723                                 while (n && n.nodeType && n.nodeType != 1 && n.parentNode)\r
12724                                         n = n.parentNode;\r
12725                                         \r
12726                                 // Is the cursor at the beginning of a blockquote?\r
12727                                 if (n && n.parentNode && n.parentNode.tagName === 'BLOCKQUOTE' && n.parentNode.firstChild == n && offset == 0) {\r
12728                                         // Remove the blockquote\r
12729                                         ed.formatter.toggle('blockquote', null, n.parentNode);\r
12730 \r
12731                                         // Move the caret to the beginning of n\r
12732                                         var rng = ed.selection.getRng();\r
12733                                         rng.setStart(n, 0);\r
12734                                         rng.setEnd(n, 0);\r
12735                                         ed.selection.setRng(rng);\r
12736                                         ed.selection.collapse(false);\r
12737                                 }\r
12738                         });\r
12739  \r
12740 \r
12741 \r
12742                         // Add reset handler\r
12743                         t.onReset.add(function() {\r
12744                                 t.setContent(t.startContent, {format : 'raw'});\r
12745                         });\r
12746 \r
12747                         // Add shortcuts\r
12748                         if (s.custom_shortcuts) {\r
12749                                 if (s.custom_undo_redo_keyboard_shortcuts) {\r
12750                                         t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo');\r
12751                                         t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo');\r
12752                                 }\r
12753 \r
12754                                 // Add default shortcuts for gecko\r
12755                                 t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');\r
12756                                 t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');\r
12757                                 t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');\r
12758 \r
12759                                 // BlockFormat shortcuts keys\r
12760                                 for (i=1; i<=6; i++)\r
12761                                         t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);\r
12762 \r
12763                                 t.addShortcut('ctrl+7', '', ['FormatBlock', false, 'p']);\r
12764                                 t.addShortcut('ctrl+8', '', ['FormatBlock', false, 'div']);\r
12765                                 t.addShortcut('ctrl+9', '', ['FormatBlock', false, 'address']);\r
12766 \r
12767                                 function find(e) {\r
12768                                         var v = null;\r
12769 \r
12770                                         if (!e.altKey && !e.ctrlKey && !e.metaKey)\r
12771                                                 return v;\r
12772 \r
12773                                         each(t.shortcuts, function(o) {\r
12774                                                 if (tinymce.isMac && o.ctrl != e.metaKey)\r
12775                                                         return;\r
12776                                                 else if (!tinymce.isMac && o.ctrl != e.ctrlKey)\r
12777                                                         return;\r
12778 \r
12779                                                 if (o.alt != e.altKey)\r
12780                                                         return;\r
12781 \r
12782                                                 if (o.shift != e.shiftKey)\r
12783                                                         return;\r
12784 \r
12785                                                 if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {\r
12786                                                         v = o;\r
12787                                                         return false;\r
12788                                                 }\r
12789                                         });\r
12790 \r
12791                                         return v;\r
12792                                 };\r
12793 \r
12794                                 t.onKeyUp.add(function(ed, e) {\r
12795                                         var o = find(e);\r
12796 \r
12797                                         if (o)\r
12798                                                 return Event.cancel(e);\r
12799                                 });\r
12800 \r
12801                                 t.onKeyPress.add(function(ed, e) {\r
12802                                         var o = find(e);\r
12803 \r
12804                                         if (o)\r
12805                                                 return Event.cancel(e);\r
12806                                 });\r
12807 \r
12808                                 t.onKeyDown.add(function(ed, e) {\r
12809                                         var o = find(e);\r
12810 \r
12811                                         if (o) {\r
12812                                                 o.func.call(o.scope);\r
12813                                                 return Event.cancel(e);\r
12814                                         }\r
12815                                 });\r
12816                         }\r
12817 \r
12818                         if (tinymce.isIE) {\r
12819                                 // Fix so resize will only update the width and height attributes not the styles of an image\r
12820                                 // It will also block mceItemNoResize items\r
12821                                 dom.bind(t.getDoc(), 'controlselect', function(e) {\r
12822                                         var re = t.resizeInfo, cb;\r
12823 \r
12824                                         e = e.target;\r
12825 \r
12826                                         // Don't do this action for non image elements\r
12827                                         if (e.nodeName !== 'IMG')\r
12828                                                 return;\r
12829 \r
12830                                         if (re)\r
12831                                                 dom.unbind(re.node, re.ev, re.cb);\r
12832 \r
12833                                         if (!dom.hasClass(e, 'mceItemNoResize')) {\r
12834                                                 ev = 'resizeend';\r
12835                                                 cb = dom.bind(e, ev, function(e) {\r
12836                                                         var v;\r
12837 \r
12838                                                         e = e.target;\r
12839 \r
12840                                                         if (v = dom.getStyle(e, 'width')) {\r
12841                                                                 dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, ''));\r
12842                                                                 dom.setStyle(e, 'width', '');\r
12843                                                         }\r
12844 \r
12845                                                         if (v = dom.getStyle(e, 'height')) {\r
12846                                                                 dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, ''));\r
12847                                                                 dom.setStyle(e, 'height', '');\r
12848                                                         }\r
12849                                                 });\r
12850                                         } else {\r
12851                                                 ev = 'resizestart';\r
12852                                                 cb = dom.bind(e, 'resizestart', Event.cancel, Event);\r
12853                                         }\r
12854 \r
12855                                         re = t.resizeInfo = {\r
12856                                                 node : e,\r
12857                                                 ev : ev,\r
12858                                                 cb : cb\r
12859                                         };\r
12860                                 });\r
12861                         }\r
12862 \r
12863                         if (tinymce.isOpera) {\r
12864                                 t.onClick.add(function(ed, e) {\r
12865                                         Event.prevent(e);\r
12866                                 });\r
12867                         }\r
12868 \r
12869                         // Add custom undo/redo handlers\r
12870                         if (s.custom_undo_redo) {\r
12871                                 function addUndo() {\r
12872                                         t.undoManager.typing = false;\r
12873                                         t.undoManager.add();\r
12874                                 };\r
12875 \r
12876                                 dom.bind(t.getDoc(), 'focusout', function(e) {\r
12877                                         if (!t.removed && t.undoManager.typing)\r
12878                                                 addUndo();\r
12879                                 });\r
12880 \r
12881                                 // Add undo level when contents is drag/dropped within the editor\r
12882                                 t.dom.bind(t.dom.getRoot(), 'dragend', function(e) {\r
12883                                         addUndo();\r
12884                                 });\r
12885 \r
12886                                 t.onKeyUp.add(function(ed, e) {\r
12887                                         var keyCode = e.keyCode;\r
12888 \r
12889                                         if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 13 || keyCode == 45 || e.ctrlKey)\r
12890                                                 addUndo();\r
12891                                 });\r
12892 \r
12893                                 t.onKeyDown.add(function(ed, e) {\r
12894                                         var keyCode = e.keyCode, sel;\r
12895 \r
12896                                         if (keyCode == 8) {\r
12897                                                 sel = t.getDoc().selection;\r
12898 \r
12899                                                 // Fix IE control + backspace browser bug\r
12900                                                 if (sel && sel.createRange && sel.createRange().item) {\r
12901                                                         t.undoManager.beforeChange();\r
12902                                                         ed.dom.remove(sel.createRange().item(0));\r
12903                                                         addUndo();\r
12904 \r
12905                                                         return Event.cancel(e);\r
12906                                                 }\r
12907                                         }\r
12908 \r
12909                                         // Is caracter positon keys left,right,up,down,home,end,pgdown,pgup,enter\r
12910                                         if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 13 || keyCode == 45) {\r
12911                                                 // Add position before enter key is pressed, used by IE since it still uses the default browser behavior\r
12912                                                 // Todo: Remove this once we normalize enter behavior on IE\r
12913                                                 if (tinymce.isIE && keyCode == 13)\r
12914                                                         t.undoManager.beforeChange();\r
12915 \r
12916                                                 if (t.undoManager.typing)\r
12917                                                         addUndo();\r
12918 \r
12919                                                 return;\r
12920                                         }\r
12921 \r
12922                                         // If key isn't shift,ctrl,alt,capslock,metakey\r
12923                                         if ((keyCode < 16 || keyCode > 20) && keyCode != 224 && keyCode != 91 && !t.undoManager.typing) {\r
12924                                                 t.undoManager.beforeChange();\r
12925                                                 t.undoManager.typing = true;\r
12926                                                 t.undoManager.add();\r
12927                                         }\r
12928                                 });\r
12929 \r
12930                                 t.onMouseDown.add(function() {\r
12931                                         if (t.undoManager.typing)\r
12932                                                 addUndo();\r
12933                                 });\r
12934                         }\r
12935 \r
12936                         // Fire a nodeChanged when the selection is changed on WebKit this fixes selection issues on iOS5\r
12937                         // It only fires the nodeChange event every 50ms since it would other wise update the UI when you type and it hogs the CPU\r
12938                         if (tinymce.isWebKit) {\r
12939                                 dom.bind(t.getDoc(), 'selectionchange', function() {\r
12940                                         if (t.selectionTimer) {\r
12941                                                 clearTimeout(t.selectionTimer);\r
12942                                                 t.selectionTimer = 0;\r
12943                                         }\r
12944 \r
12945                                         t.selectionTimer = window.setTimeout(function() {\r
12946                                                 t.nodeChanged();\r
12947                                         }, 50);\r
12948                                 });\r
12949                         }\r
12950 \r
12951                         // Bug fix for FireFox keeping styles from end of selection instead of start.\r
12952                         if (tinymce.isGecko) {\r
12953                                 function getAttributeApplyFunction() {\r
12954                                         var template = t.dom.getAttribs(t.selection.getStart().cloneNode(false));\r
12955 \r
12956                                         return function() {\r
12957                                                 var target = t.selection.getStart();\r
12958 \r
12959                                                 if (target !== t.getBody()) {\r
12960                                                         t.dom.removeAllAttribs(target);\r
12961 \r
12962                                                         each(template, function(attr) {\r
12963                                                                 target.setAttributeNode(attr.cloneNode(true));\r
12964                                                         });\r
12965                                                 }\r
12966                                         };\r
12967                                 }\r
12968 \r
12969                                 function isSelectionAcrossElements() {\r
12970                                         var s = t.selection;\r
12971 \r
12972                                         return !s.isCollapsed() && s.getStart() != s.getEnd();\r
12973                                 }\r
12974 \r
12975                                 t.onKeyPress.add(function(ed, e) {\r
12976                                         var applyAttributes;\r
12977 \r
12978                                         if ((e.keyCode == 8 || e.keyCode == 46) && isSelectionAcrossElements()) {\r
12979                                                 applyAttributes = getAttributeApplyFunction();\r
12980                                                 t.getDoc().execCommand('delete', false, null);\r
12981                                                 applyAttributes();\r
12982 \r
12983                                                 return Event.cancel(e);\r
12984                                         }\r
12985                                 });\r
12986 \r
12987                                 t.dom.bind(t.getDoc(), 'cut', function(e) {\r
12988                                         var applyAttributes;\r
12989 \r
12990                                         if (isSelectionAcrossElements()) {\r
12991                                                 applyAttributes = getAttributeApplyFunction();\r
12992                                                 t.onKeyUp.addToTop(Event.cancel, Event);\r
12993 \r
12994                                                 setTimeout(function() {\r
12995                                                         applyAttributes();\r
12996                                                         t.onKeyUp.remove(Event.cancel, Event);\r
12997                                                 }, 0);\r
12998                                         }\r
12999                                 });\r
13000                         }\r
13001                 },\r
13002 \r
13003                 _refreshContentEditable : function() {\r
13004                         var self = this, body, parent;\r
13005 \r
13006                         // Check if the editor was hidden and the re-initalize contentEditable mode by removing and adding the body again\r
13007                         if (self._isHidden()) {\r
13008                                 body = self.getBody();\r
13009                                 parent = body.parentNode;\r
13010 \r
13011                                 parent.removeChild(body);\r
13012                                 parent.appendChild(body);\r
13013 \r
13014                                 body.focus();\r
13015                         }\r
13016                 },\r
13017 \r
13018                 _isHidden : function() {\r
13019                         var s;\r
13020 \r
13021                         if (!isGecko)\r
13022                                 return 0;\r
13023 \r
13024                         // Weird, wheres that cursor selection?\r
13025                         s = this.selection.getSel();\r
13026                         return (!s || !s.rangeCount || s.rangeCount == 0);\r
13027                 }\r
13028         });\r
13029 })(tinymce);\r
13030 \r
13031 (function(tinymce) {\r
13032         // Added for compression purposes\r
13033         var each = tinymce.each, undefined, TRUE = true, FALSE = false;\r
13034 \r
13035         tinymce.EditorCommands = function(editor) {\r
13036                 var dom = editor.dom,\r
13037                         selection = editor.selection,\r
13038                         commands = {state: {}, exec : {}, value : {}},\r
13039                         settings = editor.settings,\r
13040                         formatter = editor.formatter,\r
13041                         bookmark;\r
13042 \r
13043                 function execCommand(command, ui, value) {\r
13044                         var func;\r
13045 \r
13046                         command = command.toLowerCase();\r
13047                         if (func = commands.exec[command]) {\r
13048                                 func(command, ui, value);\r
13049                                 return TRUE;\r
13050                         }\r
13051 \r
13052                         return FALSE;\r
13053                 };\r
13054 \r
13055                 function queryCommandState(command) {\r
13056                         var func;\r
13057 \r
13058                         command = command.toLowerCase();\r
13059                         if (func = commands.state[command])\r
13060                                 return func(command);\r
13061 \r
13062                         return -1;\r
13063                 };\r
13064 \r
13065                 function queryCommandValue(command) {\r
13066                         var func;\r
13067 \r
13068                         command = command.toLowerCase();\r
13069                         if (func = commands.value[command])\r
13070                                 return func(command);\r
13071 \r
13072                         return FALSE;\r
13073                 };\r
13074 \r
13075                 function addCommands(command_list, type) {\r
13076                         type = type || 'exec';\r
13077 \r
13078                         each(command_list, function(callback, command) {\r
13079                                 each(command.toLowerCase().split(','), function(command) {\r
13080                                         commands[type][command] = callback;\r
13081                                 });\r
13082                         });\r
13083                 };\r
13084 \r
13085                 // Expose public methods\r
13086                 tinymce.extend(this, {\r
13087                         execCommand : execCommand,\r
13088                         queryCommandState : queryCommandState,\r
13089                         queryCommandValue : queryCommandValue,\r
13090                         addCommands : addCommands\r
13091                 });\r
13092 \r
13093                 // Private methods\r
13094 \r
13095                 function execNativeCommand(command, ui, value) {\r
13096                         if (ui === undefined)\r
13097                                 ui = FALSE;\r
13098 \r
13099                         if (value === undefined)\r
13100                                 value = null;\r
13101 \r
13102                         return editor.getDoc().execCommand(command, ui, value);\r
13103                 };\r
13104 \r
13105                 function isFormatMatch(name) {\r
13106                         return formatter.match(name);\r
13107                 };\r
13108 \r
13109                 function toggleFormat(name, value) {\r
13110                         formatter.toggle(name, value ? {value : value} : undefined);\r
13111                 };\r
13112 \r
13113                 function storeSelection(type) {\r
13114                         bookmark = selection.getBookmark(type);\r
13115                 };\r
13116 \r
13117                 function restoreSelection() {\r
13118                         selection.moveToBookmark(bookmark);\r
13119                 };\r
13120 \r
13121                 // Add execCommand overrides\r
13122                 addCommands({\r
13123                         // Ignore these, added for compatibility\r
13124                         'mceResetDesignMode,mceBeginUndoLevel' : function() {},\r
13125 \r
13126                         // Add undo manager logic\r
13127                         'mceEndUndoLevel,mceAddUndoLevel' : function() {\r
13128                                 editor.undoManager.add();\r
13129                         },\r
13130 \r
13131                         'Cut,Copy,Paste' : function(command) {\r
13132                                 var doc = editor.getDoc(), failed;\r
13133 \r
13134                                 // Try executing the native command\r
13135                                 try {\r
13136                                         execNativeCommand(command);\r
13137                                 } catch (ex) {\r
13138                                         // Command failed\r
13139                                         failed = TRUE;\r
13140                                 }\r
13141 \r
13142                                 // Present alert message about clipboard access not being available\r
13143                                 if (failed || !doc.queryCommandSupported(command)) {\r
13144                                         if (tinymce.isGecko) {\r
13145                                                 editor.windowManager.confirm(editor.getLang('clipboard_msg'), function(state) {\r
13146                                                         if (state)\r
13147                                                                 open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', '_blank');\r
13148                                                 });\r
13149                                         } else\r
13150                                                 editor.windowManager.alert(editor.getLang('clipboard_no_support'));\r
13151                                 }\r
13152                         },\r
13153 \r
13154                         // Override unlink command\r
13155                         unlink : function(command) {\r
13156                                 if (selection.isCollapsed())\r
13157                                         selection.select(selection.getNode());\r
13158 \r
13159                                 execNativeCommand(command);\r
13160                                 selection.collapse(FALSE);\r
13161                         },\r
13162 \r
13163                         // Override justify commands to use the text formatter engine\r
13164                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
13165                                 var align = command.substring(7);\r
13166 \r
13167                                 // Remove all other alignments first\r
13168                                 each('left,center,right,full'.split(','), function(name) {\r
13169                                         if (align != name)\r
13170                                                 formatter.remove('align' + name);\r
13171                                 });\r
13172 \r
13173                                 toggleFormat('align' + align);\r
13174                                 execCommand('mceRepaint');\r
13175                         },\r
13176 \r
13177                         // Override list commands to fix WebKit bug\r
13178                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
13179                                 var listElm, listParent;\r
13180 \r
13181                                 execNativeCommand(command);\r
13182 \r
13183                                 // WebKit produces lists within block elements so we need to split them\r
13184                                 // we will replace the native list creation logic to custom logic later on\r
13185                                 // TODO: Remove this when the list creation logic is removed\r
13186                                 listElm = dom.getParent(selection.getNode(), 'ol,ul');\r
13187                                 if (listElm) {\r
13188                                         listParent = listElm.parentNode;\r
13189 \r
13190                                         // If list is within a text block then split that block\r
13191                                         if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {\r
13192                                                 storeSelection();\r
13193                                                 dom.split(listParent, listElm);\r
13194                                                 restoreSelection();\r
13195                                         }\r
13196                                 }\r
13197                         },\r
13198 \r
13199                         // Override commands to use the text formatter engine\r
13200                         'Bold,Italic,Underline,Strikethrough,Superscript,Subscript' : function(command) {\r
13201                                 toggleFormat(command);\r
13202                         },\r
13203 \r
13204                         // Override commands to use the text formatter engine\r
13205                         'ForeColor,HiliteColor,FontName' : function(command, ui, value) {\r
13206                                 toggleFormat(command, value);\r
13207                         },\r
13208 \r
13209                         FontSize : function(command, ui, value) {\r
13210                                 var fontClasses, fontSizes;\r
13211 \r
13212                                 // Convert font size 1-7 to styles\r
13213                                 if (value >= 1 && value <= 7) {\r
13214                                         fontSizes = tinymce.explode(settings.font_size_style_values);\r
13215                                         fontClasses = tinymce.explode(settings.font_size_classes);\r
13216 \r
13217                                         if (fontClasses)\r
13218                                                 value = fontClasses[value - 1] || value;\r
13219                                         else\r
13220                                                 value = fontSizes[value - 1] || value;\r
13221                                 }\r
13222 \r
13223                                 toggleFormat(command, value);\r
13224                         },\r
13225 \r
13226                         RemoveFormat : function(command) {\r
13227                                 formatter.remove(command);\r
13228                         },\r
13229 \r
13230                         mceBlockQuote : function(command) {\r
13231                                 toggleFormat('blockquote');\r
13232                         },\r
13233 \r
13234                         FormatBlock : function(command, ui, value) {\r
13235                                 return toggleFormat(value || 'p');\r
13236                         },\r
13237 \r
13238                         mceCleanup : function() {\r
13239                                 var bookmark = selection.getBookmark();\r
13240 \r
13241                                 editor.setContent(editor.getContent({cleanup : TRUE}), {cleanup : TRUE});\r
13242 \r
13243                                 selection.moveToBookmark(bookmark);\r
13244                         },\r
13245 \r
13246                         mceRemoveNode : function(command, ui, value) {\r
13247                                 var node = value || selection.getNode();\r
13248 \r
13249                                 // Make sure that the body node isn't removed\r
13250                                 if (node != editor.getBody()) {\r
13251                                         storeSelection();\r
13252                                         editor.dom.remove(node, TRUE);\r
13253                                         restoreSelection();\r
13254                                 }\r
13255                         },\r
13256 \r
13257                         mceSelectNodeDepth : function(command, ui, value) {\r
13258                                 var counter = 0;\r
13259 \r
13260                                 dom.getParent(selection.getNode(), function(node) {\r
13261                                         if (node.nodeType == 1 && counter++ == value) {\r
13262                                                 selection.select(node);\r
13263                                                 return FALSE;\r
13264                                         }\r
13265                                 }, editor.getBody());\r
13266                         },\r
13267 \r
13268                         mceSelectNode : function(command, ui, value) {\r
13269                                 selection.select(value);\r
13270                         },\r
13271 \r
13272                         mceInsertContent : function(command, ui, value) {\r
13273                                 var parser, serializer, parentNode, rootNode, fragment, args,\r
13274                                         marker, nodeRect, viewPortRect, rng, node, node2, bookmarkHtml, viewportBodyElement;\r
13275 \r
13276                                 // Setup parser and serializer\r
13277                                 parser = editor.parser;\r
13278                                 serializer = new tinymce.html.Serializer({}, editor.schema);\r
13279                                 bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">\uFEFF</span>';\r
13280 \r
13281                                 // Run beforeSetContent handlers on the HTML to be inserted\r
13282                                 args = {content: value, format: 'html'};\r
13283                                 selection.onBeforeSetContent.dispatch(selection, args);\r
13284                                 value = args.content;\r
13285 \r
13286                                 // Add caret at end of contents if it's missing\r
13287                                 if (value.indexOf('{$caret}') == -1)\r
13288                                         value += '{$caret}';\r
13289 \r
13290                                 // Replace the caret marker with a span bookmark element\r
13291                                 value = value.replace(/\{\$caret\}/, bookmarkHtml);\r
13292 \r
13293                                 // Insert node maker where we will insert the new HTML and get it's parent\r
13294                                 if (!selection.isCollapsed())\r
13295                                         editor.getDoc().execCommand('Delete', false, null);\r
13296 \r
13297                                 parentNode = selection.getNode();\r
13298 \r
13299                                 // Parse the fragment within the context of the parent node\r
13300                                 args = {context : parentNode.nodeName.toLowerCase()};\r
13301                                 fragment = parser.parse(value, args);\r
13302 \r
13303                                 // Move the caret to a more suitable location\r
13304                                 node = fragment.lastChild;\r
13305                                 if (node.attr('id') == 'mce_marker') {\r
13306                                         marker = node;\r
13307 \r
13308                                         for (node = node.prev; node; node = node.walk(true)) {\r
13309                                                 if (node.type == 3 || !dom.isBlock(node.name)) {\r
13310                                                         node.parent.insert(marker, node, node.name === 'br');\r
13311                                                         break;\r
13312                                                 }\r
13313                                         }\r
13314                                 }\r
13315 \r
13316                                 // If parser says valid we can insert the contents into that parent\r
13317                                 if (!args.invalid) {\r
13318                                         value = serializer.serialize(fragment);\r
13319 \r
13320                                         // Check if parent is empty or only has one BR element then set the innerHTML of that parent\r
13321                                         node = parentNode.firstChild;\r
13322                                         node2 = parentNode.lastChild;\r
13323                                         if (!node || (node === node2 && node.nodeName === 'BR'))\r
13324                                                 dom.setHTML(parentNode, value);\r
13325                                         else\r
13326                                                 selection.setContent(value);\r
13327                                 } else {\r
13328                                         // If the fragment was invalid within that context then we need\r
13329                                         // to parse and process the parent it's inserted into\r
13330 \r
13331                                         // Insert bookmark node and get the parent\r
13332                                         selection.setContent(bookmarkHtml);\r
13333                                         parentNode = editor.selection.getNode();\r
13334                                         rootNode = editor.getBody();\r
13335 \r
13336                                         // Opera will return the document node when selection is in root\r
13337                                         if (parentNode.nodeType == 9)\r
13338                                                 parentNode = node = rootNode;\r
13339                                         else\r
13340                                                 node = parentNode;\r
13341 \r
13342                                         // Find the ancestor just before the root element\r
13343                                         while (node !== rootNode) {\r
13344                                                 parentNode = node;\r
13345                                                 node = node.parentNode;\r
13346                                         }\r
13347 \r
13348                                         // Get the outer/inner HTML depending on if we are in the root and parser and serialize that\r
13349                                         value = parentNode == rootNode ? rootNode.innerHTML : dom.getOuterHTML(parentNode);\r
13350                                         value = serializer.serialize(\r
13351                                                 parser.parse(\r
13352                                                         // Need to replace by using a function since $ in the contents would otherwise be a problem\r
13353                                                         value.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i, function() {\r
13354                                                                 return serializer.serialize(fragment);\r
13355                                                         })\r
13356                                                 )\r
13357                                         );\r
13358 \r
13359                                         // Set the inner/outer HTML depending on if we are in the root or not\r
13360                                         if (parentNode == rootNode)\r
13361                                                 dom.setHTML(rootNode, value);\r
13362                                         else\r
13363                                                 dom.setOuterHTML(parentNode, value);\r
13364                                 }\r
13365 \r
13366                                 marker = dom.get('mce_marker');\r
13367 \r
13368                                 // Scroll range into view scrollIntoView on element can't be used since it will scroll the main view port as well\r
13369                                 nodeRect = dom.getRect(marker);\r
13370                                 viewPortRect = dom.getViewPort(editor.getWin());\r
13371 \r
13372                                 // Check if node is out side the viewport if it is then scroll to it\r
13373                                 if ((nodeRect.y + nodeRect.h > viewPortRect.y + viewPortRect.h || nodeRect.y < viewPortRect.y) ||\r
13374                                         (nodeRect.x > viewPortRect.x + viewPortRect.w || nodeRect.x < viewPortRect.x)) {\r
13375                                         viewportBodyElement = tinymce.isIE ? editor.getDoc().documentElement : editor.getBody();\r
13376                                         viewportBodyElement.scrollLeft = nodeRect.x;\r
13377                                         viewportBodyElement.scrollTop = nodeRect.y - viewPortRect.h + 25;\r
13378                                 }\r
13379 \r
13380                                 // Move selection before marker and remove it\r
13381                                 rng = dom.createRng();\r
13382 \r
13383                                 // If previous sibling is a text node set the selection to the end of that node\r
13384                                 node = marker.previousSibling;\r
13385                                 if (node && node.nodeType == 3) {\r
13386                                         rng.setStart(node, node.nodeValue.length);\r
13387                                 } else {\r
13388                                         // If the previous sibling isn't a text node or doesn't exist set the selection before the marker node\r
13389                                         rng.setStartBefore(marker);\r
13390                                         rng.setEndBefore(marker);\r
13391                                 }\r
13392 \r
13393                                 // Remove the marker node and set the new range\r
13394                                 dom.remove(marker);\r
13395                                 selection.setRng(rng);\r
13396 \r
13397                                 // Dispatch after event and add any visual elements needed\r
13398                                 selection.onSetContent.dispatch(selection, args);\r
13399                                 editor.addVisual();\r
13400                         },\r
13401 \r
13402                         mceInsertRawHTML : function(command, ui, value) {\r
13403                                 selection.setContent('tiny_mce_marker');\r
13404                                 editor.setContent(editor.getContent().replace(/tiny_mce_marker/g, function() { return value }));\r
13405                         },\r
13406 \r
13407                         mceSetContent : function(command, ui, value) {\r
13408                                 editor.setContent(value);\r
13409                         },\r
13410 \r
13411                         'Indent,Outdent' : function(command) {\r
13412                                 var intentValue, indentUnit, value;\r
13413 \r
13414                                 // Setup indent level\r
13415                                 intentValue = settings.indentation;\r
13416                                 indentUnit = /[a-z%]+$/i.exec(intentValue);\r
13417                                 intentValue = parseInt(intentValue);\r
13418 \r
13419                                 if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) {\r
13420                                         each(selection.getSelectedBlocks(), function(element) {\r
13421                                                 if (command == 'outdent') {\r
13422                                                         value = Math.max(0, parseInt(element.style.paddingLeft || 0) - intentValue);\r
13423                                                         dom.setStyle(element, 'paddingLeft', value ? value + indentUnit : '');\r
13424                                                 } else\r
13425                                                         dom.setStyle(element, 'paddingLeft', (parseInt(element.style.paddingLeft || 0) + intentValue) + indentUnit);\r
13426                                         });\r
13427                                 } else\r
13428                                         execNativeCommand(command);\r
13429                         },\r
13430 \r
13431                         mceRepaint : function() {\r
13432                                 var bookmark;\r
13433 \r
13434                                 if (tinymce.isGecko) {\r
13435                                         try {\r
13436                                                 storeSelection(TRUE);\r
13437 \r
13438                                                 if (selection.getSel())\r
13439                                                         selection.getSel().selectAllChildren(editor.getBody());\r
13440 \r
13441                                                 selection.collapse(TRUE);\r
13442                                                 restoreSelection();\r
13443                                         } catch (ex) {\r
13444                                                 // Ignore\r
13445                                         }\r
13446                                 }\r
13447                         },\r
13448 \r
13449                         mceToggleFormat : function(command, ui, value) {\r
13450                                 formatter.toggle(value);\r
13451                         },\r
13452 \r
13453                         InsertHorizontalRule : function() {\r
13454                                 editor.execCommand('mceInsertContent', false, '<hr />');\r
13455                         },\r
13456 \r
13457                         mceToggleVisualAid : function() {\r
13458                                 editor.hasVisual = !editor.hasVisual;\r
13459                                 editor.addVisual();\r
13460                         },\r
13461 \r
13462                         mceReplaceContent : function(command, ui, value) {\r
13463                                 editor.execCommand('mceInsertContent', false, value.replace(/\{\$selection\}/g, selection.getContent({format : 'text'})));\r
13464                         },\r
13465 \r
13466                         mceInsertLink : function(command, ui, value) {\r
13467                                 var anchor;\r
13468 \r
13469                                 if (typeof(value) == 'string')\r
13470                                         value = {href : value};\r
13471 \r
13472                                 anchor = dom.getParent(selection.getNode(), 'a');\r
13473 \r
13474                                 // Spaces are never valid in URLs and it's a very common mistake for people to make so we fix it here.\r
13475                                 value.href = value.href.replace(' ', '%20');\r
13476 \r
13477                                 // Remove existing links if there could be child links or that the href isn't specified\r
13478                                 if (!anchor || !value.href) {\r
13479                                         formatter.remove('link');\r
13480                                 }               \r
13481 \r
13482                                 // Apply new link to selection\r
13483                                 if (value.href) {\r
13484                                         formatter.apply('link', value, anchor);\r
13485                                 }\r
13486                         },\r
13487 \r
13488                         selectAll : function() {\r
13489                                 var root = dom.getRoot(), rng = dom.createRng();\r
13490 \r
13491                                 rng.setStart(root, 0);\r
13492                                 rng.setEnd(root, root.childNodes.length);\r
13493 \r
13494                                 editor.selection.setRng(rng);\r
13495                         }\r
13496                 });\r
13497 \r
13498                 // Add queryCommandState overrides\r
13499                 addCommands({\r
13500                         // Override justify commands\r
13501                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
13502                                 return isFormatMatch('align' + command.substring(7));\r
13503                         },\r
13504 \r
13505                         'Bold,Italic,Underline,Strikethrough,Superscript,Subscript' : function(command) {\r
13506                                 return isFormatMatch(command);\r
13507                         },\r
13508 \r
13509                         mceBlockQuote : function() {\r
13510                                 return isFormatMatch('blockquote');\r
13511                         },\r
13512 \r
13513                         Outdent : function() {\r
13514                                 var node;\r
13515 \r
13516                                 if (settings.inline_styles) {\r
13517                                         if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
13518                                                 return TRUE;\r
13519 \r
13520                                         if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
13521                                                 return TRUE;\r
13522                                 }\r
13523 \r
13524                                 return queryCommandState('InsertUnorderedList') || queryCommandState('InsertOrderedList') || (!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE'));\r
13525                         },\r
13526 \r
13527                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
13528                                 return dom.getParent(selection.getNode(), command == 'insertunorderedlist' ? 'UL' : 'OL');\r
13529                         }\r
13530                 }, 'state');\r
13531 \r
13532                 // Add queryCommandValue overrides\r
13533                 addCommands({\r
13534                         'FontSize,FontName' : function(command) {\r
13535                                 var value = 0, parent;\r
13536 \r
13537                                 if (parent = dom.getParent(selection.getNode(), 'span')) {\r
13538                                         if (command == 'fontsize')\r
13539                                                 value = parent.style.fontSize;\r
13540                                         else\r
13541                                                 value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();\r
13542                                 }\r
13543 \r
13544                                 return value;\r
13545                         }\r
13546                 }, 'value');\r
13547 \r
13548                 // Add undo manager logic\r
13549                 if (settings.custom_undo_redo) {\r
13550                         addCommands({\r
13551                                 Undo : function() {\r
13552                                         editor.undoManager.undo();\r
13553                                 },\r
13554 \r
13555                                 Redo : function() {\r
13556                                         editor.undoManager.redo();\r
13557                                 }\r
13558                         });\r
13559                 }\r
13560         };\r
13561 })(tinymce);\r
13562 \r
13563 (function(tinymce) {\r
13564         var Dispatcher = tinymce.util.Dispatcher;\r
13565 \r
13566         tinymce.UndoManager = function(editor) {\r
13567                 var self, index = 0, data = [], beforeBookmark;\r
13568 \r
13569                 function getContent() {\r
13570                         return tinymce.trim(editor.getContent({format : 'raw', no_events : 1}));\r
13571                 };\r
13572 \r
13573                 return self = {\r
13574                         typing : false,\r
13575 \r
13576                         onAdd : new Dispatcher(self),\r
13577 \r
13578                         onUndo : new Dispatcher(self),\r
13579 \r
13580                         onRedo : new Dispatcher(self),\r
13581 \r
13582                         beforeChange : function() {\r
13583                                 beforeBookmark = editor.selection.getBookmark(2, true);\r
13584                         },\r
13585 \r
13586                         add : function(level) {\r
13587                                 var i, settings = editor.settings, lastLevel;\r
13588 \r
13589                                 level = level || {};\r
13590                                 level.content = getContent();\r
13591 \r
13592                                 // Add undo level if needed\r
13593                                 lastLevel = data[index];\r
13594                                 if (lastLevel && lastLevel.content == level.content)\r
13595                                         return null;\r
13596 \r
13597                                 // Set before bookmark on previous level\r
13598                                 if (data[index])\r
13599                                         data[index].beforeBookmark = beforeBookmark;\r
13600 \r
13601                                 // Time to compress\r
13602                                 if (settings.custom_undo_redo_levels) {\r
13603                                         if (data.length > settings.custom_undo_redo_levels) {\r
13604                                                 for (i = 0; i < data.length - 1; i++)\r
13605                                                         data[i] = data[i + 1];\r
13606 \r
13607                                                 data.length--;\r
13608                                                 index = data.length;\r
13609                                         }\r
13610                                 }\r
13611 \r
13612                                 // Get a non intrusive normalized bookmark\r
13613                                 level.bookmark = editor.selection.getBookmark(2, true);\r
13614 \r
13615                                 // Crop array if needed\r
13616                                 if (index < data.length - 1)\r
13617                                         data.length = index + 1;\r
13618 \r
13619                                 data.push(level);\r
13620                                 index = data.length - 1;\r
13621 \r
13622                                 self.onAdd.dispatch(self, level);\r
13623                                 editor.isNotDirty = 0;\r
13624 \r
13625                                 return level;\r
13626                         },\r
13627 \r
13628                         undo : function() {\r
13629                                 var level, i;\r
13630 \r
13631                                 if (self.typing) {\r
13632                                         self.add();\r
13633                                         self.typing = false;\r
13634                                 }\r
13635 \r
13636                                 if (index > 0) {\r
13637                                         level = data[--index];\r
13638 \r
13639                                         editor.setContent(level.content, {format : 'raw'});\r
13640                                         editor.selection.moveToBookmark(level.beforeBookmark);\r
13641 \r
13642                                         self.onUndo.dispatch(self, level);\r
13643                                 }\r
13644 \r
13645                                 return level;\r
13646                         },\r
13647 \r
13648                         redo : function() {\r
13649                                 var level;\r
13650 \r
13651                                 if (index < data.length - 1) {\r
13652                                         level = data[++index];\r
13653 \r
13654                                         editor.setContent(level.content, {format : 'raw'});\r
13655                                         editor.selection.moveToBookmark(level.bookmark);\r
13656 \r
13657                                         self.onRedo.dispatch(self, level);\r
13658                                 }\r
13659 \r
13660                                 return level;\r
13661                         },\r
13662 \r
13663                         clear : function() {\r
13664                                 data = [];\r
13665                                 index = 0;\r
13666                                 self.typing = false;\r
13667                         },\r
13668 \r
13669                         hasUndo : function() {\r
13670                                 return index > 0 || this.typing;\r
13671                         },\r
13672 \r
13673                         hasRedo : function() {\r
13674                                 return index < data.length - 1 && !this.typing;\r
13675                         }\r
13676                 };\r
13677         };\r
13678 })(tinymce);\r
13679 \r
13680 (function(tinymce) {\r
13681         // Shorten names\r
13682         var Event = tinymce.dom.Event,\r
13683                 isIE = tinymce.isIE,\r
13684                 isGecko = tinymce.isGecko,\r
13685                 isOpera = tinymce.isOpera,\r
13686                 each = tinymce.each,\r
13687                 extend = tinymce.extend,\r
13688                 TRUE = true,\r
13689                 FALSE = false;\r
13690 \r
13691         function cloneFormats(node) {\r
13692                 var clone, temp, inner;\r
13693 \r
13694                 do {\r
13695                         if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) {\r
13696                                 if (clone) {\r
13697                                         temp = node.cloneNode(false);\r
13698                                         temp.appendChild(clone);\r
13699                                         clone = temp;\r
13700                                 } else {\r
13701                                         clone = inner = node.cloneNode(false);\r
13702                                 }\r
13703 \r
13704                                 clone.removeAttribute('id');\r
13705                         }\r
13706                 } while (node = node.parentNode);\r
13707 \r
13708                 if (clone)\r
13709                         return {wrapper : clone, inner : inner};\r
13710         };\r
13711 \r
13712         // Checks if the selection/caret is at the end of the specified block element\r
13713         function isAtEnd(rng, par) {\r
13714                 var rng2 = par.ownerDocument.createRange();\r
13715 \r
13716                 rng2.setStart(rng.endContainer, rng.endOffset);\r
13717                 rng2.setEndAfter(par);\r
13718 \r
13719                 // Get number of characters to the right of the cursor if it's zero then we are at the end and need to merge the next block element\r
13720                 return rng2.cloneContents().textContent.length == 0;\r
13721         };\r
13722 \r
13723         function splitList(selection, dom, li) {\r
13724                 var listBlock, block;\r
13725 \r
13726                 if (dom.isEmpty(li)) {\r
13727                         listBlock = dom.getParent(li, 'ul,ol');\r
13728 \r
13729                         if (!dom.getParent(listBlock.parentNode, 'ul,ol')) {\r
13730                                 dom.split(listBlock, li);\r
13731                                 block = dom.create('p', 0, '<br data-mce-bogus="1" />');\r
13732                                 dom.replace(block, li);\r
13733                                 selection.select(block, 1);\r
13734                         }\r
13735 \r
13736                         return FALSE;\r
13737                 }\r
13738 \r
13739                 return TRUE;\r
13740         };\r
13741 \r
13742         tinymce.create('tinymce.ForceBlocks', {\r
13743                 ForceBlocks : function(ed) {\r
13744                         var t = this, s = ed.settings, elm;\r
13745 \r
13746                         t.editor = ed;\r
13747                         t.dom = ed.dom;\r
13748                         elm = (s.forced_root_block || 'p').toLowerCase();\r
13749                         s.element = elm.toUpperCase();\r
13750 \r
13751                         ed.onPreInit.add(t.setup, t);\r
13752                 },\r
13753 \r
13754                 setup : function() {\r
13755                         var t = this, ed = t.editor, s = ed.settings, dom = ed.dom, selection = ed.selection, blockElements = ed.schema.getBlockElements();\r
13756 \r
13757                         // Force root blocks\r
13758                         if (s.forced_root_block) {\r
13759                                 function addRootBlocks() {\r
13760                                         var node = selection.getStart(), rootNode = ed.getBody(), rng, startContainer, startOffset, endContainer, endOffset, rootBlockNode, tempNode, offset = -0xFFFFFF;\r
13761 \r
13762                                         if (!node || node.nodeType !== 1)\r
13763                                                 return;\r
13764 \r
13765                                         // Check if node is wrapped in block\r
13766                                         while (node != rootNode) {\r
13767                                                 if (blockElements[node.nodeName])\r
13768                                                         return;\r
13769 \r
13770                                                 node = node.parentNode;\r
13771                                         }\r
13772 \r
13773                                         // Get current selection\r
13774                                         rng = selection.getRng();\r
13775                                         if (rng.setStart) {\r
13776                                                 startContainer = rng.startContainer;\r
13777                                                 startOffset = rng.startOffset;\r
13778                                                 endContainer = rng.endContainer;\r
13779                                                 endOffset = rng.endOffset;\r
13780                                         } else {\r
13781                                                 // Force control range into text range\r
13782                                                 if (rng.item) {\r
13783                                                         rng = ed.getDoc().body.createTextRange();\r
13784                                                         rng.moveToElementText(rng.item(0));\r
13785                                                 }\r
13786 \r
13787                                                 tmpRng = rng.duplicate();\r
13788                                                 tmpRng.collapse(true);\r
13789                                                 startOffset = tmpRng.move('character', offset) * -1;\r
13790 \r
13791                                                 if (!tmpRng.collapsed) {\r
13792                                                         tmpRng = rng.duplicate();\r
13793                                                         tmpRng.collapse(false);\r
13794                                                         endOffset = (tmpRng.move('character', offset) * -1) - startOffset;\r
13795                                                 }\r
13796                                         }\r
13797 \r
13798                                         // Wrap non block elements and text nodes\r
13799                                         for (node = rootNode.firstChild; node; node) {\r
13800                                                 if (node.nodeType === 3 || (node.nodeType == 1 && !blockElements[node.nodeName])) {\r
13801                                                         if (!rootBlockNode) {\r
13802                                                                 rootBlockNode = dom.create(s.forced_root_block);\r
13803                                                                 node.parentNode.insertBefore(rootBlockNode, node);\r
13804                                                         }\r
13805 \r
13806                                                         tempNode = node;\r
13807                                                         node = node.nextSibling;\r
13808                                                         rootBlockNode.appendChild(tempNode);\r
13809                                                 } else {\r
13810                                                         rootBlockNode = null;\r
13811                                                         node = node.nextSibling;\r
13812                                                 }\r
13813                                         }\r
13814 \r
13815                                         if (rng.setStart) {\r
13816                                                 rng.setStart(startContainer, startOffset);\r
13817                                                 rng.setEnd(endContainer, endOffset);\r
13818                                                 selection.setRng(rng);\r
13819                                         } else {\r
13820                                                 try {\r
13821                                                         rng = ed.getDoc().body.createTextRange();\r
13822                                                         rng.moveToElementText(rootNode);\r
13823                                                         rng.collapse(true);\r
13824                                                         rng.moveStart('character', startOffset);\r
13825 \r
13826                                                         if (endOffset > 0)\r
13827                                                                 rng.moveEnd('character', endOffset);\r
13828 \r
13829                                                         rng.select();\r
13830                                                 } catch (ex) {\r
13831                                                         // Ignore\r
13832                                                 }\r
13833                                         }\r
13834 \r
13835                                         ed.nodeChanged();\r
13836                                 };\r
13837 \r
13838                                 ed.onKeyUp.add(addRootBlocks);\r
13839                                 ed.onClick.add(addRootBlocks);\r
13840                         }\r
13841 \r
13842                         if (s.force_br_newlines) {\r
13843                                 // Force IE to produce BRs on enter\r
13844                                 if (isIE) {\r
13845                                         ed.onKeyPress.add(function(ed, e) {\r
13846                                                 var n;\r
13847 \r
13848                                                 if (e.keyCode == 13 && selection.getNode().nodeName != 'LI') {\r
13849                                                         selection.setContent('<br id="__" /> ', {format : 'raw'});\r
13850                                                         n = dom.get('__');\r
13851                                                         n.removeAttribute('id');\r
13852                                                         selection.select(n);\r
13853                                                         selection.collapse();\r
13854                                                         return Event.cancel(e);\r
13855                                                 }\r
13856                                         });\r
13857                                 }\r
13858                         }\r
13859 \r
13860                         if (s.force_p_newlines) {\r
13861                                 if (!isIE) {\r
13862                                         ed.onKeyPress.add(function(ed, e) {\r
13863                                                 if (e.keyCode == 13 && !e.shiftKey && !t.insertPara(e))\r
13864                                                         Event.cancel(e);\r
13865                                         });\r
13866                                 } else {\r
13867                                         // Ungly hack to for IE to preserve the formatting when you press\r
13868                                         // enter at the end of a block element with formatted contents\r
13869                                         // This logic overrides the browsers default logic with\r
13870                                         // custom logic that enables us to control the output\r
13871                                         tinymce.addUnload(function() {\r
13872                                                 t._previousFormats = 0; // Fix IE leak\r
13873                                         });\r
13874 \r
13875                                         ed.onKeyPress.add(function(ed, e) {\r
13876                                                 t._previousFormats = 0;\r
13877 \r
13878                                                 // Clone the current formats, this will later be applied to the new block contents\r
13879                                                 if (e.keyCode == 13 && !e.shiftKey && ed.selection.isCollapsed() && s.keep_styles)\r
13880                                                         t._previousFormats = cloneFormats(ed.selection.getStart());\r
13881                                         });\r
13882 \r
13883                                         ed.onKeyUp.add(function(ed, e) {\r
13884                                                 // Let IE break the element and the wrap the new caret location in the previous formats\r
13885                                                 if (e.keyCode == 13 && !e.shiftKey) {\r
13886                                                         var parent = ed.selection.getStart(), fmt = t._previousFormats;\r
13887 \r
13888                                                         // Parent is an empty block\r
13889                                                         if (!parent.hasChildNodes() && fmt) {\r
13890                                                                 parent = dom.getParent(parent, dom.isBlock);\r
13891 \r
13892                                                                 if (parent && parent.nodeName != 'LI') {\r
13893                                                                         parent.innerHTML = '';\r
13894 \r
13895                                                                         if (t._previousFormats) {\r
13896                                                                                 parent.appendChild(fmt.wrapper);\r
13897                                                                                 fmt.inner.innerHTML = '\uFEFF';\r
13898                                                                         } else\r
13899                                                                                 parent.innerHTML = '\uFEFF';\r
13900 \r
13901                                                                         selection.select(parent, 1);\r
13902                                                                         selection.collapse(true);\r
13903                                                                         ed.getDoc().execCommand('Delete', false, null);\r
13904                                                                         t._previousFormats = 0;\r
13905                                                                 }\r
13906                                                         }\r
13907                                                 }\r
13908                                         });\r
13909                                 }\r
13910 \r
13911                                 if (isGecko) {\r
13912                                         ed.onKeyDown.add(function(ed, e) {\r
13913                                                 if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey)\r
13914                                                         t.backspaceDelete(e, e.keyCode == 8);\r
13915                                         });\r
13916                                 }\r
13917                         }\r
13918 \r
13919                         // Workaround for missing shift+enter support, http://bugs.webkit.org/show_bug.cgi?id=16973\r
13920                         if (tinymce.isWebKit) {\r
13921                                 function insertBr(ed) {\r
13922                                         var rng = selection.getRng(), br, div = dom.create('div', null, ' '), divYPos, vpHeight = dom.getViewPort(ed.getWin()).h;\r
13923 \r
13924                                         // Insert BR element\r
13925                                         rng.insertNode(br = dom.create('br'));\r
13926 \r
13927                                         // Place caret after BR\r
13928                                         rng.setStartAfter(br);\r
13929                                         rng.setEndAfter(br);\r
13930                                         selection.setRng(rng);\r
13931 \r
13932                                         // Could not place caret after BR then insert an nbsp entity and move the caret\r
13933                                         if (selection.getSel().focusNode == br.previousSibling) {\r
13934                                                 selection.select(dom.insertAfter(dom.doc.createTextNode('\u00a0'), br));\r
13935                                                 selection.collapse(TRUE);\r
13936                                         }\r
13937 \r
13938                                         // Create a temporary DIV after the BR and get the position as it\r
13939                                         // seems like getPos() returns 0 for text nodes and BR elements.\r
13940                                         dom.insertAfter(div, br);\r
13941                                         divYPos = dom.getPos(div).y;\r
13942                                         dom.remove(div);\r
13943 \r
13944                                         // Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117\r
13945                                         if (divYPos > vpHeight) // It is not necessary to scroll if the DIV is inside the view port.\r
13946                                                 ed.getWin().scrollTo(0, divYPos);\r
13947                                 };\r
13948 \r
13949                                 ed.onKeyPress.add(function(ed, e) {\r
13950                                         if (e.keyCode == 13 && (e.shiftKey || (s.force_br_newlines && !dom.getParent(selection.getNode(), 'h1,h2,h3,h4,h5,h6,ol,ul')))) {\r
13951                                                 insertBr(ed);\r
13952                                                 Event.cancel(e);\r
13953                                         }\r
13954                                 });\r
13955                         }\r
13956 \r
13957                         // IE specific fixes\r
13958                         if (isIE) {\r
13959                                 // Replaces IE:s auto generated paragraphs with the specified element name\r
13960                                 if (s.element != 'P') {\r
13961                                         ed.onKeyPress.add(function(ed, e) {\r
13962                                                 t.lastElm = selection.getNode().nodeName;\r
13963                                         });\r
13964 \r
13965                                         ed.onKeyUp.add(function(ed, e) {\r
13966                                                 var bl, n = selection.getNode(), b = ed.getBody();\r
13967 \r
13968                                                 if (b.childNodes.length === 1 && n.nodeName == 'P') {\r
13969                                                         n = dom.rename(n, s.element);\r
13970                                                         selection.select(n);\r
13971                                                         selection.collapse();\r
13972                                                         ed.nodeChanged();\r
13973                                                 } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') {\r
13974                                                         bl = dom.getParent(n, 'p');\r
13975 \r
13976                                                         if (bl) {\r
13977                                                                 dom.rename(bl, s.element);\r
13978                                                                 ed.nodeChanged();\r
13979                                                         }\r
13980                                                 }\r
13981                                         });\r
13982                                 }\r
13983                         }\r
13984                 },\r
13985 \r
13986                 getParentBlock : function(n) {\r
13987                         var d = this.dom;\r
13988 \r
13989                         return d.getParent(n, d.isBlock);\r
13990                 },\r
13991 \r
13992                 insertPara : function(e) {\r
13993                         var t = this, ed = t.editor, dom = ed.dom, d = ed.getDoc(), se = ed.settings, s = ed.selection.getSel(), r = s.getRangeAt(0), b = d.body;\r
13994                         var rb, ra, dir, sn, so, en, eo, sb, eb, bn, bef, aft, sc, ec, n, vp = dom.getViewPort(ed.getWin()), y, ch, car;\r
13995 \r
13996                         ed.undoManager.beforeChange();\r
13997 \r
13998                         // If root blocks are forced then use Operas default behavior since it's really good\r
13999 // Removed due to bug: #1853816\r
14000 //                      if (se.forced_root_block && isOpera)\r
14001 //                              return TRUE;\r
14002 \r
14003                         // Setup before range\r
14004                         rb = d.createRange();\r
14005 \r
14006                         // If is before the first block element and in body, then move it into first block element\r
14007                         rb.setStart(s.anchorNode, s.anchorOffset);\r
14008                         rb.collapse(TRUE);\r
14009 \r
14010                         // Setup after range\r
14011                         ra = d.createRange();\r
14012 \r
14013                         // If is before the first block element and in body, then move it into first block element\r
14014                         ra.setStart(s.focusNode, s.focusOffset);\r
14015                         ra.collapse(TRUE);\r
14016 \r
14017                         // Setup start/end points\r
14018                         dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0;\r
14019                         sn = dir ? s.anchorNode : s.focusNode;\r
14020                         so = dir ? s.anchorOffset : s.focusOffset;\r
14021                         en = dir ? s.focusNode : s.anchorNode;\r
14022                         eo = dir ? s.focusOffset : s.anchorOffset;\r
14023 \r
14024                         // If selection is in empty table cell\r
14025                         if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) {\r
14026                                 if (sn.firstChild.nodeName == 'BR')\r
14027                                         dom.remove(sn.firstChild); // Remove BR\r
14028 \r
14029                                 // Create two new block elements\r
14030                                 if (sn.childNodes.length == 0) {\r
14031                                         ed.dom.add(sn, se.element, null, '<br />');\r
14032                                         aft = ed.dom.add(sn, se.element, null, '<br />');\r
14033                                 } else {\r
14034                                         n = sn.innerHTML;\r
14035                                         sn.innerHTML = '';\r
14036                                         ed.dom.add(sn, se.element, null, n);\r
14037                                         aft = ed.dom.add(sn, se.element, null, '<br />');\r
14038                                 }\r
14039 \r
14040                                 // Move caret into the last one\r
14041                                 r = d.createRange();\r
14042                                 r.selectNodeContents(aft);\r
14043                                 r.collapse(1);\r
14044                                 ed.selection.setRng(r);\r
14045 \r
14046                                 return FALSE;\r
14047                         }\r
14048 \r
14049                         // If the caret is in an invalid location in FF we need to move it into the first block\r
14050                         if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) {\r
14051                                 sn = en = sn.firstChild;\r
14052                                 so = eo = 0;\r
14053                                 rb = d.createRange();\r
14054                                 rb.setStart(sn, 0);\r
14055                                 ra = d.createRange();\r
14056                                 ra.setStart(en, 0);\r
14057                         }\r
14058 \r
14059                         // If the body is totally empty add a BR element this might happen on webkit\r
14060                         if (!d.body.hasChildNodes()) {\r
14061                                 d.body.appendChild(dom.create('br'));\r
14062                         }\r
14063 \r
14064                         // Never use body as start or end node\r
14065                         sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes\r
14066                         sn = sn.nodeName == "BODY" ? sn.firstChild : sn;\r
14067                         en = en.nodeName == "HTML" ? d.body : en; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes\r
14068                         en = en.nodeName == "BODY" ? en.firstChild : en;\r
14069 \r
14070                         // Get start and end blocks\r
14071                         sb = t.getParentBlock(sn);\r
14072                         eb = t.getParentBlock(en);\r
14073                         bn = sb ? sb.nodeName : se.element; // Get block name to create\r
14074 \r
14075                         // Return inside list use default browser behavior\r
14076                         if (n = t.dom.getParent(sb, 'li,pre')) {\r
14077                                 if (n.nodeName == 'LI')\r
14078                                         return splitList(ed.selection, t.dom, n);\r
14079 \r
14080                                 return TRUE;\r
14081                         }\r
14082 \r
14083                         // If caption or absolute layers then always generate new blocks within\r
14084                         if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {\r
14085                                 bn = se.element;\r
14086                                 sb = null;\r
14087                         }\r
14088 \r
14089                         // If caption or absolute layers then always generate new blocks within\r
14090                         if (eb && (eb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {\r
14091                                 bn = se.element;\r
14092                                 eb = null;\r
14093                         }\r
14094 \r
14095                         // Use P instead\r
14096                         if (/(TD|TABLE|TH|CAPTION)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(dom.getStyle(sb, 'float', 1)))) {\r
14097                                 bn = se.element;\r
14098                                 sb = eb = null;\r
14099                         }\r
14100 \r
14101                         // Setup new before and after blocks\r
14102                         bef = (sb && sb.nodeName == bn) ? sb.cloneNode(0) : ed.dom.create(bn);\r
14103                         aft = (eb && eb.nodeName == bn) ? eb.cloneNode(0) : ed.dom.create(bn);\r
14104 \r
14105                         // Remove id from after clone\r
14106                         aft.removeAttribute('id');\r
14107 \r
14108                         // Is header and cursor is at the end, then force paragraph under\r
14109                         if (/^(H[1-6])$/.test(bn) && isAtEnd(r, sb))\r
14110                                 aft = ed.dom.create(se.element);\r
14111 \r
14112                         // Find start chop node\r
14113                         n = sc = sn;\r
14114                         do {\r
14115                                 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))\r
14116                                         break;\r
14117 \r
14118                                 sc = n;\r
14119                         } while ((n = n.previousSibling ? n.previousSibling : n.parentNode));\r
14120 \r
14121                         // Find end chop node\r
14122                         n = ec = en;\r
14123                         do {\r
14124                                 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))\r
14125                                         break;\r
14126 \r
14127                                 ec = n;\r
14128                         } while ((n = n.nextSibling ? n.nextSibling : n.parentNode));\r
14129 \r
14130                         // Place first chop part into before block element\r
14131                         if (sc.nodeName == bn)\r
14132                                 rb.setStart(sc, 0);\r
14133                         else\r
14134                                 rb.setStartBefore(sc);\r
14135 \r
14136                         rb.setEnd(sn, so);\r
14137                         bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari\r
14138 \r
14139                         // Place secnd chop part within new block element\r
14140                         try {\r
14141                                 ra.setEndAfter(ec);\r
14142                         } catch(ex) {\r
14143                                 //console.debug(s.focusNode, s.focusOffset);\r
14144                         }\r
14145 \r
14146                         ra.setStart(en, eo);\r
14147                         aft.appendChild(ra.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari\r
14148 \r
14149                         // Create range around everything\r
14150                         r = d.createRange();\r
14151                         if (!sc.previousSibling && sc.parentNode.nodeName == bn) {\r
14152                                 r.setStartBefore(sc.parentNode);\r
14153                         } else {\r
14154                                 if (rb.startContainer.nodeName == bn && rb.startOffset == 0)\r
14155                                         r.setStartBefore(rb.startContainer);\r
14156                                 else\r
14157                                         r.setStart(rb.startContainer, rb.startOffset);\r
14158                         }\r
14159 \r
14160                         if (!ec.nextSibling && ec.parentNode.nodeName == bn)\r
14161                                 r.setEndAfter(ec.parentNode);\r
14162                         else\r
14163                                 r.setEnd(ra.endContainer, ra.endOffset);\r
14164 \r
14165                         // Delete and replace it with new block elements\r
14166                         r.deleteContents();\r
14167 \r
14168                         if (isOpera)\r
14169                                 ed.getWin().scrollTo(0, vp.y);\r
14170 \r
14171                         // Never wrap blocks in blocks\r
14172                         if (bef.firstChild && bef.firstChild.nodeName == bn)\r
14173                                 bef.innerHTML = bef.firstChild.innerHTML;\r
14174 \r
14175                         if (aft.firstChild && aft.firstChild.nodeName == bn)\r
14176                                 aft.innerHTML = aft.firstChild.innerHTML;\r
14177 \r
14178                         function appendStyles(e, en) {\r
14179                                 var nl = [], nn, n, i;\r
14180 \r
14181                                 e.innerHTML = '';\r
14182 \r
14183                                 // Make clones of style elements\r
14184                                 if (se.keep_styles) {\r
14185                                         n = en;\r
14186                                         do {\r
14187                                                 // We only want style specific elements\r
14188                                                 if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) {\r
14189                                                         nn = n.cloneNode(FALSE);\r
14190                                                         dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique\r
14191                                                         nl.push(nn);\r
14192                                                 }\r
14193                                         } while (n = n.parentNode);\r
14194                                 }\r
14195 \r
14196                                 // Append style elements to aft\r
14197                                 if (nl.length > 0) {\r
14198                                         for (i = nl.length - 1, nn = e; i >= 0; i--)\r
14199                                                 nn = nn.appendChild(nl[i]);\r
14200 \r
14201                                         // Padd most inner style element\r
14202                                         nl[0].innerHTML = isOpera ? '\u00a0' : '<br />'; // Extra space for Opera so that the caret can move there\r
14203                                         return nl[0]; // Move caret to most inner element\r
14204                                 } else\r
14205                                         e.innerHTML = isOpera ? '\u00a0' : '<br />'; // Extra space for Opera so that the caret can move there\r
14206                         };\r
14207                                 \r
14208                         // Padd empty blocks\r
14209                         if (dom.isEmpty(bef))\r
14210                                 appendStyles(bef, sn);\r
14211 \r
14212                         // Fill empty afterblook with current style\r
14213                         if (dom.isEmpty(aft))\r
14214                                 car = appendStyles(aft, en);\r
14215 \r
14216                         // Opera needs this one backwards for older versions\r
14217                         if (isOpera && parseFloat(opera.version()) < 9.5) {\r
14218                                 r.insertNode(bef);\r
14219                                 r.insertNode(aft);\r
14220                         } else {\r
14221                                 r.insertNode(aft);\r
14222                                 r.insertNode(bef);\r
14223                         }\r
14224 \r
14225                         // Normalize\r
14226                         aft.normalize();\r
14227                         bef.normalize();\r
14228 \r
14229                         // Move cursor and scroll into view\r
14230                         ed.selection.select(aft, true);\r
14231                         ed.selection.collapse(true);\r
14232 \r
14233                         // 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
14234                         y = ed.dom.getPos(aft).y;\r
14235                         //ch = aft.clientHeight;\r
14236 \r
14237                         // Is element within viewport\r
14238                         if (y < vp.y || y + 25 > vp.y + vp.h) {\r
14239                                 ed.getWin().scrollTo(0, y < vp.y ? y : y - vp.h + 25); // Needs to be hardcoded to roughly one line of text if a huge text block is broken into two blocks\r
14240 \r
14241                                 /*console.debug(\r
14242                                         'Element: y=' + y + ', h=' + ch + ', ' +\r
14243                                         'Viewport: y=' + vp.y + ", h=" + vp.h + ', bottom=' + (vp.y + vp.h)\r
14244                                 );*/\r
14245                         }\r
14246 \r
14247                         ed.undoManager.add();\r
14248 \r
14249                         return FALSE;\r
14250                 },\r
14251 \r
14252                 backspaceDelete : function(e, bs) {\r
14253                         var t = this, ed = t.editor, b = ed.getBody(), dom = ed.dom, n, se = ed.selection, r = se.getRng(), sc = r.startContainer, n, w, tn, walker;\r
14254 \r
14255                         // Delete when caret is behind a element doesn't work correctly on Gecko see #3011651\r
14256                         if (!bs && r.collapsed && sc.nodeType == 1 && r.startOffset == sc.childNodes.length) {\r
14257                                 walker = new tinymce.dom.TreeWalker(sc.lastChild, sc);\r
14258 \r
14259                                 // Walk the dom backwards until we find a text node\r
14260                                 for (n = sc.lastChild; n; n = walker.prev()) {\r
14261                                         if (n.nodeType == 3) {\r
14262                                                 r.setStart(n, n.nodeValue.length);\r
14263                                                 r.collapse(true);\r
14264                                                 se.setRng(r);\r
14265                                                 return;\r
14266                                         }\r
14267                                 }\r
14268                         }\r
14269 \r
14270                         // The caret sometimes gets stuck in Gecko if you delete empty paragraphs\r
14271                         // This workaround removes the element by hand and moves the caret to the previous element\r
14272                         if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) {\r
14273                                 if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) {\r
14274                                         // Find previous block element\r
14275                                         n = sc;\r
14276                                         while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ;\r
14277 \r
14278                                         if (n) {\r
14279                                                 if (sc != b.firstChild) {\r
14280                                                         // Find last text node\r
14281                                                         w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE);\r
14282                                                         while (tn = w.nextNode())\r
14283                                                                 n = tn;\r
14284 \r
14285                                                         // Place caret at the end of last text node\r
14286                                                         r = ed.getDoc().createRange();\r
14287                                                         r.setStart(n, n.nodeValue ? n.nodeValue.length : 0);\r
14288                                                         r.setEnd(n, n.nodeValue ? n.nodeValue.length : 0);\r
14289                                                         se.setRng(r);\r
14290 \r
14291                                                         // Remove the target container\r
14292                                                         ed.dom.remove(sc);\r
14293                                                 }\r
14294 \r
14295                                                 return Event.cancel(e);\r
14296                                         }\r
14297                                 }\r
14298                         }\r
14299                 }\r
14300         });\r
14301 })(tinymce);\r
14302 \r
14303 (function(tinymce) {\r
14304         // Shorten names\r
14305         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;\r
14306 \r
14307         tinymce.create('tinymce.ControlManager', {\r
14308                 ControlManager : function(ed, s) {\r
14309                         var t = this, i;\r
14310 \r
14311                         s = s || {};\r
14312                         t.editor = ed;\r
14313                         t.controls = {};\r
14314                         t.onAdd = new tinymce.util.Dispatcher(t);\r
14315                         t.onPostRender = new tinymce.util.Dispatcher(t);\r
14316                         t.prefix = s.prefix || ed.id + '_';\r
14317                         t._cls = {};\r
14318 \r
14319                         t.onPostRender.add(function() {\r
14320                                 each(t.controls, function(c) {\r
14321                                         c.postRender();\r
14322                                 });\r
14323                         });\r
14324                 },\r
14325 \r
14326                 get : function(id) {\r
14327                         return this.controls[this.prefix + id] || this.controls[id];\r
14328                 },\r
14329 \r
14330                 setActive : function(id, s) {\r
14331                         var c = null;\r
14332 \r
14333                         if (c = this.get(id))\r
14334                                 c.setActive(s);\r
14335 \r
14336                         return c;\r
14337                 },\r
14338 \r
14339                 setDisabled : function(id, s) {\r
14340                         var c = null;\r
14341 \r
14342                         if (c = this.get(id))\r
14343                                 c.setDisabled(s);\r
14344 \r
14345                         return c;\r
14346                 },\r
14347 \r
14348                 add : function(c) {\r
14349                         var t = this;\r
14350 \r
14351                         if (c) {\r
14352                                 t.controls[c.id] = c;\r
14353                                 t.onAdd.dispatch(c, t);\r
14354                         }\r
14355 \r
14356                         return c;\r
14357                 },\r
14358 \r
14359                 createControl : function(n) {\r
14360                         var c, t = this, ed = t.editor;\r
14361 \r
14362                         each(ed.plugins, function(p) {\r
14363                                 if (p.createControl) {\r
14364                                         c = p.createControl(n, t);\r
14365 \r
14366                                         if (c)\r
14367                                                 return false;\r
14368                                 }\r
14369                         });\r
14370 \r
14371                         switch (n) {\r
14372                                 case "|":\r
14373                                 case "separator":\r
14374                                         return t.createSeparator();\r
14375                         }\r
14376 \r
14377                         if (!c && ed.buttons && (c = ed.buttons[n]))\r
14378                                 return t.createButton(n, c);\r
14379 \r
14380                         return t.add(c);\r
14381                 },\r
14382 \r
14383                 createDropMenu : function(id, s, cc) {\r
14384                         var t = this, ed = t.editor, c, bm, v, cls;\r
14385 \r
14386                         s = extend({\r
14387                                 'class' : 'mceDropDown',\r
14388                                 constrain : ed.settings.constrain_menus\r
14389                         }, s);\r
14390 \r
14391                         s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin';\r
14392                         if (v = ed.getParam('skin_variant'))\r
14393                                 s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);\r
14394 \r
14395                         id = t.prefix + id;\r
14396                         cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;\r
14397                         c = t.controls[id] = new cls(id, s);\r
14398                         c.onAddItem.add(function(c, o) {\r
14399                                 var s = o.settings;\r
14400 \r
14401                                 s.title = ed.getLang(s.title, s.title);\r
14402 \r
14403                                 if (!s.onclick) {\r
14404                                         s.onclick = function(v) {\r
14405                                                 if (s.cmd)\r
14406                                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
14407                                         };\r
14408                                 }\r
14409                         });\r
14410 \r
14411                         ed.onRemove.add(function() {\r
14412                                 c.destroy();\r
14413                         });\r
14414 \r
14415                         // Fix for bug #1897785, #1898007\r
14416                         if (tinymce.isIE) {\r
14417                                 c.onShowMenu.add(function() {\r
14418                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
14419                                         ed.focus();\r
14420 \r
14421                                         bm = ed.selection.getBookmark(1);\r
14422                                 });\r
14423 \r
14424                                 c.onHideMenu.add(function() {\r
14425                                         if (bm) {\r
14426                                                 ed.selection.moveToBookmark(bm);\r
14427                                                 bm = 0;\r
14428                                         }\r
14429                                 });\r
14430                         }\r
14431 \r
14432                         return t.add(c);\r
14433                 },\r
14434 \r
14435                 createListBox : function(id, s, cc) {\r
14436                         var t = this, ed = t.editor, cmd, c, cls;\r
14437 \r
14438                         if (t.get(id))\r
14439                                 return null;\r
14440 \r
14441                         s.title = ed.translate(s.title);\r
14442                         s.scope = s.scope || ed;\r
14443 \r
14444                         if (!s.onselect) {\r
14445                                 s.onselect = function(v) {\r
14446                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
14447                                 };\r
14448                         }\r
14449 \r
14450                         s = extend({\r
14451                                 title : s.title,\r
14452                                 'class' : 'mce_' + id,\r
14453                                 scope : s.scope,\r
14454                                 control_manager : t\r
14455                         }, s);\r
14456 \r
14457                         id = t.prefix + id;\r
14458 \r
14459                         if (ed.settings.use_native_selects)\r
14460                                 c = new tinymce.ui.NativeListBox(id, s);\r
14461                         else {\r
14462                                 cls = cc || t._cls.listbox || tinymce.ui.ListBox;\r
14463                                 c = new cls(id, s, ed);\r
14464                         }\r
14465 \r
14466                         t.controls[id] = c;\r
14467 \r
14468                         // Fix focus problem in Safari\r
14469                         if (tinymce.isWebKit) {\r
14470                                 c.onPostRender.add(function(c, n) {\r
14471                                         // Store bookmark on mousedown\r
14472                                         Event.add(n, 'mousedown', function() {\r
14473                                                 ed.bookmark = ed.selection.getBookmark(1);\r
14474                                         });\r
14475 \r
14476                                         // Restore on focus, since it might be lost\r
14477                                         Event.add(n, 'focus', function() {\r
14478                                                 ed.selection.moveToBookmark(ed.bookmark);\r
14479                                                 ed.bookmark = null;\r
14480                                         });\r
14481                                 });\r
14482                         }\r
14483 \r
14484                         if (c.hideMenu)\r
14485                                 ed.onMouseDown.add(c.hideMenu, c);\r
14486 \r
14487                         return t.add(c);\r
14488                 },\r
14489 \r
14490                 createButton : function(id, s, cc) {\r
14491                         var t = this, ed = t.editor, o, c, cls;\r
14492 \r
14493                         if (t.get(id))\r
14494                                 return null;\r
14495 \r
14496                         s.title = ed.translate(s.title);\r
14497                         s.label = ed.translate(s.label);\r
14498                         s.scope = s.scope || ed;\r
14499 \r
14500                         if (!s.onclick && !s.menu_button) {\r
14501                                 s.onclick = function() {\r
14502                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
14503                                 };\r
14504                         }\r
14505 \r
14506                         s = extend({\r
14507                                 title : s.title,\r
14508                                 'class' : 'mce_' + id,\r
14509                                 unavailable_prefix : ed.getLang('unavailable', ''),\r
14510                                 scope : s.scope,\r
14511                                 control_manager : t\r
14512                         }, s);\r
14513 \r
14514                         id = t.prefix + id;\r
14515 \r
14516                         if (s.menu_button) {\r
14517                                 cls = cc || t._cls.menubutton || tinymce.ui.MenuButton;\r
14518                                 c = new cls(id, s, ed);\r
14519                                 ed.onMouseDown.add(c.hideMenu, c);\r
14520                         } else {\r
14521                                 cls = t._cls.button || tinymce.ui.Button;\r
14522                                 c = new cls(id, s, ed);\r
14523                         }\r
14524 \r
14525                         return t.add(c);\r
14526                 },\r
14527 \r
14528                 createMenuButton : function(id, s, cc) {\r
14529                         s = s || {};\r
14530                         s.menu_button = 1;\r
14531 \r
14532                         return this.createButton(id, s, cc);\r
14533                 },\r
14534 \r
14535                 createSplitButton : function(id, s, cc) {\r
14536                         var t = this, ed = t.editor, cmd, c, cls;\r
14537 \r
14538                         if (t.get(id))\r
14539                                 return null;\r
14540 \r
14541                         s.title = ed.translate(s.title);\r
14542                         s.scope = s.scope || ed;\r
14543 \r
14544                         if (!s.onclick) {\r
14545                                 s.onclick = function(v) {\r
14546                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
14547                                 };\r
14548                         }\r
14549 \r
14550                         if (!s.onselect) {\r
14551                                 s.onselect = function(v) {\r
14552                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
14553                                 };\r
14554                         }\r
14555 \r
14556                         s = extend({\r
14557                                 title : s.title,\r
14558                                 'class' : 'mce_' + id,\r
14559                                 scope : s.scope,\r
14560                                 control_manager : t\r
14561                         }, s);\r
14562 \r
14563                         id = t.prefix + id;\r
14564                         cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton;\r
14565                         c = t.add(new cls(id, s, ed));\r
14566                         ed.onMouseDown.add(c.hideMenu, c);\r
14567 \r
14568                         return c;\r
14569                 },\r
14570 \r
14571                 createColorSplitButton : function(id, s, cc) {\r
14572                         var t = this, ed = t.editor, cmd, c, cls, bm;\r
14573 \r
14574                         if (t.get(id))\r
14575                                 return null;\r
14576 \r
14577                         s.title = ed.translate(s.title);\r
14578                         s.scope = s.scope || ed;\r
14579 \r
14580                         if (!s.onclick) {\r
14581                                 s.onclick = function(v) {\r
14582                                         if (tinymce.isIE)\r
14583                                                 bm = ed.selection.getBookmark(1);\r
14584 \r
14585                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
14586                                 };\r
14587                         }\r
14588 \r
14589                         if (!s.onselect) {\r
14590                                 s.onselect = function(v) {\r
14591                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
14592                                 };\r
14593                         }\r
14594 \r
14595                         s = extend({\r
14596                                 title : s.title,\r
14597                                 'class' : 'mce_' + id,\r
14598                                 'menu_class' : ed.getParam('skin') + 'Skin',\r
14599                                 scope : s.scope,\r
14600                                 more_colors_title : ed.getLang('more_colors')\r
14601                         }, s);\r
14602 \r
14603                         id = t.prefix + id;\r
14604                         cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton;\r
14605                         c = new cls(id, s, ed);\r
14606                         ed.onMouseDown.add(c.hideMenu, c);\r
14607 \r
14608                         // Remove the menu element when the editor is removed\r
14609                         ed.onRemove.add(function() {\r
14610                                 c.destroy();\r
14611                         });\r
14612 \r
14613                         // Fix for bug #1897785, #1898007\r
14614                         if (tinymce.isIE) {\r
14615                                 c.onShowMenu.add(function() {\r
14616                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
14617                                         ed.focus();\r
14618                                         bm = ed.selection.getBookmark(1);\r
14619                                 });\r
14620 \r
14621                                 c.onHideMenu.add(function() {\r
14622                                         if (bm) {\r
14623                                                 ed.selection.moveToBookmark(bm);\r
14624                                                 bm = 0;\r
14625                                         }\r
14626                                 });\r
14627                         }\r
14628 \r
14629                         return t.add(c);\r
14630                 },\r
14631 \r
14632                 createToolbar : function(id, s, cc) {\r
14633                         var c, t = this, cls;\r
14634 \r
14635                         id = t.prefix + id;\r
14636                         cls = cc || t._cls.toolbar || tinymce.ui.Toolbar;\r
14637                         c = new cls(id, s, t.editor);\r
14638 \r
14639                         if (t.get(id))\r
14640                                 return null;\r
14641 \r
14642                         return t.add(c);\r
14643                 },\r
14644                 \r
14645                 createToolbarGroup : function(id, s, cc) {\r
14646                         var c, t = this, cls;\r
14647                         id = t.prefix + id;\r
14648                         cls = cc || this._cls.toolbarGroup || tinymce.ui.ToolbarGroup;\r
14649                         c = new cls(id, s, t.editor);\r
14650                         \r
14651                         if (t.get(id))\r
14652                                 return null;\r
14653                         \r
14654                         return t.add(c);\r
14655                 },\r
14656 \r
14657                 createSeparator : function(cc) {\r
14658                         var cls = cc || this._cls.separator || tinymce.ui.Separator;\r
14659 \r
14660                         return new cls();\r
14661                 },\r
14662 \r
14663                 setControlType : function(n, c) {\r
14664                         return this._cls[n.toLowerCase()] = c;\r
14665                 },\r
14666         \r
14667                 destroy : function() {\r
14668                         each(this.controls, function(c) {\r
14669                                 c.destroy();\r
14670                         });\r
14671 \r
14672                         this.controls = null;\r
14673                 }\r
14674         });\r
14675 })(tinymce);\r
14676 \r
14677 (function(tinymce) {\r
14678         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;\r
14679 \r
14680         tinymce.create('tinymce.WindowManager', {\r
14681                 WindowManager : function(ed) {\r
14682                         var t = this;\r
14683 \r
14684                         t.editor = ed;\r
14685                         t.onOpen = new Dispatcher(t);\r
14686                         t.onClose = new Dispatcher(t);\r
14687                         t.params = {};\r
14688                         t.features = {};\r
14689                 },\r
14690 \r
14691                 open : function(s, p) {\r
14692                         var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u;\r
14693 \r
14694                         // Default some options\r
14695                         s = s || {};\r
14696                         p = p || {};\r
14697                         sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window\r
14698                         sh = isOpera ? vp.h : screen.height;\r
14699                         s.name = s.name || 'mc_' + new Date().getTime();\r
14700                         s.width = parseInt(s.width || 320);\r
14701                         s.height = parseInt(s.height || 240);\r
14702                         s.resizable = true;\r
14703                         s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0);\r
14704                         s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0);\r
14705                         p.inline = false;\r
14706                         p.mce_width = s.width;\r
14707                         p.mce_height = s.height;\r
14708                         p.mce_auto_focus = s.auto_focus;\r
14709 \r
14710                         if (mo) {\r
14711                                 if (isIE) {\r
14712                                         s.center = true;\r
14713                                         s.help = false;\r
14714                                         s.dialogWidth = s.width + 'px';\r
14715                                         s.dialogHeight = s.height + 'px';\r
14716                                         s.scroll = s.scrollbars || false;\r
14717                                 }\r
14718                         }\r
14719 \r
14720                         // Build features string\r
14721                         each(s, function(v, k) {\r
14722                                 if (tinymce.is(v, 'boolean'))\r
14723                                         v = v ? 'yes' : 'no';\r
14724 \r
14725                                 if (!/^(name|url)$/.test(k)) {\r
14726                                         if (isIE && mo)\r
14727                                                 f += (f ? ';' : '') + k + ':' + v;\r
14728                                         else\r
14729                                                 f += (f ? ',' : '') + k + '=' + v;\r
14730                                 }\r
14731                         });\r
14732 \r
14733                         t.features = s;\r
14734                         t.params = p;\r
14735                         t.onOpen.dispatch(t, s, p);\r
14736 \r
14737                         u = s.url || s.file;\r
14738                         u = tinymce._addVer(u);\r
14739 \r
14740                         try {\r
14741                                 if (isIE && mo) {\r
14742                                         w = 1;\r
14743                                         window.showModalDialog(u, window, f);\r
14744                                 } else\r
14745                                         w = window.open(u, s.name, f);\r
14746                         } catch (ex) {\r
14747                                 // Ignore\r
14748                         }\r
14749 \r
14750                         if (!w)\r
14751                                 alert(t.editor.getLang('popup_blocked'));\r
14752                 },\r
14753 \r
14754                 close : function(w) {\r
14755                         w.close();\r
14756                         this.onClose.dispatch(this);\r
14757                 },\r
14758 \r
14759                 createInstance : function(cl, a, b, c, d, e) {\r
14760                         var f = tinymce.resolve(cl);\r
14761 \r
14762                         return new f(a, b, c, d, e);\r
14763                 },\r
14764 \r
14765                 confirm : function(t, cb, s, w) {\r
14766                         w = w || window;\r
14767 \r
14768                         cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t))));\r
14769                 },\r
14770 \r
14771                 alert : function(tx, cb, s, w) {\r
14772                         var t = this;\r
14773 \r
14774                         w = w || window;\r
14775                         w.alert(t._decode(t.editor.getLang(tx, tx)));\r
14776 \r
14777                         if (cb)\r
14778                                 cb.call(s || t);\r
14779                 },\r
14780 \r
14781                 resizeBy : function(dw, dh, win) {\r
14782                         win.resizeBy(dw, dh);\r
14783                 },\r
14784 \r
14785                 // Internal functions\r
14786 \r
14787                 _decode : function(s) {\r
14788                         return tinymce.DOM.decode(s).replace(/\\n/g, '\n');\r
14789                 }\r
14790         });\r
14791 }(tinymce));\r
14792 (function(tinymce) {\r
14793         tinymce.Formatter = function(ed) {\r
14794                 var formats = {},\r
14795                         each = tinymce.each,\r
14796                         dom = ed.dom,\r
14797                         selection = ed.selection,\r
14798                         TreeWalker = tinymce.dom.TreeWalker,\r
14799                         rangeUtils = new tinymce.dom.RangeUtils(dom),\r
14800                         isValid = ed.schema.isValidChild,\r
14801                         isBlock = dom.isBlock,\r
14802                         forcedRootBlock = ed.settings.forced_root_block,\r
14803                         nodeIndex = dom.nodeIndex,\r
14804                         INVISIBLE_CHAR = '\uFEFF',\r
14805                         MCE_ATTR_RE = /^(src|href|style)$/,\r
14806                         FALSE = false,\r
14807                         TRUE = true,\r
14808                         undefined,\r
14809                         pendingFormats = {apply : [], remove : []};\r
14810 \r
14811                 function isArray(obj) {\r
14812                         return obj instanceof Array;\r
14813                 };\r
14814 \r
14815                 function getParents(node, selector) {\r
14816                         return dom.getParents(node, selector, dom.getRoot());\r
14817                 };\r
14818 \r
14819                 function isCaretNode(node) {\r
14820                         return node.nodeType === 1 && (node.face === 'mceinline' || node.style.fontFamily === 'mceinline');\r
14821                 };\r
14822 \r
14823                 // Public functions\r
14824 \r
14825                 function get(name) {\r
14826                         return name ? formats[name] : formats;\r
14827                 };\r
14828 \r
14829                 function register(name, format) {\r
14830                         if (name) {\r
14831                                 if (typeof(name) !== 'string') {\r
14832                                         each(name, function(format, name) {\r
14833                                                 register(name, format);\r
14834                                         });\r
14835                                 } else {\r
14836                                         // Force format into array and add it to internal collection\r
14837                                         format = format.length ? format : [format];\r
14838 \r
14839                                         each(format, function(format) {\r
14840                                                 // Set deep to false by default on selector formats this to avoid removing\r
14841                                                 // alignment on images inside paragraphs when alignment is changed on paragraphs\r
14842                                                 if (format.deep === undefined)\r
14843                                                         format.deep = !format.selector;\r
14844 \r
14845                                                 // Default to true\r
14846                                                 if (format.split === undefined)\r
14847                                                         format.split = !format.selector || format.inline;\r
14848 \r
14849                                                 // Default to true\r
14850                                                 if (format.remove === undefined && format.selector && !format.inline)\r
14851                                                         format.remove = 'none';\r
14852 \r
14853                                                 // Mark format as a mixed format inline + block level\r
14854                                                 if (format.selector && format.inline) {\r
14855                                                         format.mixed = true;\r
14856                                                         format.block_expand = true;\r
14857                                                 }\r
14858 \r
14859                                                 // Split classes if needed\r
14860                                                 if (typeof(format.classes) === 'string')\r
14861                                                         format.classes = format.classes.split(/\s+/);\r
14862                                         });\r
14863 \r
14864                                         formats[name] = format;\r
14865                                 }\r
14866                         }\r
14867                 };\r
14868 \r
14869                 var getTextDecoration = function(node) {\r
14870                         var decoration;\r
14871 \r
14872                         ed.dom.getParent(node, function(n) {\r
14873                                 decoration = ed.dom.getStyle(n, 'text-decoration');\r
14874                                 return decoration && decoration !== 'none';\r
14875                         });\r
14876 \r
14877                         return decoration;\r
14878                 };\r
14879 \r
14880                 var processUnderlineAndColor = function(node) {\r
14881                         var textDecoration;\r
14882                         if (node.nodeType === 1 && node.parentNode && node.parentNode.nodeType === 1) {\r
14883                                 textDecoration = getTextDecoration(node.parentNode);\r
14884                                 if (ed.dom.getStyle(node, 'color') && textDecoration) {\r
14885                                         ed.dom.setStyle(node, 'text-decoration', textDecoration);\r
14886                                 } else if (ed.dom.getStyle(node, 'textdecoration') === textDecoration) {\r
14887                                         ed.dom.setStyle(node, 'text-decoration', null);\r
14888                                 }\r
14889                         }\r
14890                 };\r
14891 \r
14892                 function apply(name, vars, node) {\r
14893                         var formatList = get(name), format = formatList[0], bookmark, rng, i, isCollapsed = selection.isCollapsed();\r
14894 \r
14895                         function moveStart(rng) {\r
14896                                 var container = rng.startContainer,\r
14897                                         offset = rng.startOffset,\r
14898                                         walker, node;\r
14899 \r
14900                                 // Move startContainer/startOffset in to a suitable node\r
14901                                 if (container.nodeType == 1 || container.nodeValue === "") {\r
14902                                         container = container.nodeType == 1 ? container.childNodes[offset] : container;\r
14903 \r
14904                                         // Might fail if the offset is behind the last element in it's container\r
14905                                         if (container) {\r
14906                                                 walker = new TreeWalker(container, container.parentNode);\r
14907                                                 for (node = walker.current(); node; node = walker.next()) {\r
14908                                                         if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {\r
14909                                                                 rng.setStart(node, 0);\r
14910                                                                 break;\r
14911                                                         }\r
14912                                                 }\r
14913                                         }\r
14914                                 }\r
14915 \r
14916                                 return rng;\r
14917                         };\r
14918 \r
14919                         function setElementFormat(elm, fmt) {\r
14920                                 fmt = fmt || format;\r
14921 \r
14922                                 if (elm) {\r
14923                                         if (fmt.onformat) {\r
14924                                                 fmt.onformat(elm, fmt, vars, node);\r
14925                                         }\r
14926 \r
14927                                         each(fmt.styles, function(value, name) {\r
14928                                                 dom.setStyle(elm, name, replaceVars(value, vars));\r
14929                                         });\r
14930 \r
14931                                         each(fmt.attributes, function(value, name) {\r
14932                                                 dom.setAttrib(elm, name, replaceVars(value, vars));\r
14933                                         });\r
14934 \r
14935                                         each(fmt.classes, function(value) {\r
14936                                                 value = replaceVars(value, vars);\r
14937 \r
14938                                                 if (!dom.hasClass(elm, value))\r
14939                                                         dom.addClass(elm, value);\r
14940                                         });\r
14941                                 }\r
14942                         };\r
14943                         function adjustSelectionToVisibleSelection() {\r
14944                                 function findSelectionEnd(start, end) {\r
14945                                         var walker = new TreeWalker(end);\r
14946                                         for (node = walker.current(); node; node = walker.prev()) {\r
14947                                                 if (node.childNodes.length > 1 || node == start) {\r
14948                                                         return node;\r
14949                                                 }\r
14950                                         }\r
14951                                 };\r
14952 \r
14953                                 // Adjust selection so that a end container with a end offset of zero is not included in the selection\r
14954                                 // as this isn't visible to the user.\r
14955                                 var rng = ed.selection.getRng();\r
14956                                 var start = rng.startContainer;\r
14957                                 var end = rng.endContainer;\r
14958 \r
14959                                 if (start != end && rng.endOffset == 0) {\r
14960                                         var newEnd = findSelectionEnd(start, end);\r
14961                                         var endOffset = newEnd.nodeType == 3 ? newEnd.length : newEnd.childNodes.length;\r
14962 \r
14963                                         rng.setEnd(newEnd, endOffset);\r
14964                                 }\r
14965 \r
14966                                 return rng;\r
14967                         }\r
14968                         \r
14969                         function applyStyleToList(node, bookmark, wrapElm, newWrappers, process){\r
14970                                 var nodes = [], listIndex = -1, list, startIndex = -1, endIndex = -1, currentWrapElm;\r
14971                                 \r
14972                                 // find the index of the first child list.\r
14973                                 each(node.childNodes, function(n, index) {\r
14974                                         if (n.nodeName === "UL" || n.nodeName === "OL") {\r
14975                                                 listIndex = index;\r
14976                                                 list = n;\r
14977                                                 return false;\r
14978                                         }\r
14979                                 });\r
14980                                 \r
14981                                 // get the index of the bookmarks\r
14982                                 each(node.childNodes, function(n, index) {\r
14983                                         if (n.nodeName === "SPAN" && dom.getAttrib(n, "data-mce-type") == "bookmark") {\r
14984                                                 if (n.id == bookmark.id + "_start") {\r
14985                                                         startIndex = index;\r
14986                                                 } else if (n.id == bookmark.id + "_end") {\r
14987                                                         endIndex = index;\r
14988                                                 }\r
14989                                         }\r
14990                                 });\r
14991                                 \r
14992                                 // if the selection spans across an embedded list, or there isn't an embedded list - handle processing normally\r
14993                                 if (listIndex <= 0 || (startIndex < listIndex && endIndex > listIndex)) {\r
14994                                         each(tinymce.grep(node.childNodes), process);\r
14995                                         return 0;\r
14996                                 } else {\r
14997                                         currentWrapElm = wrapElm.cloneNode(FALSE);\r
14998                                         \r
14999                                         // create a list of the nodes on the same side of the list as the selection\r
15000                                         each(tinymce.grep(node.childNodes), function(n, index) {\r
15001                                                 if ((startIndex < listIndex && index < listIndex) || (startIndex > listIndex && index > listIndex)) {\r
15002                                                         nodes.push(n); \r
15003                                                         n.parentNode.removeChild(n);\r
15004                                                 }\r
15005                                         });\r
15006                                         \r
15007                                         // insert the wrapping element either before or after the list.\r
15008                                         if (startIndex < listIndex) {\r
15009                                                 node.insertBefore(currentWrapElm, list);\r
15010                                         } else if (startIndex > listIndex) {\r
15011                                                 node.insertBefore(currentWrapElm, list.nextSibling);\r
15012                                         }\r
15013                                         \r
15014                                         // add the new nodes to the list.\r
15015                                         newWrappers.push(currentWrapElm);\r
15016 \r
15017                                         each(nodes, function(node) {\r
15018                                                 currentWrapElm.appendChild(node);\r
15019                                         });\r
15020 \r
15021                                         return currentWrapElm;\r
15022                                 }\r
15023                         };\r
15024                         \r
15025                         function applyRngStyle(rng, bookmark) {\r
15026                                 var newWrappers = [], wrapName, wrapElm;\r
15027 \r
15028                                 // Setup wrapper element\r
15029                                 wrapName = format.inline || format.block;\r
15030                                 wrapElm = dom.create(wrapName);\r
15031                                 setElementFormat(wrapElm);\r
15032 \r
15033                                 rangeUtils.walk(rng, function(nodes) {\r
15034                                         var currentWrapElm;\r
15035 \r
15036                                         function process(node) {\r
15037                                                 var nodeName = node.nodeName.toLowerCase(), parentName = node.parentNode.nodeName.toLowerCase(), found;\r
15038 \r
15039                                                 // Stop wrapping on br elements\r
15040                                                 if (isEq(nodeName, 'br')) {\r
15041                                                         currentWrapElm = 0;\r
15042 \r
15043                                                         // Remove any br elements when we wrap things\r
15044                                                         if (format.block)\r
15045                                                                 dom.remove(node);\r
15046 \r
15047                                                         return;\r
15048                                                 }\r
15049 \r
15050                                                 // If node is wrapper type\r
15051                                                 if (format.wrapper && matchNode(node, name, vars)) {\r
15052                                                         currentWrapElm = 0;\r
15053                                                         return;\r
15054                                                 }\r
15055 \r
15056                                                 // Can we rename the block\r
15057                                                 if (format.block && !format.wrapper && isTextBlock(nodeName)) {\r
15058                                                         node = dom.rename(node, wrapName);\r
15059                                                         setElementFormat(node);\r
15060                                                         newWrappers.push(node);\r
15061                                                         currentWrapElm = 0;\r
15062                                                         return;\r
15063                                                 }\r
15064 \r
15065                                                 // Handle selector patterns\r
15066                                                 if (format.selector) {\r
15067                                                         // Look for matching formats\r
15068                                                         each(formatList, function(format) {\r
15069                                                                 // Check collapsed state if it exists\r
15070                                                                 if ('collapsed' in format && format.collapsed !== isCollapsed) {\r
15071                                                                         return;\r
15072                                                                 }\r
15073 \r
15074                                                                 if (dom.is(node, format.selector) && !isCaretNode(node)) {\r
15075                                                                         setElementFormat(node, format);\r
15076                                                                         found = true;\r
15077                                                                 }\r
15078                                                         });\r
15079 \r
15080                                                         // Continue processing if a selector match wasn't found and a inline element is defined\r
15081                                                         if (!format.inline || found) {\r
15082                                                                 currentWrapElm = 0;\r
15083                                                                 return;\r
15084                                                         }\r
15085                                                 }\r
15086 \r
15087                                                 // Is it valid to wrap this item\r
15088                                                 if (isValid(wrapName, nodeName) && isValid(parentName, wrapName) &&\r
15089                                                                 !(node.nodeType === 3 && node.nodeValue.length === 1 && node.nodeValue.charCodeAt(0) === 65279)) {\r
15090                                                         // Start wrapping\r
15091                                                         if (!currentWrapElm) {\r
15092                                                                 // Wrap the node\r
15093                                                                 currentWrapElm = wrapElm.cloneNode(FALSE);\r
15094                                                                 node.parentNode.insertBefore(currentWrapElm, node);\r
15095                                                                 newWrappers.push(currentWrapElm);\r
15096                                                         }\r
15097 \r
15098                                                         currentWrapElm.appendChild(node);\r
15099                                                 } else if (nodeName == 'li' && bookmark) {\r
15100                                                         // 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
15101                                                         currentWrapElm = applyStyleToList(node, bookmark, wrapElm, newWrappers, process);\r
15102                                                 } else {\r
15103                                                         // Start a new wrapper for possible children\r
15104                                                         currentWrapElm = 0;\r
15105 \r
15106                                                         each(tinymce.grep(node.childNodes), process);\r
15107 \r
15108                                                         // End the last wrapper\r
15109                                                         currentWrapElm = 0;\r
15110                                                 }\r
15111                                         };\r
15112 \r
15113                                         // Process siblings from range\r
15114                                         each(nodes, process);\r
15115                                 });\r
15116 \r
15117                                 // Wrap links inside as well, for example color inside a link when the wrapper is around the link\r
15118                                 if (format.wrap_links === false) {\r
15119                                         each(newWrappers, function(node) {\r
15120                                                 function process(node) {\r
15121                                                         var i, currentWrapElm, children;\r
15122 \r
15123                                                         if (node.nodeName === 'A') {\r
15124                                                                 currentWrapElm = wrapElm.cloneNode(FALSE);\r
15125                                                                 newWrappers.push(currentWrapElm);\r
15126 \r
15127                                                                 children = tinymce.grep(node.childNodes);\r
15128                                                                 for (i = 0; i < children.length; i++)\r
15129                                                                         currentWrapElm.appendChild(children[i]);\r
15130 \r
15131                                                                 node.appendChild(currentWrapElm);\r
15132                                                         }\r
15133 \r
15134                                                         each(tinymce.grep(node.childNodes), process);\r
15135                                                 };\r
15136 \r
15137                                                 process(node);\r
15138                                         });\r
15139                                 }\r
15140 \r
15141                                 // Cleanup\r
15142                                 each(newWrappers, function(node) {\r
15143                                         var childCount;\r
15144 \r
15145                                         function getChildCount(node) {\r
15146                                                 var count = 0;\r
15147 \r
15148                                                 each(node.childNodes, function(node) {\r
15149                                                         if (!isWhiteSpaceNode(node) && !isBookmarkNode(node))\r
15150                                                                 count++;\r
15151                                                 });\r
15152 \r
15153                                                 return count;\r
15154                                         };\r
15155 \r
15156                                         function mergeStyles(node) {\r
15157                                                 var child, clone;\r
15158 \r
15159                                                 each(node.childNodes, function(node) {\r
15160                                                         if (node.nodeType == 1 && !isBookmarkNode(node) && !isCaretNode(node)) {\r
15161                                                                 child = node;\r
15162                                                                 return FALSE; // break loop\r
15163                                                         }\r
15164                                                 });\r
15165 \r
15166                                                 // If child was found and of the same type as the current node\r
15167                                                 if (child && matchName(child, format)) {\r
15168                                                         clone = child.cloneNode(FALSE);\r
15169                                                         setElementFormat(clone);\r
15170 \r
15171                                                         dom.replace(clone, node, TRUE);\r
15172                                                         dom.remove(child, 1);\r
15173                                                 }\r
15174 \r
15175                                                 return clone || node;\r
15176                                         };\r
15177 \r
15178                                         childCount = getChildCount(node);\r
15179 \r
15180                                         // Remove empty nodes but only if there is multiple wrappers and they are not block\r
15181                                         // elements so never remove single <h1></h1> since that would remove the currrent empty block element where the caret is at\r
15182                                         if ((newWrappers.length > 1 || !isBlock(node)) && childCount === 0) {\r
15183                                                 dom.remove(node, 1);\r
15184                                                 return;\r
15185                                         }\r
15186 \r
15187                                         if (format.inline || format.wrapper) {\r
15188                                                 // Merges the current node with it's children of similar type to reduce the number of elements\r
15189                                                 if (!format.exact && childCount === 1)\r
15190                                                         node = mergeStyles(node);\r
15191 \r
15192                                                 // Remove/merge children\r
15193                                                 each(formatList, function(format) {\r
15194                                                         // Merge all children of similar type will move styles from child to parent\r
15195                                                         // this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span>\r
15196                                                         // will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span>\r
15197                                                         each(dom.select(format.inline, node), function(child) {\r
15198                                                                 var parent;\r
15199 \r
15200                                                                 // When wrap_links is set to false we don't want\r
15201                                                                 // to remove the format on children within links\r
15202                                                                 if (format.wrap_links === false) {\r
15203                                                                         parent = child.parentNode;\r
15204 \r
15205                                                                         do {\r
15206                                                                                 if (parent.nodeName === 'A')\r
15207                                                                                         return;\r
15208                                                                         } while (parent = parent.parentNode);\r
15209                                                                 }\r
15210 \r
15211                                                                 removeFormat(format, vars, child, format.exact ? child : null);\r
15212                                                         });\r
15213                                                 });\r
15214 \r
15215                                                 // Remove child if direct parent is of same type\r
15216                                                 if (matchNode(node.parentNode, name, vars)) {\r
15217                                                         dom.remove(node, 1);\r
15218                                                         node = 0;\r
15219                                                         return TRUE;\r
15220                                                 }\r
15221 \r
15222                                                 // Look for parent with similar style format\r
15223                                                 if (format.merge_with_parents) {\r
15224                                                         dom.getParent(node.parentNode, function(parent) {\r
15225                                                                 if (matchNode(parent, name, vars)) {\r
15226                                                                         dom.remove(node, 1);\r
15227                                                                         node = 0;\r
15228                                                                         return TRUE;\r
15229                                                                 }\r
15230                                                         });\r
15231                                                 }\r
15232 \r
15233                                                 // Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>\r
15234                                                 if (node && format.merge_siblings !== false) {\r
15235                                                         node = mergeSiblings(getNonWhiteSpaceSibling(node), node);\r
15236                                                         node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE));\r
15237                                                 }\r
15238                                         }\r
15239                                 });\r
15240                         };\r
15241 \r
15242                         if (format) {\r
15243                                 if (node) {\r
15244                                         rng = dom.createRng();\r
15245 \r
15246                                         rng.setStartBefore(node);\r
15247                                         rng.setEndAfter(node);\r
15248 \r
15249                                         applyRngStyle(expandRng(rng, formatList));\r
15250                                 } else {\r
15251                                         if (!isCollapsed || !format.inline || dom.select('td.mceSelected,th.mceSelected').length) {\r
15252                                                 // Obtain selection node before selection is unselected by applyRngStyle()\r
15253                                                 var curSelNode = ed.selection.getNode();\r
15254 \r
15255                                                 // Apply formatting to selection\r
15256                                                 ed.selection.setRng(adjustSelectionToVisibleSelection());\r
15257                                                 bookmark = selection.getBookmark();\r
15258                                                 applyRngStyle(expandRng(selection.getRng(TRUE), formatList), bookmark);\r
15259 \r
15260                                                 // Colored nodes should be underlined so that the color of the underline matches the text color.\r
15261                                                 if (format.styles && (format.styles.color || format.styles.textDecoration)) {\r
15262                                                         tinymce.walk(curSelNode, processUnderlineAndColor, 'childNodes');\r
15263                                                         processUnderlineAndColor(curSelNode);\r
15264                                                 }\r
15265 \r
15266                                                 selection.moveToBookmark(bookmark);\r
15267                                                 selection.setRng(moveStart(selection.getRng(TRUE)));\r
15268                                                 ed.nodeChanged();\r
15269                                         } else\r
15270                                                 performCaretAction('apply', name, vars);\r
15271                                 }\r
15272                         }\r
15273                 };\r
15274 \r
15275                 function remove(name, vars, node) {\r
15276                         var formatList = get(name), format = formatList[0], bookmark, i, rng;\r
15277                         function moveStart(rng) {\r
15278                                 var container = rng.startContainer,\r
15279                                         offset = rng.startOffset,\r
15280                                         walker, node, nodes, tmpNode;\r
15281 \r
15282                                 // Convert text node into index if possible\r
15283                                 if (container.nodeType == 3 && offset >= container.nodeValue.length - 1) {\r
15284                                         container = container.parentNode;\r
15285                                         offset = nodeIndex(container) + 1;\r
15286                                 }\r
15287 \r
15288                                 // Move startContainer/startOffset in to a suitable node\r
15289                                 if (container.nodeType == 1) {\r
15290                                         nodes = container.childNodes;\r
15291                                         container = nodes[Math.min(offset, nodes.length - 1)];\r
15292                                         walker = new TreeWalker(container);\r
15293 \r
15294                                         // If offset is at end of the parent node walk to the next one\r
15295                                         if (offset > nodes.length - 1)\r
15296                                                 walker.next();\r
15297 \r
15298                                         for (node = walker.current(); node; node = walker.next()) {\r
15299                                                 if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {\r
15300                                                         // IE has a "neat" feature where it moves the start node into the closest element\r
15301                                                         // we can avoid this by inserting an element before it and then remove it after we set the selection\r
15302                                                         tmpNode = dom.create('a', null, INVISIBLE_CHAR);\r
15303                                                         node.parentNode.insertBefore(tmpNode, node);\r
15304 \r
15305                                                         // Set selection and remove tmpNode\r
15306                                                         rng.setStart(node, 0);\r
15307                                                         selection.setRng(rng);\r
15308                                                         dom.remove(tmpNode);\r
15309 \r
15310                                                         return;\r
15311                                                 }\r
15312                                         }\r
15313                                 }\r
15314                         };\r
15315 \r
15316                         // Merges the styles for each node\r
15317                         function process(node) {\r
15318                                 var children, i, l;\r
15319 \r
15320                                 // Grab the children first since the nodelist might be changed\r
15321                                 children = tinymce.grep(node.childNodes);\r
15322 \r
15323                                 // Process current node\r
15324                                 for (i = 0, l = formatList.length; i < l; i++) {\r
15325                                         if (removeFormat(formatList[i], vars, node, node))\r
15326                                                 break;\r
15327                                 }\r
15328 \r
15329                                 // Process the children\r
15330                                 if (format.deep) {\r
15331                                         for (i = 0, l = children.length; i < l; i++)\r
15332                                                 process(children[i]);\r
15333                                 }\r
15334                         };\r
15335 \r
15336                         function findFormatRoot(container) {\r
15337                                 var formatRoot;\r
15338 \r
15339                                 // Find format root\r
15340                                 each(getParents(container.parentNode).reverse(), function(parent) {\r
15341                                         var format;\r
15342 \r
15343                                         // Find format root element\r
15344                                         if (!formatRoot && parent.id != '_start' && parent.id != '_end') {\r
15345                                                 // Is the node matching the format we are looking for\r
15346                                                 format = matchNode(parent, name, vars);\r
15347                                                 if (format && format.split !== false)\r
15348                                                         formatRoot = parent;\r
15349                                         }\r
15350                                 });\r
15351 \r
15352                                 return formatRoot;\r
15353                         };\r
15354 \r
15355                         function wrapAndSplit(format_root, container, target, split) {\r
15356                                 var parent, clone, lastClone, firstClone, i, formatRootParent;\r
15357 \r
15358                                 // Format root found then clone formats and split it\r
15359                                 if (format_root) {\r
15360                                         formatRootParent = format_root.parentNode;\r
15361 \r
15362                                         for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) {\r
15363                                                 clone = parent.cloneNode(FALSE);\r
15364 \r
15365                                                 for (i = 0; i < formatList.length; i++) {\r
15366                                                         if (removeFormat(formatList[i], vars, clone, clone)) {\r
15367                                                                 clone = 0;\r
15368                                                                 break;\r
15369                                                         }\r
15370                                                 }\r
15371 \r
15372                                                 // Build wrapper node\r
15373                                                 if (clone) {\r
15374                                                         if (lastClone)\r
15375                                                                 clone.appendChild(lastClone);\r
15376 \r
15377                                                         if (!firstClone)\r
15378                                                                 firstClone = clone;\r
15379 \r
15380                                                         lastClone = clone;\r
15381                                                 }\r
15382                                         }\r
15383 \r
15384                                         // Never split block elements if the format is mixed\r
15385                                         if (split && (!format.mixed || !isBlock(format_root)))\r
15386                                                 container = dom.split(format_root, container);\r
15387 \r
15388                                         // Wrap container in cloned formats\r
15389                                         if (lastClone) {\r
15390                                                 target.parentNode.insertBefore(lastClone, target);\r
15391                                                 firstClone.appendChild(target);\r
15392                                         }\r
15393                                 }\r
15394 \r
15395                                 return container;\r
15396                         };\r
15397 \r
15398                         function splitToFormatRoot(container) {\r
15399                                 return wrapAndSplit(findFormatRoot(container), container, container, true);\r
15400                         };\r
15401 \r
15402                         function unwrap(start) {\r
15403                                 var node = dom.get(start ? '_start' : '_end'),\r
15404                                         out = node[start ? 'firstChild' : 'lastChild'];\r
15405 \r
15406                                 // If the end is placed within the start the result will be removed\r
15407                                 // So this checks if the out node is a bookmark node if it is it\r
15408                                 // checks for another more suitable node\r
15409                                 if (isBookmarkNode(out))\r
15410                                         out = out[start ? 'firstChild' : 'lastChild'];\r
15411 \r
15412                                 dom.remove(node, true);\r
15413 \r
15414                                 return out;\r
15415                         };\r
15416 \r
15417                         function removeRngStyle(rng) {\r
15418                                 var startContainer, endContainer;\r
15419 \r
15420                                 rng = expandRng(rng, formatList, TRUE);\r
15421 \r
15422                                 if (format.split) {\r
15423                                         startContainer = getContainer(rng, TRUE);\r
15424                                         endContainer = getContainer(rng);\r
15425 \r
15426                                         if (startContainer != endContainer) {\r
15427                                                 // Wrap start/end nodes in span element since these might be cloned/moved\r
15428                                                 startContainer = wrap(startContainer, 'span', {id : '_start', 'data-mce-type' : 'bookmark'});\r
15429                                                 endContainer = wrap(endContainer, 'span', {id : '_end', 'data-mce-type' : 'bookmark'});\r
15430 \r
15431                                                 // Split start/end\r
15432                                                 splitToFormatRoot(startContainer);\r
15433                                                 splitToFormatRoot(endContainer);\r
15434 \r
15435                                                 // Unwrap start/end to get real elements again\r
15436                                                 startContainer = unwrap(TRUE);\r
15437                                                 endContainer = unwrap();\r
15438                                         } else\r
15439                                                 startContainer = endContainer = splitToFormatRoot(startContainer);\r
15440 \r
15441                                         // Update range positions since they might have changed after the split operations\r
15442                                         rng.startContainer = startContainer.parentNode;\r
15443                                         rng.startOffset = nodeIndex(startContainer);\r
15444                                         rng.endContainer = endContainer.parentNode;\r
15445                                         rng.endOffset = nodeIndex(endContainer) + 1;\r
15446                                 }\r
15447 \r
15448                                 // Remove items between start/end\r
15449                                 rangeUtils.walk(rng, function(nodes) {\r
15450                                         each(nodes, function(node) {\r
15451                                                 process(node);\r
15452 \r
15453                                                 // Remove parent span if it only contains text-decoration: underline, yet a parent node is also underlined.\r
15454                                                 if (node.nodeType === 1 && ed.dom.getStyle(node, 'text-decoration') === 'underline' && node.parentNode && getTextDecoration(node.parentNode) === 'underline') {\r
15455                                                         removeFormat({'deep': false, 'exact': true, 'inline': 'span', 'styles': {'textDecoration' : 'underline'}}, null, node);\r
15456                                                 }\r
15457                                         });\r
15458                                 });\r
15459                         };\r
15460 \r
15461                         // Handle node\r
15462                         if (node) {\r
15463                                 rng = dom.createRng();\r
15464                                 rng.setStartBefore(node);\r
15465                                 rng.setEndAfter(node);\r
15466                                 removeRngStyle(rng);\r
15467                                 return;\r
15468                         }\r
15469 \r
15470                         if (!selection.isCollapsed() || !format.inline || dom.select('td.mceSelected,th.mceSelected').length) {\r
15471                                 bookmark = selection.getBookmark();\r
15472                                 removeRngStyle(selection.getRng(TRUE));\r
15473                                 selection.moveToBookmark(bookmark);\r
15474 \r
15475                                 // 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
15476                                 if (match(name, vars, selection.getStart())) {\r
15477                                         moveStart(selection.getRng(true));\r
15478                                 }\r
15479 \r
15480                                 ed.nodeChanged();\r
15481                         } else\r
15482                                 performCaretAction('remove', name, vars);\r
15483                 };\r
15484 \r
15485                 function toggle(name, vars, node) {\r
15486                         var fmt = get(name);\r
15487 \r
15488                         if (match(name, vars, node) && (!('toggle' in fmt[0]) || fmt[0]['toggle']))\r
15489                                 remove(name, vars, node);\r
15490                         else\r
15491                                 apply(name, vars, node);\r
15492                 };\r
15493 \r
15494                 function matchNode(node, name, vars, similar) {\r
15495                         var formatList = get(name), format, i, classes;\r
15496 \r
15497                         function matchItems(node, format, item_name) {\r
15498                                 var key, value, items = format[item_name], i;\r
15499 \r
15500                                 // Custom match\r
15501                                 if (format.onmatch) {\r
15502                                         return format.onmatch(node, format, item_name);\r
15503                                 }\r
15504 \r
15505                                 // Check all items\r
15506                                 if (items) {\r
15507                                         // Non indexed object\r
15508                                         if (items.length === undefined) {\r
15509                                                 for (key in items) {\r
15510                                                         if (items.hasOwnProperty(key)) {\r
15511                                                                 if (item_name === 'attributes')\r
15512                                                                         value = dom.getAttrib(node, key);\r
15513                                                                 else\r
15514                                                                         value = getStyle(node, key);\r
15515 \r
15516                                                                 if (similar && !value && !format.exact)\r
15517                                                                         return;\r
15518 \r
15519                                                                 if ((!similar || format.exact) && !isEq(value, replaceVars(items[key], vars)))\r
15520                                                                         return;\r
15521                                                         }\r
15522                                                 }\r
15523                                         } else {\r
15524                                                 // Only one match needed for indexed arrays\r
15525                                                 for (i = 0; i < items.length; i++) {\r
15526                                                         if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i]))\r
15527                                                                 return format;\r
15528                                                 }\r
15529                                         }\r
15530                                 }\r
15531 \r
15532                                 return format;\r
15533                         };\r
15534 \r
15535                         if (formatList && node) {\r
15536                                 // Check each format in list\r
15537                                 for (i = 0; i < formatList.length; i++) {\r
15538                                         format = formatList[i];\r
15539 \r
15540                                         // Name name, attributes, styles and classes\r
15541                                         if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) {\r
15542                                                 // Match classes\r
15543                                                 if (classes = format.classes) {\r
15544                                                         for (i = 0; i < classes.length; i++) {\r
15545                                                                 if (!dom.hasClass(node, classes[i]))\r
15546                                                                         return;\r
15547                                                         }\r
15548                                                 }\r
15549 \r
15550                                                 return format;\r
15551                                         }\r
15552                                 }\r
15553                         }\r
15554                 };\r
15555 \r
15556                 function match(name, vars, node) {\r
15557                         var startNode, i;\r
15558 \r
15559                         function matchParents(node) {\r
15560                                 // Find first node with similar format settings\r
15561                                 node = dom.getParent(node, function(node) {\r
15562                                         return !!matchNode(node, name, vars, true);\r
15563                                 });\r
15564 \r
15565                                 // Do an exact check on the similar format element\r
15566                                 return matchNode(node, name, vars);\r
15567                         };\r
15568 \r
15569                         // Check specified node\r
15570                         if (node)\r
15571                                 return matchParents(node);\r
15572 \r
15573                         // Check pending formats\r
15574                         if (selection.isCollapsed()) {\r
15575                                 for (i = pendingFormats.apply.length - 1; i >= 0; i--) {\r
15576                                         if (pendingFormats.apply[i].name == name)\r
15577                                                 return true;\r
15578                                 }\r
15579 \r
15580                                 for (i = pendingFormats.remove.length - 1; i >= 0; i--) {\r
15581                                         if (pendingFormats.remove[i].name == name)\r
15582                                                 return false;\r
15583                                 }\r
15584 \r
15585                                 return matchParents(selection.getNode());\r
15586                         }\r
15587 \r
15588                         // Check selected node\r
15589                         node = selection.getNode();\r
15590                         if (matchParents(node))\r
15591                                 return TRUE;\r
15592 \r
15593                         // Check start node if it's different\r
15594                         startNode = selection.getStart();\r
15595                         if (startNode != node) {\r
15596                                 if (matchParents(startNode))\r
15597                                         return TRUE;\r
15598                         }\r
15599 \r
15600                         return FALSE;\r
15601                 };\r
15602 \r
15603                 function matchAll(names, vars) {\r
15604                         var startElement, matchedFormatNames = [], checkedMap = {}, i, ni, name;\r
15605 \r
15606                         // If the selection is collapsed then check pending formats\r
15607                         if (selection.isCollapsed()) {\r
15608                                 for (ni = 0; ni < names.length; ni++) {\r
15609                                         // If the name is to be removed, then stop it from being added\r
15610                                         for (i = pendingFormats.remove.length - 1; i >= 0; i--) {\r
15611                                                 name = names[ni];\r
15612 \r
15613                                                 if (pendingFormats.remove[i].name == name) {\r
15614                                                         checkedMap[name] = true;\r
15615                                                         break;\r
15616                                                 }\r
15617                                         }\r
15618                                 }\r
15619 \r
15620                                 // If the format is to be applied\r
15621                                 for (i = pendingFormats.apply.length - 1; i >= 0; i--) {\r
15622                                         for (ni = 0; ni < names.length; ni++) {\r
15623                                                 name = names[ni];\r
15624 \r
15625                                                 if (!checkedMap[name] && pendingFormats.apply[i].name == name) {\r
15626                                                         checkedMap[name] = true;\r
15627                                                         matchedFormatNames.push(name);\r
15628                                                 }\r
15629                                         }\r
15630                                 }\r
15631                         }\r
15632 \r
15633                         // Check start of selection for formats\r
15634                         startElement = selection.getStart();\r
15635                         dom.getParent(startElement, function(node) {\r
15636                                 var i, name;\r
15637 \r
15638                                 for (i = 0; i < names.length; i++) {\r
15639                                         name = names[i];\r
15640 \r
15641                                         if (!checkedMap[name] && matchNode(node, name, vars)) {\r
15642                                                 checkedMap[name] = true;\r
15643                                                 matchedFormatNames.push(name);\r
15644                                         }\r
15645                                 }\r
15646                         });\r
15647 \r
15648                         return matchedFormatNames;\r
15649                 };\r
15650 \r
15651                 function canApply(name) {\r
15652                         var formatList = get(name), startNode, parents, i, x, selector;\r
15653 \r
15654                         if (formatList) {\r
15655                                 startNode = selection.getStart();\r
15656                                 parents = getParents(startNode);\r
15657 \r
15658                                 for (x = formatList.length - 1; x >= 0; x--) {\r
15659                                         selector = formatList[x].selector;\r
15660 \r
15661                                         // Format is not selector based, then always return TRUE\r
15662                                         if (!selector)\r
15663                                                 return TRUE;\r
15664 \r
15665                                         for (i = parents.length - 1; i >= 0; i--) {\r
15666                                                 if (dom.is(parents[i], selector))\r
15667                                                         return TRUE;\r
15668                                         }\r
15669                                 }\r
15670                         }\r
15671 \r
15672                         return FALSE;\r
15673                 };\r
15674 \r
15675                 // Expose to public\r
15676                 tinymce.extend(this, {\r
15677                         get : get,\r
15678                         register : register,\r
15679                         apply : apply,\r
15680                         remove : remove,\r
15681                         toggle : toggle,\r
15682                         match : match,\r
15683                         matchAll : matchAll,\r
15684                         matchNode : matchNode,\r
15685                         canApply : canApply\r
15686                 });\r
15687 \r
15688                 // Private functions\r
15689 \r
15690                 function matchName(node, format) {\r
15691                         // Check for inline match\r
15692                         if (isEq(node, format.inline))\r
15693                                 return TRUE;\r
15694 \r
15695                         // Check for block match\r
15696                         if (isEq(node, format.block))\r
15697                                 return TRUE;\r
15698 \r
15699                         // Check for selector match\r
15700                         if (format.selector)\r
15701                                 return dom.is(node, format.selector);\r
15702                 };\r
15703 \r
15704                 function isEq(str1, str2) {\r
15705                         str1 = str1 || '';\r
15706                         str2 = str2 || '';\r
15707 \r
15708                         str1 = '' + (str1.nodeName || str1);\r
15709                         str2 = '' + (str2.nodeName || str2);\r
15710 \r
15711                         return str1.toLowerCase() == str2.toLowerCase();\r
15712                 };\r
15713 \r
15714                 function getStyle(node, name) {\r
15715                         var styleVal = dom.getStyle(node, name);\r
15716 \r
15717                         // Force the format to hex\r
15718                         if (name == 'color' || name == 'backgroundColor')\r
15719                                 styleVal = dom.toHex(styleVal);\r
15720 \r
15721                         // Opera will return bold as 700\r
15722                         if (name == 'fontWeight' && styleVal == 700)\r
15723                                 styleVal = 'bold';\r
15724 \r
15725                         return '' + styleVal;\r
15726                 };\r
15727 \r
15728                 function replaceVars(value, vars) {\r
15729                         if (typeof(value) != "string")\r
15730                                 value = value(vars);\r
15731                         else if (vars) {\r
15732                                 value = value.replace(/%(\w+)/g, function(str, name) {\r
15733                                         return vars[name] || str;\r
15734                                 });\r
15735                         }\r
15736 \r
15737                         return value;\r
15738                 };\r
15739 \r
15740                 function isWhiteSpaceNode(node) {\r
15741                         return node && node.nodeType === 3 && /^([\s\r\n]+|)$/.test(node.nodeValue);\r
15742                 };\r
15743 \r
15744                 function wrap(node, name, attrs) {\r
15745                         var wrapper = dom.create(name, attrs);\r
15746 \r
15747                         node.parentNode.insertBefore(wrapper, node);\r
15748                         wrapper.appendChild(node);\r
15749 \r
15750                         return wrapper;\r
15751                 };\r
15752 \r
15753                 function expandRng(rng, format, remove) {\r
15754                         var startContainer = rng.startContainer,\r
15755                                 startOffset = rng.startOffset,\r
15756                                 endContainer = rng.endContainer,\r
15757                                 endOffset = rng.endOffset, sibling, lastIdx, leaf;\r
15758 \r
15759                         // This function walks up the tree if there is no siblings before/after the node\r
15760                         function findParentContainer(container, child_name, sibling_name, root) {\r
15761                                 var parent, child;\r
15762 \r
15763                                 root = root || dom.getRoot();\r
15764 \r
15765                                 for (;;) {\r
15766                                         // Check if we can move up are we at root level or body level\r
15767                                         parent = container.parentNode;\r
15768 \r
15769                                         // Stop expanding on block elements or root depending on format\r
15770                                         if (parent == root || (!format[0].block_expand && isBlock(parent)))\r
15771                                                 return container;\r
15772 \r
15773                                         for (sibling = parent[child_name]; sibling && sibling != container; sibling = sibling[sibling_name]) {\r
15774                                                 if (sibling.nodeType == 1 && !isBookmarkNode(sibling))\r
15775                                                         return container;\r
15776 \r
15777                                                 if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling))\r
15778                                                         return container;\r
15779                                         }\r
15780 \r
15781                                         container = container.parentNode;\r
15782                                 }\r
15783 \r
15784                                 return container;\r
15785                         };\r
15786 \r
15787                         // This function walks down the tree to find the leaf at the selection.\r
15788                         // The offset is also returned as if node initially a leaf, the offset may be in the middle of the text node.\r
15789                         function findLeaf(node, offset) {\r
15790                                 if (offset === undefined)\r
15791                                         offset = node.nodeType === 3 ? node.length : node.childNodes.length;\r
15792                                 while (node && node.hasChildNodes()) {\r
15793                                         node = node.childNodes[offset];\r
15794                                         if (node)\r
15795                                                 offset = node.nodeType === 3 ? node.length : node.childNodes.length;\r
15796                                 }\r
15797                                 return { node: node, offset: offset };\r
15798                         }\r
15799 \r
15800                         // If index based start position then resolve it\r
15801                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {\r
15802                                 lastIdx = startContainer.childNodes.length - 1;\r
15803                                 startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset];\r
15804 \r
15805                                 if (startContainer.nodeType == 3)\r
15806                                         startOffset = 0;\r
15807                         }\r
15808 \r
15809                         // If index based end position then resolve it\r
15810                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {\r
15811                                 lastIdx = endContainer.childNodes.length - 1;\r
15812                                 endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1];\r
15813 \r
15814                                 if (endContainer.nodeType == 3)\r
15815                                         endOffset = endContainer.nodeValue.length;\r
15816                         }\r
15817 \r
15818                         // Exclude bookmark nodes if possible\r
15819                         if (isBookmarkNode(startContainer.parentNode))\r
15820                                 startContainer = startContainer.parentNode;\r
15821 \r
15822                         if (isBookmarkNode(startContainer))\r
15823                                 startContainer = startContainer.nextSibling || startContainer;\r
15824 \r
15825                         if (isBookmarkNode(endContainer.parentNode)) {\r
15826                                 endOffset = dom.nodeIndex(endContainer);\r
15827                                 endContainer = endContainer.parentNode;\r
15828                         }\r
15829 \r
15830                         if (isBookmarkNode(endContainer) && endContainer.previousSibling) {\r
15831                                 endContainer = endContainer.previousSibling;\r
15832                                 endOffset = endContainer.length;\r
15833                         }\r
15834 \r
15835                         if (format[0].inline) {\r
15836                                 // Avoid applying formatting to a trailing space.\r
15837                                 leaf = findLeaf(endContainer, endOffset);\r
15838                                 if (leaf.node) {\r
15839                                         while (leaf.node && leaf.offset === 0 && leaf.node.previousSibling)\r
15840                                                 leaf = findLeaf(leaf.node.previousSibling);\r
15841 \r
15842                                         if (leaf.node && leaf.offset > 0 && leaf.node.nodeType === 3 &&\r
15843                                                         leaf.node.nodeValue.charAt(leaf.offset - 1) === ' ') {\r
15844 \r
15845                                                 if (leaf.offset > 1) {\r
15846                                                         endContainer = leaf.node;\r
15847                                                         endContainer.splitText(leaf.offset - 1);\r
15848                                                 } else if (leaf.node.previousSibling) {\r
15849                                                         endContainer = leaf.node.previousSibling;\r
15850                                                 }\r
15851                                         }\r
15852                                 }\r
15853                         }\r
15854                         \r
15855                         // Move start/end point up the tree if the leaves are sharp and if we are in different containers\r
15856                         // Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!\r
15857                         // This will reduce the number of wrapper elements that needs to be created\r
15858                         // Move start point up the tree\r
15859                         if (format[0].inline || format[0].block_expand) {\r
15860                                 startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');\r
15861                                 endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');\r
15862                         }\r
15863 \r
15864                         // Expand start/end container to matching selector\r
15865                         if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) {\r
15866                                 function findSelectorEndPoint(container, sibling_name) {\r
15867                                         var parents, i, y, curFormat;\r
15868 \r
15869                                         if (container.nodeType == 3 && container.nodeValue.length == 0 && container[sibling_name])\r
15870                                                 container = container[sibling_name];\r
15871 \r
15872                                         parents = getParents(container);\r
15873                                         for (i = 0; i < parents.length; i++) {\r
15874                                                 for (y = 0; y < format.length; y++) {\r
15875                                                         curFormat = format[y];\r
15876 \r
15877                                                         // If collapsed state is set then skip formats that doesn't match that\r
15878                                                         if ("collapsed" in curFormat && curFormat.collapsed !== rng.collapsed)\r
15879                                                                 continue;\r
15880 \r
15881                                                         if (dom.is(parents[i], curFormat.selector))\r
15882                                                                 return parents[i];\r
15883                                                 }\r
15884                                         }\r
15885 \r
15886                                         return container;\r
15887                                 };\r
15888 \r
15889                                 // Find new startContainer/endContainer if there is better one\r
15890                                 startContainer = findSelectorEndPoint(startContainer, 'previousSibling');\r
15891                                 endContainer = findSelectorEndPoint(endContainer, 'nextSibling');\r
15892                         }\r
15893 \r
15894                         // Expand start/end container to matching block element or text node\r
15895                         if (format[0].block || format[0].selector) {\r
15896                                 function findBlockEndPoint(container, sibling_name, sibling_name2) {\r
15897                                         var node;\r
15898 \r
15899                                         // Expand to block of similar type\r
15900                                         if (!format[0].wrapper)\r
15901                                                 node = dom.getParent(container, format[0].block);\r
15902 \r
15903                                         // Expand to first wrappable block element or any block element\r
15904                                         if (!node)\r
15905                                                 node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, isBlock);\r
15906 \r
15907                                         // Exclude inner lists from wrapping\r
15908                                         if (node && format[0].wrapper)\r
15909                                                 node = getParents(node, 'ul,ol').reverse()[0] || node;\r
15910 \r
15911                                         // Didn't find a block element look for first/last wrappable element\r
15912                                         if (!node) {\r
15913                                                 node = container;\r
15914 \r
15915                                                 while (node[sibling_name] && !isBlock(node[sibling_name])) {\r
15916                                                         node = node[sibling_name];\r
15917 \r
15918                                                         // Break on BR but include it will be removed later on\r
15919                                                         // we can't remove it now since we need to check if it can be wrapped\r
15920                                                         if (isEq(node, 'br'))\r
15921                                                                 break;\r
15922                                                 }\r
15923                                         }\r
15924 \r
15925                                         return node || container;\r
15926                                 };\r
15927 \r
15928                                 // Find new startContainer/endContainer if there is better one\r
15929                                 startContainer = findBlockEndPoint(startContainer, 'previousSibling');\r
15930                                 endContainer = findBlockEndPoint(endContainer, 'nextSibling');\r
15931 \r
15932                                 // Non block element then try to expand up the leaf\r
15933                                 if (format[0].block) {\r
15934                                         if (!isBlock(startContainer))\r
15935                                                 startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');\r
15936 \r
15937                                         if (!isBlock(endContainer))\r
15938                                                 endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');\r
15939                                 }\r
15940                         }\r
15941 \r
15942                         // Setup index for startContainer\r
15943                         if (startContainer.nodeType == 1) {\r
15944                                 startOffset = nodeIndex(startContainer);\r
15945                                 startContainer = startContainer.parentNode;\r
15946                         }\r
15947 \r
15948                         // Setup index for endContainer\r
15949                         if (endContainer.nodeType == 1) {\r
15950                                 endOffset = nodeIndex(endContainer) + 1;\r
15951                                 endContainer = endContainer.parentNode;\r
15952                         }\r
15953 \r
15954                         // Return new range like object\r
15955                         return {\r
15956                                 startContainer : startContainer,\r
15957                                 startOffset : startOffset,\r
15958                                 endContainer : endContainer,\r
15959                                 endOffset : endOffset\r
15960                         };\r
15961                 }\r
15962 \r
15963                 function removeFormat(format, vars, node, compare_node) {\r
15964                         var i, attrs, stylesModified;\r
15965 \r
15966                         // Check if node matches format\r
15967                         if (!matchName(node, format))\r
15968                                 return FALSE;\r
15969 \r
15970                         // Should we compare with format attribs and styles\r
15971                         if (format.remove != 'all') {\r
15972                                 // Remove styles\r
15973                                 each(format.styles, function(value, name) {\r
15974                                         value = replaceVars(value, vars);\r
15975 \r
15976                                         // Indexed array\r
15977                                         if (typeof(name) === 'number') {\r
15978                                                 name = value;\r
15979                                                 compare_node = 0;\r
15980                                         }\r
15981 \r
15982                                         if (!compare_node || isEq(getStyle(compare_node, name), value))\r
15983                                                 dom.setStyle(node, name, '');\r
15984 \r
15985                                         stylesModified = 1;\r
15986                                 });\r
15987 \r
15988                                 // Remove style attribute if it's empty\r
15989                                 if (stylesModified && dom.getAttrib(node, 'style') == '') {\r
15990                                         node.removeAttribute('style');\r
15991                                         node.removeAttribute('data-mce-style');\r
15992                                 }\r
15993 \r
15994                                 // Remove attributes\r
15995                                 each(format.attributes, function(value, name) {\r
15996                                         var valueOut;\r
15997 \r
15998                                         value = replaceVars(value, vars);\r
15999 \r
16000                                         // Indexed array\r
16001                                         if (typeof(name) === 'number') {\r
16002                                                 name = value;\r
16003                                                 compare_node = 0;\r
16004                                         }\r
16005 \r
16006                                         if (!compare_node || isEq(dom.getAttrib(compare_node, name), value)) {\r
16007                                                 // Keep internal classes\r
16008                                                 if (name == 'class') {\r
16009                                                         value = dom.getAttrib(node, name);\r
16010                                                         if (value) {\r
16011                                                                 // Build new class value where everything is removed except the internal prefixed classes\r
16012                                                                 valueOut = '';\r
16013                                                                 each(value.split(/\s+/), function(cls) {\r
16014                                                                         if (/mce\w+/.test(cls))\r
16015                                                                                 valueOut += (valueOut ? ' ' : '') + cls;\r
16016                                                                 });\r
16017 \r
16018                                                                 // We got some internal classes left\r
16019                                                                 if (valueOut) {\r
16020                                                                         dom.setAttrib(node, name, valueOut);\r
16021                                                                         return;\r
16022                                                                 }\r
16023                                                         }\r
16024                                                 }\r
16025 \r
16026                                                 // IE6 has a bug where the attribute doesn't get removed correctly\r
16027                                                 if (name == "class")\r
16028                                                         node.removeAttribute('className');\r
16029 \r
16030                                                 // Remove mce prefixed attributes\r
16031                                                 if (MCE_ATTR_RE.test(name))\r
16032                                                         node.removeAttribute('data-mce-' + name);\r
16033 \r
16034                                                 node.removeAttribute(name);\r
16035                                         }\r
16036                                 });\r
16037 \r
16038                                 // Remove classes\r
16039                                 each(format.classes, function(value) {\r
16040                                         value = replaceVars(value, vars);\r
16041 \r
16042                                         if (!compare_node || dom.hasClass(compare_node, value))\r
16043                                                 dom.removeClass(node, value);\r
16044                                 });\r
16045 \r
16046                                 // Check for non internal attributes\r
16047                                 attrs = dom.getAttribs(node);\r
16048                                 for (i = 0; i < attrs.length; i++) {\r
16049                                         if (attrs[i].nodeName.indexOf('_') !== 0)\r
16050                                                 return FALSE;\r
16051                                 }\r
16052                         }\r
16053 \r
16054                         // Remove the inline child if it's empty for example <b> or <span>\r
16055                         if (format.remove != 'none') {\r
16056                                 removeNode(node, format);\r
16057                                 return TRUE;\r
16058                         }\r
16059                 };\r
16060 \r
16061                 function removeNode(node, format) {\r
16062                         var parentNode = node.parentNode, rootBlockElm;\r
16063 \r
16064                         if (format.block) {\r
16065                                 if (!forcedRootBlock) {\r
16066                                         function find(node, next, inc) {\r
16067                                                 node = getNonWhiteSpaceSibling(node, next, inc);\r
16068 \r
16069                                                 return !node || (node.nodeName == 'BR' || isBlock(node));\r
16070                                         };\r
16071 \r
16072                                         // Append BR elements if needed before we remove the block\r
16073                                         if (isBlock(node) && !isBlock(parentNode)) {\r
16074                                                 if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1))\r
16075                                                         node.insertBefore(dom.create('br'), node.firstChild);\r
16076 \r
16077                                                 if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1))\r
16078                                                         node.appendChild(dom.create('br'));\r
16079                                         }\r
16080                                 } else {\r
16081                                         // Wrap the block in a forcedRootBlock if we are at the root of document\r
16082                                         if (parentNode == dom.getRoot()) {\r
16083                                                 if (!format.list_block || !isEq(node, format.list_block)) {\r
16084                                                         each(tinymce.grep(node.childNodes), function(node) {\r
16085                                                                 if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) {\r
16086                                                                         if (!rootBlockElm)\r
16087                                                                                 rootBlockElm = wrap(node, forcedRootBlock);\r
16088                                                                         else\r
16089                                                                                 rootBlockElm.appendChild(node);\r
16090                                                                 } else\r
16091                                                                         rootBlockElm = 0;\r
16092                                                         });\r
16093                                                 }\r
16094                                         }\r
16095                                 }\r
16096                         }\r
16097 \r
16098                         // Never remove nodes that isn't the specified inline element if a selector is specified too\r
16099                         if (format.selector && format.inline && !isEq(format.inline, node))\r
16100                                 return;\r
16101 \r
16102                         dom.remove(node, 1);\r
16103                 };\r
16104 \r
16105                 function getNonWhiteSpaceSibling(node, next, inc) {\r
16106                         if (node) {\r
16107                                 next = next ? 'nextSibling' : 'previousSibling';\r
16108 \r
16109                                 for (node = inc ? node : node[next]; node; node = node[next]) {\r
16110                                         if (node.nodeType == 1 || !isWhiteSpaceNode(node))\r
16111                                                 return node;\r
16112                                 }\r
16113                         }\r
16114                 };\r
16115 \r
16116                 function isBookmarkNode(node) {\r
16117                         return node && node.nodeType == 1 && node.getAttribute('data-mce-type') == 'bookmark';\r
16118                 };\r
16119 \r
16120                 function mergeSiblings(prev, next) {\r
16121                         var marker, sibling, tmpSibling;\r
16122 \r
16123                         function compareElements(node1, node2) {\r
16124                                 // Not the same name\r
16125                                 if (node1.nodeName != node2.nodeName)\r
16126                                         return FALSE;\r
16127 \r
16128                                 function getAttribs(node) {\r
16129                                         var attribs = {};\r
16130 \r
16131                                         each(dom.getAttribs(node), function(attr) {\r
16132                                                 var name = attr.nodeName.toLowerCase();\r
16133 \r
16134                                                 // Don't compare internal attributes or style\r
16135                                                 if (name.indexOf('_') !== 0 && name !== 'style')\r
16136                                                         attribs[name] = dom.getAttrib(node, name);\r
16137                                         });\r
16138 \r
16139                                         return attribs;\r
16140                                 };\r
16141 \r
16142                                 function compareObjects(obj1, obj2) {\r
16143                                         var value, name;\r
16144 \r
16145                                         for (name in obj1) {\r
16146                                                 // Obj1 has item obj2 doesn't have\r
16147                                                 if (obj1.hasOwnProperty(name)) {\r
16148                                                         value = obj2[name];\r
16149 \r
16150                                                         // Obj2 doesn't have obj1 item\r
16151                                                         if (value === undefined)\r
16152                                                                 return FALSE;\r
16153 \r
16154                                                         // Obj2 item has a different value\r
16155                                                         if (obj1[name] != value)\r
16156                                                                 return FALSE;\r
16157 \r
16158                                                         // Delete similar value\r
16159                                                         delete obj2[name];\r
16160                                                 }\r
16161                                         }\r
16162 \r
16163                                         // Check if obj 2 has something obj 1 doesn't have\r
16164                                         for (name in obj2) {\r
16165                                                 // Obj2 has item obj1 doesn't have\r
16166                                                 if (obj2.hasOwnProperty(name))\r
16167                                                         return FALSE;\r
16168                                         }\r
16169 \r
16170                                         return TRUE;\r
16171                                 };\r
16172 \r
16173                                 // Attribs are not the same\r
16174                                 if (!compareObjects(getAttribs(node1), getAttribs(node2)))\r
16175                                         return FALSE;\r
16176 \r
16177                                 // Styles are not the same\r
16178                                 if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style'))))\r
16179                                         return FALSE;\r
16180 \r
16181                                 return TRUE;\r
16182                         };\r
16183 \r
16184                         // Check if next/prev exists and that they are elements\r
16185                         if (prev && next) {\r
16186                                 function findElementSibling(node, sibling_name) {\r
16187                                         for (sibling = node; sibling; sibling = sibling[sibling_name]) {\r
16188                                                 if (sibling.nodeType == 3 && sibling.nodeValue.length !== 0)\r
16189                                                         return node;\r
16190 \r
16191                                                 if (sibling.nodeType == 1 && !isBookmarkNode(sibling))\r
16192                                                         return sibling;\r
16193                                         }\r
16194 \r
16195                                         return node;\r
16196                                 };\r
16197 \r
16198                                 // If previous sibling is empty then jump over it\r
16199                                 prev = findElementSibling(prev, 'previousSibling');\r
16200                                 next = findElementSibling(next, 'nextSibling');\r
16201 \r
16202                                 // Compare next and previous nodes\r
16203                                 if (compareElements(prev, next)) {\r
16204                                         // Append nodes between\r
16205                                         for (sibling = prev.nextSibling; sibling && sibling != next;) {\r
16206                                                 tmpSibling = sibling;\r
16207                                                 sibling = sibling.nextSibling;\r
16208                                                 prev.appendChild(tmpSibling);\r
16209                                         }\r
16210 \r
16211                                         // Remove next node\r
16212                                         dom.remove(next);\r
16213 \r
16214                                         // Move children into prev node\r
16215                                         each(tinymce.grep(next.childNodes), function(node) {\r
16216                                                 prev.appendChild(node);\r
16217                                         });\r
16218 \r
16219                                         return prev;\r
16220                                 }\r
16221                         }\r
16222 \r
16223                         return next;\r
16224                 };\r
16225 \r
16226                 function isTextBlock(name) {\r
16227                         return /^(h[1-6]|p|div|pre|address|dl|dt|dd)$/.test(name);\r
16228                 };\r
16229 \r
16230                 function getContainer(rng, start) {\r
16231                         var container, offset, lastIdx;\r
16232 \r
16233                         container = rng[start ? 'startContainer' : 'endContainer'];\r
16234                         offset = rng[start ? 'startOffset' : 'endOffset'];\r
16235 \r
16236                         if (container.nodeType == 1) {\r
16237                                 lastIdx = container.childNodes.length - 1;\r
16238 \r
16239                                 if (!start && offset)\r
16240                                         offset--;\r
16241 \r
16242                                 container = container.childNodes[offset > lastIdx ? lastIdx : offset];\r
16243                         }\r
16244 \r
16245                         return container;\r
16246                 };\r
16247 \r
16248                 function performCaretAction(type, name, vars) {\r
16249                         var i, currentPendingFormats = pendingFormats[type],\r
16250                                 otherPendingFormats = pendingFormats[type == 'apply' ? 'remove' : 'apply'];\r
16251 \r
16252                         function hasPending() {\r
16253                                 return pendingFormats.apply.length || pendingFormats.remove.length;\r
16254                         };\r
16255 \r
16256                         function resetPending() {\r
16257                                 pendingFormats.apply = [];\r
16258                                 pendingFormats.remove = [];\r
16259                         };\r
16260 \r
16261                         function perform(caret_node) {\r
16262                                 // Apply pending formats\r
16263                                 each(pendingFormats.apply.reverse(), function(item) {\r
16264                                         apply(item.name, item.vars, caret_node);\r
16265 \r
16266                                         // Colored nodes should be underlined so that the color of the underline matches the text color.\r
16267                                         if (item.name === 'forecolor' && item.vars.value)\r
16268                                                 processUnderlineAndColor(caret_node.parentNode);\r
16269                                 });\r
16270 \r
16271                                 // Remove pending formats\r
16272                                 each(pendingFormats.remove.reverse(), function(item) {\r
16273                                         remove(item.name, item.vars, caret_node);\r
16274                                 });\r
16275 \r
16276                                 dom.remove(caret_node, 1);\r
16277                                 resetPending();\r
16278                         };\r
16279 \r
16280                         // Check if it already exists then ignore it\r
16281                         for (i = currentPendingFormats.length - 1; i >= 0; i--) {\r
16282                                 if (currentPendingFormats[i].name == name)\r
16283                                         return;\r
16284                         }\r
16285 \r
16286                         currentPendingFormats.push({name : name, vars : vars});\r
16287 \r
16288                         // Check if it's in the other type, then remove it\r
16289                         for (i = otherPendingFormats.length - 1; i >= 0; i--) {\r
16290                                 if (otherPendingFormats[i].name == name)\r
16291                                         otherPendingFormats.splice(i, 1);\r
16292                         }\r
16293 \r
16294                         // Pending apply or remove formats\r
16295                         if (hasPending()) {\r
16296                                 ed.getDoc().execCommand('FontName', false, 'mceinline');\r
16297                                 pendingFormats.lastRng = selection.getRng();\r
16298 \r
16299                                 // IE will convert the current word\r
16300                                 each(dom.select('font,span'), function(node) {\r
16301                                         var bookmark;\r
16302 \r
16303                                         if (isCaretNode(node)) {\r
16304                                                 bookmark = selection.getBookmark();\r
16305                                                 perform(node);\r
16306                                                 selection.moveToBookmark(bookmark);\r
16307                                                 ed.nodeChanged();\r
16308                                         }\r
16309                                 });\r
16310 \r
16311                                 // Only register listeners once if we need to\r
16312                                 if (!pendingFormats.isListening && hasPending()) {\r
16313                                         pendingFormats.isListening = true;\r
16314                                         function performPendingFormat(node, textNode) {\r
16315                                                 var rng = dom.createRng();\r
16316                                                 perform(node);\r
16317 \r
16318                                                 rng.setStart(textNode, textNode.nodeValue.length);\r
16319                                                 rng.setEnd(textNode, textNode.nodeValue.length);\r
16320                                                 selection.setRng(rng);\r
16321                                                 ed.nodeChanged();\r
16322                                         }\r
16323                                         var enterKeyPressed = false;\r
16324 \r
16325                                         each('onKeyDown,onKeyUp,onKeyPress,onMouseUp'.split(','), function(event) {\r
16326                                                 ed[event].addToTop(function(ed, e) {\r
16327                                                         if (e.keyCode==13 && !e.shiftKey) {\r
16328                                                                 enterKeyPressed = true;\r
16329                                                                 return;\r
16330                                                         }\r
16331                                                         // Do we have pending formats and is the selection moved has moved\r
16332                                                         if (hasPending() && !tinymce.dom.RangeUtils.compareRanges(pendingFormats.lastRng, selection.getRng())) {\r
16333                                                                 var foundCaret = false;\r
16334                                                                 each(dom.select('font,span'), function(node) {\r
16335                                                                         var textNode, rng;\r
16336 \r
16337                                                                         // Look for marker\r
16338                                                                         if (isCaretNode(node)) {\r
16339                                                                                 foundCaret = true;\r
16340                                                                                 textNode = node.firstChild;\r
16341 \r
16342                                                                                 // Find the first text node within node\r
16343                                                                                 while (textNode && textNode.nodeType != 3)\r
16344                                                                                         textNode = textNode.firstChild;\r
16345 \r
16346                                                                                 if (textNode) \r
16347                                                                                         performPendingFormat(node, textNode);\r
16348                                                                                 else\r
16349                                                                                         dom.remove(node);\r
16350                                                                         }\r
16351                                                                 });\r
16352                                                                 \r
16353                                                                 // no caret - so we are \r
16354                                                                 if (enterKeyPressed && !foundCaret) {\r
16355                                                                         var node = selection.getNode();\r
16356                                                                         var textNode = node;\r
16357 \r
16358                                                                         // Find the first text node within node\r
16359                                                                         while (textNode && textNode.nodeType != 3)\r
16360                                                                                 textNode = textNode.firstChild;\r
16361                                                                         if (textNode) {\r
16362                                                                                 node=textNode.parentNode;\r
16363                                                                                 while (!isBlock(node)){\r
16364                                                                                         node=node.parentNode;\r
16365                                                                                 }\r
16366                                                                                 performPendingFormat(node, textNode);\r
16367                                                                         }\r
16368                                                                 }\r
16369 \r
16370                                                                 // Always unbind and clear pending styles on keyup\r
16371                                                                 if (e.type == 'keyup' || e.type == 'mouseup') {\r
16372                                                                         resetPending();\r
16373                                                                         enterKeyPressed=false;\r
16374                                                                 }\r
16375                                                         }\r
16376                                                 });\r
16377                                         });\r
16378                                 }\r
16379                         }\r
16380                 };\r
16381         };\r
16382 })(tinymce);\r
16383 \r
16384 tinymce.onAddEditor.add(function(tinymce, ed) {\r
16385         var filters, fontSizes, dom, settings = ed.settings;\r
16386 \r
16387         if (settings.inline_styles) {\r
16388                 fontSizes = tinymce.explode(settings.font_size_style_values);\r
16389 \r
16390                 function replaceWithSpan(node, styles) {\r
16391                         tinymce.each(styles, function(value, name) {\r
16392                                 if (value)\r
16393                                         dom.setStyle(node, name, value);\r
16394                         });\r
16395 \r
16396                         dom.rename(node, 'span');\r
16397                 };\r
16398 \r
16399                 filters = {\r
16400                         font : function(dom, node) {\r
16401                                 replaceWithSpan(node, {\r
16402                                         backgroundColor : node.style.backgroundColor,\r
16403                                         color : node.color,\r
16404                                         fontFamily : node.face,\r
16405                                         fontSize : fontSizes[parseInt(node.size) - 1]\r
16406                                 });\r
16407                         },\r
16408 \r
16409                         u : function(dom, node) {\r
16410                                 replaceWithSpan(node, {\r
16411                                         textDecoration : 'underline'\r
16412                                 });\r
16413                         },\r
16414 \r
16415                         strike : function(dom, node) {\r
16416                                 replaceWithSpan(node, {\r
16417                                         textDecoration : 'line-through'\r
16418                                 });\r
16419                         }\r
16420                 };\r
16421 \r
16422                 function convert(editor, params) {\r
16423                         dom = editor.dom;\r
16424 \r
16425                         if (settings.convert_fonts_to_spans) {\r
16426                                 tinymce.each(dom.select('font,u,strike', params.node), function(node) {\r
16427                                         filters[node.nodeName.toLowerCase()](ed.dom, node);\r
16428                                 });\r
16429                         }\r
16430                 };\r
16431 \r
16432                 ed.onPreProcess.add(convert);\r
16433                 ed.onSetContent.add(convert);\r
16434 \r
16435                 ed.onInit.add(function() {\r
16436                         ed.selection.onSetContent.add(convert);\r
16437                 });\r
16438         }\r
16439 });\r
16440 \r