Upgrade TinyMCE
[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 : '3.9.2',\r
9 \r
10                 releaseDate : '2010-09-29',\r
11 \r
12                 _init : function() {\r
13                         var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;\r
14 \r
15                         t.isOpera = win.opera && opera.buildNumber;\r
16 \r
17                         t.isWebKit = /WebKit/.test(ua);\r
18 \r
19                         t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName);\r
20 \r
21                         t.isIE6 = t.isIE && /MSIE [56]/.test(ua);\r
22 \r
23                         t.isGecko = !t.isWebKit && /Gecko/.test(ua);\r
24 \r
25                         t.isMac = ua.indexOf('Mac') != -1;\r
26 \r
27                         t.isAir = /adobeair/i.test(ua);\r
28 \r
29                         t.isIDevice = /(iPad|iPhone)/.test(ua);\r
30 \r
31                         // TinyMCE .NET webcontrol might be setting the values for TinyMCE\r
32                         if (win.tinyMCEPreInit) {\r
33                                 t.suffix = tinyMCEPreInit.suffix;\r
34                                 t.baseURL = tinyMCEPreInit.base;\r
35                                 t.query = tinyMCEPreInit.query;\r
36                                 return;\r
37                         }\r
38 \r
39                         // Get suffix and base\r
40                         t.suffix = '';\r
41 \r
42                         // If base element found, add that infront of baseURL\r
43                         nl = d.getElementsByTagName('base');\r
44                         for (i=0; i<nl.length; i++) {\r
45                                 if (v = nl[i].href) {\r
46                                         // Host only value like http://site.com or http://site.com:8008\r
47                                         if (/^https?:\/\/[^\/]+$/.test(v))\r
48                                                 v += '/';\r
49 \r
50                                         base = v ? v.match(/.*\//)[0] : ''; // Get only directory\r
51                                 }\r
52                         }\r
53 \r
54                         function getBase(n) {\r
55                                 if (n.src && /tiny_mce(|_gzip|_jquery|_prototype|_full)(_dev|_src)?.js/.test(n.src)) {\r
56                                         if (/_(src|dev)\.js/g.test(n.src))\r
57                                                 t.suffix = '_src';\r
58 \r
59                                         if ((p = n.src.indexOf('?')) != -1)\r
60                                                 t.query = n.src.substring(p + 1);\r
61 \r
62                                         t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));\r
63 \r
64                                         // If path to script is relative and a base href was found add that one infront\r
65                                         // the src property will always be an absolute one on non IE browsers and IE 8\r
66                                         // so this logic will basically only be executed on older IE versions\r
67                                         if (base && t.baseURL.indexOf('://') == -1 && t.baseURL.indexOf('/') !== 0)\r
68                                                 t.baseURL = base + t.baseURL;\r
69 \r
70                                         return t.baseURL;\r
71                                 }\r
72 \r
73                                 return null;\r
74                         };\r
75 \r
76                         // Check document\r
77                         nl = d.getElementsByTagName('script');\r
78                         for (i=0; i<nl.length; i++) {\r
79                                 if (getBase(nl[i]))\r
80                                         return;\r
81                         }\r
82 \r
83                         // Check head\r
84                         n = d.getElementsByTagName('head')[0];\r
85                         if (n) {\r
86                                 nl = n.getElementsByTagName('script');\r
87                                 for (i=0; i<nl.length; i++) {\r
88                                         if (getBase(nl[i]))\r
89                                                 return;\r
90                                 }\r
91                         }\r
92 \r
93                         return;\r
94                 },\r
95 \r
96                 is : function(o, t) {\r
97                         if (!t)\r
98                                 return o !== undefined;\r
99 \r
100                         if (t == 'array' && (o.hasOwnProperty && o instanceof Array))\r
101                                 return true;\r
102 \r
103                         return typeof(o) == t;\r
104                 },\r
105 \r
106                 each : function(o, cb, s) {\r
107                         var n, l;\r
108 \r
109                         if (!o)\r
110                                 return 0;\r
111 \r
112                         s = s || o;\r
113 \r
114                         if (o.length !== undefined) {\r
115                                 // Indexed arrays, needed for Safari\r
116                                 for (n=0, l = o.length; n < l; n++) {\r
117                                         if (cb.call(s, o[n], n, o) === false)\r
118                                                 return 0;\r
119                                 }\r
120                         } else {\r
121                                 // Hashtables\r
122                                 for (n in o) {\r
123                                         if (o.hasOwnProperty(n)) {\r
124                                                 if (cb.call(s, o[n], n, o) === false)\r
125                                                         return 0;\r
126                                         }\r
127                                 }\r
128                         }\r
129 \r
130                         return 1;\r
131                 },\r
132 \r
133 \r
134                 map : function(a, f) {\r
135                         var o = [];\r
136 \r
137                         tinymce.each(a, function(v) {\r
138                                 o.push(f(v));\r
139                         });\r
140 \r
141                         return o;\r
142                 },\r
143 \r
144                 grep : function(a, f) {\r
145                         var o = [];\r
146 \r
147                         tinymce.each(a, function(v) {\r
148                                 if (!f || f(v))\r
149                                         o.push(v);\r
150                         });\r
151 \r
152                         return o;\r
153                 },\r
154 \r
155                 inArray : function(a, v) {\r
156                         var i, l;\r
157 \r
158                         if (a) {\r
159                                 for (i = 0, l = a.length; i < l; i++) {\r
160                                         if (a[i] === v)\r
161                                                 return i;\r
162                                 }\r
163                         }\r
164 \r
165                         return -1;\r
166                 },\r
167 \r
168                 extend : function(o, e) {\r
169                         var i, l, a = arguments;\r
170 \r
171                         for (i = 1, l = a.length; i < l; i++) {\r
172                                 e = a[i];\r
173 \r
174                                 tinymce.each(e, function(v, n) {\r
175                                         if (v !== undefined)\r
176                                                 o[n] = v;\r
177                                 });\r
178                         }\r
179 \r
180                         return o;\r
181                 },\r
182 \r
183 \r
184                 trim : function(s) {\r
185                         return (s ? '' + s : '').replace(whiteSpaceRe, '');\r
186                 },\r
187 \r
188                 create : function(s, p) {\r
189                         var t = this, sp, ns, cn, scn, c, de = 0;\r
190 \r
191                         // Parse : <prefix> <class>:<super class>\r
192                         s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);\r
193                         cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name\r
194 \r
195                         // Create namespace for new class\r
196                         ns = t.createNS(s[3].replace(/\.\w+$/, ''));\r
197 \r
198                         // Class already exists\r
199                         if (ns[cn])\r
200                                 return;\r
201 \r
202                         // Make pure static class\r
203                         if (s[2] == 'static') {\r
204                                 ns[cn] = p;\r
205 \r
206                                 if (this.onCreate)\r
207                                         this.onCreate(s[2], s[3], ns[cn]);\r
208 \r
209                                 return;\r
210                         }\r
211 \r
212                         // Create default constructor\r
213                         if (!p[cn]) {\r
214                                 p[cn] = function() {};\r
215                                 de = 1;\r
216                         }\r
217 \r
218                         // Add constructor and methods\r
219                         ns[cn] = p[cn];\r
220                         t.extend(ns[cn].prototype, p);\r
221 \r
222                         // Extend\r
223                         if (s[5]) {\r
224                                 sp = t.resolve(s[5]).prototype;\r
225                                 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name\r
226 \r
227                                 // Extend constructor\r
228                                 c = ns[cn];\r
229                                 if (de) {\r
230                                         // Add passthrough constructor\r
231                                         ns[cn] = function() {\r
232                                                 return sp[scn].apply(this, arguments);\r
233                                         };\r
234                                 } else {\r
235                                         // Add inherit constructor\r
236                                         ns[cn] = function() {\r
237                                                 this.parent = sp[scn];\r
238                                                 return c.apply(this, arguments);\r
239                                         };\r
240                                 }\r
241                                 ns[cn].prototype[cn] = ns[cn];\r
242 \r
243                                 // Add super methods\r
244                                 t.each(sp, function(f, n) {\r
245                                         ns[cn].prototype[n] = sp[n];\r
246                                 });\r
247 \r
248                                 // Add overridden methods\r
249                                 t.each(p, function(f, n) {\r
250                                         // Extend methods if needed\r
251                                         if (sp[n]) {\r
252                                                 ns[cn].prototype[n] = function() {\r
253                                                         this.parent = sp[n];\r
254                                                         return f.apply(this, arguments);\r
255                                                 };\r
256                                         } else {\r
257                                                 if (n != cn)\r
258                                                         ns[cn].prototype[n] = f;\r
259                                         }\r
260                                 });\r
261                         }\r
262 \r
263                         // Add static methods\r
264                         t.each(p['static'], function(f, n) {\r
265                                 ns[cn][n] = f;\r
266                         });\r
267 \r
268                         if (this.onCreate)\r
269                                 this.onCreate(s[2], s[3], ns[cn].prototype);\r
270                 },\r
271 \r
272                 walk : function(o, f, n, s) {\r
273                         s = s || this;\r
274 \r
275                         if (o) {\r
276                                 if (n)\r
277                                         o = o[n];\r
278 \r
279                                 tinymce.each(o, function(o, i) {\r
280                                         if (f.call(s, o, i, n) === false)\r
281                                                 return false;\r
282 \r
283                                         tinymce.walk(o, f, n, s);\r
284                                 });\r
285                         }\r
286                 },\r
287 \r
288                 createNS : function(n, o) {\r
289                         var i, v;\r
290 \r
291                         o = o || win;\r
292 \r
293                         n = n.split('.');\r
294                         for (i=0; i<n.length; i++) {\r
295                                 v = n[i];\r
296 \r
297                                 if (!o[v])\r
298                                         o[v] = {};\r
299 \r
300                                 o = o[v];\r
301                         }\r
302 \r
303                         return o;\r
304                 },\r
305 \r
306                 resolve : function(n, o) {\r
307                         var i, l;\r
308 \r
309                         o = o || win;\r
310 \r
311                         n = n.split('.');\r
312                         for (i = 0, l = n.length; i < l; i++) {\r
313                                 o = o[n[i]];\r
314 \r
315                                 if (!o)\r
316                                         break;\r
317                         }\r
318 \r
319                         return o;\r
320                 },\r
321 \r
322                 addUnload : function(f, s) {\r
323                         var t = this;\r
324 \r
325                         f = {func : f, scope : s || this};\r
326 \r
327                         if (!t.unloads) {\r
328                                 function unload() {\r
329                                         var li = t.unloads, o, n;\r
330 \r
331                                         if (li) {\r
332                                                 // Call unload handlers\r
333                                                 for (n in li) {\r
334                                                         o = li[n];\r
335 \r
336                                                         if (o && o.func)\r
337                                                                 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy\r
338                                                 }\r
339 \r
340                                                 // Detach unload function\r
341                                                 if (win.detachEvent) {\r
342                                                         win.detachEvent('onbeforeunload', fakeUnload);\r
343                                                         win.detachEvent('onunload', unload);\r
344                                                 } else if (win.removeEventListener)\r
345                                                         win.removeEventListener('unload', unload, false);\r
346 \r
347                                                 // Destroy references\r
348                                                 t.unloads = o = li = w = unload = 0;\r
349 \r
350                                                 // Run garbarge collector on IE\r
351                                                 if (win.CollectGarbage)\r
352                                                         CollectGarbage();\r
353                                         }\r
354                                 };\r
355 \r
356                                 function fakeUnload() {\r
357                                         var d = document;\r
358 \r
359                                         // Is there things still loading, then do some magic\r
360                                         if (d.readyState == 'interactive') {\r
361                                                 function stop() {\r
362                                                         // Prevent memory leak\r
363                                                         d.detachEvent('onstop', stop);\r
364 \r
365                                                         // Call unload handler\r
366                                                         if (unload)\r
367                                                                 unload();\r
368 \r
369                                                         d = 0;\r
370                                                 };\r
371 \r
372                                                 // Fire unload when the currently loading page is stopped\r
373                                                 if (d)\r
374                                                         d.attachEvent('onstop', stop);\r
375 \r
376                                                 // Remove onstop listener after a while to prevent the unload function\r
377                                                 // to execute if the user presses cancel in an onbeforeunload\r
378                                                 // confirm dialog and then presses the browser stop button\r
379                                                 win.setTimeout(function() {\r
380                                                         if (d)\r
381                                                                 d.detachEvent('onstop', stop);\r
382                                                 }, 0);\r
383                                         }\r
384                                 };\r
385 \r
386                                 // Attach unload handler\r
387                                 if (win.attachEvent) {\r
388                                         win.attachEvent('onunload', unload);\r
389                                         win.attachEvent('onbeforeunload', fakeUnload);\r
390                                 } else if (win.addEventListener)\r
391                                         win.addEventListener('unload', unload, false);\r
392 \r
393                                 // Setup initial unload handler array\r
394                                 t.unloads = [f];\r
395                         } else\r
396                                 t.unloads.push(f);\r
397 \r
398                         return f;\r
399                 },\r
400 \r
401                 removeUnload : function(f) {\r
402                         var u = this.unloads, r = null;\r
403 \r
404                         tinymce.each(u, function(o, i) {\r
405                                 if (o && o.func == f) {\r
406                                         u.splice(i, 1);\r
407                                         r = f;\r
408                                         return false;\r
409                                 }\r
410                         });\r
411 \r
412                         return r;\r
413                 },\r
414 \r
415                 explode : function(s, d) {\r
416                         return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;\r
417                 },\r
418 \r
419                 _addVer : function(u) {\r
420                         var v;\r
421 \r
422                         if (!this.query)\r
423                                 return u;\r
424 \r
425                         v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;\r
426 \r
427                         if (u.indexOf('#') == -1)\r
428                                 return u + v;\r
429 \r
430                         return u.replace('#', v + '#');\r
431                 },\r
432 \r
433                 // Fix function for IE 9 where regexps isn't working correctly\r
434                 // Todo: remove me once MS fixes the bug\r
435                 _replace : function(find, replace, str) {\r
436                         // On IE9 we have to fake $x replacement\r
437                         if (isRegExpBroken) {\r
438                                 return str.replace(find, function() {\r
439                                         var val = replace, args = arguments, i;\r
440 \r
441                                         for (i = 0; i < args.length - 2; i++) {\r
442                                                 if (args[i] === undefined) {\r
443                                                         val = val.replace(new RegExp('\\$' + i, 'g'), '');\r
444                                                 } else {\r
445                                                         val = val.replace(new RegExp('\\$' + i, 'g'), args[i]);\r
446                                                 }\r
447                                         }\r
448 \r
449                                         return val;\r
450                                 });\r
451                         }\r
452 \r
453                         return str.replace(find, replace);\r
454                 }\r
455 \r
456                 };\r
457 \r
458         // Initialize the API\r
459         tinymce._init();\r
460 \r
461         // Expose tinymce namespace to the global namespace (window)\r
462         win.tinymce = win.tinyMCE = tinymce;\r
463 })(window);\r
464 \r
465 \r
466 tinymce.create('tinymce.util.Dispatcher', {\r
467         scope : null,\r
468         listeners : null,\r
469 \r
470         Dispatcher : function(s) {\r
471                 this.scope = s || this;\r
472                 this.listeners = [];\r
473         },\r
474 \r
475         add : function(cb, s) {\r
476                 this.listeners.push({cb : cb, scope : s || this.scope});\r
477 \r
478                 return cb;\r
479         },\r
480 \r
481         addToTop : function(cb, s) {\r
482                 this.listeners.unshift({cb : cb, scope : s || this.scope});\r
483 \r
484                 return cb;\r
485         },\r
486 \r
487         remove : function(cb) {\r
488                 var l = this.listeners, o = null;\r
489 \r
490                 tinymce.each(l, function(c, i) {\r
491                         if (cb == c.cb) {\r
492                                 o = cb;\r
493                                 l.splice(i, 1);\r
494                                 return false;\r
495                         }\r
496                 });\r
497 \r
498                 return o;\r
499         },\r
500 \r
501         dispatch : function() {\r
502                 var s, a = arguments, i, li = this.listeners, c;\r
503 \r
504                 // Needs to be a real loop since the listener count might change while looping\r
505                 // And this is also more efficient\r
506                 for (i = 0; i<li.length; i++) {\r
507                         c = li[i];\r
508                         s = c.cb.apply(c.scope, a);\r
509 \r
510                         if (s === false)\r
511                                 break;\r
512                 }\r
513 \r
514                 return s;\r
515         }\r
516 \r
517         });\r
518 \r
519 (function() {\r
520         var each = tinymce.each;\r
521 \r
522         tinymce.create('tinymce.util.URI', {\r
523                 URI : function(u, s) {\r
524                         var t = this, o, a, b;\r
525 \r
526                         // Trim whitespace\r
527                         u = tinymce.trim(u);\r
528 \r
529                         // Default settings\r
530                         s = t.settings = s || {};\r
531 \r
532                         // Strange app protocol or local anchor\r
533                         if (/^(mailto|tel|news|javascript|about|data):/i.test(u) || /^\s*#/.test(u)) {\r
534                                 t.source = u;\r
535                                 return;\r
536                         }\r
537 \r
538                         // Absolute path with no host, fake host and protocol\r
539                         if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)\r
540                                 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;\r
541 \r
542                         // Relative path http:// or protocol relative //path\r
543                         if (!/^\w*:?\/\//.test(u))\r
544                                 u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u);\r
545 \r
546                         // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)\r
547                         u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something\r
548                         u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);\r
549                         each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {\r
550                                 var s = u[i];\r
551 \r
552                                 // Zope 3 workaround, they use @@something\r
553                                 if (s)\r
554                                         s = s.replace(/\(mce_at\)/g, '@@');\r
555 \r
556                                 t[v] = s;\r
557                         });\r
558 \r
559                         if (b = s.base_uri) {\r
560                                 if (!t.protocol)\r
561                                         t.protocol = b.protocol;\r
562 \r
563                                 if (!t.userInfo)\r
564                                         t.userInfo = b.userInfo;\r
565 \r
566                                 if (!t.port && t.host == 'mce_host')\r
567                                         t.port = b.port;\r
568 \r
569                                 if (!t.host || t.host == 'mce_host')\r
570                                         t.host = b.host;\r
571 \r
572                                 t.source = '';\r
573                         }\r
574 \r
575                         //t.path = t.path || '/';\r
576                 },\r
577 \r
578                 setPath : function(p) {\r
579                         var t = this;\r
580 \r
581                         p = /^(.*?)\/?(\w+)?$/.exec(p);\r
582 \r
583                         // Update path parts\r
584                         t.path = p[0];\r
585                         t.directory = p[1];\r
586                         t.file = p[2];\r
587 \r
588                         // Rebuild source\r
589                         t.source = '';\r
590                         t.getURI();\r
591                 },\r
592 \r
593                 toRelative : function(u) {\r
594                         var t = this, o;\r
595 \r
596                         if (u === "./")\r
597                                 return u;\r
598 \r
599                         u = new tinymce.util.URI(u, {base_uri : t});\r
600 \r
601                         // Not on same domain/port or protocol\r
602                         if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)\r
603                                 return u.getURI();\r
604 \r
605                         o = t.toRelPath(t.path, u.path);\r
606 \r
607                         // Add query\r
608                         if (u.query)\r
609                                 o += '?' + u.query;\r
610 \r
611                         // Add anchor\r
612                         if (u.anchor)\r
613                                 o += '#' + u.anchor;\r
614 \r
615                         return o;\r
616                 },\r
617         \r
618                 toAbsolute : function(u, nh) {\r
619                         var u = new tinymce.util.URI(u, {base_uri : this});\r
620 \r
621                         return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0);\r
622                 },\r
623 \r
624                 toRelPath : function(base, path) {\r
625                         var items, bp = 0, out = '', i, l;\r
626 \r
627                         // Split the paths\r
628                         base = base.substring(0, base.lastIndexOf('/'));\r
629                         base = base.split('/');\r
630                         items = path.split('/');\r
631 \r
632                         if (base.length >= items.length) {\r
633                                 for (i = 0, l = base.length; i < l; i++) {\r
634                                         if (i >= items.length || base[i] != items[i]) {\r
635                                                 bp = i + 1;\r
636                                                 break;\r
637                                         }\r
638                                 }\r
639                         }\r
640 \r
641                         if (base.length < items.length) {\r
642                                 for (i = 0, l = items.length; i < l; i++) {\r
643                                         if (i >= base.length || base[i] != items[i]) {\r
644                                                 bp = i + 1;\r
645                                                 break;\r
646                                         }\r
647                                 }\r
648                         }\r
649 \r
650                         if (bp == 1)\r
651                                 return path;\r
652 \r
653                         for (i = 0, l = base.length - (bp - 1); i < l; i++)\r
654                                 out += "../";\r
655 \r
656                         for (i = bp - 1, l = items.length; i < l; i++) {\r
657                                 if (i != bp - 1)\r
658                                         out += "/" + items[i];\r
659                                 else\r
660                                         out += items[i];\r
661                         }\r
662 \r
663                         return out;\r
664                 },\r
665 \r
666                 toAbsPath : function(base, path) {\r
667                         var i, nb = 0, o = [], tr, outPath;\r
668 \r
669                         // Split paths\r
670                         tr = /\/$/.test(path) ? '/' : '';\r
671                         base = base.split('/');\r
672                         path = path.split('/');\r
673 \r
674                         // Remove empty chunks\r
675                         each(base, function(k) {\r
676                                 if (k)\r
677                                         o.push(k);\r
678                         });\r
679 \r
680                         base = o;\r
681 \r
682                         // Merge relURLParts chunks\r
683                         for (i = path.length - 1, o = []; i >= 0; i--) {\r
684                                 // Ignore empty or .\r
685                                 if (path[i].length == 0 || path[i] == ".")\r
686                                         continue;\r
687 \r
688                                 // Is parent\r
689                                 if (path[i] == '..') {\r
690                                         nb++;\r
691                                         continue;\r
692                                 }\r
693 \r
694                                 // Move up\r
695                                 if (nb > 0) {\r
696                                         nb--;\r
697                                         continue;\r
698                                 }\r
699 \r
700                                 o.push(path[i]);\r
701                         }\r
702 \r
703                         i = base.length - nb;\r
704 \r
705                         // If /a/b/c or /\r
706                         if (i <= 0)\r
707                                 outPath = o.reverse().join('/');\r
708                         else\r
709                                 outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');\r
710 \r
711                         // Add front / if it's needed\r
712                         if (outPath.indexOf('/') !== 0)\r
713                                 outPath = '/' + outPath;\r
714 \r
715                         // Add traling / if it's needed\r
716                         if (tr && outPath.lastIndexOf('/') !== outPath.length - 1)\r
717                                 outPath += tr;\r
718 \r
719                         return outPath;\r
720                 },\r
721 \r
722                 getURI : function(nh) {\r
723                         var s, t = this;\r
724 \r
725                         // Rebuild source\r
726                         if (!t.source || nh) {\r
727                                 s = '';\r
728 \r
729                                 if (!nh) {\r
730                                         if (t.protocol)\r
731                                                 s += t.protocol + '://';\r
732 \r
733                                         if (t.userInfo)\r
734                                                 s += t.userInfo + '@';\r
735 \r
736                                         if (t.host)\r
737                                                 s += t.host;\r
738 \r
739                                         if (t.port)\r
740                                                 s += ':' + t.port;\r
741                                 }\r
742 \r
743                                 if (t.path)\r
744                                         s += t.path;\r
745 \r
746                                 if (t.query)\r
747                                         s += '?' + t.query;\r
748 \r
749                                 if (t.anchor)\r
750                                         s += '#' + t.anchor;\r
751 \r
752                                 t.source = s;\r
753                         }\r
754 \r
755                         return t.source;\r
756                 }\r
757         });\r
758 })();\r
759 \r
760 (function() {\r
761         var each = tinymce.each;\r
762 \r
763         tinymce.create('static tinymce.util.Cookie', {\r
764                 getHash : function(n) {\r
765                         var v = this.get(n), h;\r
766 \r
767                         if (v) {\r
768                                 each(v.split('&'), function(v) {\r
769                                         v = v.split('=');\r
770                                         h = h || {};\r
771                                         h[unescape(v[0])] = unescape(v[1]);\r
772                                 });\r
773                         }\r
774 \r
775                         return h;\r
776                 },\r
777 \r
778                 setHash : function(n, v, e, p, d, s) {\r
779                         var o = '';\r
780 \r
781                         each(v, function(v, k) {\r
782                                 o += (!o ? '' : '&') + escape(k) + '=' + escape(v);\r
783                         });\r
784 \r
785                         this.set(n, o, e, p, d, s);\r
786                 },\r
787 \r
788                 get : function(n) {\r
789                         var c = document.cookie, e, p = n + "=", b;\r
790 \r
791                         // Strict mode\r
792                         if (!c)\r
793                                 return;\r
794 \r
795                         b = c.indexOf("; " + p);\r
796 \r
797                         if (b == -1) {\r
798                                 b = c.indexOf(p);\r
799 \r
800                                 if (b != 0)\r
801                                         return null;\r
802                         } else\r
803                                 b += 2;\r
804 \r
805                         e = c.indexOf(";", b);\r
806 \r
807                         if (e == -1)\r
808                                 e = c.length;\r
809 \r
810                         return unescape(c.substring(b + p.length, e));\r
811                 },\r
812 \r
813                 set : function(n, v, e, p, d, s) {\r
814                         document.cookie = n + "=" + escape(v) +\r
815                                 ((e) ? "; expires=" + e.toGMTString() : "") +\r
816                                 ((p) ? "; path=" + escape(p) : "") +\r
817                                 ((d) ? "; domain=" + d : "") +\r
818                                 ((s) ? "; secure" : "");\r
819                 },\r
820 \r
821                 remove : function(n, p) {\r
822                         var d = new Date();\r
823 \r
824                         d.setTime(d.getTime() - 1000);\r
825 \r
826                         this.set(n, '', d, p, d);\r
827                 }\r
828         });\r
829 })();\r
830 \r
831 tinymce.create('static tinymce.util.JSON', {\r
832         serialize : function(o) {\r
833                 var i, v, s = tinymce.util.JSON.serialize, t;\r
834 \r
835                 if (o == null)\r
836                         return 'null';\r
837 \r
838                 t = typeof o;\r
839 \r
840                 if (t == 'string') {\r
841                         v = '\bb\tt\nn\ff\rr\""\'\'\\\\';\r
842 \r
843                         return '"' + o.replace(/([\u0080-\uFFFF\x00-\x1f\"])/g, function(a, b) {\r
844                                 i = v.indexOf(b);\r
845 \r
846                                 if (i + 1)\r
847                                         return '\\' + v.charAt(i + 1);\r
848 \r
849                                 a = b.charCodeAt().toString(16);\r
850 \r
851                                 return '\\u' + '0000'.substring(a.length) + a;\r
852                         }) + '"';\r
853                 }\r
854 \r
855                 if (t == 'object') {\r
856                         if (o.hasOwnProperty && o instanceof Array) {\r
857                                         for (i=0, v = '['; i<o.length; i++)\r
858                                                 v += (i > 0 ? ',' : '') + s(o[i]);\r
859 \r
860                                         return v + ']';\r
861                                 }\r
862 \r
863                                 v = '{';\r
864 \r
865                                 for (i in o)\r
866                                         v += typeof o[i] != 'function' ? (v.length > 1 ? ',"' : '"') + i + '":' + s(o[i]) : '';\r
867 \r
868                                 return v + '}';\r
869                 }\r
870 \r
871                 return '' + o;\r
872         },\r
873 \r
874         parse : function(s) {\r
875                 try {\r
876                         return eval('(' + s + ')');\r
877                 } catch (ex) {\r
878                         // Ignore\r
879                 }\r
880         }\r
881 \r
882         });\r
883 \r
884 tinymce.create('static tinymce.util.XHR', {\r
885         send : function(o) {\r
886                 var x, t, w = window, c = 0;\r
887 \r
888                 // Default settings\r
889                 o.scope = o.scope || this;\r
890                 o.success_scope = o.success_scope || o.scope;\r
891                 o.error_scope = o.error_scope || o.scope;\r
892                 o.async = o.async === false ? false : true;\r
893                 o.data = o.data || '';\r
894 \r
895                 function get(s) {\r
896                         x = 0;\r
897 \r
898                         try {\r
899                                 x = new ActiveXObject(s);\r
900                         } catch (ex) {\r
901                         }\r
902 \r
903                         return x;\r
904                 };\r
905 \r
906                 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');\r
907 \r
908                 if (x) {\r
909                         if (x.overrideMimeType)\r
910                                 x.overrideMimeType(o.content_type);\r
911 \r
912                         x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);\r
913 \r
914                         if (o.content_type)\r
915                                 x.setRequestHeader('Content-Type', o.content_type);\r
916 \r
917                         x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\r
918 \r
919                         x.send(o.data);\r
920 \r
921                         function ready() {\r
922                                 if (!o.async || x.readyState == 4 || c++ > 10000) {\r
923                                         if (o.success && c < 10000 && x.status == 200)\r
924                                                 o.success.call(o.success_scope, '' + x.responseText, x, o);\r
925                                         else if (o.error)\r
926                                                 o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);\r
927 \r
928                                         x = null;\r
929                                 } else\r
930                                         w.setTimeout(ready, 10);\r
931                         };\r
932 \r
933                         // Syncronous request\r
934                         if (!o.async)\r
935                                 return ready();\r
936 \r
937                         // Wait for response, onReadyStateChange can not be used since it leaks memory in IE\r
938                         t = w.setTimeout(ready, 10);\r
939                 }\r
940         }\r
941 });\r
942 \r
943 (function() {\r
944         var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;\r
945 \r
946         tinymce.create('tinymce.util.JSONRequest', {\r
947                 JSONRequest : function(s) {\r
948                         this.settings = extend({\r
949                         }, s);\r
950                         this.count = 0;\r
951                 },\r
952 \r
953                 send : function(o) {\r
954                         var ecb = o.error, scb = o.success;\r
955 \r
956                         o = extend(this.settings, o);\r
957 \r
958                         o.success = function(c, x) {\r
959                                 c = JSON.parse(c);\r
960 \r
961                                 if (typeof(c) == 'undefined') {\r
962                                         c = {\r
963                                                 error : 'JSON Parse error.'\r
964                                         };\r
965                                 }\r
966 \r
967                                 if (c.error)\r
968                                         ecb.call(o.error_scope || o.scope, c.error, x);\r
969                                 else\r
970                                         scb.call(o.success_scope || o.scope, c.result);\r
971                         };\r
972 \r
973                         o.error = function(ty, x) {\r
974                                 ecb.call(o.error_scope || o.scope, ty, x);\r
975                         };\r
976 \r
977                         o.data = JSON.serialize({\r
978                                 id : o.id || 'c' + (this.count++),\r
979                                 method : o.method,\r
980                                 params : o.params\r
981                         });\r
982 \r
983                         // JSON content type for Ruby on rails. Bug: #1883287\r
984                         o.content_type = 'application/json';\r
985 \r
986                         XHR.send(o);\r
987                 },\r
988 \r
989                 'static' : {\r
990                         sendRPC : function(o) {\r
991                                 return new tinymce.util.JSONRequest().send(o);\r
992                         }\r
993                 }\r
994         });\r
995 }());\r
996 (function(tinymce) {\r
997         // Shorten names\r
998         var each = tinymce.each,\r
999                 is = tinymce.is,\r
1000                 isWebKit = tinymce.isWebKit,\r
1001                 isIE = tinymce.isIE,\r
1002                 blockRe = /^(H[1-6R]|P|DIV|ADDRESS|PRE|FORM|T(ABLE|BODY|HEAD|FOOT|H|R|D)|LI|OL|UL|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|NOSCRIPT|MENU|ISINDEX|SAMP)$/,\r
1003                 boolAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'),\r
1004                 mceAttribs = makeMap('src,href,style,coords,shape'),\r
1005                 encodedChars = {'&' : '&amp;', '"' : '&quot;', '<' : '&lt;', '>' : '&gt;'},\r
1006                 encodeCharsRe = /[<>&\"]/g,\r
1007                 simpleSelectorRe = /^([a-z0-9],?)+$/i,\r
1008                 tagRegExp = /<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)(\s*\/?)>/g,\r
1009                 attrRegExp = /(\w+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;\r
1010 \r
1011         function makeMap(str) {\r
1012                 var map = {}, i;\r
1013 \r
1014                 str = str.split(',');\r
1015                 for (i = str.length; i >= 0; i--)\r
1016                         map[str[i]] = 1;\r
1017 \r
1018                 return map;\r
1019         };\r
1020 \r
1021         tinymce.create('tinymce.dom.DOMUtils', {\r
1022                 doc : null,\r
1023                 root : null,\r
1024                 files : null,\r
1025                 pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,\r
1026                 props : {\r
1027                         "for" : "htmlFor",\r
1028                         "class" : "className",\r
1029                         className : "className",\r
1030                         checked : "checked",\r
1031                         disabled : "disabled",\r
1032                         maxlength : "maxLength",\r
1033                         readonly : "readOnly",\r
1034                         selected : "selected",\r
1035                         value : "value",\r
1036                         id : "id",\r
1037                         name : "name",\r
1038                         type : "type"\r
1039                 },\r
1040 \r
1041                 DOMUtils : function(d, s) {\r
1042                         var t = this, globalStyle;\r
1043 \r
1044                         t.doc = d;\r
1045                         t.win = window;\r
1046                         t.files = {};\r
1047                         t.cssFlicker = false;\r
1048                         t.counter = 0;\r
1049                         t.stdMode = d.documentMode >= 8;\r
1050                         t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat" || t.stdMode;\r
1051 \r
1052                         t.settings = s = tinymce.extend({\r
1053                                 keep_values : false,\r
1054                                 hex_colors : 1,\r
1055                                 process_html : 1\r
1056                         }, s);\r
1057 \r
1058                         // Fix IE6SP2 flicker and check it failed for pre SP2\r
1059                         if (tinymce.isIE6) {\r
1060                                 try {\r
1061                                         d.execCommand('BackgroundImageCache', false, true);\r
1062                                 } catch (e) {\r
1063                                         t.cssFlicker = true;\r
1064                                 }\r
1065                         }\r
1066 \r
1067                         // Build styles list\r
1068                         if (s.valid_styles) {\r
1069                                 t._styles = {};\r
1070 \r
1071                                 // Convert styles into a rule list\r
1072                                 each(s.valid_styles, function(value, key) {\r
1073                                         t._styles[key] = tinymce.explode(value);\r
1074                                 });\r
1075                         }\r
1076 \r
1077                         tinymce.addUnload(t.destroy, t);\r
1078                 },\r
1079 \r
1080                 getRoot : function() {\r
1081                         var t = this, s = t.settings;\r
1082 \r
1083                         return (s && t.get(s.root_element)) || t.doc.body;\r
1084                 },\r
1085 \r
1086                 getViewPort : function(w) {\r
1087                         var d, b;\r
1088 \r
1089                         w = !w ? this.win : w;\r
1090                         d = w.document;\r
1091                         b = this.boxModel ? d.documentElement : d.body;\r
1092 \r
1093                         // Returns viewport size excluding scrollbars\r
1094                         return {\r
1095                                 x : w.pageXOffset || b.scrollLeft,\r
1096                                 y : w.pageYOffset || b.scrollTop,\r
1097                                 w : w.innerWidth || b.clientWidth,\r
1098                                 h : w.innerHeight || b.clientHeight\r
1099                         };\r
1100                 },\r
1101 \r
1102                 getRect : function(e) {\r
1103                         var p, t = this, sr;\r
1104 \r
1105                         e = t.get(e);\r
1106                         p = t.getPos(e);\r
1107                         sr = t.getSize(e);\r
1108 \r
1109                         return {\r
1110                                 x : p.x,\r
1111                                 y : p.y,\r
1112                                 w : sr.w,\r
1113                                 h : sr.h\r
1114                         };\r
1115                 },\r
1116 \r
1117                 getSize : function(e) {\r
1118                         var t = this, w, h;\r
1119 \r
1120                         e = t.get(e);\r
1121                         w = t.getStyle(e, 'width');\r
1122                         h = t.getStyle(e, 'height');\r
1123 \r
1124                         // Non pixel value, then force offset/clientWidth\r
1125                         if (w.indexOf('px') === -1)\r
1126                                 w = 0;\r
1127 \r
1128                         // Non pixel value, then force offset/clientWidth\r
1129                         if (h.indexOf('px') === -1)\r
1130                                 h = 0;\r
1131 \r
1132                         return {\r
1133                                 w : parseInt(w) || e.offsetWidth || e.clientWidth,\r
1134                                 h : parseInt(h) || e.offsetHeight || e.clientHeight\r
1135                         };\r
1136                 },\r
1137 \r
1138                 getParent : function(n, f, r) {\r
1139                         return this.getParents(n, f, r, false);\r
1140                 },\r
1141 \r
1142                 getParents : function(n, f, r, c) {\r
1143                         var t = this, na, se = t.settings, o = [];\r
1144 \r
1145                         n = t.get(n);\r
1146                         c = c === undefined;\r
1147 \r
1148                         if (se.strict_root)\r
1149                                 r = r || t.getRoot();\r
1150 \r
1151                         // Wrap node name as func\r
1152                         if (is(f, 'string')) {\r
1153                                 na = f;\r
1154 \r
1155                                 if (f === '*') {\r
1156                                         f = function(n) {return n.nodeType == 1;};\r
1157                                 } else {\r
1158                                         f = function(n) {\r
1159                                                 return t.is(n, na);\r
1160                                         };\r
1161                                 }\r
1162                         }\r
1163 \r
1164                         while (n) {\r
1165                                 if (n == r || !n.nodeType || n.nodeType === 9)\r
1166                                         break;\r
1167 \r
1168                                 if (!f || f(n)) {\r
1169                                         if (c)\r
1170                                                 o.push(n);\r
1171                                         else\r
1172                                                 return n;\r
1173                                 }\r
1174 \r
1175                                 n = n.parentNode;\r
1176                         }\r
1177 \r
1178                         return c ? o : null;\r
1179                 },\r
1180 \r
1181                 get : function(e) {\r
1182                         var n;\r
1183 \r
1184                         if (e && this.doc && typeof(e) == 'string') {\r
1185                                 n = e;\r
1186                                 e = this.doc.getElementById(e);\r
1187 \r
1188                                 // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick\r
1189                                 if (e && e.id !== n)\r
1190                                         return this.doc.getElementsByName(n)[1];\r
1191                         }\r
1192 \r
1193                         return e;\r
1194                 },\r
1195 \r
1196                 getNext : function(node, selector) {\r
1197                         return this._findSib(node, selector, 'nextSibling');\r
1198                 },\r
1199 \r
1200                 getPrev : function(node, selector) {\r
1201                         return this._findSib(node, selector, 'previousSibling');\r
1202                 },\r
1203 \r
1204 \r
1205                 select : function(pa, s) {\r
1206                         var t = this;\r
1207 \r
1208                         return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []);\r
1209                 },\r
1210 \r
1211                 is : function(n, selector) {\r
1212                         var i;\r
1213 \r
1214                         // If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance\r
1215                         if (n.length === undefined) {\r
1216                                 // Simple all selector\r
1217                                 if (selector === '*')\r
1218                                         return n.nodeType == 1;\r
1219 \r
1220                                 // Simple selector just elements\r
1221                                 if (simpleSelectorRe.test(selector)) {\r
1222                                         selector = selector.toLowerCase().split(/,/);\r
1223                                         n = n.nodeName.toLowerCase();\r
1224 \r
1225                                         for (i = selector.length - 1; i >= 0; i--) {\r
1226                                                 if (selector[i] == n)\r
1227                                                         return true;\r
1228                                         }\r
1229 \r
1230                                         return false;\r
1231                                 }\r
1232                         }\r
1233 \r
1234                         return tinymce.dom.Sizzle.matches(selector, n.nodeType ? [n] : n).length > 0;\r
1235                 },\r
1236 \r
1237 \r
1238                 add : function(p, n, a, h, c) {\r
1239                         var t = this;\r
1240 \r
1241                         return this.run(p, function(p) {\r
1242                                 var e, k;\r
1243 \r
1244                                 e = is(n, 'string') ? t.doc.createElement(n) : n;\r
1245                                 t.setAttribs(e, a);\r
1246 \r
1247                                 if (h) {\r
1248                                         if (h.nodeType)\r
1249                                                 e.appendChild(h);\r
1250                                         else\r
1251                                                 t.setHTML(e, h);\r
1252                                 }\r
1253 \r
1254                                 return !c ? p.appendChild(e) : e;\r
1255                         });\r
1256                 },\r
1257 \r
1258                 create : function(n, a, h) {\r
1259                         return this.add(this.doc.createElement(n), n, a, h, 1);\r
1260                 },\r
1261 \r
1262                 createHTML : function(n, a, h) {\r
1263                         var o = '', t = this, k;\r
1264 \r
1265                         o += '<' + n;\r
1266 \r
1267                         for (k in a) {\r
1268                                 if (a.hasOwnProperty(k))\r
1269                                         o += ' ' + k + '="' + t.encode(a[k]) + '"';\r
1270                         }\r
1271 \r
1272                         // A call to tinymce.is doesn't work for some odd reason on IE9 possible bug inside their JS runtime\r
1273                         if (typeof(h) != "undefined")\r
1274                                 return o + '>' + h + '</' + n + '>';\r
1275 \r
1276                         return o + ' />';\r
1277                 },\r
1278 \r
1279                 remove : function(node, keep_children) {\r
1280                         return this.run(node, function(node) {\r
1281                                 var parent, child;\r
1282 \r
1283                                 parent = node.parentNode;\r
1284 \r
1285                                 if (!parent)\r
1286                                         return null;\r
1287 \r
1288                                 if (keep_children) {\r
1289                                         while (child = node.firstChild) {\r
1290                                                 // IE 8 will crash if you don't remove completely empty text nodes\r
1291                                                 if (!tinymce.isIE || child.nodeType !== 3 || child.nodeValue)\r
1292                                                         parent.insertBefore(child, node);\r
1293                                                 else\r
1294                                                         node.removeChild(child);\r
1295                                         }\r
1296                                 }\r
1297 \r
1298                                 return parent.removeChild(node);\r
1299                         });\r
1300                 },\r
1301 \r
1302                 setStyle : function(n, na, v) {\r
1303                         var t = this;\r
1304 \r
1305                         return t.run(n, function(e) {\r
1306                                 var s, i;\r
1307 \r
1308                                 s = e.style;\r
1309 \r
1310                                 // Camelcase it, if needed\r
1311                                 na = na.replace(/-(\D)/g, function(a, b){\r
1312                                         return b.toUpperCase();\r
1313                                 });\r
1314 \r
1315                                 // Default px suffix on these\r
1316                                 if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v)))\r
1317                                         v += 'px';\r
1318 \r
1319                                 switch (na) {\r
1320                                         case 'opacity':\r
1321                                                 // IE specific opacity\r
1322                                                 if (isIE) {\r
1323                                                         s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")";\r
1324 \r
1325                                                         if (!n.currentStyle || !n.currentStyle.hasLayout)\r
1326                                                                 s.display = 'inline-block';\r
1327                                                 }\r
1328 \r
1329                                                 // Fix for older browsers\r
1330                                                 s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || '';\r
1331                                                 break;\r
1332 \r
1333                                         case 'float':\r
1334                                                 isIE ? s.styleFloat = v : s.cssFloat = v;\r
1335                                                 break;\r
1336                                         \r
1337                                         default:\r
1338                                                 s[na] = v || '';\r
1339                                 }\r
1340 \r
1341                                 // Force update of the style data\r
1342                                 if (t.settings.update_styles)\r
1343                                         t.setAttrib(e, '_mce_style');\r
1344                         });\r
1345                 },\r
1346 \r
1347                 getStyle : function(n, na, c) {\r
1348                         n = this.get(n);\r
1349 \r
1350                         if (!n)\r
1351                                 return false;\r
1352 \r
1353                         // Gecko\r
1354                         if (this.doc.defaultView && c) {\r
1355                                 // Remove camelcase\r
1356                                 na = na.replace(/[A-Z]/g, function(a){\r
1357                                         return '-' + a;\r
1358                                 });\r
1359 \r
1360                                 try {\r
1361                                         return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);\r
1362                                 } catch (ex) {\r
1363                                         // Old safari might fail\r
1364                                         return null;\r
1365                                 }\r
1366                         }\r
1367 \r
1368                         // Camelcase it, if needed\r
1369                         na = na.replace(/-(\D)/g, function(a, b){\r
1370                                 return b.toUpperCase();\r
1371                         });\r
1372 \r
1373                         if (na == 'float')\r
1374                                 na = isIE ? 'styleFloat' : 'cssFloat';\r
1375 \r
1376                         // IE & Opera\r
1377                         if (n.currentStyle && c)\r
1378                                 return n.currentStyle[na];\r
1379 \r
1380                         return n.style[na];\r
1381                 },\r
1382 \r
1383                 setStyles : function(e, o) {\r
1384                         var t = this, s = t.settings, ol;\r
1385 \r
1386                         ol = s.update_styles;\r
1387                         s.update_styles = 0;\r
1388 \r
1389                         each(o, function(v, n) {\r
1390                                 t.setStyle(e, n, v);\r
1391                         });\r
1392 \r
1393                         // Update style info\r
1394                         s.update_styles = ol;\r
1395                         if (s.update_styles)\r
1396                                 t.setAttrib(e, s.cssText);\r
1397                 },\r
1398 \r
1399                 setAttrib : function(e, n, v) {\r
1400                         var t = this;\r
1401 \r
1402                         // Whats the point\r
1403                         if (!e || !n)\r
1404                                 return;\r
1405 \r
1406                         // Strict XML mode\r
1407                         if (t.settings.strict)\r
1408                                 n = n.toLowerCase();\r
1409 \r
1410                         return this.run(e, function(e) {\r
1411                                 var s = t.settings;\r
1412 \r
1413                                 switch (n) {\r
1414                                         case "style":\r
1415                                                 if (!is(v, 'string')) {\r
1416                                                         each(v, function(v, n) {\r
1417                                                                 t.setStyle(e, n, v);\r
1418                                                         });\r
1419 \r
1420                                                         return;\r
1421                                                 }\r
1422 \r
1423                                                 // No mce_style for elements with these since they might get resized by the user\r
1424                                                 if (s.keep_values) {\r
1425                                                         if (v && !t._isRes(v))\r
1426                                                                 e.setAttribute('_mce_style', v, 2);\r
1427                                                         else\r
1428                                                                 e.removeAttribute('_mce_style', 2);\r
1429                                                 }\r
1430 \r
1431                                                 e.style.cssText = v;\r
1432                                                 break;\r
1433 \r
1434                                         case "class":\r
1435                                                 e.className = v || ''; // Fix IE null bug\r
1436                                                 break;\r
1437 \r
1438                                         case "src":\r
1439                                         case "href":\r
1440                                                 if (s.keep_values) {\r
1441                                                         if (s.url_converter)\r
1442                                                                 v = s.url_converter.call(s.url_converter_scope || t, v, n, e);\r
1443 \r
1444                                                         t.setAttrib(e, '_mce_' + n, v, 2);\r
1445                                                 }\r
1446 \r
1447                                                 break;\r
1448                                         \r
1449                                         case "shape":\r
1450                                                 e.setAttribute('_mce_style', v);\r
1451                                                 break;\r
1452                                 }\r
1453 \r
1454                                 if (is(v) && v !== null && v.length !== 0)\r
1455                                         e.setAttribute(n, '' + v, 2);\r
1456                                 else\r
1457                                         e.removeAttribute(n, 2);\r
1458                         });\r
1459                 },\r
1460 \r
1461                 setAttribs : function(e, o) {\r
1462                         var t = this;\r
1463 \r
1464                         return this.run(e, function(e) {\r
1465                                 each(o, function(v, n) {\r
1466                                         t.setAttrib(e, n, v);\r
1467                                 });\r
1468                         });\r
1469                 },\r
1470 \r
1471                 getAttrib : function(e, n, dv) {\r
1472                         var v, t = this;\r
1473 \r
1474                         e = t.get(e);\r
1475 \r
1476                         if (!e || e.nodeType !== 1)\r
1477                                 return false;\r
1478 \r
1479                         if (!is(dv))\r
1480                                 dv = '';\r
1481 \r
1482                         // Try the mce variant for these\r
1483                         if (/^(src|href|style|coords|shape)$/.test(n)) {\r
1484                                 v = e.getAttribute("_mce_" + n);\r
1485 \r
1486                                 if (v)\r
1487                                         return v;\r
1488                         }\r
1489 \r
1490                         if (isIE && t.props[n]) {\r
1491                                 v = e[t.props[n]];\r
1492                                 v = v && v.nodeValue ? v.nodeValue : v;\r
1493                         }\r
1494 \r
1495                         if (!v)\r
1496                                 v = e.getAttribute(n, 2);\r
1497 \r
1498                         // Check boolean attribs\r
1499                         if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(n)) {\r
1500                                 if (e[t.props[n]] === true && v === '')\r
1501                                         return n;\r
1502 \r
1503                                 return v ? n : '';\r
1504                         }\r
1505 \r
1506                         // Inner input elements will override attributes on form elements\r
1507                         if (e.nodeName === "FORM" && e.getAttributeNode(n))\r
1508                                 return e.getAttributeNode(n).nodeValue;\r
1509 \r
1510                         if (n === 'style') {\r
1511                                 v = v || e.style.cssText;\r
1512 \r
1513                                 if (v) {\r
1514                                         v = t.serializeStyle(t.parseStyle(v), e.nodeName);\r
1515 \r
1516                                         if (t.settings.keep_values && !t._isRes(v))\r
1517                                                 e.setAttribute('_mce_style', v);\r
1518                                 }\r
1519                         }\r
1520 \r
1521                         // Remove Apple and WebKit stuff\r
1522                         if (isWebKit && n === "class" && v)\r
1523                                 v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, '');\r
1524 \r
1525                         // Handle IE issues\r
1526                         if (isIE) {\r
1527                                 switch (n) {\r
1528                                         case 'rowspan':\r
1529                                         case 'colspan':\r
1530                                                 // IE returns 1 as default value\r
1531                                                 if (v === 1)\r
1532                                                         v = '';\r
1533 \r
1534                                                 break;\r
1535 \r
1536                                         case 'size':\r
1537                                                 // IE returns +0 as default value for size\r
1538                                                 if (v === '+0' || v === 20 || v === 0)\r
1539                                                         v = '';\r
1540 \r
1541                                                 break;\r
1542 \r
1543                                         case 'width':\r
1544                                         case 'height':\r
1545                                         case 'vspace':\r
1546                                         case 'checked':\r
1547                                         case 'disabled':\r
1548                                         case 'readonly':\r
1549                                                 if (v === 0)\r
1550                                                         v = '';\r
1551 \r
1552                                                 break;\r
1553 \r
1554                                         case 'hspace':\r
1555                                                 // IE returns -1 as default value\r
1556                                                 if (v === -1)\r
1557                                                         v = '';\r
1558 \r
1559                                                 break;\r
1560 \r
1561                                         case 'maxlength':\r
1562                                         case 'tabindex':\r
1563                                                 // IE returns default value\r
1564                                                 if (v === 32768 || v === 2147483647 || v === '32768')\r
1565                                                         v = '';\r
1566 \r
1567                                                 break;\r
1568 \r
1569                                         case 'multiple':\r
1570                                         case 'compact':\r
1571                                         case 'noshade':\r
1572                                         case 'nowrap':\r
1573                                                 if (v === 65535)\r
1574                                                         return n;\r
1575 \r
1576                                                 return dv;\r
1577 \r
1578                                         case 'shape':\r
1579                                                 v = v.toLowerCase();\r
1580                                                 break;\r
1581 \r
1582                                         default:\r
1583                                                 // IE has odd anonymous function for event attributes\r
1584                                                 if (n.indexOf('on') === 0 && v)\r
1585                                                         v = tinymce._replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1', '' + v);\r
1586                                 }\r
1587                         }\r
1588 \r
1589                         return (v !== undefined && v !== null && v !== '') ? '' + v : dv;\r
1590                 },\r
1591 \r
1592                 getPos : function(n, ro) {\r
1593                         var t = this, x = 0, y = 0, e, d = t.doc, r;\r
1594 \r
1595                         n = t.get(n);\r
1596                         ro = ro || d.body;\r
1597 \r
1598                         if (n) {\r
1599                                 // Use getBoundingClientRect on IE, Opera has it but it's not perfect\r
1600                                 if (isIE && !t.stdMode) {\r
1601                                         n = n.getBoundingClientRect();\r
1602                                         e = t.boxModel ? d.documentElement : d.body;\r
1603                                         x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border\r
1604                                         x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x;\r
1605 \r
1606                                         return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x};\r
1607                                 }\r
1608 \r
1609                                 r = n;\r
1610                                 while (r && r != ro && r.nodeType) {\r
1611                                         x += r.offsetLeft || 0;\r
1612                                         y += r.offsetTop || 0;\r
1613                                         r = r.offsetParent;\r
1614                                 }\r
1615 \r
1616                                 r = n.parentNode;\r
1617                                 while (r && r != ro && r.nodeType) {\r
1618                                         x -= r.scrollLeft || 0;\r
1619                                         y -= r.scrollTop || 0;\r
1620                                         r = r.parentNode;\r
1621                                 }\r
1622                         }\r
1623 \r
1624                         return {x : x, y : y};\r
1625                 },\r
1626 \r
1627                 parseStyle : function(st) {\r
1628                         var t = this, s = t.settings, o = {};\r
1629 \r
1630                         if (!st)\r
1631                                 return o;\r
1632 \r
1633                         function compress(p, s, ot) {\r
1634                                 var t, r, b, l;\r
1635 \r
1636                                 // Get values and check it it needs compressing\r
1637                                 t = o[p + '-top' + s];\r
1638                                 if (!t)\r
1639                                         return;\r
1640 \r
1641                                 r = o[p + '-right' + s];\r
1642                                 if (t != r)\r
1643                                         return;\r
1644 \r
1645                                 b = o[p + '-bottom' + s];\r
1646                                 if (r != b)\r
1647                                         return;\r
1648 \r
1649                                 l = o[p + '-left' + s];\r
1650                                 if (b != l)\r
1651                                         return;\r
1652 \r
1653                                 // Compress\r
1654                                 o[ot] = l;\r
1655                                 delete o[p + '-top' + s];\r
1656                                 delete o[p + '-right' + s];\r
1657                                 delete o[p + '-bottom' + s];\r
1658                                 delete o[p + '-left' + s];\r
1659                         };\r
1660 \r
1661                         function compress2(ta, a, b, c) {\r
1662                                 var t;\r
1663 \r
1664                                 t = o[a];\r
1665                                 if (!t)\r
1666                                         return;\r
1667 \r
1668                                 t = o[b];\r
1669                                 if (!t)\r
1670                                         return;\r
1671 \r
1672                                 t = o[c];\r
1673                                 if (!t)\r
1674                                         return;\r
1675 \r
1676                                 // Compress\r
1677                                 o[ta] = o[a] + ' ' + o[b] + ' ' + o[c];\r
1678                                 delete o[a];\r
1679                                 delete o[b];\r
1680                                 delete o[c];\r
1681                         };\r
1682 \r
1683                         st = st.replace(/&(#?[a-z0-9]+);/g, '&$1_MCE_SEMI_'); // Protect entities\r
1684 \r
1685                         each(st.split(';'), function(v) {\r
1686                                 var sv, ur = [];\r
1687 \r
1688                                 if (v) {\r
1689                                         v = v.replace(/_MCE_SEMI_/g, ';'); // Restore entities\r
1690                                         v = v.replace(/url\([^\)]+\)/g, function(v) {ur.push(v);return 'url(' + ur.length + ')';});\r
1691                                         v = v.split(':');\r
1692                                         sv = tinymce.trim(v[1]);\r
1693                                         sv = sv.replace(/url\(([^\)]+)\)/g, function(a, b) {return ur[parseInt(b) - 1];});\r
1694 \r
1695                                         sv = sv.replace(/rgb\([^\)]+\)/g, function(v) {\r
1696                                                 return t.toHex(v);\r
1697                                         });\r
1698 \r
1699                                         if (s.url_converter) {\r
1700                                                 sv = sv.replace(/url\([\'\"]?([^\)\'\"]+)[\'\"]?\)/g, function(x, c) {\r
1701                                                         return 'url(' + s.url_converter.call(s.url_converter_scope || t, t.decode(c), 'style', null) + ')';\r
1702                                                 });\r
1703                                         }\r
1704 \r
1705                                         o[tinymce.trim(v[0]).toLowerCase()] = sv;\r
1706                                 }\r
1707                         });\r
1708 \r
1709                         compress("border", "", "border");\r
1710                         compress("border", "-width", "border-width");\r
1711                         compress("border", "-color", "border-color");\r
1712                         compress("border", "-style", "border-style");\r
1713                         compress("padding", "", "padding");\r
1714                         compress("margin", "", "margin");\r
1715                         compress2('border', 'border-width', 'border-style', 'border-color');\r
1716 \r
1717                         if (isIE) {\r
1718                                 // Remove pointless border\r
1719                                 if (o.border == 'medium none')\r
1720                                         o.border = '';\r
1721                         }\r
1722 \r
1723                         return o;\r
1724                 },\r
1725 \r
1726                 serializeStyle : function(o, name) {\r
1727                         var t = this, s = '';\r
1728 \r
1729                         function add(v, k) {\r
1730                                 if (k && v) {\r
1731                                         // Remove browser specific styles like -moz- or -webkit-\r
1732                                         if (k.indexOf('-') === 0)\r
1733                                                 return;\r
1734 \r
1735                                         switch (k) {\r
1736                                                 case 'font-weight':\r
1737                                                         // Opera will output bold as 700\r
1738                                                         if (v == 700)\r
1739                                                                 v = 'bold';\r
1740 \r
1741                                                         break;\r
1742 \r
1743                                                 case 'color':\r
1744                                                 case 'background-color':\r
1745                                                         v = v.toLowerCase();\r
1746                                                         break;\r
1747                                         }\r
1748 \r
1749                                         s += (s ? ' ' : '') + k + ': ' + v + ';';\r
1750                                 }\r
1751                         };\r
1752 \r
1753                         // Validate style output\r
1754                         if (name && t._styles) {\r
1755                                 each(t._styles['*'], function(name) {\r
1756                                         add(o[name], name);\r
1757                                 });\r
1758 \r
1759                                 each(t._styles[name.toLowerCase()], function(name) {\r
1760                                         add(o[name], name);\r
1761                                 });\r
1762                         } else\r
1763                                 each(o, add);\r
1764 \r
1765                         return s;\r
1766                 },\r
1767 \r
1768                 loadCSS : function(u) {\r
1769                         var t = this, d = t.doc, head;\r
1770 \r
1771                         if (!u)\r
1772                                 u = '';\r
1773 \r
1774                         head = t.select('head')[0];\r
1775 \r
1776                         each(u.split(','), function(u) {\r
1777                                 var link;\r
1778 \r
1779                                 if (t.files[u])\r
1780                                         return;\r
1781 \r
1782                                 t.files[u] = true;\r
1783                                 link = t.create('link', {rel : 'stylesheet', href : tinymce._addVer(u)});\r
1784 \r
1785                                 // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug\r
1786                                 // This fix seems to resolve that issue by realcing the document ones a stylesheet finishes loading\r
1787                                 // It's ugly but it seems to work fine.\r
1788                                 if (isIE && d.documentMode && d.recalc) {\r
1789                                         link.onload = function() {\r
1790                                                 d.recalc();\r
1791                                                 link.onload = null;\r
1792                                         };\r
1793                                 }\r
1794 \r
1795                                 head.appendChild(link);\r
1796                         });\r
1797                 },\r
1798 \r
1799                 addClass : function(e, c) {\r
1800                         return this.run(e, function(e) {\r
1801                                 var o;\r
1802 \r
1803                                 if (!c)\r
1804                                         return 0;\r
1805 \r
1806                                 if (this.hasClass(e, c))\r
1807                                         return e.className;\r
1808 \r
1809                                 o = this.removeClass(e, c);\r
1810 \r
1811                                 return e.className = (o != '' ? (o + ' ') : '') + c;\r
1812                         });\r
1813                 },\r
1814 \r
1815                 removeClass : function(e, c) {\r
1816                         var t = this, re;\r
1817 \r
1818                         return t.run(e, function(e) {\r
1819                                 var v;\r
1820 \r
1821                                 if (t.hasClass(e, c)) {\r
1822                                         if (!re)\r
1823                                                 re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g");\r
1824 \r
1825                                         v = e.className.replace(re, ' ');\r
1826                                         v = tinymce.trim(v != ' ' ? v : '');\r
1827 \r
1828                                         e.className = v;\r
1829 \r
1830                                         // Empty class attr\r
1831                                         if (!v) {\r
1832                                                 e.removeAttribute('class');\r
1833                                                 e.removeAttribute('className');\r
1834                                         }\r
1835 \r
1836                                         return v;\r
1837                                 }\r
1838 \r
1839                                 return e.className;\r
1840                         });\r
1841                 },\r
1842 \r
1843                 hasClass : function(n, c) {\r
1844                         n = this.get(n);\r
1845 \r
1846                         if (!n || !c)\r
1847                                 return false;\r
1848 \r
1849                         return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1;\r
1850                 },\r
1851 \r
1852                 show : function(e) {\r
1853                         return this.setStyle(e, 'display', 'block');\r
1854                 },\r
1855 \r
1856                 hide : function(e) {\r
1857                         return this.setStyle(e, 'display', 'none');\r
1858                 },\r
1859 \r
1860                 isHidden : function(e) {\r
1861                         e = this.get(e);\r
1862 \r
1863                         return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none';\r
1864                 },\r
1865 \r
1866                 uniqueId : function(p) {\r
1867                         return (!p ? 'mce_' : p) + (this.counter++);\r
1868                 },\r
1869 \r
1870                 setHTML : function(e, h) {\r
1871                         var t = this;\r
1872 \r
1873                         return this.run(e, function(e) {\r
1874                                 var x, i, nl, n, p, x;\r
1875 \r
1876                                 h = t.processHTML(h);\r
1877 \r
1878                                 if (isIE) {\r
1879                                         function set() {\r
1880                                                 // Remove all child nodes\r
1881                                                 while (e.firstChild)\r
1882                                                         e.firstChild.removeNode();\r
1883 \r
1884                                                 try {\r
1885                                                         // IE will remove comments from the beginning\r
1886                                                         // unless you padd the contents with something\r
1887                                                         e.innerHTML = '<br />' + h;\r
1888                                                         e.removeChild(e.firstChild);\r
1889                                                 } catch (ex) {\r
1890                                                         // 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
1891                                                         // This seems to fix this problem\r
1892 \r
1893                                                         // Create new div with HTML contents and a BR infront to keep comments\r
1894                                                         x = t.create('div');\r
1895                                                         x.innerHTML = '<br />' + h;\r
1896 \r
1897                                                         // Add all children from div to target\r
1898                                                         each (x.childNodes, function(n, i) {\r
1899                                                                 // Skip br element\r
1900                                                                 if (i)\r
1901                                                                         e.appendChild(n);\r
1902                                                         });\r
1903                                                 }\r
1904                                         };\r
1905 \r
1906                                         // IE has a serious bug when it comes to paragraphs it can produce an invalid\r
1907                                         // DOM tree if contents like this <p><ul><li>Item 1</li></ul></p> is inserted\r
1908                                         // It seems to be that IE doesn't like a root block element placed inside another root block element\r
1909                                         if (t.settings.fix_ie_paragraphs)\r
1910                                                 h = h.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi, '<p$1 _mce_keep="true">&nbsp;</p>');\r
1911 \r
1912                                         set();\r
1913 \r
1914                                         if (t.settings.fix_ie_paragraphs) {\r
1915                                                 // Check for odd paragraphs this is a sign of a broken DOM\r
1916                                                 nl = e.getElementsByTagName("p");\r
1917                                                 for (i = nl.length - 1, x = 0; i >= 0; i--) {\r
1918                                                         n = nl[i];\r
1919 \r
1920                                                         if (!n.hasChildNodes()) {\r
1921                                                                 if (!n._mce_keep) {\r
1922                                                                         x = 1; // Is broken\r
1923                                                                         break;\r
1924                                                                 }\r
1925 \r
1926                                                                 n.removeAttribute('_mce_keep');\r
1927                                                         }\r
1928                                                 }\r
1929                                         }\r
1930 \r
1931                                         // Time to fix the madness IE left us\r
1932                                         if (x) {\r
1933                                                 // So if we replace the p elements with divs and mark them and then replace them back to paragraphs\r
1934                                                 // after we use innerHTML we can fix the DOM tree\r
1935                                                 h = h.replace(/<p ([^>]+)>|<p>/ig, '<div $1 _mce_tmp="1">');\r
1936                                                 h = h.replace(/<\/p>/gi, '</div>');\r
1937 \r
1938                                                 // Set the new HTML with DIVs\r
1939                                                 set();\r
1940 \r
1941                                                 // Replace all DIV elements with the _mce_tmp attibute back to paragraphs\r
1942                                                 // This is needed since IE has a annoying bug see above for details\r
1943                                                 // This is a slow process but it has to be done. :(\r
1944                                                 if (t.settings.fix_ie_paragraphs) {\r
1945                                                         nl = e.getElementsByTagName("DIV");\r
1946                                                         for (i = nl.length - 1; i >= 0; i--) {\r
1947                                                                 n = nl[i];\r
1948 \r
1949                                                                 // Is it a temp div\r
1950                                                                 if (n._mce_tmp) {\r
1951                                                                         // Create new paragraph\r
1952                                                                         p = t.doc.createElement('p');\r
1953 \r
1954                                                                         // Copy all attributes\r
1955                                                                         n.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi, function(a, b) {\r
1956                                                                                 var v;\r
1957 \r
1958                                                                                 if (b !== '_mce_tmp') {\r
1959                                                                                         v = n.getAttribute(b);\r
1960 \r
1961                                                                                         if (!v && b === 'class')\r
1962                                                                                                 v = n.className;\r
1963 \r
1964                                                                                         p.setAttribute(b, v);\r
1965                                                                                 }\r
1966                                                                         });\r
1967 \r
1968                                                                         // Append all children to new paragraph\r
1969                                                                         for (x = 0; x<n.childNodes.length; x++)\r
1970                                                                                 p.appendChild(n.childNodes[x].cloneNode(true));\r
1971 \r
1972                                                                         // Replace div with new paragraph\r
1973                                                                         n.swapNode(p);\r
1974                                                                 }\r
1975                                                         }\r
1976                                                 }\r
1977                                         }\r
1978                                 } else\r
1979                                         e.innerHTML = h;\r
1980 \r
1981                                 return h;\r
1982                         });\r
1983                 },\r
1984 \r
1985                 processHTML : function(h) {\r
1986                         var t = this, s = t.settings, codeBlocks = [];\r
1987 \r
1988                         if (!s.process_html)\r
1989                                 return h;\r
1990 \r
1991                         if (isIE) {\r
1992                                 h = h.replace(/&apos;/g, '&#39;'); // IE can't handle apos\r
1993                                 h = h.replace(/\s+(disabled|checked|readonly|selected)\s*=\s*[\"\']?(false|0)[\"\']?/gi, ''); // IE doesn't handle default values correct\r
1994                         }\r
1995 \r
1996                         // Force tags open, and on IE9 replace $1$2 that got left behind due to bugs in their RegExp engine\r
1997                         h = tinymce._replace(/<a( )([^>]+)\/>|<a\/>/gi, '<a$1$2></a>', h); // Force open\r
1998 \r
1999                         // Store away src and href in _mce_src and mce_href since browsers mess them up\r
2000                         if (s.keep_values) {\r
2001                                 // Wrap scripts and styles in comments for serialization purposes\r
2002                                 if (/<script|noscript|style/i.test(h)) {\r
2003                                         function trim(s) {\r
2004                                                 // Remove prefix and suffix code for element\r
2005                                                 s = s.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n');\r
2006                                                 s = s.replace(/^[\r\n]*|[\r\n]*$/g, '');\r
2007                                                 s = s.replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, '');\r
2008                                                 s = s.replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, '');\r
2009 \r
2010                                                 return s;\r
2011                                         };\r
2012 \r
2013                                         // Wrap the script contents in CDATA and keep them from executing\r
2014                                         h = h.replace(/<script([^>]+|)>([\s\S]*?)<\/script>/gi, function(v, attribs, text) {\r
2015                                                 // Force type attribute\r
2016                                                 if (!attribs)\r
2017                                                         attribs = ' type="text/javascript"';\r
2018 \r
2019                                                 // Convert the src attribute of the scripts\r
2020                                                 attribs = attribs.replace(/src=\"([^\"]+)\"?/i, function(a, url) {\r
2021                                                         if (s.url_converter)\r
2022                                                                 url = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(url), 'src', 'script'));\r
2023 \r
2024                                                         return '_mce_src="' + url + '"';\r
2025                                                 });\r
2026 \r
2027                                                 // Wrap text contents\r
2028                                                 if (tinymce.trim(text)) {\r
2029                                                         codeBlocks.push(trim(text));\r
2030                                                         text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n// -->';\r
2031                                                 }\r
2032 \r
2033                                                 return '<mce:script' + attribs + '>' + text + '</mce:script>';\r
2034                                         });\r
2035 \r
2036                                         // Wrap style elements\r
2037                                         h = h.replace(/<style([^>]+|)>([\s\S]*?)<\/style>/gi, function(v, attribs, text) {\r
2038                                                 // Wrap text contents\r
2039                                                 if (text) {\r
2040                                                         codeBlocks.push(trim(text));\r
2041                                                         text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n-->';\r
2042                                                 }\r
2043 \r
2044                                                 return '<mce:style' + attribs + '>' + text + '</mce:style><style ' + attribs + ' _mce_bogus="1">' + text + '</style>';\r
2045                                         });\r
2046 \r
2047                                         // Wrap noscript elements\r
2048                                         h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {\r
2049                                                 return '<mce:noscript' + attribs + '><!--' + t.encode(text).replace(/--/g, '&#45;&#45;') + '--></mce:noscript>';\r
2050                                         });\r
2051                                 }\r
2052 \r
2053                                 h = tinymce._replace(/<!\[CDATA\[([\s\S]+)\]\]>/g, '<!--[CDATA[$1]]-->', h);\r
2054 \r
2055                                 // This function processes the attributes in the HTML string to force boolean\r
2056                                 // attributes to the attr="attr" format and convert style, src and href to _mce_ versions\r
2057                                 function processTags(html) {\r
2058                                         return html.replace(tagRegExp, function(match, elm_name, attrs, end) {\r
2059                                                 return '<' + elm_name + attrs.replace(attrRegExp, function(match, name, value, val2, val3) {\r
2060                                                         var mceValue;\r
2061 \r
2062                                                         name = name.toLowerCase();\r
2063                                                         value = value || val2 || val3 || "";\r
2064 \r
2065                                                         // Treat boolean attributes\r
2066                                                         if (boolAttrs[name]) {\r
2067                                                                 // false or 0 is treated as a missing attribute\r
2068                                                                 if (value === 'false' || value === '0')\r
2069                                                                         return;\r
2070 \r
2071                                                                 return name + '="' + name + '"';\r
2072                                                         }\r
2073 \r
2074                                                         // Is attribute one that needs special treatment\r
2075                                                         if (mceAttribs[name] && attrs.indexOf('_mce_' + name) == -1) {\r
2076                                                                 mceValue = t.decode(value);\r
2077 \r
2078                                                                 // Convert URLs to relative/absolute ones\r
2079                                                                 if (s.url_converter && (name == "src" || name == "href"))\r
2080                                                                         mceValue = s.url_converter.call(s.url_converter_scope || t, mceValue, name, elm_name);\r
2081 \r
2082                                                                 // Process styles lowercases them and compresses them\r
2083                                                                 if (name == 'style')\r
2084                                                                         mceValue = t.serializeStyle(t.parseStyle(mceValue), name);\r
2085 \r
2086                                                                 return name + '="' + value + '"' + ' _mce_' + name + '="' + t.encode(mceValue) + '"';\r
2087                                                         }\r
2088 \r
2089                                                         return match;\r
2090                                                 }) + end + '>';\r
2091                                         });\r
2092                                 };\r
2093 \r
2094                                 h = processTags(h);\r
2095 \r
2096                                 // Restore script blocks\r
2097                                 h = h.replace(/MCE_SCRIPT:([0-9]+)/g, function(val, idx) {\r
2098                                         return codeBlocks[idx];\r
2099                                 });\r
2100                         }\r
2101 \r
2102                         return h;\r
2103                 },\r
2104 \r
2105                 getOuterHTML : function(e) {\r
2106                         var d;\r
2107 \r
2108                         e = this.get(e);\r
2109 \r
2110                         if (!e)\r
2111                                 return null;\r
2112 \r
2113                         if (e.outerHTML !== undefined)\r
2114                                 return e.outerHTML;\r
2115 \r
2116                         d = (e.ownerDocument || this.doc).createElement("body");\r
2117                         d.appendChild(e.cloneNode(true));\r
2118 \r
2119                         return d.innerHTML;\r
2120                 },\r
2121 \r
2122                 setOuterHTML : function(e, h, d) {\r
2123                         var t = this;\r
2124 \r
2125                         function setHTML(e, h, d) {\r
2126                                 var n, tp;\r
2127 \r
2128                                 tp = d.createElement("body");\r
2129                                 tp.innerHTML = h;\r
2130 \r
2131                                 n = tp.lastChild;\r
2132                                 while (n) {\r
2133                                         t.insertAfter(n.cloneNode(true), e);\r
2134                                         n = n.previousSibling;\r
2135                                 }\r
2136 \r
2137                                 t.remove(e);\r
2138                         };\r
2139 \r
2140                         return this.run(e, function(e) {\r
2141                                 e = t.get(e);\r
2142 \r
2143                                 // Only set HTML on elements\r
2144                                 if (e.nodeType == 1) {\r
2145                                         d = d || e.ownerDocument || t.doc;\r
2146 \r
2147                                         if (isIE) {\r
2148                                                 try {\r
2149                                                         // Try outerHTML for IE it sometimes produces an unknown runtime error\r
2150                                                         if (isIE && e.nodeType == 1)\r
2151                                                                 e.outerHTML = h;\r
2152                                                         else\r
2153                                                                 setHTML(e, h, d);\r
2154                                                 } catch (ex) {\r
2155                                                         // Fix for unknown runtime error\r
2156                                                         setHTML(e, h, d);\r
2157                                                 }\r
2158                                         } else\r
2159                                                 setHTML(e, h, d);\r
2160                                 }\r
2161                         });\r
2162                 },\r
2163 \r
2164                 decode : function(s) {\r
2165                         var e, n, v;\r
2166 \r
2167                         // Look for entities to decode\r
2168                         if (/&[\w#]+;/.test(s)) {\r
2169                                 // Decode the entities using a div element not super efficient but less code\r
2170                                 e = this.doc.createElement("div");\r
2171                                 e.innerHTML = s;\r
2172                                 n = e.firstChild;\r
2173                                 v = '';\r
2174 \r
2175                                 if (n) {\r
2176                                         do {\r
2177                                                 v += n.nodeValue;\r
2178                                         } while (n = n.nextSibling);\r
2179                                 }\r
2180 \r
2181                                 return v || s;\r
2182                         }\r
2183 \r
2184                         return s;\r
2185                 },\r
2186 \r
2187                 encode : function(str) {\r
2188                         return ('' + str).replace(encodeCharsRe, function(chr) {\r
2189                                 return encodedChars[chr];\r
2190                         });\r
2191                 },\r
2192 \r
2193                 insertAfter : function(node, reference_node) {\r
2194                         reference_node = this.get(reference_node);\r
2195 \r
2196                         return this.run(node, function(node) {\r
2197                                 var parent, nextSibling;\r
2198 \r
2199                                 parent = reference_node.parentNode;\r
2200                                 nextSibling = reference_node.nextSibling;\r
2201 \r
2202                                 if (nextSibling)\r
2203                                         parent.insertBefore(node, nextSibling);\r
2204                                 else\r
2205                                         parent.appendChild(node);\r
2206 \r
2207                                 return node;\r
2208                         });\r
2209                 },\r
2210 \r
2211                 isBlock : function(n) {\r
2212                         if (n.nodeType && n.nodeType !== 1)\r
2213                                 return false;\r
2214 \r
2215                         n = n.nodeName || n;\r
2216 \r
2217                         return blockRe.test(n);\r
2218                 },\r
2219 \r
2220                 replace : function(n, o, k) {\r
2221                         var t = this;\r
2222 \r
2223                         if (is(o, 'array'))\r
2224                                 n = n.cloneNode(true);\r
2225 \r
2226                         return t.run(o, function(o) {\r
2227                                 if (k) {\r
2228                                         each(tinymce.grep(o.childNodes), function(c) {\r
2229                                                 n.appendChild(c);\r
2230                                         });\r
2231                                 }\r
2232 \r
2233                                 return o.parentNode.replaceChild(n, o);\r
2234                         });\r
2235                 },\r
2236 \r
2237                 rename : function(elm, name) {\r
2238                         var t = this, newElm;\r
2239 \r
2240                         if (elm.nodeName != name.toUpperCase()) {\r
2241                                 // Rename block element\r
2242                                 newElm = t.create(name);\r
2243 \r
2244                                 // Copy attribs to new block\r
2245                                 each(t.getAttribs(elm), function(attr_node) {\r
2246                                         t.setAttrib(newElm, attr_node.nodeName, t.getAttrib(elm, attr_node.nodeName));\r
2247                                 });\r
2248 \r
2249                                 // Replace block\r
2250                                 t.replace(newElm, elm, 1);\r
2251                         }\r
2252 \r
2253                         return newElm || elm;\r
2254                 },\r
2255 \r
2256                 findCommonAncestor : function(a, b) {\r
2257                         var ps = a, pe;\r
2258 \r
2259                         while (ps) {\r
2260                                 pe = b;\r
2261 \r
2262                                 while (pe && ps != pe)\r
2263                                         pe = pe.parentNode;\r
2264 \r
2265                                 if (ps == pe)\r
2266                                         break;\r
2267 \r
2268                                 ps = ps.parentNode;\r
2269                         }\r
2270 \r
2271                         if (!ps && a.ownerDocument)\r
2272                                 return a.ownerDocument.documentElement;\r
2273 \r
2274                         return ps;\r
2275                 },\r
2276 \r
2277                 toHex : function(s) {\r
2278                         var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);\r
2279 \r
2280                         function hex(s) {\r
2281                                 s = parseInt(s).toString(16);\r
2282 \r
2283                                 return s.length > 1 ? s : '0' + s; // 0 -> 00\r
2284                         };\r
2285 \r
2286                         if (c) {\r
2287                                 s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);\r
2288 \r
2289                                 return s;\r
2290                         }\r
2291 \r
2292                         return s;\r
2293                 },\r
2294 \r
2295                 getClasses : function() {\r
2296                         var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;\r
2297 \r
2298                         if (t.classes)\r
2299                                 return t.classes;\r
2300 \r
2301                         function addClasses(s) {\r
2302                                 // IE style imports\r
2303                                 each(s.imports, function(r) {\r
2304                                         addClasses(r);\r
2305                                 });\r
2306 \r
2307                                 each(s.cssRules || s.rules, function(r) {\r
2308                                         // Real type or fake it on IE\r
2309                                         switch (r.type || 1) {\r
2310                                                 // Rule\r
2311                                                 case 1:\r
2312                                                         if (r.selectorText) {\r
2313                                                                 each(r.selectorText.split(','), function(v) {\r
2314                                                                         v = v.replace(/^\s*|\s*$|^\s\./g, "");\r
2315 \r
2316                                                                         // Is internal or it doesn't contain a class\r
2317                                                                         if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v))\r
2318                                                                                 return;\r
2319 \r
2320                                                                         // Remove everything but class name\r
2321                                                                         ov = v;\r
2322                                                                         v = tinymce._replace(/.*\.([a-z0-9_\-]+).*/i, '$1', v);\r
2323 \r
2324                                                                         // Filter classes\r
2325                                                                         if (f && !(v = f(v, ov)))\r
2326                                                                                 return;\r
2327 \r
2328                                                                         if (!lo[v]) {\r
2329                                                                                 cl.push({'class' : v});\r
2330                                                                                 lo[v] = 1;\r
2331                                                                         }\r
2332                                                                 });\r
2333                                                         }\r
2334                                                         break;\r
2335 \r
2336                                                 // Import\r
2337                                                 case 3:\r
2338                                                         addClasses(r.styleSheet);\r
2339                                                         break;\r
2340                                         }\r
2341                                 });\r
2342                         };\r
2343 \r
2344                         try {\r
2345                                 each(t.doc.styleSheets, addClasses);\r
2346                         } catch (ex) {\r
2347                                 // Ignore\r
2348                         }\r
2349 \r
2350                         if (cl.length > 0)\r
2351                                 t.classes = cl;\r
2352 \r
2353                         return cl;\r
2354                 },\r
2355 \r
2356                 run : function(e, f, s) {\r
2357                         var t = this, o;\r
2358 \r
2359                         if (t.doc && typeof(e) === 'string')\r
2360                                 e = t.get(e);\r
2361 \r
2362                         if (!e)\r
2363                                 return false;\r
2364 \r
2365                         s = s || this;\r
2366                         if (!e.nodeType && (e.length || e.length === 0)) {\r
2367                                 o = [];\r
2368 \r
2369                                 each(e, function(e, i) {\r
2370                                         if (e) {\r
2371                                                 if (typeof(e) == 'string')\r
2372                                                         e = t.doc.getElementById(e);\r
2373 \r
2374                                                 o.push(f.call(s, e, i));\r
2375                                         }\r
2376                                 });\r
2377 \r
2378                                 return o;\r
2379                         }\r
2380 \r
2381                         return f.call(s, e);\r
2382                 },\r
2383 \r
2384                 getAttribs : function(n) {\r
2385                         var o;\r
2386 \r
2387                         n = this.get(n);\r
2388 \r
2389                         if (!n)\r
2390                                 return [];\r
2391 \r
2392                         if (isIE) {\r
2393                                 o = [];\r
2394 \r
2395                                 // Object will throw exception in IE\r
2396                                 if (n.nodeName == 'OBJECT')\r
2397                                         return n.attributes;\r
2398 \r
2399                                 // IE doesn't keep the selected attribute if you clone option elements\r
2400                                 if (n.nodeName === 'OPTION' && this.getAttrib(n, 'selected'))\r
2401                                         o.push({specified : 1, nodeName : 'selected'});\r
2402 \r
2403                                 // It's crazy that this is faster in IE but it's because it returns all attributes all the time\r
2404                                 n.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi, '').replace(/[\w:\-]+/gi, function(a) {\r
2405                                         o.push({specified : 1, nodeName : a});\r
2406                                 });\r
2407 \r
2408                                 return o;\r
2409                         }\r
2410 \r
2411                         return n.attributes;\r
2412                 },\r
2413 \r
2414                 destroy : function(s) {\r
2415                         var t = this;\r
2416 \r
2417                         if (t.events)\r
2418                                 t.events.destroy();\r
2419 \r
2420                         t.win = t.doc = t.root = t.events = null;\r
2421 \r
2422                         // Manual destroy then remove unload handler\r
2423                         if (!s)\r
2424                                 tinymce.removeUnload(t.destroy);\r
2425                 },\r
2426 \r
2427                 createRng : function() {\r
2428                         var d = this.doc;\r
2429 \r
2430                         return d.createRange ? d.createRange() : new tinymce.dom.Range(this);\r
2431                 },\r
2432 \r
2433                 nodeIndex : function(node, normalized) {\r
2434                         var idx = 0, lastNodeType, lastNode, nodeType;\r
2435 \r
2436                         if (node) {\r
2437                                 for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {\r
2438                                         nodeType = node.nodeType;\r
2439 \r
2440                                         // Normalize text nodes\r
2441                                         if (normalized && nodeType == 3) {\r
2442                                                 if (nodeType == lastNodeType || !node.nodeValue.length)\r
2443                                                         continue;\r
2444                                         }\r
2445 \r
2446                                         idx++;\r
2447                                         lastNodeType = nodeType;\r
2448                                 }\r
2449                         }\r
2450 \r
2451                         return idx;\r
2452                 },\r
2453 \r
2454                 split : function(pe, e, re) {\r
2455                         var t = this, r = t.createRng(), bef, aft, pa;\r
2456 \r
2457                         // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sense\r
2458                         // but we don't want that in our code since it serves no purpose for the end user\r
2459                         // For example if this is chopped:\r
2460                         //   <p>text 1<span><b>CHOP</b></span>text 2</p>\r
2461                         // would produce:\r
2462                         //   <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>\r
2463                         // this function will then trim of empty edges and produce:\r
2464                         //   <p>text 1</p><b>CHOP</b><p>text 2</p>\r
2465                         function trim(node) {\r
2466                                 var i, children = node.childNodes;\r
2467 \r
2468                                 if (node.nodeType == 1 && node.getAttribute('_mce_type') == 'bookmark')\r
2469                                         return;\r
2470 \r
2471                                 for (i = children.length - 1; i >= 0; i--)\r
2472                                         trim(children[i]);\r
2473 \r
2474                                 if (node.nodeType != 9) {\r
2475                                         // Keep non whitespace text nodes\r
2476                                         if (node.nodeType == 3 && node.nodeValue.length > 0) {\r
2477                                                 // If parent element isn't a block or there isn't any useful contents for example "<p>   </p>"\r
2478                                                 if (!t.isBlock(node.parentNode) || tinymce.trim(node.nodeValue).length > 0)\r
2479                                                         return;\r
2480                                         }\r
2481 \r
2482                                         if (node.nodeType == 1) {\r
2483                                                 // If the only child is a bookmark then move it up\r
2484                                                 children = node.childNodes;\r
2485                                                 if (children.length == 1 && children[0] && children[0].nodeType == 1 && children[0].getAttribute('_mce_type') == 'bookmark')\r
2486                                                         node.parentNode.insertBefore(children[0], node);\r
2487 \r
2488                                                 // Keep non empty elements or img, hr etc\r
2489                                                 if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName))\r
2490                                                         return;\r
2491                                         }\r
2492 \r
2493                                         t.remove(node);\r
2494                                 }\r
2495 \r
2496                                 return node;\r
2497                         };\r
2498 \r
2499                         if (pe && e) {\r
2500                                 // Get before chunk\r
2501                                 r.setStart(pe.parentNode, t.nodeIndex(pe));\r
2502                                 r.setEnd(e.parentNode, t.nodeIndex(e));\r
2503                                 bef = r.extractContents();\r
2504 \r
2505                                 // Get after chunk\r
2506                                 r = t.createRng();\r
2507                                 r.setStart(e.parentNode, t.nodeIndex(e) + 1);\r
2508                                 r.setEnd(pe.parentNode, t.nodeIndex(pe) + 1);\r
2509                                 aft = r.extractContents();\r
2510 \r
2511                                 // Insert before chunk\r
2512                                 pa = pe.parentNode;\r
2513                                 pa.insertBefore(trim(bef), pe);\r
2514 \r
2515                                 // Insert middle chunk\r
2516                                 if (re)\r
2517                                         pa.replaceChild(re, e);\r
2518                                 else\r
2519                                         pa.insertBefore(e, pe);\r
2520 \r
2521                                 // Insert after chunk\r
2522                                 pa.insertBefore(trim(aft), pe);\r
2523                                 t.remove(pe);\r
2524 \r
2525                                 return re || e;\r
2526                         }\r
2527                 },\r
2528 \r
2529                 bind : function(target, name, func, scope) {\r
2530                         var t = this;\r
2531 \r
2532                         if (!t.events)\r
2533                                 t.events = new tinymce.dom.EventUtils();\r
2534 \r
2535                         return t.events.add(target, name, func, scope || this);\r
2536                 },\r
2537 \r
2538                 unbind : function(target, name, func) {\r
2539                         var t = this;\r
2540 \r
2541                         if (!t.events)\r
2542                                 t.events = new tinymce.dom.EventUtils();\r
2543 \r
2544                         return t.events.remove(target, name, func);\r
2545                 },\r
2546 \r
2547 \r
2548                 _findSib : function(node, selector, name) {\r
2549                         var t = this, f = selector;\r
2550 \r
2551                         if (node) {\r
2552                                 // If expression make a function of it using is\r
2553                                 if (is(f, 'string')) {\r
2554                                         f = function(node) {\r
2555                                                 return t.is(node, selector);\r
2556                                         };\r
2557                                 }\r
2558 \r
2559                                 // Loop all siblings\r
2560                                 for (node = node[name]; node; node = node[name]) {\r
2561                                         if (f(node))\r
2562                                                 return node;\r
2563                                 }\r
2564                         }\r
2565 \r
2566                         return null;\r
2567                 },\r
2568 \r
2569                 _isRes : function(c) {\r
2570                         // Is live resizble element\r
2571                         return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c);\r
2572                 }\r
2573 \r
2574                 /*\r
2575                 walk : function(n, f, s) {\r
2576                         var d = this.doc, w;\r
2577 \r
2578                         if (d.createTreeWalker) {\r
2579                                 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);\r
2580 \r
2581                                 while ((n = w.nextNode()) != null)\r
2582                                         f.call(s || this, n);\r
2583                         } else\r
2584                                 tinymce.walk(n, f, 'childNodes', s);\r
2585                 }\r
2586                 */\r
2587 \r
2588                 /*\r
2589                 toRGB : function(s) {\r
2590                         var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);\r
2591 \r
2592                         if (c) {\r
2593                                 // #FFF -> #FFFFFF\r
2594                                 if (!is(c[3]))\r
2595                                         c[3] = c[2] = c[1];\r
2596 \r
2597                                 return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";\r
2598                         }\r
2599 \r
2600                         return s;\r
2601                 }\r
2602                 */\r
2603         });\r
2604 \r
2605         tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});\r
2606 })(tinymce);\r
2607 \r
2608 (function(ns) {\r
2609         // Range constructor\r
2610         function Range(dom) {\r
2611                 var t = this,\r
2612                         doc = dom.doc,\r
2613                         EXTRACT = 0,\r
2614                         CLONE = 1,\r
2615                         DELETE = 2,\r
2616                         TRUE = true,\r
2617                         FALSE = false,\r
2618                         START_OFFSET = 'startOffset',\r
2619                         START_CONTAINER = 'startContainer',\r
2620                         END_CONTAINER = 'endContainer',\r
2621                         END_OFFSET = 'endOffset',\r
2622                         extend = tinymce.extend,\r
2623                         nodeIndex = dom.nodeIndex;\r
2624 \r
2625                 extend(t, {\r
2626                         // Inital states\r
2627                         startContainer : doc,\r
2628                         startOffset : 0,\r
2629                         endContainer : doc,\r
2630                         endOffset : 0,\r
2631                         collapsed : TRUE,\r
2632                         commonAncestorContainer : doc,\r
2633 \r
2634                         // Range constants\r
2635                         START_TO_START : 0,\r
2636                         START_TO_END : 1,\r
2637                         END_TO_END : 2,\r
2638                         END_TO_START : 3,\r
2639 \r
2640                         // Public methods\r
2641                         setStart : setStart,\r
2642                         setEnd : setEnd,\r
2643                         setStartBefore : setStartBefore,\r
2644                         setStartAfter : setStartAfter,\r
2645                         setEndBefore : setEndBefore,\r
2646                         setEndAfter : setEndAfter,\r
2647                         collapse : collapse,\r
2648                         selectNode : selectNode,\r
2649                         selectNodeContents : selectNodeContents,\r
2650                         compareBoundaryPoints : compareBoundaryPoints,\r
2651                         deleteContents : deleteContents,\r
2652                         extractContents : extractContents,\r
2653                         cloneContents : cloneContents,\r
2654                         insertNode : insertNode,\r
2655                         surroundContents : surroundContents,\r
2656                         cloneRange : cloneRange\r
2657                 });\r
2658 \r
2659                 function setStart(n, o) {\r
2660                         _setEndPoint(TRUE, n, o);\r
2661                 };\r
2662 \r
2663                 function setEnd(n, o) {\r
2664                         _setEndPoint(FALSE, n, o);\r
2665                 };\r
2666 \r
2667                 function setStartBefore(n) {\r
2668                         setStart(n.parentNode, nodeIndex(n));\r
2669                 };\r
2670 \r
2671                 function setStartAfter(n) {\r
2672                         setStart(n.parentNode, nodeIndex(n) + 1);\r
2673                 };\r
2674 \r
2675                 function setEndBefore(n) {\r
2676                         setEnd(n.parentNode, nodeIndex(n));\r
2677                 };\r
2678 \r
2679                 function setEndAfter(n) {\r
2680                         setEnd(n.parentNode, nodeIndex(n) + 1);\r
2681                 };\r
2682 \r
2683                 function collapse(ts) {\r
2684                         if (ts) {\r
2685                                 t[END_CONTAINER] = t[START_CONTAINER];\r
2686                                 t[END_OFFSET] = t[START_OFFSET];\r
2687                         } else {\r
2688                                 t[START_CONTAINER] = t[END_CONTAINER];\r
2689                                 t[START_OFFSET] = t[END_OFFSET];\r
2690                         }\r
2691 \r
2692                         t.collapsed = TRUE;\r
2693                 };\r
2694 \r
2695                 function selectNode(n) {\r
2696                         setStartBefore(n);\r
2697                         setEndAfter(n);\r
2698                 };\r
2699 \r
2700                 function selectNodeContents(n) {\r
2701                         setStart(n, 0);\r
2702                         setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);\r
2703                 };\r
2704 \r
2705                 function compareBoundaryPoints(h, r) {\r
2706                         var sc = t[START_CONTAINER], so = t[START_OFFSET], ec = t[END_CONTAINER], eo = t[END_OFFSET];\r
2707 \r
2708                         // Check START_TO_START\r
2709                         if (h === 0)\r
2710                                 return _compareBoundaryPoints(sc, so, sc, so);\r
2711 \r
2712                         // Check START_TO_END\r
2713                         if (h === 1)\r
2714                                 return _compareBoundaryPoints(sc, so, ec, eo);\r
2715 \r
2716                         // Check END_TO_END\r
2717                         if (h === 2)\r
2718                                 return _compareBoundaryPoints(ec, eo, ec, eo);\r
2719 \r
2720                         // Check END_TO_START\r
2721                         if (h === 3)\r
2722                                 return _compareBoundaryPoints(ec, eo, sc, so);\r
2723                 };\r
2724 \r
2725                 function deleteContents() {\r
2726                         _traverse(DELETE);\r
2727                 };\r
2728 \r
2729                 function extractContents() {\r
2730                         return _traverse(EXTRACT);\r
2731                 };\r
2732 \r
2733                 function cloneContents() {\r
2734                         return _traverse(CLONE);\r
2735                 };\r
2736 \r
2737                 function insertNode(n) {\r
2738                         var startContainer = this[START_CONTAINER],\r
2739                                 startOffset = this[START_OFFSET], nn, o;\r
2740 \r
2741                         // Node is TEXT_NODE or CDATA\r
2742                         if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {\r
2743                                 if (!startOffset) {\r
2744                                         // At the start of text\r
2745                                         startContainer.parentNode.insertBefore(n, startContainer);\r
2746                                 } else if (startOffset >= startContainer.nodeValue.length) {\r
2747                                         // At the end of text\r
2748                                         dom.insertAfter(n, startContainer);\r
2749                                 } else {\r
2750                                         // Middle, need to split\r
2751                                         nn = startContainer.splitText(startOffset);\r
2752                                         startContainer.parentNode.insertBefore(n, nn);\r
2753                                 }\r
2754                         } else {\r
2755                                 // Insert element node\r
2756                                 if (startContainer.childNodes.length > 0)\r
2757                                         o = startContainer.childNodes[startOffset];\r
2758 \r
2759                                 if (o)\r
2760                                         startContainer.insertBefore(n, o);\r
2761                                 else\r
2762                                         startContainer.appendChild(n);\r
2763                         }\r
2764                 };\r
2765 \r
2766                 function surroundContents(n) {\r
2767                         var f = t.extractContents();\r
2768 \r
2769                         t.insertNode(n);\r
2770                         n.appendChild(f);\r
2771                         t.selectNode(n);\r
2772                 };\r
2773 \r
2774                 function cloneRange() {\r
2775                         return extend(new Range(dom), {\r
2776                                 startContainer : t[START_CONTAINER],\r
2777                                 startOffset : t[START_OFFSET],\r
2778                                 endContainer : t[END_CONTAINER],\r
2779                                 endOffset : t[END_OFFSET],\r
2780                                 collapsed : t.collapsed,\r
2781                                 commonAncestorContainer : t.commonAncestorContainer\r
2782                         });\r
2783                 };\r
2784 \r
2785                 // Private methods\r
2786 \r
2787                 function _getSelectedNode(container, offset) {\r
2788                         var child;\r
2789 \r
2790                         if (container.nodeType == 3 /* TEXT_NODE */)\r
2791                                 return container;\r
2792 \r
2793                         if (offset < 0)\r
2794                                 return container;\r
2795 \r
2796                         child = container.firstChild;\r
2797                         while (child && offset > 0) {\r
2798                                 --offset;\r
2799                                 child = child.nextSibling;\r
2800                         }\r
2801 \r
2802                         if (child)\r
2803                                 return child;\r
2804 \r
2805                         return container;\r
2806                 };\r
2807 \r
2808                 function _isCollapsed() {\r
2809                         return (t[START_CONTAINER] == t[END_CONTAINER] && t[START_OFFSET] == t[END_OFFSET]);\r
2810                 };\r
2811 \r
2812                 function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {\r
2813                         var c, offsetC, n, cmnRoot, childA, childB;\r
2814 \r
2815                         // In the first case the boundary-points have the same container. A is before B\r
2816                         // if its offset is less than the offset of B, A is equal to B if its offset is\r
2817                         // equal to the offset of B, and A is after B if its offset is greater than the\r
2818                         // offset of B.\r
2819                         if (containerA == containerB) {\r
2820                                 if (offsetA == offsetB)\r
2821                                         return 0; // equal\r
2822 \r
2823                                 if (offsetA < offsetB)\r
2824                                         return -1; // before\r
2825 \r
2826                                 return 1; // after\r
2827                         }\r
2828 \r
2829                         // In the second case a child node C of the container of A is an ancestor\r
2830                         // container of B. In this case, A is before B if the offset of A is less than or\r
2831                         // equal to the index of the child node C and A is after B otherwise.\r
2832                         c = containerB;\r
2833                         while (c && c.parentNode != containerA)\r
2834                                 c = c.parentNode;\r
2835 \r
2836                         if (c) {\r
2837                                 offsetC = 0;\r
2838                                 n = containerA.firstChild;\r
2839 \r
2840                                 while (n != c && offsetC < offsetA) {\r
2841                                         offsetC++;\r
2842                                         n = n.nextSibling;\r
2843                                 }\r
2844 \r
2845                                 if (offsetA <= offsetC)\r
2846                                         return -1; // before\r
2847 \r
2848                                 return 1; // after\r
2849                         }\r
2850 \r
2851                         // In the third case a child node C of the container of B is an ancestor container\r
2852                         // of A. In this case, A is before B if the index of the child node C is less than\r
2853                         // the offset of B and A is after B otherwise.\r
2854                         c = containerA;\r
2855                         while (c && c.parentNode != containerB) {\r
2856                                 c = c.parentNode;\r
2857                         }\r
2858 \r
2859                         if (c) {\r
2860                                 offsetC = 0;\r
2861                                 n = containerB.firstChild;\r
2862 \r
2863                                 while (n != c && offsetC < offsetB) {\r
2864                                         offsetC++;\r
2865                                         n = n.nextSibling;\r
2866                                 }\r
2867 \r
2868                                 if (offsetC < offsetB)\r
2869                                         return -1; // before\r
2870 \r
2871                                 return 1; // after\r
2872                         }\r
2873 \r
2874                         // In the fourth case, none of three other cases hold: the containers of A and B\r
2875                         // are siblings or descendants of sibling nodes. In this case, A is before B if\r
2876                         // the container of A is before the container of B in a pre-order traversal of the\r
2877                         // Ranges' context tree and A is after B otherwise.\r
2878                         cmnRoot = dom.findCommonAncestor(containerA, containerB);\r
2879                         childA = containerA;\r
2880 \r
2881                         while (childA && childA.parentNode != cmnRoot)\r
2882                                 childA = childA.parentNode;\r
2883 \r
2884                         if (!childA)\r
2885                                 childA = cmnRoot;\r
2886 \r
2887                         childB = containerB;\r
2888                         while (childB && childB.parentNode != cmnRoot)\r
2889                                 childB = childB.parentNode;\r
2890 \r
2891                         if (!childB)\r
2892                                 childB = cmnRoot;\r
2893 \r
2894                         if (childA == childB)\r
2895                                 return 0; // equal\r
2896 \r
2897                         n = cmnRoot.firstChild;\r
2898                         while (n) {\r
2899                                 if (n == childA)\r
2900                                         return -1; // before\r
2901 \r
2902                                 if (n == childB)\r
2903                                         return 1; // after\r
2904 \r
2905                                 n = n.nextSibling;\r
2906                         }\r
2907                 };\r
2908 \r
2909                 function _setEndPoint(st, n, o) {\r
2910                         var ec, sc;\r
2911 \r
2912                         if (st) {\r
2913                                 t[START_CONTAINER] = n;\r
2914                                 t[START_OFFSET] = o;\r
2915                         } else {\r
2916                                 t[END_CONTAINER] = n;\r
2917                                 t[END_OFFSET] = o;\r
2918                         }\r
2919 \r
2920                         // If one boundary-point of a Range is set to have a root container\r
2921                         // other than the current one for the Range, the Range is collapsed to\r
2922                         // the new position. This enforces the restriction that both boundary-\r
2923                         // points of a Range must have the same root container.\r
2924                         ec = t[END_CONTAINER];\r
2925                         while (ec.parentNode)\r
2926                                 ec = ec.parentNode;\r
2927 \r
2928                         sc = t[START_CONTAINER];\r
2929                         while (sc.parentNode)\r
2930                                 sc = sc.parentNode;\r
2931 \r
2932                         if (sc == ec) {\r
2933                                 // The start position of a Range is guaranteed to never be after the\r
2934                                 // end position. To enforce this restriction, if the start is set to\r
2935                                 // be at a position after the end, the Range is collapsed to that\r
2936                                 // position.\r
2937                                 if (_compareBoundaryPoints(t[START_CONTAINER], t[START_OFFSET], t[END_CONTAINER], t[END_OFFSET]) > 0)\r
2938                                         t.collapse(st);\r
2939                         } else\r
2940                                 t.collapse(st);\r
2941 \r
2942                         t.collapsed = _isCollapsed();\r
2943                         t.commonAncestorContainer = dom.findCommonAncestor(t[START_CONTAINER], t[END_CONTAINER]);\r
2944                 };\r
2945 \r
2946                 function _traverse(how) {\r
2947                         var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;\r
2948 \r
2949                         if (t[START_CONTAINER] == t[END_CONTAINER])\r
2950                                 return _traverseSameContainer(how);\r
2951 \r
2952                         for (c = t[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
2953                                 if (p == t[START_CONTAINER])\r
2954                                         return _traverseCommonStartContainer(c, how);\r
2955 \r
2956                                 ++endContainerDepth;\r
2957                         }\r
2958 \r
2959                         for (c = t[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
2960                                 if (p == t[END_CONTAINER])\r
2961                                         return _traverseCommonEndContainer(c, how);\r
2962 \r
2963                                 ++startContainerDepth;\r
2964                         }\r
2965 \r
2966                         depthDiff = startContainerDepth - endContainerDepth;\r
2967 \r
2968                         startNode = t[START_CONTAINER];\r
2969                         while (depthDiff > 0) {\r
2970                                 startNode = startNode.parentNode;\r
2971                                 depthDiff--;\r
2972                         }\r
2973 \r
2974                         endNode = t[END_CONTAINER];\r
2975                         while (depthDiff < 0) {\r
2976                                 endNode = endNode.parentNode;\r
2977                                 depthDiff++;\r
2978                         }\r
2979 \r
2980                         // ascend the ancestor hierarchy until we have a common parent.\r
2981                         for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {\r
2982                                 startNode = sp;\r
2983                                 endNode = ep;\r
2984                         }\r
2985 \r
2986                         return _traverseCommonAncestors(startNode, endNode, how);\r
2987                 };\r
2988 \r
2989                  function _traverseSameContainer(how) {\r
2990                         var frag, s, sub, n, cnt, sibling, xferNode;\r
2991 \r
2992                         if (how != DELETE)\r
2993                                 frag = doc.createDocumentFragment();\r
2994 \r
2995                         // If selection is empty, just return the fragment\r
2996                         if (t[START_OFFSET] == t[END_OFFSET])\r
2997                                 return frag;\r
2998 \r
2999                         // Text node needs special case handling\r
3000                         if (t[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {\r
3001                                 // get the substring\r
3002                                 s = t[START_CONTAINER].nodeValue;\r
3003                                 sub = s.substring(t[START_OFFSET], t[END_OFFSET]);\r
3004 \r
3005                                 // set the original text node to its new value\r
3006                                 if (how != CLONE) {\r
3007                                         t[START_CONTAINER].deleteData(t[START_OFFSET], t[END_OFFSET] - t[START_OFFSET]);\r
3008 \r
3009                                         // Nothing is partially selected, so collapse to start point\r
3010                                         t.collapse(TRUE);\r
3011                                 }\r
3012 \r
3013                                 if (how == DELETE)\r
3014                                         return;\r
3015 \r
3016                                 frag.appendChild(doc.createTextNode(sub));\r
3017                                 return frag;\r
3018                         }\r
3019 \r
3020                         // Copy nodes between the start/end offsets.\r
3021                         n = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]);\r
3022                         cnt = t[END_OFFSET] - t[START_OFFSET];\r
3023 \r
3024                         while (cnt > 0) {\r
3025                                 sibling = n.nextSibling;\r
3026                                 xferNode = _traverseFullySelected(n, how);\r
3027 \r
3028                                 if (frag)\r
3029                                         frag.appendChild( xferNode );\r
3030 \r
3031                                 --cnt;\r
3032                                 n = sibling;\r
3033                         }\r
3034 \r
3035                         // Nothing is partially selected, so collapse to start point\r
3036                         if (how != CLONE)\r
3037                                 t.collapse(TRUE);\r
3038 \r
3039                         return frag;\r
3040                 };\r
3041 \r
3042                 function _traverseCommonStartContainer(endAncestor, how) {\r
3043                         var frag, n, endIdx, cnt, sibling, xferNode;\r
3044 \r
3045                         if (how != DELETE)\r
3046                                 frag = doc.createDocumentFragment();\r
3047 \r
3048                         n = _traverseRightBoundary(endAncestor, how);\r
3049 \r
3050                         if (frag)\r
3051                                 frag.appendChild(n);\r
3052 \r
3053                         endIdx = nodeIndex(endAncestor);\r
3054                         cnt = endIdx - t[START_OFFSET];\r
3055 \r
3056                         if (cnt <= 0) {\r
3057                                 // Collapse to just before the endAncestor, which\r
3058                                 // is partially selected.\r
3059                                 if (how != CLONE) {\r
3060                                         t.setEndBefore(endAncestor);\r
3061                                         t.collapse(FALSE);\r
3062                                 }\r
3063 \r
3064                                 return frag;\r
3065                         }\r
3066 \r
3067                         n = endAncestor.previousSibling;\r
3068                         while (cnt > 0) {\r
3069                                 sibling = n.previousSibling;\r
3070                                 xferNode = _traverseFullySelected(n, how);\r
3071 \r
3072                                 if (frag)\r
3073                                         frag.insertBefore(xferNode, frag.firstChild);\r
3074 \r
3075                                 --cnt;\r
3076                                 n = sibling;\r
3077                         }\r
3078 \r
3079                         // Collapse to just before the endAncestor, which\r
3080                         // is partially selected.\r
3081                         if (how != CLONE) {\r
3082                                 t.setEndBefore(endAncestor);\r
3083                                 t.collapse(FALSE);\r
3084                         }\r
3085 \r
3086                         return frag;\r
3087                 };\r
3088 \r
3089                 function _traverseCommonEndContainer(startAncestor, how) {\r
3090                         var frag, startIdx, n, cnt, sibling, xferNode;\r
3091 \r
3092                         if (how != DELETE)\r
3093                                 frag = doc.createDocumentFragment();\r
3094 \r
3095                         n = _traverseLeftBoundary(startAncestor, how);\r
3096                         if (frag)\r
3097                                 frag.appendChild(n);\r
3098 \r
3099                         startIdx = nodeIndex(startAncestor);\r
3100                         ++startIdx;  // Because we already traversed it....\r
3101 \r
3102                         cnt = t[END_OFFSET] - startIdx;\r
3103                         n = startAncestor.nextSibling;\r
3104                         while (cnt > 0) {\r
3105                                 sibling = n.nextSibling;\r
3106                                 xferNode = _traverseFullySelected(n, how);\r
3107 \r
3108                                 if (frag)\r
3109                                         frag.appendChild(xferNode);\r
3110 \r
3111                                 --cnt;\r
3112                                 n = sibling;\r
3113                         }\r
3114 \r
3115                         if (how != CLONE) {\r
3116                                 t.setStartAfter(startAncestor);\r
3117                                 t.collapse(TRUE);\r
3118                         }\r
3119 \r
3120                         return frag;\r
3121                 };\r
3122 \r
3123                 function _traverseCommonAncestors(startAncestor, endAncestor, how) {\r
3124                         var n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;\r
3125 \r
3126                         if (how != DELETE)\r
3127                                 frag = doc.createDocumentFragment();\r
3128 \r
3129                         n = _traverseLeftBoundary(startAncestor, how);\r
3130                         if (frag)\r
3131                                 frag.appendChild(n);\r
3132 \r
3133                         commonParent = startAncestor.parentNode;\r
3134                         startOffset = nodeIndex(startAncestor);\r
3135                         endOffset = nodeIndex(endAncestor);\r
3136                         ++startOffset;\r
3137 \r
3138                         cnt = endOffset - startOffset;\r
3139                         sibling = startAncestor.nextSibling;\r
3140 \r
3141                         while (cnt > 0) {\r
3142                                 nextSibling = sibling.nextSibling;\r
3143                                 n = _traverseFullySelected(sibling, how);\r
3144 \r
3145                                 if (frag)\r
3146                                         frag.appendChild(n);\r
3147 \r
3148                                 sibling = nextSibling;\r
3149                                 --cnt;\r
3150                         }\r
3151 \r
3152                         n = _traverseRightBoundary(endAncestor, how);\r
3153 \r
3154                         if (frag)\r
3155                                 frag.appendChild(n);\r
3156 \r
3157                         if (how != CLONE) {\r
3158                                 t.setStartAfter(startAncestor);\r
3159                                 t.collapse(TRUE);\r
3160                         }\r
3161 \r
3162                         return frag;\r
3163                 };\r
3164 \r
3165                 function _traverseRightBoundary(root, how) {\r
3166                         var next = _getSelectedNode(t[END_CONTAINER], t[END_OFFSET] - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != t[END_CONTAINER];\r
3167 \r
3168                         if (next == root)\r
3169                                 return _traverseNode(next, isFullySelected, FALSE, how);\r
3170 \r
3171                         parent = next.parentNode;\r
3172                         clonedParent = _traverseNode(parent, FALSE, FALSE, how);\r
3173 \r
3174                         while (parent) {\r
3175                                 while (next) {\r
3176                                         prevSibling = next.previousSibling;\r
3177                                         clonedChild = _traverseNode(next, isFullySelected, FALSE, how);\r
3178 \r
3179                                         if (how != DELETE)\r
3180                                                 clonedParent.insertBefore(clonedChild, clonedParent.firstChild);\r
3181 \r
3182                                         isFullySelected = TRUE;\r
3183                                         next = prevSibling;\r
3184                                 }\r
3185 \r
3186                                 if (parent == root)\r
3187                                         return clonedParent;\r
3188 \r
3189                                 next = parent.previousSibling;\r
3190                                 parent = parent.parentNode;\r
3191 \r
3192                                 clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);\r
3193 \r
3194                                 if (how != DELETE)\r
3195                                         clonedGrandParent.appendChild(clonedParent);\r
3196 \r
3197                                 clonedParent = clonedGrandParent;\r
3198                         }\r
3199                 };\r
3200 \r
3201                 function _traverseLeftBoundary(root, how) {\r
3202                         var next = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]), isFullySelected = next != t[START_CONTAINER], parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;\r
3203 \r
3204                         if (next == root)\r
3205                                 return _traverseNode(next, isFullySelected, TRUE, how);\r
3206 \r
3207                         parent = next.parentNode;\r
3208                         clonedParent = _traverseNode(parent, FALSE, TRUE, how);\r
3209 \r
3210                         while (parent) {\r
3211                                 while (next) {\r
3212                                         nextSibling = next.nextSibling;\r
3213                                         clonedChild = _traverseNode(next, isFullySelected, TRUE, how);\r
3214 \r
3215                                         if (how != DELETE)\r
3216                                                 clonedParent.appendChild(clonedChild);\r
3217 \r
3218                                         isFullySelected = TRUE;\r
3219                                         next = nextSibling;\r
3220                                 }\r
3221 \r
3222                                 if (parent == root)\r
3223                                         return clonedParent;\r
3224 \r
3225                                 next = parent.nextSibling;\r
3226                                 parent = parent.parentNode;\r
3227 \r
3228                                 clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);\r
3229 \r
3230                                 if (how != DELETE)\r
3231                                         clonedGrandParent.appendChild(clonedParent);\r
3232 \r
3233                                 clonedParent = clonedGrandParent;\r
3234                         }\r
3235                 };\r
3236 \r
3237                 function _traverseNode(n, isFullySelected, isLeft, how) {\r
3238                         var txtValue, newNodeValue, oldNodeValue, offset, newNode;\r
3239 \r
3240                         if (isFullySelected)\r
3241                                 return _traverseFullySelected(n, how);\r
3242 \r
3243                         if (n.nodeType == 3 /* TEXT_NODE */) {\r
3244                                 txtValue = n.nodeValue;\r
3245 \r
3246                                 if (isLeft) {\r
3247                                         offset = t[START_OFFSET];\r
3248                                         newNodeValue = txtValue.substring(offset);\r
3249                                         oldNodeValue = txtValue.substring(0, offset);\r
3250                                 } else {\r
3251                                         offset = t[END_OFFSET];\r
3252                                         newNodeValue = txtValue.substring(0, offset);\r
3253                                         oldNodeValue = txtValue.substring(offset);\r
3254                                 }\r
3255 \r
3256                                 if (how != CLONE)\r
3257                                         n.nodeValue = oldNodeValue;\r
3258 \r
3259                                 if (how == DELETE)\r
3260                                         return;\r
3261 \r
3262                                 newNode = n.cloneNode(FALSE);\r
3263                                 newNode.nodeValue = newNodeValue;\r
3264 \r
3265                                 return newNode;\r
3266                         }\r
3267 \r
3268                         if (how == DELETE)\r
3269                                 return;\r
3270 \r
3271                         return n.cloneNode(FALSE);\r
3272                 };\r
3273 \r
3274                 function _traverseFullySelected(n, how) {\r
3275                         if (how != DELETE)\r
3276                                 return how == CLONE ? n.cloneNode(TRUE) : n;\r
3277 \r
3278                         n.parentNode.removeChild(n);\r
3279                 };\r
3280         };\r
3281 \r
3282         ns.Range = Range;\r
3283 })(tinymce.dom);\r
3284 \r
3285 (function() {\r
3286         function Selection(selection) {\r
3287                 var t = this, invisibleChar = '\uFEFF', range, lastIERng, dom = selection.dom, TRUE = true, FALSE = false;\r
3288 \r
3289                 // Returns a W3C DOM compatible range object by using the IE Range API\r
3290                 function getRange() {\r
3291                         var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed;\r
3292 \r
3293                         // If selection is outside the current document just return an empty range\r
3294                         element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();\r
3295                         if (element.ownerDocument != dom.doc)\r
3296                                 return domRange;\r
3297 \r
3298                         // Handle control selection or text selection of a image\r
3299                         if (ieRange.item || !element.hasChildNodes()) {\r
3300                                 domRange.setStart(element.parentNode, dom.nodeIndex(element));\r
3301                                 domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);\r
3302 \r
3303                                 return domRange;\r
3304                         }\r
3305 \r
3306                         collapsed = selection.isCollapsed();\r
3307 \r
3308                         function findEndPoint(start) {\r
3309                                 var marker, container, offset, nodes, startIndex = 0, endIndex, index, parent, checkRng, position;\r
3310 \r
3311                                 // Setup temp range and collapse it\r
3312                                 checkRng = ieRange.duplicate();\r
3313                                 checkRng.collapse(start);\r
3314 \r
3315                                 // Create marker and insert it at the end of the endpoints parent\r
3316                                 marker = dom.create('a');\r
3317                                 parent = checkRng.parentElement();\r
3318 \r
3319                                 // If parent doesn't have any children then set the container to that parent and the index to 0\r
3320                                 if (!parent.hasChildNodes()) {\r
3321                                         domRange[start ? 'setStart' : 'setEnd'](parent, 0);\r
3322                                         return;\r
3323                                 }\r
3324 \r
3325                                 parent.appendChild(marker);\r
3326                                 checkRng.moveToElementText(marker);\r
3327                                 position = ieRange.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', checkRng);\r
3328                                 if (position > 0) {\r
3329                                         // The position is after the end of the parent element.\r
3330                                         // This is the case where IE puts the caret to the left edge of a table.\r
3331                                         domRange[start ? 'setStartAfter' : 'setEndAfter'](parent);\r
3332                                         dom.remove(marker);\r
3333                                         return;\r
3334                                 }\r
3335 \r
3336                                 // Setup node list and endIndex\r
3337                                 nodes = tinymce.grep(parent.childNodes);\r
3338                                 endIndex = nodes.length - 1;\r
3339                                 // Perform a binary search for the position\r
3340                                 while (startIndex <= endIndex) {\r
3341                                         index = Math.floor((startIndex + endIndex) / 2);\r
3342 \r
3343                                         // Insert marker and check it's position relative to the selection\r
3344                                         parent.insertBefore(marker, nodes[index]);\r
3345                                         checkRng.moveToElementText(marker);\r
3346                                         position = ieRange.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', checkRng);\r
3347                                         if (position > 0) {\r
3348                                                 // Marker is to the right\r
3349                                                 startIndex = index + 1;\r
3350                                         } else if (position < 0) {\r
3351                                                 // Marker is to the left\r
3352                                                 endIndex = index - 1;\r
3353                                         } else {\r
3354                                                 // Maker is where we are\r
3355                                                 found = true;\r
3356                                                 break;\r
3357                                         }\r
3358                                 }\r
3359 \r
3360                                 // Setup container\r
3361                                 container = position > 0 || index == 0 ? marker.nextSibling : marker.previousSibling;\r
3362 \r
3363                                 // Handle element selection\r
3364                                 if (container.nodeType == 1) {\r
3365                                         dom.remove(marker);\r
3366 \r
3367                                         // Find offset and container\r
3368                                         offset = dom.nodeIndex(container);\r
3369                                         container = container.parentNode;\r
3370 \r
3371                                         // Move the offset if we are setting the end or the position is after an element\r
3372                                         if (!start || index > 0)\r
3373                                                 offset++;\r
3374                                 } else {\r
3375                                         // Calculate offset within text node\r
3376                                         if (position > 0 || index == 0) {\r
3377                                                 checkRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', ieRange);\r
3378                                                 offset = checkRng.text.length;\r
3379                                         } else {\r
3380                                                 checkRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', ieRange);\r
3381                                                 offset = container.nodeValue.length - checkRng.text.length;\r
3382                                         }\r
3383 \r
3384                                         dom.remove(marker);\r
3385                                 }\r
3386 \r
3387                                 domRange[start ? 'setStart' : 'setEnd'](container, offset);\r
3388                         };\r
3389 \r
3390                         // Find start point\r
3391                         findEndPoint(true);\r
3392 \r
3393                         // Find end point if needed\r
3394                         if (!collapsed)\r
3395                                 findEndPoint();\r
3396 \r
3397                         return domRange;\r
3398                 };\r
3399 \r
3400                 this.addRange = function(rng) {\r
3401                         var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, doc = selection.dom.doc, body = doc.body;\r
3402 \r
3403                         function setEndPoint(start) {\r
3404                                 var container, offset, marker, tmpRng, nodes;\r
3405 \r
3406                                 marker = dom.create('a');\r
3407                                 container = start ? startContainer : endContainer;\r
3408                                 offset = start ? startOffset : endOffset;\r
3409                                 tmpRng = ieRng.duplicate();\r
3410 \r
3411                                 if (container == doc) {\r
3412                                         container = body;\r
3413                                         offset = 0;\r
3414                                 }\r
3415 \r
3416                                 if (container.nodeType == 3) {\r
3417                                         container.parentNode.insertBefore(marker, container);\r
3418                                         tmpRng.moveToElementText(marker);\r
3419                                         tmpRng.moveStart('character', offset);\r
3420                                         dom.remove(marker);\r
3421                                         ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);\r
3422                                 } else {\r
3423                                         nodes = container.childNodes;\r
3424 \r
3425                                         if (nodes.length) {\r
3426                                                 if (offset >= nodes.length) {\r
3427                                                         dom.insertAfter(marker, nodes[nodes.length - 1]);\r
3428                                                 } else {\r
3429                                                         container.insertBefore(marker, nodes[offset]);\r
3430                                                 }\r
3431 \r
3432                                                 tmpRng.moveToElementText(marker);\r
3433                                         } else {\r
3434                                                 // Empty node selection for example <div>|</div>\r
3435                                                 marker = doc.createTextNode(invisibleChar);\r
3436                                                 container.appendChild(marker);\r
3437                                                 tmpRng.moveToElementText(marker.parentNode);\r
3438                                                 tmpRng.collapse(TRUE);\r
3439                                         }\r
3440 \r
3441                                         ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);\r
3442                                         dom.remove(marker);\r
3443                                 }\r
3444                         }\r
3445 \r
3446                         // Destroy cached range\r
3447                         this.destroy();\r
3448 \r
3449                         // Setup some shorter versions\r
3450                         startContainer = rng.startContainer;\r
3451                         startOffset = rng.startOffset;\r
3452                         endContainer = rng.endContainer;\r
3453                         endOffset = rng.endOffset;\r
3454                         ieRng = body.createTextRange();\r
3455 \r
3456                         // If single element selection then try making a control selection out of it\r
3457                         if (startContainer == endContainer && startContainer.nodeType == 1 && startOffset == endOffset - 1) {\r
3458                                 if (startOffset == endOffset - 1) {\r
3459                                         try {\r
3460                                                 ctrlRng = body.createControlRange();\r
3461                                                 ctrlRng.addElement(startContainer.childNodes[startOffset]);\r
3462                                                 ctrlRng.select();\r
3463                                                 ctrlRng.scrollIntoView();\r
3464                                                 return;\r
3465                                         } catch (ex) {\r
3466                                                 // Ignore\r
3467                                         }\r
3468                                 }\r
3469                         }\r
3470 \r
3471                         // Set start/end point of selection\r
3472                         setEndPoint(true);\r
3473                         setEndPoint();\r
3474 \r
3475                         // Select the new range and scroll it into view\r
3476                         ieRng.select();\r
3477                         ieRng.scrollIntoView();\r
3478                 };\r
3479 \r
3480                 this.getRangeAt = function() {\r
3481                         // Setup new range if the cache is empty\r
3482                         if (!range || !tinymce.dom.RangeUtils.compareRanges(lastIERng, selection.getRng())) {\r
3483                                 range = getRange();\r
3484 \r
3485                                 // Store away text range for next call\r
3486                                 lastIERng = selection.getRng();\r
3487                         }\r
3488 \r
3489                         // IE will say that the range is equal then produce an invalid argument exception\r
3490                         // if you perform specific operations in a keyup event. For example Ctrl+Del.\r
3491                         // This hack will invalidate the range cache if the exception occurs\r
3492                         try {\r
3493                                 range.startContainer.nextSibling;\r
3494                         } catch (ex) {\r
3495                                 range = getRange();\r
3496                                 lastIERng = null;\r
3497                         }\r
3498 \r
3499                         // Return cached range\r
3500                         return range;\r
3501                 };\r
3502 \r
3503                 this.destroy = function() {\r
3504                         // Destroy cached range and last IE range to avoid memory leaks\r
3505                         lastIERng = range = null;\r
3506                 };\r
3507         };\r
3508 \r
3509         // Expose the selection object\r
3510         tinymce.dom.TridentSelection = Selection;\r
3511 })();\r
3512 \r
3513 \r
3514 /*\r
3515  * Sizzle CSS Selector Engine - v1.0\r
3516  *  Copyright 2009, The Dojo Foundation\r
3517  *  Released under the MIT, BSD, and GPL Licenses.\r
3518  *  More information: http://sizzlejs.com/\r
3519  */\r
3520 (function(){\r
3521 \r
3522 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,\r
3523         done = 0,\r
3524         toString = Object.prototype.toString,\r
3525         hasDuplicate = false,\r
3526         baseHasDuplicate = true;\r
3527 \r
3528 // Here we check if the JavaScript engine is using some sort of\r
3529 // optimization where it does not always call our comparision\r
3530 // function. If that is the case, discard the hasDuplicate value.\r
3531 //   Thus far that includes Google Chrome.\r
3532 [0, 0].sort(function(){\r
3533         baseHasDuplicate = false;\r
3534         return 0;\r
3535 });\r
3536 \r
3537 var Sizzle = function(selector, context, results, seed) {\r
3538         results = results || [];\r
3539         context = context || document;\r
3540 \r
3541         var origContext = context;\r
3542 \r
3543         if ( context.nodeType !== 1 && context.nodeType !== 9 ) {\r
3544                 return [];\r
3545         }\r
3546         \r
3547         if ( !selector || typeof selector !== "string" ) {\r
3548                 return results;\r
3549         }\r
3550 \r
3551         var parts = [], m, set, checkSet, extra, prune = true, contextXML = Sizzle.isXML(context),\r
3552                 soFar = selector, ret, cur, pop, i;\r
3553         \r
3554         // Reset the position of the chunker regexp (start from head)\r
3555         do {\r
3556                 chunker.exec("");\r
3557                 m = chunker.exec(soFar);\r
3558 \r
3559                 if ( m ) {\r
3560                         soFar = m[3];\r
3561                 \r
3562                         parts.push( m[1] );\r
3563                 \r
3564                         if ( m[2] ) {\r
3565                                 extra = m[3];\r
3566                                 break;\r
3567                         }\r
3568                 }\r
3569         } while ( m );\r
3570 \r
3571         if ( parts.length > 1 && origPOS.exec( selector ) ) {\r
3572                 if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {\r
3573                         set = posProcess( parts[0] + parts[1], context );\r
3574                 } else {\r
3575                         set = Expr.relative[ parts[0] ] ?\r
3576                                 [ context ] :\r
3577                                 Sizzle( parts.shift(), context );\r
3578 \r
3579                         while ( parts.length ) {\r
3580                                 selector = parts.shift();\r
3581 \r
3582                                 if ( Expr.relative[ selector ] ) {\r
3583                                         selector += parts.shift();\r
3584                                 }\r
3585                                 \r
3586                                 set = posProcess( selector, set );\r
3587                         }\r
3588                 }\r
3589         } else {\r
3590                 // Take a shortcut and set the context if the root selector is an ID\r
3591                 // (but not if it'll be faster if the inner selector is an ID)\r
3592                 if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&\r
3593                                 Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {\r
3594                         ret = Sizzle.find( parts.shift(), context, contextXML );\r
3595                         context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];\r
3596                 }\r
3597 \r
3598                 if ( context ) {\r
3599                         ret = seed ?\r
3600                                 { expr: parts.pop(), set: makeArray(seed) } :\r
3601                                 Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );\r
3602                         set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;\r
3603 \r
3604                         if ( parts.length > 0 ) {\r
3605                                 checkSet = makeArray(set);\r
3606                         } else {\r
3607                                 prune = false;\r
3608                         }\r
3609 \r
3610                         while ( parts.length ) {\r
3611                                 cur = parts.pop();\r
3612                                 pop = cur;\r
3613 \r
3614                                 if ( !Expr.relative[ cur ] ) {\r
3615                                         cur = "";\r
3616                                 } else {\r
3617                                         pop = parts.pop();\r
3618                                 }\r
3619 \r
3620                                 if ( pop == null ) {\r
3621                                         pop = context;\r
3622                                 }\r
3623 \r
3624                                 Expr.relative[ cur ]( checkSet, pop, contextXML );\r
3625                         }\r
3626                 } else {\r
3627                         checkSet = parts = [];\r
3628                 }\r
3629         }\r
3630 \r
3631         if ( !checkSet ) {\r
3632                 checkSet = set;\r
3633         }\r
3634 \r
3635         if ( !checkSet ) {\r
3636                 Sizzle.error( cur || selector );\r
3637         }\r
3638 \r
3639         if ( toString.call(checkSet) === "[object Array]" ) {\r
3640                 if ( !prune ) {\r
3641                         results.push.apply( results, checkSet );\r
3642                 } else if ( context && context.nodeType === 1 ) {\r
3643                         for ( i = 0; checkSet[i] != null; i++ ) {\r
3644                                 if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {\r
3645                                         results.push( set[i] );\r
3646                                 }\r
3647                         }\r
3648                 } else {\r
3649                         for ( i = 0; checkSet[i] != null; i++ ) {\r
3650                                 if ( checkSet[i] && checkSet[i].nodeType === 1 ) {\r
3651                                         results.push( set[i] );\r
3652                                 }\r
3653                         }\r
3654                 }\r
3655         } else {\r
3656                 makeArray( checkSet, results );\r
3657         }\r
3658 \r
3659         if ( extra ) {\r
3660                 Sizzle( extra, origContext, results, seed );\r
3661                 Sizzle.uniqueSort( results );\r
3662         }\r
3663 \r
3664         return results;\r
3665 };\r
3666 \r
3667 Sizzle.uniqueSort = function(results){\r
3668         if ( sortOrder ) {\r
3669                 hasDuplicate = baseHasDuplicate;\r
3670                 results.sort(sortOrder);\r
3671 \r
3672                 if ( hasDuplicate ) {\r
3673                         for ( var i = 1; i < results.length; i++ ) {\r
3674                                 if ( results[i] === results[i-1] ) {\r
3675                                         results.splice(i--, 1);\r
3676                                 }\r
3677                         }\r
3678                 }\r
3679         }\r
3680 \r
3681         return results;\r
3682 };\r
3683 \r
3684 Sizzle.matches = function(expr, set){\r
3685         return Sizzle(expr, null, null, set);\r
3686 };\r
3687 \r
3688 Sizzle.find = function(expr, context, isXML){\r
3689         var set;\r
3690 \r
3691         if ( !expr ) {\r
3692                 return [];\r
3693         }\r
3694 \r
3695         for ( var i = 0, l = Expr.order.length; i < l; i++ ) {\r
3696                 var type = Expr.order[i], match;\r
3697                 \r
3698                 if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {\r
3699                         var left = match[1];\r
3700                         match.splice(1,1);\r
3701 \r
3702                         if ( left.substr( left.length - 1 ) !== "\\" ) {\r
3703                                 match[1] = (match[1] || "").replace(/\\/g, "");\r
3704                                 set = Expr.find[ type ]( match, context, isXML );\r
3705                                 if ( set != null ) {\r
3706                                         expr = expr.replace( Expr.match[ type ], "" );\r
3707                                         break;\r
3708                                 }\r
3709                         }\r
3710                 }\r
3711         }\r
3712 \r
3713         if ( !set ) {\r
3714                 set = context.getElementsByTagName("*");\r
3715         }\r
3716 \r
3717         return {set: set, expr: expr};\r
3718 };\r
3719 \r
3720 Sizzle.filter = function(expr, set, inplace, not){\r
3721         var old = expr, result = [], curLoop = set, match, anyFound,\r
3722                 isXMLFilter = set && set[0] && Sizzle.isXML(set[0]);\r
3723 \r
3724         while ( expr && set.length ) {\r
3725                 for ( var type in Expr.filter ) {\r
3726                         if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {\r
3727                                 var filter = Expr.filter[ type ], found, item, left = match[1];\r
3728                                 anyFound = false;\r
3729 \r
3730                                 match.splice(1,1);\r
3731 \r
3732                                 if ( left.substr( left.length - 1 ) === "\\" ) {\r
3733                                         continue;\r
3734                                 }\r
3735 \r
3736                                 if ( curLoop === result ) {\r
3737                                         result = [];\r
3738                                 }\r
3739 \r
3740                                 if ( Expr.preFilter[ type ] ) {\r
3741                                         match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );\r
3742 \r
3743                                         if ( !match ) {\r
3744                                                 anyFound = found = true;\r
3745                                         } else if ( match === true ) {\r
3746                                                 continue;\r
3747                                         }\r
3748                                 }\r
3749 \r
3750                                 if ( match ) {\r
3751                                         for ( var i = 0; (item = curLoop[i]) != null; i++ ) {\r
3752                                                 if ( item ) {\r
3753                                                         found = filter( item, match, i, curLoop );\r
3754                                                         var pass = not ^ !!found;\r
3755 \r
3756                                                         if ( inplace && found != null ) {\r
3757                                                                 if ( pass ) {\r
3758                                                                         anyFound = true;\r
3759                                                                 } else {\r
3760                                                                         curLoop[i] = false;\r
3761                                                                 }\r
3762                                                         } else if ( pass ) {\r
3763                                                                 result.push( item );\r
3764                                                                 anyFound = true;\r
3765                                                         }\r
3766                                                 }\r
3767                                         }\r
3768                                 }\r
3769 \r
3770                                 if ( found !== undefined ) {\r
3771                                         if ( !inplace ) {\r
3772                                                 curLoop = result;\r
3773                                         }\r
3774 \r
3775                                         expr = expr.replace( Expr.match[ type ], "" );\r
3776 \r
3777                                         if ( !anyFound ) {\r
3778                                                 return [];\r
3779                                         }\r
3780 \r
3781                                         break;\r
3782                                 }\r
3783                         }\r
3784                 }\r
3785 \r
3786                 // Improper expression\r
3787                 if ( expr === old ) {\r
3788                         if ( anyFound == null ) {\r
3789                                 Sizzle.error( expr );\r
3790                         } else {\r
3791                                 break;\r
3792                         }\r
3793                 }\r
3794 \r
3795                 old = expr;\r
3796         }\r
3797 \r
3798         return curLoop;\r
3799 };\r
3800 \r
3801 Sizzle.error = function( msg ) {\r
3802         throw "Syntax error, unrecognized expression: " + msg;\r
3803 };\r
3804 \r
3805 var Expr = Sizzle.selectors = {\r
3806         order: [ "ID", "NAME", "TAG" ],\r
3807         match: {\r
3808                 ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,\r
3809                 CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,\r
3810                 NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,\r
3811                 ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,\r
3812                 TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,\r
3813                 CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,\r
3814                 POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,\r
3815                 PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/\r
3816         },\r
3817         leftMatch: {},\r
3818         attrMap: {\r
3819                 "class": "className",\r
3820                 "for": "htmlFor"\r
3821         },\r
3822         attrHandle: {\r
3823                 href: function(elem){\r
3824                         return elem.getAttribute("href");\r
3825                 }\r
3826         },\r
3827         relative: {\r
3828                 "+": function(checkSet, part){\r
3829                         var isPartStr = typeof part === "string",\r
3830                                 isTag = isPartStr && !/\W/.test(part),\r
3831                                 isPartStrNotTag = isPartStr && !isTag;\r
3832 \r
3833                         if ( isTag ) {\r
3834                                 part = part.toLowerCase();\r
3835                         }\r
3836 \r
3837                         for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {\r
3838                                 if ( (elem = checkSet[i]) ) {\r
3839                                         while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}\r
3840 \r
3841                                         checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?\r
3842                                                 elem || false :\r
3843                                                 elem === part;\r
3844                                 }\r
3845                         }\r
3846 \r
3847                         if ( isPartStrNotTag ) {\r
3848                                 Sizzle.filter( part, checkSet, true );\r
3849                         }\r
3850                 },\r
3851                 ">": function(checkSet, part){\r
3852                         var isPartStr = typeof part === "string",\r
3853                                 elem, i = 0, l = checkSet.length;\r
3854 \r
3855                         if ( isPartStr && !/\W/.test(part) ) {\r
3856                                 part = part.toLowerCase();\r
3857 \r
3858                                 for ( ; i < l; i++ ) {\r
3859                                         elem = checkSet[i];\r
3860                                         if ( elem ) {\r
3861                                                 var parent = elem.parentNode;\r
3862                                                 checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;\r
3863                                         }\r
3864                                 }\r
3865                         } else {\r
3866                                 for ( ; i < l; i++ ) {\r
3867                                         elem = checkSet[i];\r
3868                                         if ( elem ) {\r
3869                                                 checkSet[i] = isPartStr ?\r
3870                                                         elem.parentNode :\r
3871                                                         elem.parentNode === part;\r
3872                                         }\r
3873                                 }\r
3874 \r
3875                                 if ( isPartStr ) {\r
3876                                         Sizzle.filter( part, checkSet, true );\r
3877                                 }\r
3878                         }\r
3879                 },\r
3880                 "": function(checkSet, part, isXML){\r
3881                         var doneName = done++, checkFn = dirCheck, nodeCheck;\r
3882 \r
3883                         if ( typeof part === "string" && !/\W/.test(part) ) {\r
3884                                 part = part.toLowerCase();\r
3885                                 nodeCheck = part;\r
3886                                 checkFn = dirNodeCheck;\r
3887                         }\r
3888 \r
3889                         checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);\r
3890                 },\r
3891                 "~": function(checkSet, part, isXML){\r
3892                         var doneName = done++, checkFn = dirCheck, nodeCheck;\r
3893 \r
3894                         if ( typeof part === "string" && !/\W/.test(part) ) {\r
3895                                 part = part.toLowerCase();\r
3896                                 nodeCheck = part;\r
3897                                 checkFn = dirNodeCheck;\r
3898                         }\r
3899 \r
3900                         checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);\r
3901                 }\r
3902         },\r
3903         find: {\r
3904                 ID: function(match, context, isXML){\r
3905                         if ( typeof context.getElementById !== "undefined" && !isXML ) {\r
3906                                 var m = context.getElementById(match[1]);\r
3907                                 return m ? [m] : [];\r
3908                         }\r
3909                 },\r
3910                 NAME: function(match, context){\r
3911                         if ( typeof context.getElementsByName !== "undefined" ) {\r
3912                                 var ret = [], results = context.getElementsByName(match[1]);\r
3913 \r
3914                                 for ( var i = 0, l = results.length; i < l; i++ ) {\r
3915                                         if ( results[i].getAttribute("name") === match[1] ) {\r
3916                                                 ret.push( results[i] );\r
3917                                         }\r
3918                                 }\r
3919 \r
3920                                 return ret.length === 0 ? null : ret;\r
3921                         }\r
3922                 },\r
3923                 TAG: function(match, context){\r
3924                         return context.getElementsByTagName(match[1]);\r
3925                 }\r
3926         },\r
3927         preFilter: {\r
3928                 CLASS: function(match, curLoop, inplace, result, not, isXML){\r
3929                         match = " " + match[1].replace(/\\/g, "") + " ";\r
3930 \r
3931                         if ( isXML ) {\r
3932                                 return match;\r
3933                         }\r
3934 \r
3935                         for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {\r
3936                                 if ( elem ) {\r
3937                                         if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {\r
3938                                                 if ( !inplace ) {\r
3939                                                         result.push( elem );\r
3940                                                 }\r
3941                                         } else if ( inplace ) {\r
3942                                                 curLoop[i] = false;\r
3943                                         }\r
3944                                 }\r
3945                         }\r
3946 \r
3947                         return false;\r
3948                 },\r
3949                 ID: function(match){\r
3950                         return match[1].replace(/\\/g, "");\r
3951                 },\r
3952                 TAG: function(match, curLoop){\r
3953                         return match[1].toLowerCase();\r
3954                 },\r
3955                 CHILD: function(match){\r
3956                         if ( match[1] === "nth" ) {\r
3957                                 // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'\r
3958                                 var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(\r
3959                                         match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||\r
3960                                         !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);\r
3961 \r
3962                                 // calculate the numbers (first)n+(last) including if they are negative\r
3963                                 match[2] = (test[1] + (test[2] || 1)) - 0;\r
3964                                 match[3] = test[3] - 0;\r
3965                         }\r
3966 \r
3967                         // TODO: Move to normal caching system\r
3968                         match[0] = done++;\r
3969 \r
3970                         return match;\r
3971                 },\r
3972                 ATTR: function(match, curLoop, inplace, result, not, isXML){\r
3973                         var name = match[1].replace(/\\/g, "");\r
3974                         \r
3975                         if ( !isXML && Expr.attrMap[name] ) {\r
3976                                 match[1] = Expr.attrMap[name];\r
3977                         }\r
3978 \r
3979                         if ( match[2] === "~=" ) {\r
3980                                 match[4] = " " + match[4] + " ";\r
3981                         }\r
3982 \r
3983                         return match;\r
3984                 },\r
3985                 PSEUDO: function(match, curLoop, inplace, result, not){\r
3986                         if ( match[1] === "not" ) {\r
3987                                 // If we're dealing with a complex expression, or a simple one\r
3988                                 if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {\r
3989                                         match[3] = Sizzle(match[3], null, null, curLoop);\r
3990                                 } else {\r
3991                                         var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);\r
3992                                         if ( !inplace ) {\r
3993                                                 result.push.apply( result, ret );\r
3994                                         }\r
3995                                         return false;\r
3996                                 }\r
3997                         } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {\r
3998                                 return true;\r
3999                         }\r
4000                         \r
4001                         return match;\r
4002                 },\r
4003                 POS: function(match){\r
4004                         match.unshift( true );\r
4005                         return match;\r
4006                 }\r
4007         },\r
4008         filters: {\r
4009                 enabled: function(elem){\r
4010                         return elem.disabled === false && elem.type !== "hidden";\r
4011                 },\r
4012                 disabled: function(elem){\r
4013                         return elem.disabled === true;\r
4014                 },\r
4015                 checked: function(elem){\r
4016                         return elem.checked === true;\r
4017                 },\r
4018                 selected: function(elem){\r
4019                         // Accessing this property makes selected-by-default\r
4020                         // options in Safari work properly\r
4021                         elem.parentNode.selectedIndex;\r
4022                         return elem.selected === true;\r
4023                 },\r
4024                 parent: function(elem){\r
4025                         return !!elem.firstChild;\r
4026                 },\r
4027                 empty: function(elem){\r
4028                         return !elem.firstChild;\r
4029                 },\r
4030                 has: function(elem, i, match){\r
4031                         return !!Sizzle( match[3], elem ).length;\r
4032                 },\r
4033                 header: function(elem){\r
4034                         return (/h\d/i).test( elem.nodeName );\r
4035                 },\r
4036                 text: function(elem){\r
4037                         return "text" === elem.type;\r
4038                 },\r
4039                 radio: function(elem){\r
4040                         return "radio" === elem.type;\r
4041                 },\r
4042                 checkbox: function(elem){\r
4043                         return "checkbox" === elem.type;\r
4044                 },\r
4045                 file: function(elem){\r
4046                         return "file" === elem.type;\r
4047                 },\r
4048                 password: function(elem){\r
4049                         return "password" === elem.type;\r
4050                 },\r
4051                 submit: function(elem){\r
4052                         return "submit" === elem.type;\r
4053                 },\r
4054                 image: function(elem){\r
4055                         return "image" === elem.type;\r
4056                 },\r
4057                 reset: function(elem){\r
4058                         return "reset" === elem.type;\r
4059                 },\r
4060                 button: function(elem){\r
4061                         return "button" === elem.type || elem.nodeName.toLowerCase() === "button";\r
4062                 },\r
4063                 input: function(elem){\r
4064                         return (/input|select|textarea|button/i).test(elem.nodeName);\r
4065                 }\r
4066         },\r
4067         setFilters: {\r
4068                 first: function(elem, i){\r
4069                         return i === 0;\r
4070                 },\r
4071                 last: function(elem, i, match, array){\r
4072                         return i === array.length - 1;\r
4073                 },\r
4074                 even: function(elem, i){\r
4075                         return i % 2 === 0;\r
4076                 },\r
4077                 odd: function(elem, i){\r
4078                         return i % 2 === 1;\r
4079                 },\r
4080                 lt: function(elem, i, match){\r
4081                         return i < match[3] - 0;\r
4082                 },\r
4083                 gt: function(elem, i, match){\r
4084                         return i > match[3] - 0;\r
4085                 },\r
4086                 nth: function(elem, i, match){\r
4087                         return match[3] - 0 === i;\r
4088                 },\r
4089                 eq: function(elem, i, match){\r
4090                         return match[3] - 0 === i;\r
4091                 }\r
4092         },\r
4093         filter: {\r
4094                 PSEUDO: function(elem, match, i, array){\r
4095                         var name = match[1], filter = Expr.filters[ name ];\r
4096 \r
4097                         if ( filter ) {\r
4098                                 return filter( elem, i, match, array );\r
4099                         } else if ( name === "contains" ) {\r
4100                                 return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;\r
4101                         } else if ( name === "not" ) {\r
4102                                 var not = match[3];\r
4103 \r
4104                                 for ( var j = 0, l = not.length; j < l; j++ ) {\r
4105                                         if ( not[j] === elem ) {\r
4106                                                 return false;\r
4107                                         }\r
4108                                 }\r
4109 \r
4110                                 return true;\r
4111                         } else {\r
4112                                 Sizzle.error( "Syntax error, unrecognized expression: " + name );\r
4113                         }\r
4114                 },\r
4115                 CHILD: function(elem, match){\r
4116                         var type = match[1], node = elem;\r
4117                         switch (type) {\r
4118                                 case 'only':\r
4119                                 case 'first':\r
4120                                         while ( (node = node.previousSibling) )  {\r
4121                                                 if ( node.nodeType === 1 ) { \r
4122                                                         return false; \r
4123                                                 }\r
4124                                         }\r
4125                                         if ( type === "first" ) { \r
4126                                                 return true; \r
4127                                         }\r
4128                                         node = elem;\r
4129                                 case 'last':\r
4130                                         while ( (node = node.nextSibling) )      {\r
4131                                                 if ( node.nodeType === 1 ) { \r
4132                                                         return false; \r
4133                                                 }\r
4134                                         }\r
4135                                         return true;\r
4136                                 case 'nth':\r
4137                                         var first = match[2], last = match[3];\r
4138 \r
4139                                         if ( first === 1 && last === 0 ) {\r
4140                                                 return true;\r
4141                                         }\r
4142                                         \r
4143                                         var doneName = match[0],\r
4144                                                 parent = elem.parentNode;\r
4145         \r
4146                                         if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {\r
4147                                                 var count = 0;\r
4148                                                 for ( node = parent.firstChild; node; node = node.nextSibling ) {\r
4149                                                         if ( node.nodeType === 1 ) {\r
4150                                                                 node.nodeIndex = ++count;\r
4151                                                         }\r
4152                                                 } \r
4153                                                 parent.sizcache = doneName;\r
4154                                         }\r
4155                                         \r
4156                                         var diff = elem.nodeIndex - last;\r
4157                                         if ( first === 0 ) {\r
4158                                                 return diff === 0;\r
4159                                         } else {\r
4160                                                 return ( diff % first === 0 && diff / first >= 0 );\r
4161                                         }\r
4162                         }\r
4163                 },\r
4164                 ID: function(elem, match){\r
4165                         return elem.nodeType === 1 && elem.getAttribute("id") === match;\r
4166                 },\r
4167                 TAG: function(elem, match){\r
4168                         return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;\r
4169                 },\r
4170                 CLASS: function(elem, match){\r
4171                         return (" " + (elem.className || elem.getAttribute("class")) + " ")\r
4172                                 .indexOf( match ) > -1;\r
4173                 },\r
4174                 ATTR: function(elem, match){\r
4175                         var name = match[1],\r
4176                                 result = Expr.attrHandle[ name ] ?\r
4177                                         Expr.attrHandle[ name ]( elem ) :\r
4178                                         elem[ name ] != null ?\r
4179                                                 elem[ name ] :\r
4180                                                 elem.getAttribute( name ),\r
4181                                 value = result + "",\r
4182                                 type = match[2],\r
4183                                 check = match[4];\r
4184 \r
4185                         return result == null ?\r
4186                                 type === "!=" :\r
4187                                 type === "=" ?\r
4188                                 value === check :\r
4189                                 type === "*=" ?\r
4190                                 value.indexOf(check) >= 0 :\r
4191                                 type === "~=" ?\r
4192                                 (" " + value + " ").indexOf(check) >= 0 :\r
4193                                 !check ?\r
4194                                 value && result !== false :\r
4195                                 type === "!=" ?\r
4196                                 value !== check :\r
4197                                 type === "^=" ?\r
4198                                 value.indexOf(check) === 0 :\r
4199                                 type === "$=" ?\r
4200                                 value.substr(value.length - check.length) === check :\r
4201                                 type === "|=" ?\r
4202                                 value === check || value.substr(0, check.length + 1) === check + "-" :\r
4203                                 false;\r
4204                 },\r
4205                 POS: function(elem, match, i, array){\r
4206                         var name = match[2], filter = Expr.setFilters[ name ];\r
4207 \r
4208                         if ( filter ) {\r
4209                                 return filter( elem, i, match, array );\r
4210                         }\r
4211                 }\r
4212         }\r
4213 };\r
4214 \r
4215 var origPOS = Expr.match.POS,\r
4216         fescape = function(all, num){\r
4217                 return "\\" + (num - 0 + 1);\r
4218         };\r
4219 \r
4220 for ( var type in Expr.match ) {\r
4221         Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );\r
4222         Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );\r
4223 }\r
4224 \r
4225 var makeArray = function(array, results) {\r
4226         array = Array.prototype.slice.call( array, 0 );\r
4227 \r
4228         if ( results ) {\r
4229                 results.push.apply( results, array );\r
4230                 return results;\r
4231         }\r
4232         \r
4233         return array;\r
4234 };\r
4235 \r
4236 // Perform a simple check to determine if the browser is capable of\r
4237 // converting a NodeList to an array using builtin methods.\r
4238 // Also verifies that the returned array holds DOM nodes\r
4239 // (which is not the case in the Blackberry browser)\r
4240 try {\r
4241         Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;\r
4242 \r
4243 // Provide a fallback method if it does not work\r
4244 } catch(e){\r
4245         makeArray = function(array, results) {\r
4246                 var ret = results || [], i = 0;\r
4247 \r
4248                 if ( toString.call(array) === "[object Array]" ) {\r
4249                         Array.prototype.push.apply( ret, array );\r
4250                 } else {\r
4251                         if ( typeof array.length === "number" ) {\r
4252                                 for ( var l = array.length; i < l; i++ ) {\r
4253                                         ret.push( array[i] );\r
4254                                 }\r
4255                         } else {\r
4256                                 for ( ; array[i]; i++ ) {\r
4257                                         ret.push( array[i] );\r
4258                                 }\r
4259                         }\r
4260                 }\r
4261 \r
4262                 return ret;\r
4263         };\r
4264 }\r
4265 \r
4266 var sortOrder;\r
4267 \r
4268 if ( document.documentElement.compareDocumentPosition ) {\r
4269         sortOrder = function( a, b ) {\r
4270                 if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {\r
4271                         if ( a == b ) {\r
4272                                 hasDuplicate = true;\r
4273                         }\r
4274                         return a.compareDocumentPosition ? -1 : 1;\r
4275                 }\r
4276 \r
4277                 var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;\r
4278                 if ( ret === 0 ) {\r
4279                         hasDuplicate = true;\r
4280                 }\r
4281                 return ret;\r
4282         };\r
4283 } else if ( "sourceIndex" in document.documentElement ) {\r
4284         sortOrder = function( a, b ) {\r
4285                 if ( !a.sourceIndex || !b.sourceIndex ) {\r
4286                         if ( a == b ) {\r
4287                                 hasDuplicate = true;\r
4288                         }\r
4289                         return a.sourceIndex ? -1 : 1;\r
4290                 }\r
4291 \r
4292                 var ret = a.sourceIndex - b.sourceIndex;\r
4293                 if ( ret === 0 ) {\r
4294                         hasDuplicate = true;\r
4295                 }\r
4296                 return ret;\r
4297         };\r
4298 } else if ( document.createRange ) {\r
4299         sortOrder = function( a, b ) {\r
4300                 if ( !a.ownerDocument || !b.ownerDocument ) {\r
4301                         if ( a == b ) {\r
4302                                 hasDuplicate = true;\r
4303                         }\r
4304                         return a.ownerDocument ? -1 : 1;\r
4305                 }\r
4306 \r
4307                 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();\r
4308                 aRange.setStart(a, 0);\r
4309                 aRange.setEnd(a, 0);\r
4310                 bRange.setStart(b, 0);\r
4311                 bRange.setEnd(b, 0);\r
4312                 var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);\r
4313                 if ( ret === 0 ) {\r
4314                         hasDuplicate = true;\r
4315                 }\r
4316                 return ret;\r
4317         };\r
4318 }\r
4319 \r
4320 // Utility function for retreiving the text value of an array of DOM nodes\r
4321 Sizzle.getText = function( elems ) {\r
4322         var ret = "", elem;\r
4323 \r
4324         for ( var i = 0; elems[i]; i++ ) {\r
4325                 elem = elems[i];\r
4326 \r
4327                 // Get the text from text nodes and CDATA nodes\r
4328                 if ( elem.nodeType === 3 || elem.nodeType === 4 ) {\r
4329                         ret += elem.nodeValue;\r
4330 \r
4331                 // Traverse everything else, except comment nodes\r
4332                 } else if ( elem.nodeType !== 8 ) {\r
4333                         ret += Sizzle.getText( elem.childNodes );\r
4334                 }\r
4335         }\r
4336 \r
4337         return ret;\r
4338 };\r
4339 \r
4340 // Check to see if the browser returns elements by name when\r
4341 // querying by getElementById (and provide a workaround)\r
4342 (function(){\r
4343         // We're going to inject a fake input element with a specified name\r
4344         var form = document.createElement("div"),\r
4345                 id = "script" + (new Date()).getTime();\r
4346         form.innerHTML = "<a name='" + id + "'/>";\r
4347 \r
4348         // Inject it into the root element, check its status, and remove it quickly\r
4349         var root = document.documentElement;\r
4350         root.insertBefore( form, root.firstChild );\r
4351 \r
4352         // The workaround has to do additional checks after a getElementById\r
4353         // Which slows things down for other browsers (hence the branching)\r
4354         if ( document.getElementById( id ) ) {\r
4355                 Expr.find.ID = function(match, context, isXML){\r
4356                         if ( typeof context.getElementById !== "undefined" && !isXML ) {\r
4357                                 var m = context.getElementById(match[1]);\r
4358                                 return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];\r
4359                         }\r
4360                 };\r
4361 \r
4362                 Expr.filter.ID = function(elem, match){\r
4363                         var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");\r
4364                         return elem.nodeType === 1 && node && node.nodeValue === match;\r
4365                 };\r
4366         }\r
4367 \r
4368         root.removeChild( form );\r
4369         root = form = null; // release memory in IE\r
4370 })();\r
4371 \r
4372 (function(){\r
4373         // Check to see if the browser returns only elements\r
4374         // when doing getElementsByTagName("*")\r
4375 \r
4376         // Create a fake element\r
4377         var div = document.createElement("div");\r
4378         div.appendChild( document.createComment("") );\r
4379 \r
4380         // Make sure no comments are found\r
4381         if ( div.getElementsByTagName("*").length > 0 ) {\r
4382                 Expr.find.TAG = function(match, context){\r
4383                         var results = context.getElementsByTagName(match[1]);\r
4384 \r
4385                         // Filter out possible comments\r
4386                         if ( match[1] === "*" ) {\r
4387                                 var tmp = [];\r
4388 \r
4389                                 for ( var i = 0; results[i]; i++ ) {\r
4390                                         if ( results[i].nodeType === 1 ) {\r
4391                                                 tmp.push( results[i] );\r
4392                                         }\r
4393                                 }\r
4394 \r
4395                                 results = tmp;\r
4396                         }\r
4397 \r
4398                         return results;\r
4399                 };\r
4400         }\r
4401 \r
4402         // Check to see if an attribute returns normalized href attributes\r
4403         div.innerHTML = "<a href='#'></a>";\r
4404         if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&\r
4405                         div.firstChild.getAttribute("href") !== "#" ) {\r
4406                 Expr.attrHandle.href = function(elem){\r
4407                         return elem.getAttribute("href", 2);\r
4408                 };\r
4409         }\r
4410 \r
4411         div = null; // release memory in IE\r
4412 })();\r
4413 \r
4414 if ( document.querySelectorAll ) {\r
4415         (function(){\r
4416                 var oldSizzle = Sizzle, div = document.createElement("div");\r
4417                 div.innerHTML = "<p class='TEST'></p>";\r
4418 \r
4419                 // Safari can't handle uppercase or unicode characters when\r
4420                 // in quirks mode.\r
4421                 if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {\r
4422                         return;\r
4423                 }\r
4424         \r
4425                 Sizzle = function(query, context, extra, seed){\r
4426                         context = context || document;\r
4427 \r
4428                         // Only use querySelectorAll on non-XML documents\r
4429                         // (ID selectors don't work in non-HTML documents)\r
4430                         if ( !seed && context.nodeType === 9 && !Sizzle.isXML(context) ) {\r
4431                                 try {\r
4432                                         return makeArray( context.querySelectorAll(query), extra );\r
4433                                 } catch(e){}\r
4434                         }\r
4435                 \r
4436                         return oldSizzle(query, context, extra, seed);\r
4437                 };\r
4438 \r
4439                 for ( var prop in oldSizzle ) {\r
4440                         Sizzle[ prop ] = oldSizzle[ prop ];\r
4441                 }\r
4442 \r
4443                 div = null; // release memory in IE\r
4444         })();\r
4445 }\r
4446 \r
4447 (function(){\r
4448         var div = document.createElement("div");\r
4449 \r
4450         div.innerHTML = "<div class='test e'></div><div class='test'></div>";\r
4451 \r
4452         // Opera can't find a second classname (in 9.6)\r
4453         // Also, make sure that getElementsByClassName actually exists\r
4454         if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {\r
4455                 return;\r
4456         }\r
4457 \r
4458         // Safari caches class attributes, doesn't catch changes (in 3.2)\r
4459         div.lastChild.className = "e";\r
4460 \r
4461         if ( div.getElementsByClassName("e").length === 1 ) {\r
4462                 return;\r
4463         }\r
4464         \r
4465         Expr.order.splice(1, 0, "CLASS");\r
4466         Expr.find.CLASS = function(match, context, isXML) {\r
4467                 if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {\r
4468                         return context.getElementsByClassName(match[1]);\r
4469                 }\r
4470         };\r
4471 \r
4472         div = null; // release memory in IE\r
4473 })();\r
4474 \r
4475 function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\r
4476         for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
4477                 var elem = checkSet[i];\r
4478                 if ( elem ) {\r
4479                         elem = elem[dir];\r
4480                         var match = false;\r
4481 \r
4482                         while ( elem ) {\r
4483                                 if ( elem.sizcache === doneName ) {\r
4484                                         match = checkSet[elem.sizset];\r
4485                                         break;\r
4486                                 }\r
4487 \r
4488                                 if ( elem.nodeType === 1 && !isXML ){\r
4489                                         elem.sizcache = doneName;\r
4490                                         elem.sizset = i;\r
4491                                 }\r
4492 \r
4493                                 if ( elem.nodeName.toLowerCase() === cur ) {\r
4494                                         match = elem;\r
4495                                         break;\r
4496                                 }\r
4497 \r
4498                                 elem = elem[dir];\r
4499                         }\r
4500 \r
4501                         checkSet[i] = match;\r
4502                 }\r
4503         }\r
4504 }\r
4505 \r
4506 function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\r
4507         for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
4508                 var elem = checkSet[i];\r
4509                 if ( elem ) {\r
4510                         elem = elem[dir];\r
4511                         var match = false;\r
4512 \r
4513                         while ( elem ) {\r
4514                                 if ( elem.sizcache === doneName ) {\r
4515                                         match = checkSet[elem.sizset];\r
4516                                         break;\r
4517                                 }\r
4518 \r
4519                                 if ( elem.nodeType === 1 ) {\r
4520                                         if ( !isXML ) {\r
4521                                                 elem.sizcache = doneName;\r
4522                                                 elem.sizset = i;\r
4523                                         }\r
4524                                         if ( typeof cur !== "string" ) {\r
4525                                                 if ( elem === cur ) {\r
4526                                                         match = true;\r
4527                                                         break;\r
4528                                                 }\r
4529 \r
4530                                         } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {\r
4531                                                 match = elem;\r
4532                                                 break;\r
4533                                         }\r
4534                                 }\r
4535 \r
4536                                 elem = elem[dir];\r
4537                         }\r
4538 \r
4539                         checkSet[i] = match;\r
4540                 }\r
4541         }\r
4542 }\r
4543 \r
4544 Sizzle.contains = document.compareDocumentPosition ? function(a, b){\r
4545         return !!(a.compareDocumentPosition(b) & 16);\r
4546 } : function(a, b){\r
4547         return a !== b && (a.contains ? a.contains(b) : true);\r
4548 };\r
4549 \r
4550 Sizzle.isXML = function(elem){\r
4551         // documentElement is verified for cases where it doesn't yet exist\r
4552         // (such as loading iframes in IE - #4833) \r
4553         var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;\r
4554         return documentElement ? documentElement.nodeName !== "HTML" : false;\r
4555 };\r
4556 \r
4557 var posProcess = function(selector, context){\r
4558         var tmpSet = [], later = "", match,\r
4559                 root = context.nodeType ? [context] : context;\r
4560 \r
4561         // Position selectors must be done after the filter\r
4562         // And so must :not(positional) so we move all PSEUDOs to the end\r
4563         while ( (match = Expr.match.PSEUDO.exec( selector )) ) {\r
4564                 later += match[0];\r
4565                 selector = selector.replace( Expr.match.PSEUDO, "" );\r
4566         }\r
4567 \r
4568         selector = Expr.relative[selector] ? selector + "*" : selector;\r
4569 \r
4570         for ( var i = 0, l = root.length; i < l; i++ ) {\r
4571                 Sizzle( selector, root[i], tmpSet );\r
4572         }\r
4573 \r
4574         return Sizzle.filter( later, tmpSet );\r
4575 };\r
4576 \r
4577 // EXPOSE\r
4578 \r
4579 window.tinymce.dom.Sizzle = Sizzle;\r
4580 \r
4581 })();\r
4582 \r
4583 \r
4584 (function(tinymce) {\r
4585         // Shorten names\r
4586         var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event;\r
4587 \r
4588         tinymce.create('tinymce.dom.EventUtils', {\r
4589                 EventUtils : function() {\r
4590                         this.inits = [];\r
4591                         this.events = [];\r
4592                 },\r
4593 \r
4594                 add : function(o, n, f, s) {\r
4595                         var cb, t = this, el = t.events, r;\r
4596 \r
4597                         if (n instanceof Array) {\r
4598                                 r = [];\r
4599 \r
4600                                 each(n, function(n) {\r
4601                                         r.push(t.add(o, n, f, s));\r
4602                                 });\r
4603 \r
4604                                 return r;\r
4605                         }\r
4606 \r
4607                         // Handle array\r
4608                         if (o && o.hasOwnProperty && o instanceof Array) {\r
4609                                 r = [];\r
4610 \r
4611                                 each(o, function(o) {\r
4612                                         o = DOM.get(o);\r
4613                                         r.push(t.add(o, n, f, s));\r
4614                                 });\r
4615 \r
4616                                 return r;\r
4617                         }\r
4618 \r
4619                         o = DOM.get(o);\r
4620 \r
4621                         if (!o)\r
4622                                 return;\r
4623 \r
4624                         // Setup event callback\r
4625                         cb = function(e) {\r
4626                                 // Is all events disabled\r
4627                                 if (t.disabled)\r
4628                                         return;\r
4629 \r
4630                                 e = e || window.event;\r
4631 \r
4632                                 // Patch in target, preventDefault and stopPropagation in IE it's W3C valid\r
4633                                 if (e && isIE) {\r
4634                                         if (!e.target)\r
4635                                                 e.target = e.srcElement;\r
4636 \r
4637                                         // Patch in preventDefault, stopPropagation methods for W3C compatibility\r
4638                                         tinymce.extend(e, t._stoppers);\r
4639                                 }\r
4640 \r
4641                                 if (!s)\r
4642                                         return f(e);\r
4643 \r
4644                                 return f.call(s, e);\r
4645                         };\r
4646 \r
4647                         if (n == 'unload') {\r
4648                                 tinymce.unloads.unshift({func : cb});\r
4649                                 return cb;\r
4650                         }\r
4651 \r
4652                         if (n == 'init') {\r
4653                                 if (t.domLoaded)\r
4654                                         cb();\r
4655                                 else\r
4656                                         t.inits.push(cb);\r
4657 \r
4658                                 return cb;\r
4659                         }\r
4660 \r
4661                         // Store away listener reference\r
4662                         el.push({\r
4663                                 obj : o,\r
4664                                 name : n,\r
4665                                 func : f,\r
4666                                 cfunc : cb,\r
4667                                 scope : s\r
4668                         });\r
4669 \r
4670                         t._add(o, n, cb);\r
4671 \r
4672                         return f;\r
4673                 },\r
4674 \r
4675                 remove : function(o, n, f) {\r
4676                         var t = this, a = t.events, s = false, r;\r
4677 \r
4678                         // Handle array\r
4679                         if (o && o.hasOwnProperty && o instanceof Array) {\r
4680                                 r = [];\r
4681 \r
4682                                 each(o, function(o) {\r
4683                                         o = DOM.get(o);\r
4684                                         r.push(t.remove(o, n, f));\r
4685                                 });\r
4686 \r
4687                                 return r;\r
4688                         }\r
4689 \r
4690                         o = DOM.get(o);\r
4691 \r
4692                         each(a, function(e, i) {\r
4693                                 if (e.obj == o && e.name == n && (!f || (e.func == f || e.cfunc == f))) {\r
4694                                         a.splice(i, 1);\r
4695                                         t._remove(o, n, e.cfunc);\r
4696                                         s = true;\r
4697                                         return false;\r
4698                                 }\r
4699                         });\r
4700 \r
4701                         return s;\r
4702                 },\r
4703 \r
4704                 clear : function(o) {\r
4705                         var t = this, a = t.events, i, e;\r
4706 \r
4707                         if (o) {\r
4708                                 o = DOM.get(o);\r
4709 \r
4710                                 for (i = a.length - 1; i >= 0; i--) {\r
4711                                         e = a[i];\r
4712 \r
4713                                         if (e.obj === o) {\r
4714                                                 t._remove(e.obj, e.name, e.cfunc);\r
4715                                                 e.obj = e.cfunc = null;\r
4716                                                 a.splice(i, 1);\r
4717                                         }\r
4718                                 }\r
4719                         }\r
4720                 },\r
4721 \r
4722                 cancel : function(e) {\r
4723                         if (!e)\r
4724                                 return false;\r
4725 \r
4726                         this.stop(e);\r
4727 \r
4728                         return this.prevent(e);\r
4729                 },\r
4730 \r
4731                 stop : function(e) {\r
4732                         if (e.stopPropagation)\r
4733                                 e.stopPropagation();\r
4734                         else\r
4735                                 e.cancelBubble = true;\r
4736 \r
4737                         return false;\r
4738                 },\r
4739 \r
4740                 prevent : function(e) {\r
4741                         if (e.preventDefault)\r
4742                                 e.preventDefault();\r
4743                         else\r
4744                                 e.returnValue = false;\r
4745 \r
4746                         return false;\r
4747                 },\r
4748 \r
4749                 destroy : function() {\r
4750                         var t = this;\r
4751 \r
4752                         each(t.events, function(e, i) {\r
4753                                 t._remove(e.obj, e.name, e.cfunc);\r
4754                                 e.obj = e.cfunc = null;\r
4755                         });\r
4756 \r
4757                         t.events = [];\r
4758                         t = null;\r
4759                 },\r
4760 \r
4761                 _add : function(o, n, f) {\r
4762                         if (o.attachEvent)\r
4763                                 o.attachEvent('on' + n, f);\r
4764                         else if (o.addEventListener)\r
4765                                 o.addEventListener(n, f, false);\r
4766                         else\r
4767                                 o['on' + n] = f;\r
4768                 },\r
4769 \r
4770                 _remove : function(o, n, f) {\r
4771                         if (o) {\r
4772                                 try {\r
4773                                         if (o.detachEvent)\r
4774                                                 o.detachEvent('on' + n, f);\r
4775                                         else if (o.removeEventListener)\r
4776                                                 o.removeEventListener(n, f, false);\r
4777                                         else\r
4778                                                 o['on' + n] = null;\r
4779                                 } catch (ex) {\r
4780                                         // Might fail with permission denined on IE so we just ignore that\r
4781                                 }\r
4782                         }\r
4783                 },\r
4784 \r
4785                 _pageInit : function(win) {\r
4786                         var t = this;\r
4787 \r
4788                         // Keep it from running more than once\r
4789                         if (t.domLoaded)\r
4790                                 return;\r
4791 \r
4792                         t.domLoaded = true;\r
4793 \r
4794                         each(t.inits, function(c) {\r
4795                                 c();\r
4796                         });\r
4797 \r
4798                         t.inits = [];\r
4799                 },\r
4800 \r
4801                 _wait : function(win) {\r
4802                         var t = this, doc = win.document;\r
4803 \r
4804                         // No need since the document is already loaded\r
4805                         if (win.tinyMCE_GZ && tinyMCE_GZ.loaded) {\r
4806                                 t.domLoaded = 1;\r
4807                                 return;\r
4808                         }\r
4809 \r
4810                         // Use IE method\r
4811                         if (doc.attachEvent) {\r
4812                                 doc.attachEvent("onreadystatechange", function() {\r
4813                                         if (doc.readyState === "complete") {\r
4814                                                 doc.detachEvent("onreadystatechange", arguments.callee);\r
4815                                                 t._pageInit(win);\r
4816                                         }\r
4817                                 });\r
4818 \r
4819                                 if (doc.documentElement.doScroll && win == win.top) {\r
4820                                         (function() {\r
4821                                                 if (t.domLoaded)\r
4822                                                         return;\r
4823 \r
4824                                                 try {\r
4825                                                         // If IE is used, use the trick by Diego Perini\r
4826                                                         // http://javascript.nwbox.com/IEContentLoaded/\r
4827                                                         doc.documentElement.doScroll("left");\r
4828                                                 } catch (ex) {\r
4829                                                         setTimeout(arguments.callee, 0);\r
4830                                                         return;\r
4831                                                 }\r
4832 \r
4833                                                 t._pageInit(win);\r
4834                                         })();\r
4835                                 }\r
4836                         } else if (doc.addEventListener) {\r
4837                                 t._add(win, 'DOMContentLoaded', function() {\r
4838                                         t._pageInit(win);\r
4839                                 });\r
4840                         }\r
4841 \r
4842                         t._add(win, 'load', function() {\r
4843                                 t._pageInit(win);\r
4844                         });\r
4845                 },\r
4846 \r
4847                 _stoppers : {\r
4848                         preventDefault :  function() {\r
4849                                 this.returnValue = false;\r
4850                         },\r
4851 \r
4852                         stopPropagation : function() {\r
4853                                 this.cancelBubble = true;\r
4854                         }\r
4855                 }\r
4856         });\r
4857 \r
4858         Event = tinymce.dom.Event = new tinymce.dom.EventUtils();\r
4859 \r
4860         // Dispatch DOM content loaded event for IE and Safari\r
4861         Event._wait(window);\r
4862 \r
4863         tinymce.addUnload(function() {\r
4864                 Event.destroy();\r
4865         });\r
4866 })(tinymce);\r
4867 \r
4868 (function(tinymce) {\r
4869         tinymce.dom.Element = function(id, settings) {\r
4870                 var t = this, dom, el;\r
4871 \r
4872                 t.settings = settings = settings || {};\r
4873                 t.id = id;\r
4874                 t.dom = dom = settings.dom || tinymce.DOM;\r
4875 \r
4876                 // Only IE leaks DOM references, this is a lot faster\r
4877                 if (!tinymce.isIE)\r
4878                         el = dom.get(t.id);\r
4879 \r
4880                 tinymce.each(\r
4881                                 ('getPos,getRect,getParent,add,setStyle,getStyle,setStyles,' + \r
4882                                 'setAttrib,setAttribs,getAttrib,addClass,removeClass,' + \r
4883                                 'hasClass,getOuterHTML,setOuterHTML,remove,show,hide,' + \r
4884                                 'isHidden,setHTML,get').split(/,/)\r
4885                         , function(k) {\r
4886                                 t[k] = function() {\r
4887                                         var a = [id], i;\r
4888 \r
4889                                         for (i = 0; i < arguments.length; i++)\r
4890                                                 a.push(arguments[i]);\r
4891 \r
4892                                         a = dom[k].apply(dom, a);\r
4893                                         t.update(k);\r
4894 \r
4895                                         return a;\r
4896                                 };\r
4897                 });\r
4898 \r
4899                 tinymce.extend(t, {\r
4900                         on : function(n, f, s) {\r
4901                                 return tinymce.dom.Event.add(t.id, n, f, s);\r
4902                         },\r
4903 \r
4904                         getXY : function() {\r
4905                                 return {\r
4906                                         x : parseInt(t.getStyle('left')),\r
4907                                         y : parseInt(t.getStyle('top'))\r
4908                                 };\r
4909                         },\r
4910 \r
4911                         getSize : function() {\r
4912                                 var n = dom.get(t.id);\r
4913 \r
4914                                 return {\r
4915                                         w : parseInt(t.getStyle('width') || n.clientWidth),\r
4916                                         h : parseInt(t.getStyle('height') || n.clientHeight)\r
4917                                 };\r
4918                         },\r
4919 \r
4920                         moveTo : function(x, y) {\r
4921                                 t.setStyles({left : x, top : y});\r
4922                         },\r
4923 \r
4924                         moveBy : function(x, y) {\r
4925                                 var p = t.getXY();\r
4926 \r
4927                                 t.moveTo(p.x + x, p.y + y);\r
4928                         },\r
4929 \r
4930                         resizeTo : function(w, h) {\r
4931                                 t.setStyles({width : w, height : h});\r
4932                         },\r
4933 \r
4934                         resizeBy : function(w, h) {\r
4935                                 var s = t.getSize();\r
4936 \r
4937                                 t.resizeTo(s.w + w, s.h + h);\r
4938                         },\r
4939 \r
4940                         update : function(k) {\r
4941                                 var b;\r
4942 \r
4943                                 if (tinymce.isIE6 && settings.blocker) {\r
4944                                         k = k || '';\r
4945 \r
4946                                         // Ignore getters\r
4947                                         if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0)\r
4948                                                 return;\r
4949 \r
4950                                         // Remove blocker on remove\r
4951                                         if (k == 'remove') {\r
4952                                                 dom.remove(t.blocker);\r
4953                                                 return;\r
4954                                         }\r
4955 \r
4956                                         if (!t.blocker) {\r
4957                                                 t.blocker = dom.uniqueId();\r
4958                                                 b = dom.add(settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'});\r
4959                                                 dom.setStyle(b, 'opacity', 0);\r
4960                                         } else\r
4961                                                 b = dom.get(t.blocker);\r
4962 \r
4963                                         dom.setStyles(b, {\r
4964                                                 left : t.getStyle('left', 1),\r
4965                                                 top : t.getStyle('top', 1),\r
4966                                                 width : t.getStyle('width', 1),\r
4967                                                 height : t.getStyle('height', 1),\r
4968                                                 display : t.getStyle('display', 1),\r
4969                                                 zIndex : parseInt(t.getStyle('zIndex', 1) || 0) - 1\r
4970                                         });\r
4971                                 }\r
4972                         }\r
4973                 });\r
4974         };\r
4975 })(tinymce);\r
4976 \r
4977 (function(tinymce) {\r
4978         function trimNl(s) {\r
4979                 return s.replace(/[\n\r]+/g, '');\r
4980         };\r
4981 \r
4982         // Shorten names\r
4983         var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each;\r
4984 \r
4985         tinymce.create('tinymce.dom.Selection', {\r
4986                 Selection : function(dom, win, serializer) {\r
4987                         var t = this;\r
4988 \r
4989                         t.dom = dom;\r
4990                         t.win = win;\r
4991                         t.serializer = serializer;\r
4992 \r
4993                         // Add events\r
4994                         each([\r
4995                                 'onBeforeSetContent',\r
4996                                 'onBeforeGetContent',\r
4997                                 'onSetContent',\r
4998                                 'onGetContent'\r
4999                         ], function(e) {\r
5000                                 t[e] = new tinymce.util.Dispatcher(t);\r
5001                         });\r
5002 \r
5003                         // No W3C Range support\r
5004                         if (!t.win.getSelection)\r
5005                                 t.tridentSel = new tinymce.dom.TridentSelection(t);\r
5006 \r
5007                         if (tinymce.isIE && dom.boxModel)\r
5008                                 this._fixIESelection();\r
5009 \r
5010                         // Prevent leaks\r
5011                         tinymce.addUnload(t.destroy, t);\r
5012                 },\r
5013 \r
5014                 getContent : function(s) {\r
5015                         var t = this, r = t.getRng(), e = t.dom.create("body"), se = t.getSel(), wb, wa, n;\r
5016 \r
5017                         s = s || {};\r
5018                         wb = wa = '';\r
5019                         s.get = true;\r
5020                         s.format = s.format || 'html';\r
5021                         t.onBeforeGetContent.dispatch(t, s);\r
5022 \r
5023                         if (s.format == 'text')\r
5024                                 return t.isCollapsed() ? '' : (r.text || (se.toString ? se.toString() : ''));\r
5025 \r
5026                         if (r.cloneContents) {\r
5027                                 n = r.cloneContents();\r
5028 \r
5029                                 if (n)\r
5030                                         e.appendChild(n);\r
5031                         } else if (is(r.item) || is(r.htmlText))\r
5032                                 e.innerHTML = r.item ? r.item(0).outerHTML : r.htmlText;\r
5033                         else\r
5034                                 e.innerHTML = r.toString();\r
5035 \r
5036                         // Keep whitespace before and after\r
5037                         if (/^\s/.test(e.innerHTML))\r
5038                                 wb = ' ';\r
5039 \r
5040                         if (/\s+$/.test(e.innerHTML))\r
5041                                 wa = ' ';\r
5042 \r
5043                         s.getInner = true;\r
5044 \r
5045                         s.content = t.isCollapsed() ? '' : wb + t.serializer.serialize(e, s) + wa;\r
5046                         t.onGetContent.dispatch(t, s);\r
5047 \r
5048                         return s.content;\r
5049                 },\r
5050 \r
5051                 setContent : function(h, s) {\r
5052                         var t = this, r = t.getRng(), c, d = t.win.document;\r
5053 \r
5054                         s = s || {format : 'html'};\r
5055                         s.set = true;\r
5056                         h = s.content = t.dom.processHTML(h);\r
5057 \r
5058                         // Dispatch before set content event\r
5059                         t.onBeforeSetContent.dispatch(t, s);\r
5060                         h = s.content;\r
5061 \r
5062                         if (r.insertNode) {\r
5063                                 // Make caret marker since insertNode places the caret in the beginning of text after insert\r
5064                                 h += '<span id="__caret">_</span>';\r
5065 \r
5066                                 // Delete and insert new node\r
5067                                 \r
5068                                 if (r.startContainer == d && r.endContainer ==  d) {\r
5069                                         // WebKit will fail if the body is empty since the range is then invalid and it can't insert contents\r
5070                                         d.body.innerHTML = h;\r
5071                                 } else {\r
5072                                         r.deleteContents();\r
5073                                         if (d.body.childNodes.length == 0) {\r
5074                                                 d.body.innerHTML = h;\r
5075                                         } else {\r
5076                                                 // createContextualFragment doesn't exists in IE 9 DOMRanges\r
5077                                                 if (r.createContextualFragment) {\r
5078                                                         r.insertNode(r.createContextualFragment(h));\r
5079                                                 } else {\r
5080                                                         // Fake createContextualFragment call in IE 9\r
5081                                                         var frag = d.createDocumentFragment(), temp = d.createElement('div');\r
5082 \r
5083                                                         frag.appendChild(temp);\r
5084                                                         temp.outerHTML = h;\r
5085 \r
5086                                                         r.insertNode(frag);\r
5087                                                 }\r
5088                                         }\r
5089                                 }\r
5090 \r
5091                                 // Move to caret marker\r
5092                                 c = t.dom.get('__caret');\r
5093                                 // Make sure we wrap it compleatly, Opera fails with a simple select call\r
5094                                 r = d.createRange();\r
5095                                 r.setStartBefore(c);\r
5096                                 r.setEndBefore(c);\r
5097                                 t.setRng(r);\r
5098 \r
5099                                 // Remove the caret position\r
5100                                 t.dom.remove('__caret');\r
5101                         } else {\r
5102                                 if (r.item) {\r
5103                                         // Delete content and get caret text selection\r
5104                                         d.execCommand('Delete', false, null);\r
5105                                         r = t.getRng();\r
5106                                 }\r
5107 \r
5108                                 r.pasteHTML(h);\r
5109                         }\r
5110 \r
5111                         // Dispatch set content event\r
5112                         t.onSetContent.dispatch(t, s);\r
5113                 },\r
5114 \r
5115                 getStart : function() {\r
5116                         var rng = this.getRng(), startElement, parentElement, checkRng, node;\r
5117 \r
5118                         if (rng.duplicate || rng.item) {\r
5119                                 // Control selection, return first item\r
5120                                 if (rng.item)\r
5121                                         return rng.item(0);\r
5122 \r
5123                                 // Get start element\r
5124                                 checkRng = rng.duplicate();\r
5125                                 checkRng.collapse(1);\r
5126                                 startElement = checkRng.parentElement();\r
5127 \r
5128                                 // Check if range parent is inside the start element, then return the inner parent element\r
5129                                 // This will fix issues when a single element is selected, IE would otherwise return the wrong start element\r
5130                                 parentElement = node = rng.parentElement();\r
5131                                 while (node = node.parentNode) {\r
5132                                         if (node == startElement) {\r
5133                                                 startElement = parentElement;\r
5134                                                 break;\r
5135                                         }\r
5136                                 }\r
5137 \r
5138                                 // If start element is body element try to move to the first child if it exists\r
5139                                 if (startElement && startElement.nodeName == 'BODY')\r
5140                                         return startElement.firstChild || startElement;\r
5141 \r
5142                                 return startElement;\r
5143                         } else {\r
5144                                 startElement = rng.startContainer;\r
5145 \r
5146                                 if (startElement.nodeType == 1 && startElement.hasChildNodes())\r
5147                                         startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];\r
5148 \r
5149                                 if (startElement && startElement.nodeType == 3)\r
5150                                         return startElement.parentNode;\r
5151 \r
5152                                 return startElement;\r
5153                         }\r
5154                 },\r
5155 \r
5156                 getEnd : function() {\r
5157                         var t = this, r = t.getRng(), e, eo;\r
5158 \r
5159                         if (r.duplicate || r.item) {\r
5160                                 if (r.item)\r
5161                                         return r.item(0);\r
5162 \r
5163                                 r = r.duplicate();\r
5164                                 r.collapse(0);\r
5165                                 e = r.parentElement();\r
5166 \r
5167                                 if (e && e.nodeName == 'BODY')\r
5168                                         return e.lastChild || e;\r
5169 \r
5170                                 return e;\r
5171                         } else {\r
5172                                 e = r.endContainer;\r
5173                                 eo = r.endOffset;\r
5174 \r
5175                                 if (e.nodeType == 1 && e.hasChildNodes())\r
5176                                         e = e.childNodes[eo > 0 ? eo - 1 : eo];\r
5177 \r
5178                                 if (e && e.nodeType == 3)\r
5179                                         return e.parentNode;\r
5180 \r
5181                                 return e;\r
5182                         }\r
5183                 },\r
5184 \r
5185                 getBookmark : function(type, normalized) {\r
5186                         var t = this, dom = t.dom, rng, rng2, id, collapsed, name, element, index, chr = '\uFEFF', styles;\r
5187 \r
5188                         function findIndex(name, element) {\r
5189                                 var index = 0;\r
5190 \r
5191                                 each(dom.select(name), function(node, i) {\r
5192                                         if (node == element)\r
5193                                                 index = i;\r
5194                                 });\r
5195 \r
5196                                 return index;\r
5197                         };\r
5198 \r
5199                         if (type == 2) {\r
5200                                 function getLocation() {\r
5201                                         var rng = t.getRng(true), root = dom.getRoot(), bookmark = {};\r
5202 \r
5203                                         function getPoint(rng, start) {\r
5204                                                 var container = rng[start ? 'startContainer' : 'endContainer'],\r
5205                                                         offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;\r
5206 \r
5207                                                 if (container.nodeType == 3) {\r
5208                                                         if (normalized) {\r
5209                                                                 for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling)\r
5210                                                                         offset += node.nodeValue.length;\r
5211                                                         }\r
5212 \r
5213                                                         point.push(offset);\r
5214                                                 } else {\r
5215                                                         childNodes = container.childNodes;\r
5216 \r
5217                                                         if (offset >= childNodes.length && childNodes.length) {\r
5218                                                                 after = 1;\r
5219                                                                 offset = Math.max(0, childNodes.length - 1);\r
5220                                                         }\r
5221 \r
5222                                                         point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after);\r
5223                                                 }\r
5224 \r
5225                                                 for (; container && container != root; container = container.parentNode)\r
5226                                                         point.push(t.dom.nodeIndex(container, normalized));\r
5227 \r
5228                                                 return point;\r
5229                                         };\r
5230 \r
5231                                         bookmark.start = getPoint(rng, true);\r
5232 \r
5233                                         if (!t.isCollapsed())\r
5234                                                 bookmark.end = getPoint(rng);\r
5235 \r
5236                                         return bookmark;\r
5237                                 };\r
5238 \r
5239                                 return getLocation();\r
5240                         }\r
5241 \r
5242                         // Handle simple range\r
5243                         if (type)\r
5244                                 return {rng : t.getRng()};\r
5245 \r
5246                         rng = t.getRng();\r
5247                         id = dom.uniqueId();\r
5248                         collapsed = tinyMCE.activeEditor.selection.isCollapsed();\r
5249                         styles = 'overflow:hidden;line-height:0px';\r
5250 \r
5251                         // Explorer method\r
5252                         if (rng.duplicate || rng.item) {\r
5253                                 // Text selection\r
5254                                 if (!rng.item) {\r
5255                                         rng2 = rng.duplicate();\r
5256 \r
5257                                         // Insert start marker\r
5258                                         rng.collapse();\r
5259                                         rng.pasteHTML('<span _mce_type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');\r
5260 \r
5261                                         // Insert end marker\r
5262                                         if (!collapsed) {\r
5263                                                 rng2.collapse(false);\r
5264                                                 rng2.pasteHTML('<span _mce_type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');\r
5265                                         }\r
5266                                 } else {\r
5267                                         // Control selection\r
5268                                         element = rng.item(0);\r
5269                                         name = element.nodeName;\r
5270 \r
5271                                         return {name : name, index : findIndex(name, element)};\r
5272                                 }\r
5273                         } else {\r
5274                                 element = t.getNode();\r
5275                                 name = element.nodeName;\r
5276                                 if (name == 'IMG')\r
5277                                         return {name : name, index : findIndex(name, element)};\r
5278 \r
5279                                 // W3C method\r
5280                                 rng2 = rng.cloneRange();\r
5281 \r
5282                                 // Insert end marker\r
5283                                 if (!collapsed) {\r
5284                                         rng2.collapse(false);\r
5285                                         rng2.insertNode(dom.create('span', {_mce_type : "bookmark", id : id + '_end', style : styles}, chr));\r
5286                                 }\r
5287 \r
5288                                 rng.collapse(true);\r
5289                                 rng.insertNode(dom.create('span', {_mce_type : "bookmark", id : id + '_start', style : styles}, chr));\r
5290                         }\r
5291 \r
5292                         t.moveToBookmark({id : id, keep : 1});\r
5293 \r
5294                         return {id : id};\r
5295                 },\r
5296 \r
5297                 moveToBookmark : function(bookmark) {\r
5298                         var t = this, dom = t.dom, marker1, marker2, rng, root, startContainer, endContainer, startOffset, endOffset;\r
5299 \r
5300                         // Clear selection cache\r
5301                         if (t.tridentSel)\r
5302                                 t.tridentSel.destroy();\r
5303 \r
5304                         if (bookmark) {\r
5305                                 if (bookmark.start) {\r
5306                                         rng = dom.createRng();\r
5307                                         root = dom.getRoot();\r
5308 \r
5309                                         function setEndPoint(start) {\r
5310                                                 var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;\r
5311 \r
5312                                                 if (point) {\r
5313                                                         // Find container node\r
5314                                                         for (node = root, i = point.length - 1; i >= 1; i--) {\r
5315                                                                 children = node.childNodes;\r
5316 \r
5317                                                                 if (children.length)\r
5318                                                                         node = children[point[i]];\r
5319                                                         }\r
5320 \r
5321                                                         // Set offset within container node\r
5322                                                         if (start)\r
5323                                                                 rng.setStart(node, point[0]);\r
5324                                                         else\r
5325                                                                 rng.setEnd(node, point[0]);\r
5326                                                 }\r
5327                                         };\r
5328 \r
5329                                         setEndPoint(true);\r
5330                                         setEndPoint();\r
5331 \r
5332                                         t.setRng(rng);\r
5333                                 } else if (bookmark.id) {\r
5334                                         function restoreEndPoint(suffix) {\r
5335                                                 var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;\r
5336 \r
5337                                                 if (marker) {\r
5338                                                         node = marker.parentNode;\r
5339 \r
5340                                                         if (suffix == 'start') {\r
5341                                                                 if (!keep) {\r
5342                                                                         idx = dom.nodeIndex(marker);\r
5343                                                                 } else {\r
5344                                                                         node = marker.firstChild;\r
5345                                                                         idx = 1;\r
5346                                                                 }\r
5347 \r
5348                                                                 startContainer = endContainer = node;\r
5349                                                                 startOffset = endOffset = idx;\r
5350                                                         } else {\r
5351                                                                 if (!keep) {\r
5352                                                                         idx = dom.nodeIndex(marker);\r
5353                                                                 } else {\r
5354                                                                         node = marker.firstChild;\r
5355                                                                         idx = 1;\r
5356                                                                 }\r
5357 \r
5358                                                                 endContainer = node;\r
5359                                                                 endOffset = idx;\r
5360                                                         }\r
5361 \r
5362                                                         if (!keep) {\r
5363                                                                 prev = marker.previousSibling;\r
5364                                                                 next = marker.nextSibling;\r
5365 \r
5366                                                                 // Remove all marker text nodes\r
5367                                                                 each(tinymce.grep(marker.childNodes), function(node) {\r
5368                                                                         if (node.nodeType == 3)\r
5369                                                                                 node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');\r
5370                                                                 });\r
5371 \r
5372                                                                 // Remove marker but keep children if for example contents where inserted into the marker\r
5373                                                                 // Also remove duplicated instances of the marker for example by a split operation or by WebKit auto split on paste feature\r
5374                                                                 while (marker = dom.get(bookmark.id + '_' + suffix))\r
5375                                                                         dom.remove(marker, 1);\r
5376 \r
5377                                                                 // If siblings are text nodes then merge them unless it's Opera since it some how removes the node\r
5378                                                                 // 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
5379                                                                 if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !tinymce.isOpera) {\r
5380                                                                         idx = prev.nodeValue.length;\r
5381                                                                         prev.appendData(next.nodeValue);\r
5382                                                                         dom.remove(next);\r
5383 \r
5384                                                                         if (suffix == 'start') {\r
5385                                                                                 startContainer = endContainer = prev;\r
5386                                                                                 startOffset = endOffset = idx;\r
5387                                                                         } else {\r
5388                                                                                 endContainer = prev;\r
5389                                                                                 endOffset = idx;\r
5390                                                                         }\r
5391                                                                 }\r
5392                                                         }\r
5393                                                 }\r
5394                                         };\r
5395 \r
5396                                         function addBogus(node) {\r
5397                                                 // Adds a bogus BR element for empty block elements\r
5398                                                 // on non IE browsers just to have a place to put the caret\r
5399                                                 if (!isIE && dom.isBlock(node) && !node.innerHTML)\r
5400                                                         node.innerHTML = '<br _mce_bogus="1" />';\r
5401 \r
5402                                                 return node;\r
5403                                         };\r
5404 \r
5405                                         // Restore start/end points\r
5406                                         restoreEndPoint('start');\r
5407                                         restoreEndPoint('end');\r
5408 \r
5409                                         if (startContainer) {\r
5410                                                 rng = dom.createRng();\r
5411                                                 rng.setStart(addBogus(startContainer), startOffset);\r
5412                                                 rng.setEnd(addBogus(endContainer), endOffset);\r
5413                                                 t.setRng(rng);\r
5414                                         }\r
5415                                 } else if (bookmark.name) {\r
5416                                         t.select(dom.select(bookmark.name)[bookmark.index]);\r
5417                                 } else if (bookmark.rng)\r
5418                                         t.setRng(bookmark.rng);\r
5419                         }\r
5420                 },\r
5421 \r
5422                 select : function(node, content) {\r
5423                         var t = this, dom = t.dom, rng = dom.createRng(), idx;\r
5424 \r
5425                         idx = dom.nodeIndex(node);\r
5426                         rng.setStart(node.parentNode, idx);\r
5427                         rng.setEnd(node.parentNode, idx + 1);\r
5428 \r
5429                         // Find first/last text node or BR element\r
5430                         if (content) {\r
5431                                 function setPoint(node, start) {\r
5432                                         var walker = new tinymce.dom.TreeWalker(node, node);\r
5433 \r
5434                                         do {\r
5435                                                 // Text node\r
5436                                                 if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length != 0) {\r
5437                                                         if (start)\r
5438                                                                 rng.setStart(node, 0);\r
5439                                                         else\r
5440                                                                 rng.setEnd(node, node.nodeValue.length);\r
5441 \r
5442                                                         return;\r
5443                                                 }\r
5444 \r
5445                                                 // BR element\r
5446                                                 if (node.nodeName == 'BR') {\r
5447                                                         if (start)\r
5448                                                                 rng.setStartBefore(node);\r
5449                                                         else\r
5450                                                                 rng.setEndBefore(node);\r
5451 \r
5452                                                         return;\r
5453                                                 }\r
5454                                         } while (node = (start ? walker.next() : walker.prev()));\r
5455                                 };\r
5456 \r
5457                                 setPoint(node, 1);\r
5458                                 setPoint(node);\r
5459                         }\r
5460 \r
5461                         t.setRng(rng);\r
5462 \r
5463                         return node;\r
5464                 },\r
5465 \r
5466                 isCollapsed : function() {\r
5467                         var t = this, r = t.getRng(), s = t.getSel();\r
5468 \r
5469                         if (!r || r.item)\r
5470                                 return false;\r
5471 \r
5472                         if (r.compareEndPoints)\r
5473                                 return r.compareEndPoints('StartToEnd', r) === 0;\r
5474 \r
5475                         return !s || r.collapsed;\r
5476                 },\r
5477 \r
5478                 collapse : function(b) {\r
5479                         var t = this, r = t.getRng(), n;\r
5480 \r
5481                         // Control range on IE\r
5482                         if (r.item) {\r
5483                                 n = r.item(0);\r
5484                                 r = this.win.document.body.createTextRange();\r
5485                                 r.moveToElementText(n);\r
5486                         }\r
5487 \r
5488                         r.collapse(!!b);\r
5489                         t.setRng(r);\r
5490                 },\r
5491 \r
5492                 getSel : function() {\r
5493                         var t = this, w = this.win;\r
5494 \r
5495                         return w.getSelection ? w.getSelection() : w.document.selection;\r
5496                 },\r
5497 \r
5498                 getRng : function(w3c) {\r
5499                         var t = this, s, r, elm, doc = t.win.document;\r
5500 \r
5501                         // Found tridentSel object then we need to use that one\r
5502                         if (w3c && t.tridentSel)\r
5503                                 return t.tridentSel.getRangeAt(0);\r
5504 \r
5505                         try {\r
5506                                 if (s = t.getSel())\r
5507                                         r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : doc.createRange());\r
5508                         } catch (ex) {\r
5509                                 // IE throws unspecified error here if TinyMCE is placed in a frame/iframe\r
5510                         }\r
5511 \r
5512                         // We have W3C ranges and it's IE then fake control selection since IE9 doesn't handle that correctly yet\r
5513                         if (tinymce.isIE && r.setStart && doc.selection.createRange().item) {\r
5514                                 elm = doc.selection.createRange().item(0);\r
5515                                 r = doc.createRange();\r
5516                                 r.setStartBefore(elm);\r
5517                                 r.setEndAfter(elm);\r
5518                         }\r
5519 \r
5520                         // No range found then create an empty one\r
5521                         // This can occur when the editor is placed in a hidden container element on Gecko\r
5522                         // Or on IE when there was an exception\r
5523                         if (!r)\r
5524                                 r = doc.createRange ? doc.createRange() : doc.body.createTextRange();\r
5525 \r
5526                         if (t.selectedRange && t.explicitRange) {\r
5527                                 if (r.compareBoundaryPoints(r.START_TO_START, t.selectedRange) === 0 && r.compareBoundaryPoints(r.END_TO_END, t.selectedRange) === 0) {\r
5528                                         // Safari, Opera and Chrome only ever select text which causes the range to change.\r
5529                                         // This lets us use the originally set range if the selection hasn't been changed by the user.\r
5530                                         r = t.explicitRange;\r
5531                                 } else {\r
5532                                         t.selectedRange = null;\r
5533                                         t.explicitRange = null;\r
5534                                 }\r
5535                         }\r
5536                         return r;\r
5537                 },\r
5538 \r
5539                 setRng : function(r) {\r
5540                         var s, t = this;\r
5541                         \r
5542                         if (!t.tridentSel) {\r
5543                                 s = t.getSel();\r
5544 \r
5545                                 if (s) {\r
5546                                         t.explicitRange = r;\r
5547                                         s.removeAllRanges();\r
5548                                         s.addRange(r);\r
5549                                         t.selectedRange = s.getRangeAt(0);\r
5550                                 }\r
5551                         } else {\r
5552                                 // Is W3C Range\r
5553                                 if (r.cloneRange) {\r
5554                                         t.tridentSel.addRange(r);\r
5555                                         return;\r
5556                                 }\r
5557 \r
5558                                 // Is IE specific range\r
5559                                 try {\r
5560                                         r.select();\r
5561                                 } catch (ex) {\r
5562                                         // Needed for some odd IE bug #1843306\r
5563                                 }\r
5564                         }\r
5565                 },\r
5566 \r
5567                 setNode : function(n) {\r
5568                         var t = this;\r
5569 \r
5570                         t.setContent(t.dom.getOuterHTML(n));\r
5571 \r
5572                         return n;\r
5573                 },\r
5574 \r
5575                 getNode : function() {\r
5576                         var t = this, rng = t.getRng(), sel = t.getSel(), elm;\r
5577 \r
5578                         if (rng.setStart) {\r
5579                                 // Range maybe lost after the editor is made visible again\r
5580                                 if (!rng)\r
5581                                         return t.dom.getRoot();\r
5582 \r
5583                                 elm = rng.commonAncestorContainer;\r
5584 \r
5585                                 // Handle selection a image or other control like element such as anchors\r
5586                                 if (!rng.collapsed) {\r
5587                                         if (rng.startContainer == rng.endContainer) {\r
5588                                                 if (rng.startOffset - rng.endOffset < 2) {\r
5589                                                         if (rng.startContainer.hasChildNodes())\r
5590                                                                 elm = rng.startContainer.childNodes[rng.startOffset];\r
5591                                                 }\r
5592                                         }\r
5593 \r
5594                                         // If the anchor node is a element instead of a text node then return this element\r
5595                                         if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1) \r
5596                                                 return sel.anchorNode.childNodes[sel.anchorOffset]; \r
5597                                 }\r
5598 \r
5599                                 if (elm && elm.nodeType == 3)\r
5600                                         return elm.parentNode;\r
5601 \r
5602                                 return elm;\r
5603                         }\r
5604 \r
5605                         return rng.item ? rng.item(0) : rng.parentElement();\r
5606                 },\r
5607 \r
5608                 getSelectedBlocks : function(st, en) {\r
5609                         var t = this, dom = t.dom, sb, eb, n, bl = [];\r
5610 \r
5611                         sb = dom.getParent(st || t.getStart(), dom.isBlock);\r
5612                         eb = dom.getParent(en || t.getEnd(), dom.isBlock);\r
5613 \r
5614                         if (sb)\r
5615                                 bl.push(sb);\r
5616 \r
5617                         if (sb && eb && sb != eb) {\r
5618                                 n = sb;\r
5619 \r
5620                                 while ((n = n.nextSibling) && n != eb) {\r
5621                                         if (dom.isBlock(n))\r
5622                                                 bl.push(n);\r
5623                                 }\r
5624                         }\r
5625 \r
5626                         if (eb && sb != eb)\r
5627                                 bl.push(eb);\r
5628 \r
5629                         return bl;\r
5630                 },\r
5631 \r
5632                 destroy : function(s) {\r
5633                         var t = this;\r
5634 \r
5635                         t.win = null;\r
5636 \r
5637                         if (t.tridentSel)\r
5638                                 t.tridentSel.destroy();\r
5639 \r
5640                         // Manual destroy then remove unload handler\r
5641                         if (!s)\r
5642                                 tinymce.removeUnload(t.destroy);\r
5643                 },\r
5644 \r
5645                 // 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
5646                 _fixIESelection : function() {\r
5647                         var dom = this.dom, doc = dom.doc, body = doc.body, started, startRng;\r
5648 \r
5649                         // Make HTML element unselectable since we are going to handle selection by hand\r
5650                         doc.documentElement.unselectable = true;\r
5651 \r
5652                         // Return range from point or null if it failed\r
5653                         function rngFromPoint(x, y) {\r
5654                                 var rng = body.createTextRange();\r
5655 \r
5656                                 try {\r
5657                                         rng.moveToPoint(x, y);\r
5658                                 } catch (ex) {\r
5659                                         // IE sometimes throws and exception, so lets just ignore it\r
5660                                         rng = null;\r
5661                                 }\r
5662 \r
5663                                 return rng;\r
5664                         };\r
5665 \r
5666                         // Fires while the selection is changing\r
5667                         function selectionChange(e) {\r
5668                                 var pointRng;\r
5669 \r
5670                                 // Check if the button is down or not\r
5671                                 if (e.button) {\r
5672                                         // Create range from mouse position\r
5673                                         pointRng = rngFromPoint(e.x, e.y);\r
5674 \r
5675                                         if (pointRng) {\r
5676                                                 // Check if pointRange is before/after selection then change the endPoint\r
5677                                                 if (pointRng.compareEndPoints('StartToStart', startRng) > 0)\r
5678                                                         pointRng.setEndPoint('StartToStart', startRng);\r
5679                                                 else\r
5680                                                         pointRng.setEndPoint('EndToEnd', startRng);\r
5681 \r
5682                                                 pointRng.select();\r
5683                                         }\r
5684                                 } else\r
5685                                         endSelection();\r
5686                         }\r
5687 \r
5688                         // Removes listeners\r
5689                         function endSelection() {\r
5690                                 dom.unbind(doc, 'mouseup', endSelection);\r
5691                                 dom.unbind(doc, 'mousemove', selectionChange);\r
5692                                 started = 0;\r
5693                         };\r
5694 \r
5695                         // Detect when user selects outside BODY\r
5696                         dom.bind(doc, 'mousedown', function(e) {\r
5697                                 if (e.target.nodeName === 'HTML') {\r
5698                                         if (started)\r
5699                                                 endSelection();\r
5700 \r
5701                                         started = 1;\r
5702 \r
5703                                         // Setup start position\r
5704                                         startRng = rngFromPoint(e.x, e.y);\r
5705                                         if (startRng) {\r
5706                                                 // Listen for selection change events\r
5707                                                 dom.bind(doc, 'mouseup', endSelection);\r
5708                                                 dom.bind(doc, 'mousemove', selectionChange);\r
5709 \r
5710                                                 dom.win.focus();\r
5711                                                 startRng.select();\r
5712                                         }\r
5713                                 }\r
5714                         });\r
5715                 }\r
5716         });\r
5717 })(tinymce);\r
5718 \r
5719 (function(tinymce) {\r
5720         tinymce.create('tinymce.dom.XMLWriter', {\r
5721                 node : null,\r
5722 \r
5723                 XMLWriter : function(s) {\r
5724                         // Get XML document\r
5725                         function getXML() {\r
5726                                 var i = document.implementation;\r
5727 \r
5728                                 if (!i || !i.createDocument) {\r
5729                                         // Try IE objects\r
5730                                         try {return new ActiveXObject('MSXML2.DOMDocument');} catch (ex) {}\r
5731                                         try {return new ActiveXObject('Microsoft.XmlDom');} catch (ex) {}\r
5732                                 } else\r
5733                                         return i.createDocument('', '', null);\r
5734                         };\r
5735 \r
5736                         this.doc = getXML();\r
5737                         \r
5738                         // Since Opera and WebKit doesn't escape > into &gt; we need to do it our self to normalize the output for all browsers\r
5739                         this.valid = tinymce.isOpera || tinymce.isWebKit;\r
5740 \r
5741                         this.reset();\r
5742                 },\r
5743 \r
5744                 reset : function() {\r
5745                         var t = this, d = t.doc;\r
5746 \r
5747                         if (d.firstChild)\r
5748                                 d.removeChild(d.firstChild);\r
5749 \r
5750                         t.node = d.appendChild(d.createElement("html"));\r
5751                 },\r
5752 \r
5753                 writeStartElement : function(n) {\r
5754                         var t = this;\r
5755 \r
5756                         t.node = t.node.appendChild(t.doc.createElement(n));\r
5757                 },\r
5758 \r
5759                 writeAttribute : function(n, v) {\r
5760                         if (this.valid)\r
5761                                 v = v.replace(/>/g, '%MCGT%');\r
5762 \r
5763                         this.node.setAttribute(n, v);\r
5764                 },\r
5765 \r
5766                 writeEndElement : function() {\r
5767                         this.node = this.node.parentNode;\r
5768                 },\r
5769 \r
5770                 writeFullEndElement : function() {\r
5771                         var t = this, n = t.node;\r
5772 \r
5773                         n.appendChild(t.doc.createTextNode(""));\r
5774                         t.node = n.parentNode;\r
5775                 },\r
5776 \r
5777                 writeText : function(v) {\r
5778                         if (this.valid)\r
5779                                 v = v.replace(/>/g, '%MCGT%');\r
5780 \r
5781                         this.node.appendChild(this.doc.createTextNode(v));\r
5782                 },\r
5783 \r
5784                 writeCDATA : function(v) {\r
5785                         this.node.appendChild(this.doc.createCDATASection(v));\r
5786                 },\r
5787 \r
5788                 writeComment : function(v) {\r
5789                         // Fix for bug #2035694\r
5790                         if (tinymce.isIE)\r
5791                                 v = v.replace(/^\-|\-$/g, ' ');\r
5792 \r
5793                         this.node.appendChild(this.doc.createComment(v.replace(/\-\-/g, ' ')));\r
5794                 },\r
5795 \r
5796                 getContent : function() {\r
5797                         var h;\r
5798 \r
5799                         h = this.doc.xml || new XMLSerializer().serializeToString(this.doc);\r
5800                         h = h.replace(/<\?[^?]+\?>|<html[^>]*>|<\/html>|<html\/>|<!DOCTYPE[^>]+>/g, '');\r
5801                         h = h.replace(/ ?\/>/g, ' />');\r
5802 \r
5803                         if (this.valid)\r
5804                                 h = h.replace(/\%MCGT%/g, '&gt;');\r
5805 \r
5806                         return h;\r
5807                 }\r
5808         });\r
5809 })(tinymce);\r
5810 \r
5811 (function(tinymce) {\r
5812         var attrsCharsRegExp = /[&\"<>]/g, textCharsRegExp = /[<>&]/g, encodedChars = {'&' : '&amp;', '"' : '&quot;', '<' : '&lt;', '>' : '&gt;'};\r
5813 \r
5814         tinymce.create('tinymce.dom.StringWriter', {\r
5815                 str : null,\r
5816                 tags : null,\r
5817                 count : 0,\r
5818                 settings : null,\r
5819                 indent : null,\r
5820 \r
5821                 StringWriter : function(s) {\r
5822                         this.settings = tinymce.extend({\r
5823                                 indent_char : ' ',\r
5824                                 indentation : 0\r
5825                         }, s);\r
5826 \r
5827                         this.reset();\r
5828                 },\r
5829 \r
5830                 reset : function() {\r
5831                         this.indent = '';\r
5832                         this.str = "";\r
5833                         this.tags = [];\r
5834                         this.count = 0;\r
5835                 },\r
5836 \r
5837                 writeStartElement : function(n) {\r
5838                         this._writeAttributesEnd();\r
5839                         this.writeRaw('<' + n);\r
5840                         this.tags.push(n);\r
5841                         this.inAttr = true;\r
5842                         this.count++;\r
5843                         this.elementCount = this.count;\r
5844                         this.attrs = {};\r
5845                 },\r
5846 \r
5847                 writeAttribute : function(n, v) {\r
5848                         var t = this;\r
5849 \r
5850                         if (!t.attrs[n]) {\r
5851                                 t.writeRaw(" " + t.encode(n, true) + '="' + t.encode(v, true) + '"');\r
5852                                 t.attrs[n] = v;\r
5853                         }\r
5854                 },\r
5855 \r
5856                 writeEndElement : function() {\r
5857                         var n;\r
5858 \r
5859                         if (this.tags.length > 0) {\r
5860                                 n = this.tags.pop();\r
5861 \r
5862                                 if (this._writeAttributesEnd(1))\r
5863                                         this.writeRaw('</' + n + '>');\r
5864 \r
5865                                 if (this.settings.indentation > 0)\r
5866                                         this.writeRaw('\n');\r
5867                         }\r
5868                 },\r
5869 \r
5870                 writeFullEndElement : function() {\r
5871                         if (this.tags.length > 0) {\r
5872                                 this._writeAttributesEnd();\r
5873                                 this.writeRaw('</' + this.tags.pop() + '>');\r
5874 \r
5875                                 if (this.settings.indentation > 0)\r
5876                                         this.writeRaw('\n');\r
5877                         }\r
5878                 },\r
5879 \r
5880                 writeText : function(v) {\r
5881                         this._writeAttributesEnd();\r
5882                         this.writeRaw(this.encode(v));\r
5883                         this.count++;\r
5884                 },\r
5885 \r
5886                 writeCDATA : function(v) {\r
5887                         this._writeAttributesEnd();\r
5888                         this.writeRaw('<![CDATA[' + v + ']]>');\r
5889                         this.count++;\r
5890                 },\r
5891 \r
5892                 writeComment : function(v) {\r
5893                         this._writeAttributesEnd();\r
5894                         this.writeRaw('<!--' + v + '-->');\r
5895                         this.count++;\r
5896                 },\r
5897 \r
5898                 writeRaw : function(v) {\r
5899                         this.str += v;\r
5900                 },\r
5901 \r
5902                 encode : function(s, attr) {\r
5903                         return s.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(v) {\r
5904                                 return encodedChars[v];\r
5905                         });\r
5906                 },\r
5907 \r
5908                 getContent : function() {\r
5909                         return this.str;\r
5910                 },\r
5911 \r
5912                 _writeAttributesEnd : function(s) {\r
5913                         if (!this.inAttr)\r
5914                                 return;\r
5915 \r
5916                         this.inAttr = false;\r
5917 \r
5918                         if (s && this.elementCount == this.count) {\r
5919                                 this.writeRaw(' />');\r
5920                                 return false;\r
5921                         }\r
5922 \r
5923                         this.writeRaw('>');\r
5924 \r
5925                         return true;\r
5926                 }\r
5927         });\r
5928 })(tinymce);\r
5929 \r
5930 (function(tinymce) {\r
5931         // Shorten names\r
5932         var extend = tinymce.extend, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, isIE = tinymce.isIE, isGecko = tinymce.isGecko;\r
5933 \r
5934         function wildcardToRE(s) {\r
5935                 return s.replace(/([?+*])/g, '.$1');\r
5936         };\r
5937 \r
5938         tinymce.create('tinymce.dom.Serializer', {\r
5939                 Serializer : function(s) {\r
5940                         var t = this;\r
5941 \r
5942                         t.key = 0;\r
5943                         t.onPreProcess = new Dispatcher(t);\r
5944                         t.onPostProcess = new Dispatcher(t);\r
5945 \r
5946                         try {\r
5947                                 t.writer = new tinymce.dom.XMLWriter();\r
5948                         } catch (ex) {\r
5949                                 // IE might throw exception if ActiveX is disabled so we then switch to the slightly slower StringWriter\r
5950                                 t.writer = new tinymce.dom.StringWriter();\r
5951                         }\r
5952 \r
5953                         // IE9 broke the XML attributes order so it can't be used anymore\r
5954                         if (tinymce.isIE && document.documentMode > 8) {\r
5955                                 t.writer = new tinymce.dom.StringWriter();\r
5956                         }\r
5957 \r
5958                         // Default settings\r
5959                         t.settings = s = extend({\r
5960                                 dom : tinymce.DOM,\r
5961                                 valid_nodes : 0,\r
5962                                 node_filter : 0,\r
5963                                 attr_filter : 0,\r
5964                                 invalid_attrs : /^(_mce_|_moz_|sizset|sizcache)/,\r
5965                                 closed : /^(br|hr|input|meta|img|link|param|area)$/,\r
5966                                 entity_encoding : 'named',\r
5967                                 entities : '160,nbsp,161,iexcl,162,cent,163,pound,164,curren,165,yen,166,brvbar,167,sect,168,uml,169,copy,170,ordf,171,laquo,172,not,173,shy,174,reg,175,macr,176,deg,177,plusmn,178,sup2,179,sup3,180,acute,181,micro,182,para,183,middot,184,cedil,185,sup1,186,ordm,187,raquo,188,frac14,189,frac12,190,frac34,191,iquest,192,Agrave,193,Aacute,194,Acirc,195,Atilde,196,Auml,197,Aring,198,AElig,199,Ccedil,200,Egrave,201,Eacute,202,Ecirc,203,Euml,204,Igrave,205,Iacute,206,Icirc,207,Iuml,208,ETH,209,Ntilde,210,Ograve,211,Oacute,212,Ocirc,213,Otilde,214,Ouml,215,times,216,Oslash,217,Ugrave,218,Uacute,219,Ucirc,220,Uuml,221,Yacute,222,THORN,223,szlig,224,agrave,225,aacute,226,acirc,227,atilde,228,auml,229,aring,230,aelig,231,ccedil,232,egrave,233,eacute,234,ecirc,235,euml,236,igrave,237,iacute,238,icirc,239,iuml,240,eth,241,ntilde,242,ograve,243,oacute,244,ocirc,245,otilde,246,ouml,247,divide,248,oslash,249,ugrave,250,uacute,251,ucirc,252,uuml,253,yacute,254,thorn,255,yuml,402,fnof,913,Alpha,914,Beta,915,Gamma,916,Delta,917,Epsilon,918,Zeta,919,Eta,920,Theta,921,Iota,922,Kappa,923,Lambda,924,Mu,925,Nu,926,Xi,927,Omicron,928,Pi,929,Rho,931,Sigma,932,Tau,933,Upsilon,934,Phi,935,Chi,936,Psi,937,Omega,945,alpha,946,beta,947,gamma,948,delta,949,epsilon,950,zeta,951,eta,952,theta,953,iota,954,kappa,955,lambda,956,mu,957,nu,958,xi,959,omicron,960,pi,961,rho,962,sigmaf,963,sigma,964,tau,965,upsilon,966,phi,967,chi,968,psi,969,omega,977,thetasym,978,upsih,982,piv,8226,bull,8230,hellip,8242,prime,8243,Prime,8254,oline,8260,frasl,8472,weierp,8465,image,8476,real,8482,trade,8501,alefsym,8592,larr,8593,uarr,8594,rarr,8595,darr,8596,harr,8629,crarr,8656,lArr,8657,uArr,8658,rArr,8659,dArr,8660,hArr,8704,forall,8706,part,8707,exist,8709,empty,8711,nabla,8712,isin,8713,notin,8715,ni,8719,prod,8721,sum,8722,minus,8727,lowast,8730,radic,8733,prop,8734,infin,8736,ang,8743,and,8744,or,8745,cap,8746,cup,8747,int,8756,there4,8764,sim,8773,cong,8776,asymp,8800,ne,8801,equiv,8804,le,8805,ge,8834,sub,8835,sup,8836,nsub,8838,sube,8839,supe,8853,oplus,8855,otimes,8869,perp,8901,sdot,8968,lceil,8969,rceil,8970,lfloor,8971,rfloor,9001,lang,9002,rang,9674,loz,9824,spades,9827,clubs,9829,hearts,9830,diams,338,OElig,339,oelig,352,Scaron,353,scaron,376,Yuml,710,circ,732,tilde,8194,ensp,8195,emsp,8201,thinsp,8204,zwnj,8205,zwj,8206,lrm,8207,rlm,8211,ndash,8212,mdash,8216,lsquo,8217,rsquo,8218,sbquo,8220,ldquo,8221,rdquo,8222,bdquo,8224,dagger,8225,Dagger,8240,permil,8249,lsaquo,8250,rsaquo,8364,euro',\r
5968                                 valid_elements : '*[*]',\r
5969                                 extended_valid_elements : 0,\r
5970                                 invalid_elements : 0,\r
5971                                 fix_table_elements : 1,\r
5972                                 fix_list_elements : true,\r
5973                                 fix_content_duplication : true,\r
5974                                 convert_fonts_to_spans : false,\r
5975                                 font_size_classes : 0,\r
5976                                 apply_source_formatting : 0,\r
5977                                 indent_mode : 'simple',\r
5978                                 indent_char : '\t',\r
5979                                 indent_levels : 1,\r
5980                                 remove_linebreaks : 1,\r
5981                                 remove_redundant_brs : 1,\r
5982                                 element_format : 'xhtml'\r
5983                         }, s);\r
5984 \r
5985                         t.dom = s.dom;\r
5986                         t.schema = s.schema;\r
5987 \r
5988                         // Use raw entities if no entities are defined\r
5989                         if (s.entity_encoding == 'named' && !s.entities)\r
5990                                 s.entity_encoding = 'raw';\r
5991 \r
5992                         if (s.remove_redundant_brs) {\r
5993                                 t.onPostProcess.add(function(se, o) {\r
5994                                         // Remove single BR at end of block elements since they get rendered\r
5995                                         o.content = o.content.replace(/(<br \/>\s*)+<\/(p|h[1-6]|div|li)>/gi, function(a, b, c) {\r
5996                                                 // Check if it's a single element\r
5997                                                 if (/^<br \/>\s*<\//.test(a))\r
5998                                                         return '</' + c + '>';\r
5999 \r
6000                                                 return a;\r
6001                                         });\r
6002                                 });\r
6003                         }\r
6004 \r
6005                         // Remove XHTML element endings i.e. produce crap :) XHTML is better\r
6006                         if (s.element_format == 'html') {\r
6007                                 t.onPostProcess.add(function(se, o) {\r
6008                                         o.content = o.content.replace(/<([^>]+) \/>/g, '<$1>');\r
6009                                 });\r
6010                         }\r
6011 \r
6012                         if (s.fix_list_elements) {\r
6013                                 t.onPreProcess.add(function(se, o) {\r
6014                                         var nl, x, a = ['ol', 'ul'], i, n, p, r = /^(OL|UL)$/, np;\r
6015 \r
6016                                         function prevNode(e, n) {\r
6017                                                 var a = n.split(','), i;\r
6018 \r
6019                                                 while ((e = e.previousSibling) != null) {\r
6020                                                         for (i=0; i<a.length; i++) {\r
6021                                                                 if (e.nodeName == a[i])\r
6022                                                                         return e;\r
6023                                                         }\r
6024                                                 }\r
6025 \r
6026                                                 return null;\r
6027                                         };\r
6028 \r
6029                                         for (x=0; x<a.length; x++) {\r
6030                                                 nl = t.dom.select(a[x], o.node);\r
6031 \r
6032                                                 for (i=0; i<nl.length; i++) {\r
6033                                                         n = nl[i];\r
6034                                                         p = n.parentNode;\r
6035 \r
6036                                                         if (r.test(p.nodeName)) {\r
6037                                                                 np = prevNode(n, 'LI');\r
6038 \r
6039                                                                 if (!np) {\r
6040                                                                         np = t.dom.create('li');\r
6041                                                                         np.innerHTML = '&nbsp;';\r
6042                                                                         np.appendChild(n);\r
6043                                                                         p.insertBefore(np, p.firstChild);\r
6044                                                                 } else\r
6045                                                                         np.appendChild(n);\r
6046                                                         }\r
6047                                                 }\r
6048                                         }\r
6049                                 });\r
6050                         }\r
6051 \r
6052                         if (s.fix_table_elements) {\r
6053                                 t.onPreProcess.add(function(se, o) {\r
6054                                         // Since Opera will crash if you attach the node to a dynamic document we need to brrowser sniff a specific build\r
6055                                         // so Opera users with an older version will have to live with less compaible output not much we can do here\r
6056                                         if (!tinymce.isOpera || opera.buildNumber() >= 1767) {\r
6057                                                 each(t.dom.select('p table', o.node).reverse(), function(n) {\r
6058                                                         var parent = t.dom.getParent(n.parentNode, 'table,p');\r
6059 \r
6060                                                         if (parent.nodeName != 'TABLE') {\r
6061                                                                 try {\r
6062                                                                         t.dom.split(parent, n);\r
6063                                                                 } catch (ex) {\r
6064                                                                         // IE can sometimes fire an unknown runtime error so we just ignore it\r
6065                                                                 }\r
6066                                                         }\r
6067                                                 });\r
6068                                         }\r
6069                                 });\r
6070                         }\r
6071                 },\r
6072 \r
6073                 setEntities : function(s) {\r
6074                         var t = this, a, i, l = {}, v;\r
6075 \r
6076                         // No need to setup more than once\r
6077                         if (t.entityLookup)\r
6078                                 return;\r
6079 \r
6080                         // Build regex and lookup array\r
6081                         a = s.split(',');\r
6082                         for (i = 0; i < a.length; i += 2) {\r
6083                                 v = a[i];\r
6084 \r
6085                                 // Don't add default &amp; &quot; etc.\r
6086                                 if (v == 34 || v == 38 || v == 60 || v == 62)\r
6087                                         continue;\r
6088 \r
6089                                 l[String.fromCharCode(a[i])] = a[i + 1];\r
6090 \r
6091                                 v = parseInt(a[i]).toString(16);\r
6092                         }\r
6093 \r
6094                         t.entityLookup = l;\r
6095                 },\r
6096 \r
6097                 setRules : function(s) {\r
6098                         var t = this;\r
6099 \r
6100                         t._setup();\r
6101                         t.rules = {};\r
6102                         t.wildRules = [];\r
6103                         t.validElements = {};\r
6104 \r
6105                         return t.addRules(s);\r
6106                 },\r
6107 \r
6108                 addRules : function(s) {\r
6109                         var t = this, dr;\r
6110 \r
6111                         if (!s)\r
6112                                 return;\r
6113 \r
6114                         t._setup();\r
6115 \r
6116                         each(s.split(','), function(s) {\r
6117                                 var p = s.split(/\[|\]/), tn = p[0].split('/'), ra, at, wat, va = [];\r
6118 \r
6119                                 // Extend with default rules\r
6120                                 if (dr)\r
6121                                         at = tinymce.extend([], dr.attribs);\r
6122 \r
6123                                 // Parse attributes\r
6124                                 if (p.length > 1) {\r
6125                                         each(p[1].split('|'), function(s) {\r
6126                                                 var ar = {}, i;\r
6127 \r
6128                                                 at = at || [];\r
6129 \r
6130                                                 // Parse attribute rule\r
6131                                                 s = s.replace(/::/g, '~');\r
6132                                                 s = /^([!\-])?([\w*.?~_\-]+|)([=:<])?(.+)?$/.exec(s);\r
6133                                                 s[2] = s[2].replace(/~/g, ':');\r
6134 \r
6135                                                 // Add required attributes\r
6136                                                 if (s[1] == '!') {\r
6137                                                         ra = ra || [];\r
6138                                                         ra.push(s[2]);\r
6139                                                 }\r
6140 \r
6141                                                 // Remove inherited attributes\r
6142                                                 if (s[1] == '-') {\r
6143                                                         for (i = 0; i <at.length; i++) {\r
6144                                                                 if (at[i].name == s[2]) {\r
6145                                                                         at.splice(i, 1);\r
6146                                                                         return;\r
6147                                                                 }\r
6148                                                         }\r
6149                                                 }\r
6150 \r
6151                                                 switch (s[3]) {\r
6152                                                         // Add default attrib values\r
6153                                                         case '=':\r
6154                                                                 ar.defaultVal = s[4] || '';\r
6155                                                                 break;\r
6156 \r
6157                                                         // Add forced attrib values\r
6158                                                         case ':':\r
6159                                                                 ar.forcedVal = s[4];\r
6160                                                                 break;\r
6161 \r
6162                                                         // Add validation values\r
6163                                                         case '<':\r
6164                                                                 ar.validVals = s[4].split('?');\r
6165                                                                 break;\r
6166                                                 }\r
6167 \r
6168                                                 if (/[*.?]/.test(s[2])) {\r
6169                                                         wat = wat || [];\r
6170                                                         ar.nameRE = new RegExp('^' + wildcardToRE(s[2]) + '$');\r
6171                                                         wat.push(ar);\r
6172                                                 } else {\r
6173                                                         ar.name = s[2];\r
6174                                                         at.push(ar);\r
6175                                                 }\r
6176 \r
6177                                                 va.push(s[2]);\r
6178                                         });\r
6179                                 }\r
6180 \r
6181                                 // Handle element names\r
6182                                 each(tn, function(s, i) {\r
6183                                         var pr = s.charAt(0), x = 1, ru = {};\r
6184 \r
6185                                         // Extend with default rule data\r
6186                                         if (dr) {\r
6187                                                 if (dr.noEmpty)\r
6188                                                         ru.noEmpty = dr.noEmpty;\r
6189 \r
6190                                                 if (dr.fullEnd)\r
6191                                                         ru.fullEnd = dr.fullEnd;\r
6192 \r
6193                                                 if (dr.padd)\r
6194                                                         ru.padd = dr.padd;\r
6195                                         }\r
6196 \r
6197                                         // Handle prefixes\r
6198                                         switch (pr) {\r
6199                                                 case '-':\r
6200                                                         ru.noEmpty = true;\r
6201                                                         break;\r
6202 \r
6203                                                 case '+':\r
6204                                                         ru.fullEnd = true;\r
6205                                                         break;\r
6206 \r
6207                                                 case '#':\r
6208                                                         ru.padd = true;\r
6209                                                         break;\r
6210 \r
6211                                                 default:\r
6212                                                         x = 0;\r
6213                                         }\r
6214 \r
6215                                         tn[i] = s = s.substring(x);\r
6216                                         t.validElements[s] = 1;\r
6217 \r
6218                                         // Add element name or element regex\r
6219                                         if (/[*.?]/.test(tn[0])) {\r
6220                                                 ru.nameRE = new RegExp('^' + wildcardToRE(tn[0]) + '$');\r
6221                                                 t.wildRules = t.wildRules || {};\r
6222                                                 t.wildRules.push(ru);\r
6223                                         } else {\r
6224                                                 ru.name = tn[0];\r
6225 \r
6226                                                 // Store away default rule\r
6227                                                 if (tn[0] == '@')\r
6228                                                         dr = ru;\r
6229 \r
6230                                                 t.rules[s] = ru;\r
6231                                         }\r
6232 \r
6233                                         ru.attribs = at;\r
6234 \r
6235                                         if (ra)\r
6236                                                 ru.requiredAttribs = ra;\r
6237 \r
6238                                         if (wat) {\r
6239                                                 // Build valid attributes regexp\r
6240                                                 s = '';\r
6241                                                 each(va, function(v) {\r
6242                                                         if (s)\r
6243                                                                 s += '|';\r
6244 \r
6245                                                         s += '(' + wildcardToRE(v) + ')';\r
6246                                                 });\r
6247                                                 ru.validAttribsRE = new RegExp('^' + s.toLowerCase() + '$');\r
6248                                                 ru.wildAttribs = wat;\r
6249                                         }\r
6250                                 });\r
6251                         });\r
6252 \r
6253                         // Build valid elements regexp\r
6254                         s = '';\r
6255                         each(t.validElements, function(v, k) {\r
6256                                 if (s)\r
6257                                         s += '|';\r
6258 \r
6259                                 if (k != '@')\r
6260                                         s += k;\r
6261                         });\r
6262                         t.validElementsRE = new RegExp('^(' + wildcardToRE(s.toLowerCase()) + ')$');\r
6263 \r
6264                         //console.debug(t.validElementsRE.toString());\r
6265                         //console.dir(t.rules);\r
6266                         //console.dir(t.wildRules);\r
6267                 },\r
6268 \r
6269                 findRule : function(n) {\r
6270                         var t = this, rl = t.rules, i, r;\r
6271 \r
6272                         t._setup();\r
6273 \r
6274                         // Exact match\r
6275                         r = rl[n];\r
6276                         if (r)\r
6277                                 return r;\r
6278 \r
6279                         // Try wildcards\r
6280                         rl = t.wildRules;\r
6281                         for (i = 0; i < rl.length; i++) {\r
6282                                 if (rl[i].nameRE.test(n))\r
6283                                         return rl[i];\r
6284                         }\r
6285 \r
6286                         return null;\r
6287                 },\r
6288 \r
6289                 findAttribRule : function(ru, n) {\r
6290                         var i, wa = ru.wildAttribs;\r
6291 \r
6292                         for (i = 0; i < wa.length; i++) {\r
6293                                 if (wa[i].nameRE.test(n))\r
6294                                         return wa[i];\r
6295                         }\r
6296 \r
6297                         return null;\r
6298                 },\r
6299 \r
6300                 serialize : function(n, o) {\r
6301                         var h, t = this, doc, oldDoc, impl, selected;\r
6302 \r
6303                         t._setup();\r
6304                         o = o || {};\r
6305                         o.format = o.format || 'html';\r
6306                         t.processObj = o;\r
6307 \r
6308                         // IE looses the selected attribute on option elements so we need to store it\r
6309                         // See: http://support.microsoft.com/kb/829907\r
6310                         if (isIE) {\r
6311                                 selected = [];\r
6312                                 each(n.getElementsByTagName('option'), function(n) {\r
6313                                         var v = t.dom.getAttrib(n, 'selected');\r
6314 \r
6315                                         selected.push(v ? v : null);\r
6316                                 });\r
6317                         }\r
6318 \r
6319                         n = n.cloneNode(true);\r
6320 \r
6321                         // IE looses the selected attribute on option elements so we need to restore it\r
6322                         if (isIE) {\r
6323                                 each(n.getElementsByTagName('option'), function(n, i) {\r
6324                                         t.dom.setAttrib(n, 'selected', selected[i]);\r
6325                                 });\r
6326                         }\r
6327 \r
6328                         // Nodes needs to be attached to something in WebKit/Opera\r
6329                         // Older builds of Opera crashes if you attach the node to an document created dynamically\r
6330                         // and since we can't feature detect a crash we need to sniff the acutal build number\r
6331                         // This fix will make DOM ranges and make Sizzle happy!\r
6332                         impl = n.ownerDocument.implementation;\r
6333                         if (impl.createHTMLDocument && (tinymce.isOpera && opera.buildNumber() >= 1767)) {\r
6334                                 // Create an empty HTML document\r
6335                                 doc = impl.createHTMLDocument("");\r
6336 \r
6337                                 // Add the element or it's children if it's a body element to the new document\r
6338                                 each(n.nodeName == 'BODY' ? n.childNodes : [n], function(node) {\r
6339                                         doc.body.appendChild(doc.importNode(node, true));\r
6340                                 });\r
6341 \r
6342                                 // Grab first child or body element for serialization\r
6343                                 if (n.nodeName != 'BODY')\r
6344                                         n = doc.body.firstChild;\r
6345                                 else\r
6346                                         n = doc.body;\r
6347 \r
6348                                 // set the new document in DOMUtils so createElement etc works\r
6349                                 oldDoc = t.dom.doc;\r
6350                                 t.dom.doc = doc;\r
6351                         }\r
6352 \r
6353                         t.key = '' + (parseInt(t.key) + 1);\r
6354 \r
6355                         // Pre process\r
6356                         if (!o.no_events) {\r
6357                                 o.node = n;\r
6358                                 t.onPreProcess.dispatch(t, o);\r
6359                         }\r
6360 \r
6361                         // Serialize HTML DOM into a string\r
6362                         t.writer.reset();\r
6363                         t._info = o;\r
6364                         t._serializeNode(n, o.getInner);\r
6365 \r
6366                         // Post process\r
6367                         o.content = t.writer.getContent();\r
6368 \r
6369                         // Restore the old document if it was changed\r
6370                         if (oldDoc)\r
6371                                 t.dom.doc = oldDoc;\r
6372 \r
6373                         if (!o.no_events)\r
6374                                 t.onPostProcess.dispatch(t, o);\r
6375 \r
6376                         t._postProcess(o);\r
6377                         o.node = null;\r
6378 \r
6379                         return tinymce.trim(o.content);\r
6380                 },\r
6381 \r
6382                 // Internal functions\r
6383 \r
6384                 _postProcess : function(o) {\r
6385                         var t = this, s = t.settings, h = o.content, sc = [], p;\r
6386 \r
6387                         if (o.format == 'html') {\r
6388                                 // Protect some elements\r
6389                                 p = t._protect({\r
6390                                         content : h,\r
6391                                         patterns : [\r
6392                                                 {pattern : /(<script[^>]*>)(.*?)(<\/script>)/g},\r
6393                                                 {pattern : /(<noscript[^>]*>)(.*?)(<\/noscript>)/g},\r
6394                                                 {pattern : /(<style[^>]*>)(.*?)(<\/style>)/g},\r
6395                                                 {pattern : /(<pre[^>]*>)(.*?)(<\/pre>)/g, encode : 1},\r
6396                                                 {pattern : /(<!--\[CDATA\[)(.*?)(\]\]-->)/g}\r
6397                                         ]\r
6398                                 });\r
6399 \r
6400                                 h = p.content;\r
6401 \r
6402                                 // Entity encode\r
6403                                 if (s.entity_encoding !== 'raw')\r
6404                                         h = t._encode(h);\r
6405 \r
6406                                 // Use BR instead of &nbsp; padded P elements inside editor and use <p>&nbsp;</p> outside editor\r
6407 /*                              if (o.set)\r
6408                                         h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p><br /></p>');\r
6409                                 else\r
6410                                         h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p>$1</p>');*/\r
6411 \r
6412                                 // Since Gecko and Safari keeps whitespace in the DOM we need to\r
6413                                 // remove it inorder to match other browsers. But I think Gecko and Safari is right.\r
6414                                 // This process is only done when getting contents out from the editor.\r
6415                                 if (!o.set) {\r
6416                                         // We need to replace paragraph whitespace with an nbsp before indentation to keep the \u00a0 char\r
6417                                         h = tinymce._replace(/<p>\s+<\/p>|<p([^>]+)>\s+<\/p>/g, s.entity_encoding == 'numeric' ? '<p$1>&#160;</p>' : '<p$1>&nbsp;</p>', h);\r
6418 \r
6419                                         if (s.remove_linebreaks) {\r
6420                                                 h = h.replace(/\r?\n|\r/g, ' ');\r
6421                                                 h = tinymce._replace(/(<[^>]+>)\s+/g, '$1 ', h);\r
6422                                                 h = tinymce._replace(/\s+(<\/[^>]+>)/g, ' $1', h);\r
6423                                                 h = tinymce._replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object) ([^>]+)>\s+/g, '<$1 $2>', h); // Trim block start\r
6424                                                 h = tinymce._replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>\s+/g, '<$1>', h); // Trim block start\r
6425                                                 h = tinymce._replace(/\s+<\/(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>/g, '</$1>', h); // Trim block end\r
6426                                         }\r
6427 \r
6428                                         // Simple indentation\r
6429                                         if (s.apply_source_formatting && s.indent_mode == 'simple') {\r
6430                                                 // Add line breaks before and after block elements\r
6431                                                 h = tinymce._replace(/<(\/?)(ul|hr|table|meta|link|tbody|tr|object|body|head|html|map)(|[^>]+)>\s*/g, '\n<$1$2$3>\n', h);\r
6432                                                 h = tinymce._replace(/\s*<(p|h[1-6]|blockquote|div|title|style|pre|script|td|li|area)(|[^>]+)>/g, '\n<$1$2>', h);\r
6433                                                 h = tinymce._replace(/<\/(p|h[1-6]|blockquote|div|title|style|pre|script|td|li)>\s*/g, '</$1>\n', h);\r
6434                                                 h = h.replace(/\n\n/g, '\n');\r
6435                                         }\r
6436                                 }\r
6437 \r
6438                                 h = t._unprotect(h, p);\r
6439 \r
6440                                 // Restore CDATA sections\r
6441                                 h = tinymce._replace(/<!--\[CDATA\[([\s\S]+)\]\]-->/g, '<![CDATA[$1]]>', h);\r
6442 \r
6443                                 // Restore the \u00a0 character if raw mode is enabled\r
6444                                 if (s.entity_encoding == 'raw')\r
6445                                         h = tinymce._replace(/<p>&nbsp;<\/p>|<p([^>]+)>&nbsp;<\/p>/g, '<p$1>\u00a0</p>', h);\r
6446 \r
6447                                 // Restore noscript elements\r
6448                                 h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {\r
6449                                         return '<noscript' + attribs + '>' + t.dom.decode(text.replace(/<!--|-->/g, '')) + '</noscript>';\r
6450                                 });\r
6451                         }\r
6452 \r
6453                         o.content = h;\r
6454                 },\r
6455 \r
6456                 _serializeNode : function(n, inner) {\r
6457                         var t = this, s = t.settings, w = t.writer, hc, el, cn, i, l, a, at, no, v, nn, ru, ar, iv, closed, keep, type, scopeName;\r
6458 \r
6459                         if (!s.node_filter || s.node_filter(n)) {\r
6460                                 switch (n.nodeType) {\r
6461                                         case 1: // Element\r
6462                                                 if (n.hasAttribute ? n.hasAttribute('_mce_bogus') : n.getAttribute('_mce_bogus'))\r
6463                                                         return;\r
6464 \r
6465                                                 iv = keep = false;\r
6466                                                 hc = n.hasChildNodes();\r
6467                                                 nn = n.getAttribute('_mce_name') || n.nodeName.toLowerCase();\r
6468 \r
6469                                                 // Get internal type\r
6470                                                 type = n.getAttribute('_mce_type');\r
6471                                                 if (type) {\r
6472                                                         if (!t._info.cleanup) {\r
6473                                                                 iv = true;\r
6474                                                                 return;\r
6475                                                         } else\r
6476                                                                 keep = 1;\r
6477                                                 }\r
6478 \r
6479                                                 // Add correct prefix on IE\r
6480                                                 if (isIE) {\r
6481                                                         scopeName = n.scopeName;\r
6482                                                         if (scopeName && scopeName !== 'HTML' && scopeName !== 'html')\r
6483                                                                 nn = scopeName + ':' + nn;\r
6484                                                 }\r
6485 \r
6486                                                 // Remove mce prefix on IE needed for the abbr element\r
6487                                                 if (nn.indexOf('mce:') === 0)\r
6488                                                         nn = nn.substring(4);\r
6489 \r
6490                                                 // Check if valid\r
6491                                                 if (!keep) {\r
6492                                                         if (!t.validElementsRE || !t.validElementsRE.test(nn) || (t.invalidElementsRE && t.invalidElementsRE.test(nn)) || inner) {\r
6493                                                                 iv = true;\r
6494                                                                 break;\r
6495                                                         }\r
6496                                                 }\r
6497 \r
6498                                                 if (isIE) {\r
6499                                                         // Fix IE content duplication (DOM can have multiple copies of the same node)\r
6500                                                         if (s.fix_content_duplication) {\r
6501                                                                 if (n._mce_serialized == t.key)\r
6502                                                                         return;\r
6503 \r
6504                                                                 n._mce_serialized = t.key;\r
6505                                                         }\r
6506 \r
6507                                                         // IE sometimes adds a / infront of the node name\r
6508                                                         if (nn.charAt(0) == '/')\r
6509                                                                 nn = nn.substring(1);\r
6510                                                 } else if (isGecko) {\r
6511                                                         // Ignore br elements\r
6512                                                         if (n.nodeName === 'BR' && n.getAttribute('type') == '_moz')\r
6513                                                                 return;\r
6514                                                 }\r
6515 \r
6516                                                 // Check if valid child\r
6517                                                 if (s.validate_children) {\r
6518                                                         if (t.elementName && !t.schema.isValid(t.elementName, nn)) {\r
6519                                                                 iv = true;\r
6520                                                                 break;\r
6521                                                         }\r
6522 \r
6523                                                         t.elementName = nn;\r
6524                                                 }\r
6525 \r
6526                                                 ru = t.findRule(nn);\r
6527                                                 \r
6528                                                 // No valid rule for this element could be found then skip it\r
6529                                                 if (!ru) {\r
6530                                                         iv = true;\r
6531                                                         break;\r
6532                                                 }\r
6533 \r
6534                                                 nn = ru.name || nn;\r
6535                                                 closed = s.closed.test(nn);\r
6536 \r
6537                                                 // Skip empty nodes or empty node name in IE\r
6538                                                 if ((!hc && ru.noEmpty) || (isIE && !nn)) {\r
6539                                                         iv = true;\r
6540                                                         break;\r
6541                                                 }\r
6542 \r
6543                                                 // Check required\r
6544                                                 if (ru.requiredAttribs) {\r
6545                                                         a = ru.requiredAttribs;\r
6546 \r
6547                                                         for (i = a.length - 1; i >= 0; i--) {\r
6548                                                                 if (this.dom.getAttrib(n, a[i]) !== '')\r
6549                                                                         break;\r
6550                                                         }\r
6551 \r
6552                                                         // None of the required was there\r
6553                                                         if (i == -1) {\r
6554                                                                 iv = true;\r
6555                                                                 break;\r
6556                                                         }\r
6557                                                 }\r
6558 \r
6559                                                 w.writeStartElement(nn);\r
6560 \r
6561                                                 // Add ordered attributes\r
6562                                                 if (ru.attribs) {\r
6563                                                         for (i=0, at = ru.attribs, l = at.length; i<l; i++) {\r
6564                                                                 a = at[i];\r
6565                                                                 v = t._getAttrib(n, a);\r
6566 \r
6567                                                                 if (v !== null)\r
6568                                                                         w.writeAttribute(a.name, v);\r
6569                                                         }\r
6570                                                 }\r
6571 \r
6572                                                 // Add wild attributes\r
6573                                                 if (ru.validAttribsRE) {\r
6574                                                         at = t.dom.getAttribs(n);\r
6575                                                         for (i=at.length-1; i>-1; i--) {\r
6576                                                                 no = at[i];\r
6577 \r
6578                                                                 if (no.specified) {\r
6579                                                                         a = no.nodeName.toLowerCase();\r
6580 \r
6581                                                                         if (s.invalid_attrs.test(a) || !ru.validAttribsRE.test(a))\r
6582                                                                                 continue;\r
6583 \r
6584                                                                         ar = t.findAttribRule(ru, a);\r
6585                                                                         v = t._getAttrib(n, ar, a);\r
6586 \r
6587                                                                         if (v !== null)\r
6588                                                                                 w.writeAttribute(a, v);\r
6589                                                                 }\r
6590                                                         }\r
6591                                                 }\r
6592 \r
6593                                                 // Keep type attribute\r
6594                                                 if (type && keep)\r
6595                                                         w.writeAttribute('_mce_type', type);\r
6596 \r
6597                                                 // Write text from script\r
6598                                                 if (nn === 'script' && tinymce.trim(n.innerHTML)) {\r
6599                                                         w.writeText('// '); // Padd it with a comment so it will parse on older browsers\r
6600                                                         w.writeCDATA(n.innerHTML.replace(/<!--|-->|<\[CDATA\[|\]\]>/g, '')); // Remove comments and cdata stuctures\r
6601                                                         hc = false;\r
6602                                                         break;\r
6603                                                 }\r
6604 \r
6605                                                 // Padd empty nodes with a &nbsp;\r
6606                                                 if (ru.padd) {\r
6607                                                         // If it has only one bogus child, padd it anyway workaround for <td><br /></td> bug\r
6608                                                         if (hc && (cn = n.firstChild) && cn.nodeType === 1 && n.childNodes.length === 1) {\r
6609                                                                 if (cn.hasAttribute ? cn.hasAttribute('_mce_bogus') : cn.getAttribute('_mce_bogus'))\r
6610                                                                         w.writeText('\u00a0');\r
6611                                                         } else if (!hc)\r
6612                                                                 w.writeText('\u00a0'); // No children then padd it\r
6613                                                 }\r
6614 \r
6615                                                 break;\r
6616 \r
6617                                         case 3: // Text\r
6618                                                 // Check if valid child\r
6619                                                 if (s.validate_children && t.elementName && !t.schema.isValid(t.elementName, '#text'))\r
6620                                                         return;\r
6621 \r
6622                                                 return w.writeText(n.nodeValue);\r
6623 \r
6624                                         case 4: // CDATA\r
6625                                                 return w.writeCDATA(n.nodeValue);\r
6626 \r
6627                                         case 8: // Comment\r
6628                                                 return w.writeComment(n.nodeValue);\r
6629                                 }\r
6630                         } else if (n.nodeType == 1)\r
6631                                 hc = n.hasChildNodes();\r
6632 \r
6633                         if (hc && !closed) {\r
6634                                 cn = n.firstChild;\r
6635 \r
6636                                 while (cn) {\r
6637                                         t._serializeNode(cn);\r
6638                                         t.elementName = nn;\r
6639                                         cn = cn.nextSibling;\r
6640                                 }\r
6641                         }\r
6642 \r
6643                         // Write element end\r
6644                         if (!iv) {\r
6645                                 if (!closed)\r
6646                                         w.writeFullEndElement();\r
6647                                 else\r
6648                                         w.writeEndElement();\r
6649                         }\r
6650                 },\r
6651 \r
6652                 _protect : function(o) {\r
6653                         var t = this;\r
6654 \r
6655                         o.items = o.items || [];\r
6656 \r
6657                         function enc(s) {\r
6658                                 return s.replace(/[\r\n\\]/g, function(c) {\r
6659                                         if (c === '\n')\r
6660                                                 return '\\n';\r
6661                                         else if (c === '\\')\r
6662                                                 return '\\\\';\r
6663 \r
6664                                         return '\\r';\r
6665                                 });\r
6666                         };\r
6667 \r
6668                         function dec(s) {\r
6669                                 return s.replace(/\\[\\rn]/g, function(c) {\r
6670                                         if (c === '\\n')\r
6671                                                 return '\n';\r
6672                                         else if (c === '\\\\')\r
6673                                                 return '\\';\r
6674 \r
6675                                         return '\r';\r
6676                                 });\r
6677                         };\r
6678 \r
6679                         each(o.patterns, function(p) {\r
6680                                 o.content = dec(enc(o.content).replace(p.pattern, function(x, a, b, c) {\r
6681                                         b = dec(b);\r
6682 \r
6683                                         if (p.encode)\r
6684                                                 b = t._encode(b);\r
6685 \r
6686                                         o.items.push(b);\r
6687                                         return a + '<!--mce:' + (o.items.length - 1) + '-->' + c;\r
6688                                 }));\r
6689                         });\r
6690 \r
6691                         return o;\r
6692                 },\r
6693 \r
6694                 _unprotect : function(h, o) {\r
6695                         h = h.replace(/\<!--mce:([0-9]+)--\>/g, function(a, b) {\r
6696                                 return o.items[parseInt(b)];\r
6697                         });\r
6698 \r
6699                         o.items = [];\r
6700 \r
6701                         return h;\r
6702                 },\r
6703 \r
6704                 _encode : function(h) {\r
6705                         var t = this, s = t.settings, l;\r
6706 \r
6707                         // Entity encode\r
6708                         if (s.entity_encoding !== 'raw') {\r
6709                                 if (s.entity_encoding.indexOf('named') != -1) {\r
6710                                         t.setEntities(s.entities);\r
6711                                         l = t.entityLookup;\r
6712 \r
6713                                         h = h.replace(/[\u007E-\uFFFF]/g, function(a) {\r
6714                                                 var v;\r
6715 \r
6716                                                 if (v = l[a])\r
6717                                                         a = '&' + v + ';';\r
6718 \r
6719                                                 return a;\r
6720                                         });\r
6721                                 }\r
6722 \r
6723                                 if (s.entity_encoding.indexOf('numeric') != -1) {\r
6724                                         h = h.replace(/[\u007E-\uFFFF]/g, function(a) {\r
6725                                                 return '&#' + a.charCodeAt(0) + ';';\r
6726                                         });\r
6727                                 }\r
6728                         }\r
6729 \r
6730                         return h;\r
6731                 },\r
6732 \r
6733                 _setup : function() {\r
6734                         var t = this, s = this.settings;\r
6735 \r
6736                         if (t.done)\r
6737                                 return;\r
6738 \r
6739                         t.done = 1;\r
6740 \r
6741                         t.setRules(s.valid_elements);\r
6742                         t.addRules(s.extended_valid_elements);\r
6743 \r
6744                         if (s.invalid_elements)\r
6745                                 t.invalidElementsRE = new RegExp('^(' + wildcardToRE(s.invalid_elements.replace(/,/g, '|').toLowerCase()) + ')$');\r
6746 \r
6747                         if (s.attrib_value_filter)\r
6748                                 t.attribValueFilter = s.attribValueFilter;\r
6749                 },\r
6750 \r
6751                 _getAttrib : function(n, a, na) {\r
6752                         var i, v;\r
6753 \r
6754                         na = na || a.name;\r
6755 \r
6756                         if (a.forcedVal && (v = a.forcedVal)) {\r
6757                                 if (v === '{$uid}')\r
6758                                         return this.dom.uniqueId();\r
6759 \r
6760                                 return v;\r
6761                         }\r
6762 \r
6763                         v = this.dom.getAttrib(n, na);\r
6764 \r
6765                         switch (na) {\r
6766                                 case 'rowspan':\r
6767                                 case 'colspan':\r
6768                                         // Whats the point? Remove usless attribute value\r
6769                                         if (v == '1')\r
6770                                                 v = '';\r
6771 \r
6772                                         break;\r
6773                         }\r
6774 \r
6775                         if (this.attribValueFilter)\r
6776                                 v = this.attribValueFilter(na, v, n);\r
6777 \r
6778                         if (a.validVals) {\r
6779                                 for (i = a.validVals.length - 1; i >= 0; i--) {\r
6780                                         if (v == a.validVals[i])\r
6781                                                 break;\r
6782                                 }\r
6783 \r
6784                                 if (i == -1)\r
6785                                         return null;\r
6786                         }\r
6787 \r
6788                         if (v === '' && typeof(a.defaultVal) != 'undefined') {\r
6789                                 v = a.defaultVal;\r
6790 \r
6791                                 if (v === '{$uid}')\r
6792                                         return this.dom.uniqueId();\r
6793 \r
6794                                 return v;\r
6795                         } else {\r
6796                                 // Remove internal mceItemXX classes when content is extracted from editor\r
6797                                 if (na == 'class' && this.processObj.get)\r
6798                                         v = v.replace(/\s?mceItem\w+\s?/g, '');\r
6799                         }\r
6800 \r
6801                         if (v === '')\r
6802                                 return null;\r
6803 \r
6804 \r
6805                         return v;\r
6806                 }\r
6807         });\r
6808 })(tinymce);\r
6809 \r
6810 (function(tinymce) {\r
6811         tinymce.dom.ScriptLoader = function(settings) {\r
6812                 var QUEUED = 0,\r
6813                         LOADING = 1,\r
6814                         LOADED = 2,\r
6815                         states = {},\r
6816                         queue = [],\r
6817                         scriptLoadedCallbacks = {},\r
6818                         queueLoadedCallbacks = [],\r
6819                         loading = 0,\r
6820                         undefined;\r
6821 \r
6822                 function loadScript(url, callback) {\r
6823                         var t = this, dom = tinymce.DOM, elm, uri, loc, id;\r
6824 \r
6825                         // Execute callback when script is loaded\r
6826                         function done() {\r
6827                                 dom.remove(id);\r
6828 \r
6829                                 if (elm)\r
6830                                         elm.onreadystatechange = elm.onload = elm = null;\r
6831 \r
6832                                 callback();\r
6833                         };\r
6834 \r
6835                         id = dom.uniqueId();\r
6836 \r
6837                         if (tinymce.isIE6) {\r
6838                                 uri = new tinymce.util.URI(url);\r
6839                                 loc = location;\r
6840 \r
6841                                 // If script is from same domain and we\r
6842                                 // use IE 6 then use XHR since it's more reliable\r
6843                                 if (uri.host == loc.hostname && uri.port == loc.port && (uri.protocol + ':') == loc.protocol) {\r
6844                                         tinymce.util.XHR.send({\r
6845                                                 url : tinymce._addVer(uri.getURI()),\r
6846                                                 success : function(content) {\r
6847                                                         // Create new temp script element\r
6848                                                         var script = dom.create('script', {\r
6849                                                                 type : 'text/javascript'\r
6850                                                         });\r
6851 \r
6852                                                         // Evaluate script in global scope\r
6853                                                         script.text = content;\r
6854                                                         document.getElementsByTagName('head')[0].appendChild(script);\r
6855                                                         dom.remove(script);\r
6856 \r
6857                                                         done();\r
6858                                                 }\r
6859                                         });\r
6860 \r
6861                                         return;\r
6862                                 }\r
6863                         }\r
6864 \r
6865                         // Create new script element\r
6866                         elm = dom.create('script', {\r
6867                                 id : id,\r
6868                                 type : 'text/javascript',\r
6869                                 src : tinymce._addVer(url)\r
6870                         });\r
6871 \r
6872                         // Add onload listener for non IE browsers since IE9\r
6873                         // fires onload event before the script is parsed and executed\r
6874                         if (!tinymce.isIE)\r
6875                                 elm.onload = done;\r
6876 \r
6877                         elm.onreadystatechange = function() {\r
6878                                 var state = elm.readyState;\r
6879 \r
6880                                 // Loaded state is passed on IE 6 however there\r
6881                                 // are known issues with this method but we can't use\r
6882                                 // XHR in a cross domain loading\r
6883                                 if (state == 'complete' || state == 'loaded')\r
6884                                         done();\r
6885                         };\r
6886 \r
6887                         // Most browsers support this feature so we report errors\r
6888                         // for those at least to help users track their missing plugins etc\r
6889                         // todo: Removed since it produced error if the document is unloaded by navigating away, re-add it as an option\r
6890                         /*elm.onerror = function() {\r
6891                                 alert('Failed to load: ' + url);\r
6892                         };*/\r
6893 \r
6894                         // Add script to document\r
6895                         (document.getElementsByTagName('head')[0] || document.body).appendChild(elm);\r
6896                 };\r
6897 \r
6898                 this.isDone = function(url) {\r
6899                         return states[url] == LOADED;\r
6900                 };\r
6901 \r
6902                 this.markDone = function(url) {\r
6903                         states[url] = LOADED;\r
6904                 };\r
6905 \r
6906                 this.add = this.load = function(url, callback, scope) {\r
6907                         var item, state = states[url];\r
6908 \r
6909                         // Add url to load queue\r
6910                         if (state == undefined) {\r
6911                                 queue.push(url);\r
6912                                 states[url] = QUEUED;\r
6913                         }\r
6914 \r
6915                         if (callback) {\r
6916                                 // Store away callback for later execution\r
6917                                 if (!scriptLoadedCallbacks[url])\r
6918                                         scriptLoadedCallbacks[url] = [];\r
6919 \r
6920                                 scriptLoadedCallbacks[url].push({\r
6921                                         func : callback,\r
6922                                         scope : scope || this\r
6923                                 });\r
6924                         }\r
6925                 };\r
6926 \r
6927                 this.loadQueue = function(callback, scope) {\r
6928                         this.loadScripts(queue, callback, scope);\r
6929                 };\r
6930 \r
6931                 this.loadScripts = function(scripts, callback, scope) {\r
6932                         var loadScripts;\r
6933 \r
6934                         function execScriptLoadedCallbacks(url) {\r
6935                                 // Execute URL callback functions\r
6936                                 tinymce.each(scriptLoadedCallbacks[url], function(callback) {\r
6937                                         callback.func.call(callback.scope);\r
6938                                 });\r
6939 \r
6940                                 scriptLoadedCallbacks[url] = undefined;\r
6941                         };\r
6942 \r
6943                         queueLoadedCallbacks.push({\r
6944                                 func : callback,\r
6945                                 scope : scope || this\r
6946                         });\r
6947 \r
6948                         loadScripts = function() {\r
6949                                 var loadingScripts = tinymce.grep(scripts);\r
6950 \r
6951                                 // Current scripts has been handled\r
6952                                 scripts.length = 0;\r
6953 \r
6954                                 // Load scripts that needs to be loaded\r
6955                                 tinymce.each(loadingScripts, function(url) {\r
6956                                         // Script is already loaded then execute script callbacks directly\r
6957                                         if (states[url] == LOADED) {\r
6958                                                 execScriptLoadedCallbacks(url);\r
6959                                                 return;\r
6960                                         }\r
6961 \r
6962                                         // Is script not loading then start loading it\r
6963                                         if (states[url] != LOADING) {\r
6964                                                 states[url] = LOADING;\r
6965                                                 loading++;\r
6966 \r
6967                                                 loadScript(url, function() {\r
6968                                                         states[url] = LOADED;\r
6969                                                         loading--;\r
6970 \r
6971                                                         execScriptLoadedCallbacks(url);\r
6972 \r
6973                                                         // Load more scripts if they where added by the recently loaded script\r
6974                                                         loadScripts();\r
6975                                                 });\r
6976                                         }\r
6977                                 });\r
6978 \r
6979                                 // No scripts are currently loading then execute all pending queue loaded callbacks\r
6980                                 if (!loading) {\r
6981                                         tinymce.each(queueLoadedCallbacks, function(callback) {\r
6982                                                 callback.func.call(callback.scope);\r
6983                                         });\r
6984 \r
6985                                         queueLoadedCallbacks.length = 0;\r
6986                                 }\r
6987                         };\r
6988 \r
6989                         loadScripts();\r
6990                 };\r
6991         };\r
6992 \r
6993         // Global script loader\r
6994         tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();\r
6995 })(tinymce);\r
6996 \r
6997 tinymce.dom.TreeWalker = function(start_node, root_node) {\r
6998         var node = start_node;\r
6999 \r
7000         function findSibling(node, start_name, sibling_name, shallow) {\r
7001                 var sibling, parent;\r
7002 \r
7003                 if (node) {\r
7004                         // Walk into nodes if it has a start\r
7005                         if (!shallow && node[start_name])\r
7006                                 return node[start_name];\r
7007 \r
7008                         // Return the sibling if it has one\r
7009                         if (node != root_node) {\r
7010                                 sibling = node[sibling_name];\r
7011                                 if (sibling)\r
7012                                         return sibling;\r
7013 \r
7014                                 // Walk up the parents to look for siblings\r
7015                                 for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) {\r
7016                                         sibling = parent[sibling_name];\r
7017                                         if (sibling)\r
7018                                                 return sibling;\r
7019                                 }\r
7020                         }\r
7021                 }\r
7022         };\r
7023 \r
7024         this.current = function() {\r
7025                 return node;\r
7026         };\r
7027 \r
7028         this.next = function(shallow) {\r
7029                 return (node = findSibling(node, 'firstChild', 'nextSibling', shallow));\r
7030         };\r
7031 \r
7032         this.prev = function(shallow) {\r
7033                 return (node = findSibling(node, 'lastChild', 'lastSibling', shallow));\r
7034         };\r
7035 };\r
7036 \r
7037 (function() {\r
7038         var transitional = {};\r
7039 \r
7040         function unpack(lookup, data) {\r
7041                 var key;\r
7042 \r
7043                 function replace(value) {\r
7044                         return value.replace(/[A-Z]+/g, function(key) {\r
7045                                 return replace(lookup[key]);\r
7046                         });\r
7047                 };\r
7048 \r
7049                 // Unpack lookup\r
7050                 for (key in lookup) {\r
7051                         if (lookup.hasOwnProperty(key))\r
7052                                 lookup[key] = replace(lookup[key]);\r
7053                 }\r
7054 \r
7055                 // Unpack and parse data into object map\r
7056                 replace(data).replace(/#/g, '#text').replace(/(\w+)\[([^\]]+)\]/g, function(str, name, children) {\r
7057                         var i, map = {};\r
7058 \r
7059                         children = children.split(/\|/);\r
7060 \r
7061                         for (i = children.length - 1; i >= 0; i--)\r
7062                                 map[children[i]] = 1;\r
7063 \r
7064                         transitional[name] = map;\r
7065                 });\r
7066         };\r
7067 \r
7068         // This is the XHTML 1.0 transitional elements with it's children packed to reduce it's size\r
7069         // we will later include the attributes here and use it as a default for valid elements but it\r
7070         // requires us to rewrite the serializer engine\r
7071         unpack({\r
7072                 Z : '#|H|K|N|O|P',\r
7073                 Y : '#|X|form|R|Q',\r
7074                 X : 'p|T|div|U|W|isindex|fieldset|table',\r
7075                 W : 'pre|hr|blockquote|address|center|noframes',\r
7076                 U : 'ul|ol|dl|menu|dir',\r
7077                 ZC : '#|p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q',\r
7078                 T : 'h1|h2|h3|h4|h5|h6',\r
7079                 ZB : '#|X|S|Q',\r
7080                 S : 'R|P',\r
7081                 ZA : '#|a|G|J|M|O|P',\r
7082                 R : '#|a|H|K|N|O',\r
7083                 Q : 'noscript|P',\r
7084                 P : 'ins|del|script',\r
7085                 O : 'input|select|textarea|label|button',\r
7086                 N : 'M|L',\r
7087                 M : 'em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym',\r
7088                 L : 'sub|sup',\r
7089                 K : 'J|I',\r
7090                 J : 'tt|i|b|u|s|strike',\r
7091                 I : 'big|small|font|basefont',\r
7092                 H : 'G|F',\r
7093                 G : 'br|span|bdo',\r
7094                 F : 'object|applet|img|map|iframe'\r
7095         }, 'script[]' + \r
7096                 'style[]' + \r
7097                 'object[#|param|X|form|a|H|K|N|O|Q]' + \r
7098                 'param[]' + \r
7099                 'p[S]' + \r
7100                 'a[Z]' + \r
7101                 'br[]' + \r
7102                 'span[S]' + \r
7103                 'bdo[S]' + \r
7104                 'applet[#|param|X|form|a|H|K|N|O|Q]' + \r
7105                 'h1[S]' + \r
7106                 'img[]' + \r
7107                 'map[X|form|Q|area]' + \r
7108                 'h2[S]' + \r
7109                 'iframe[#|X|form|a|H|K|N|O|Q]' + \r
7110                 'h3[S]' + \r
7111                 'tt[S]' + \r
7112                 'i[S]' + \r
7113                 'b[S]' + \r
7114                 'u[S]' + \r
7115                 's[S]' + \r
7116                 'strike[S]' + \r
7117                 'big[S]' + \r
7118                 'small[S]' + \r
7119                 'font[S]' + \r
7120                 'basefont[]' + \r
7121                 'em[S]' + \r
7122                 'strong[S]' + \r
7123                 'dfn[S]' + \r
7124                 'code[S]' + \r
7125                 'q[S]' + \r
7126                 'samp[S]' + \r
7127                 'kbd[S]' + \r
7128                 'var[S]' + \r
7129                 'cite[S]' + \r
7130                 'abbr[S]' + \r
7131                 'acronym[S]' + \r
7132                 'sub[S]' + \r
7133                 'sup[S]' + \r
7134                 'input[]' + \r
7135                 'select[optgroup|option]' + \r
7136                 'optgroup[option]' + \r
7137                 'option[]' + \r
7138                 'textarea[]' + \r
7139                 'label[S]' + \r
7140                 'button[#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]' + \r
7141                 'h4[S]' + \r
7142                 'ins[#|X|form|a|H|K|N|O|Q]' + \r
7143                 'h5[S]' + \r
7144                 'del[#|X|form|a|H|K|N|O|Q]' + \r
7145                 'h6[S]' + \r
7146                 'div[#|X|form|a|H|K|N|O|Q]' + \r
7147                 'ul[li]' + \r
7148                 'li[#|X|form|a|H|K|N|O|Q]' + \r
7149                 'ol[li]' + \r
7150                 'dl[dt|dd]' + \r
7151                 'dt[S]' + \r
7152                 'dd[#|X|form|a|H|K|N|O|Q]' + \r
7153                 'menu[li]' + \r
7154                 'dir[li]' + \r
7155                 'pre[ZA]' + \r
7156                 'hr[]' + \r
7157                 'blockquote[#|X|form|a|H|K|N|O|Q]' + \r
7158                 'address[S|p]' + \r
7159                 'center[#|X|form|a|H|K|N|O|Q]' + \r
7160                 'noframes[#|X|form|a|H|K|N|O|Q]' + \r
7161                 'isindex[]' + \r
7162                 'fieldset[#|legend|X|form|a|H|K|N|O|Q]' + \r
7163                 'legend[S]' + \r
7164                 'table[caption|col|colgroup|thead|tfoot|tbody|tr]' + \r
7165                 'caption[S]' + \r
7166                 'col[]' + \r
7167                 'colgroup[col]' + \r
7168                 'thead[tr]' + \r
7169                 'tr[th|td]' + \r
7170                 'th[#|X|form|a|H|K|N|O|Q]' + \r
7171                 'form[#|X|a|H|K|N|O|Q]' + \r
7172                 'noscript[#|X|form|a|H|K|N|O|Q]' + \r
7173                 'td[#|X|form|a|H|K|N|O|Q]' + \r
7174                 'tfoot[tr]' + \r
7175                 'tbody[tr]' + \r
7176                 'area[]' + \r
7177                 'base[]' + \r
7178                 'body[#|X|form|a|H|K|N|O|Q]'\r
7179         );\r
7180 \r
7181         tinymce.dom.Schema = function() {\r
7182                 var t = this, elements = transitional;\r
7183 \r
7184                 t.isValid = function(name, child_name) {\r
7185                         var element = elements[name];\r
7186 \r
7187                         return !!(element && (!child_name || element[child_name]));\r
7188                 };\r
7189         };\r
7190 })();\r
7191 (function(tinymce) {\r
7192         tinymce.dom.RangeUtils = function(dom) {\r
7193                 var INVISIBLE_CHAR = '\uFEFF';\r
7194 \r
7195                 this.walk = function(rng, callback) {\r
7196                         var startContainer = rng.startContainer,\r
7197                                 startOffset = rng.startOffset,\r
7198                                 endContainer = rng.endContainer,\r
7199                                 endOffset = rng.endOffset,\r
7200                                 ancestor, startPoint,\r
7201                                 endPoint, node, parent, siblings, nodes;\r
7202 \r
7203                         // Handle table cell selection the table plugin enables\r
7204                         // you to fake select table cells and perform formatting actions on them\r
7205                         nodes = dom.select('td.mceSelected,th.mceSelected');\r
7206                         if (nodes.length > 0) {\r
7207                                 tinymce.each(nodes, function(node) {\r
7208                                         callback([node]);\r
7209                                 });\r
7210 \r
7211                                 return;\r
7212                         }\r
7213 \r
7214                         function collectSiblings(node, name, end_node) {\r
7215                                 var siblings = [];\r
7216 \r
7217                                 for (; node && node != end_node; node = node[name])\r
7218                                         siblings.push(node);\r
7219 \r
7220                                 return siblings;\r
7221                         };\r
7222 \r
7223                         function findEndPoint(node, root) {\r
7224                                 do {\r
7225                                         if (node.parentNode == root)\r
7226                                                 return node;\r
7227 \r
7228                                         node = node.parentNode;\r
7229                                 } while(node);\r
7230                         };\r
7231 \r
7232                         function walkBoundary(start_node, end_node, next) {\r
7233                                 var siblingName = next ? 'nextSibling' : 'previousSibling';\r
7234 \r
7235                                 for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) {\r
7236                                         parent = node.parentNode;\r
7237                                         siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName);\r
7238 \r
7239                                         if (siblings.length) {\r
7240                                                 if (!next)\r
7241                                                         siblings.reverse();\r
7242 \r
7243                                                 callback(siblings);\r
7244                                         }\r
7245                                 }\r
7246                         };\r
7247 \r
7248                         // If index based start position then resolve it\r
7249                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes())\r
7250                                 startContainer = startContainer.childNodes[startOffset];\r
7251 \r
7252                         // If index based end position then resolve it\r
7253                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes())\r
7254                                 endContainer = endContainer.childNodes[Math.min(startOffset == endOffset ? endOffset : endOffset - 1, endContainer.childNodes.length - 1)];\r
7255 \r
7256                         // Find common ancestor and end points\r
7257                         ancestor = dom.findCommonAncestor(startContainer, endContainer);\r
7258 \r
7259                         // Same container\r
7260                         if (startContainer == endContainer)\r
7261                                 return callback([startContainer]);\r
7262 \r
7263                         // Process left side\r
7264                         for (node = startContainer; node; node = node.parentNode) {\r
7265                                 if (node == endContainer)\r
7266                                         return walkBoundary(startContainer, ancestor, true);\r
7267 \r
7268                                 if (node == ancestor)\r
7269                                         break;\r
7270                         }\r
7271 \r
7272                         // Process right side\r
7273                         for (node = endContainer; node; node = node.parentNode) {\r
7274                                 if (node == startContainer)\r
7275                                         return walkBoundary(endContainer, ancestor);\r
7276 \r
7277                                 if (node == ancestor)\r
7278                                         break;\r
7279                         }\r
7280 \r
7281                         // Find start/end point\r
7282                         startPoint = findEndPoint(startContainer, ancestor) || startContainer;\r
7283                         endPoint = findEndPoint(endContainer, ancestor) || endContainer;\r
7284 \r
7285                         // Walk left leaf\r
7286                         walkBoundary(startContainer, startPoint, true);\r
7287 \r
7288                         // Walk the middle from start to end point\r
7289                         siblings = collectSiblings(\r
7290                                 startPoint == startContainer ? startPoint : startPoint.nextSibling,\r
7291                                 'nextSibling',\r
7292                                 endPoint == endContainer ? endPoint.nextSibling : endPoint\r
7293                         );\r
7294 \r
7295                         if (siblings.length)\r
7296                                 callback(siblings);\r
7297 \r
7298                         // Walk right leaf\r
7299                         walkBoundary(endContainer, endPoint);\r
7300                 };\r
7301 \r
7302                 /*              this.split = function(rng) {\r
7303                         var startContainer = rng.startContainer,\r
7304                                 startOffset = rng.startOffset,\r
7305                                 endContainer = rng.endContainer,\r
7306                                 endOffset = rng.endOffset;\r
7307 \r
7308                         function splitText(node, offset) {\r
7309                                 if (offset == node.nodeValue.length)\r
7310                                         node.appendData(INVISIBLE_CHAR);\r
7311 \r
7312                                 node = node.splitText(offset);\r
7313 \r
7314                                 if (node.nodeValue === INVISIBLE_CHAR)\r
7315                                         node.nodeValue = '';\r
7316 \r
7317                                 return node;\r
7318                         };\r
7319 \r
7320                         // Handle single text node\r
7321                         if (startContainer == endContainer) {\r
7322                                 if (startContainer.nodeType == 3) {\r
7323                                         if (startOffset != 0)\r
7324                                                 startContainer = endContainer = splitText(startContainer, startOffset);\r
7325 \r
7326                                         if (endOffset - startOffset != startContainer.nodeValue.length)\r
7327                                                 splitText(startContainer, endOffset - startOffset);\r
7328                                 }\r
7329                         } else {\r
7330                                 // Split startContainer text node if needed\r
7331                                 if (startContainer.nodeType == 3 && startOffset != 0) {\r
7332                                         startContainer = splitText(startContainer, startOffset);\r
7333                                         startOffset = 0;\r
7334                                 }\r
7335 \r
7336                                 // Split endContainer text node if needed\r
7337                                 if (endContainer.nodeType == 3 && endOffset != endContainer.nodeValue.length) {\r
7338                                         endContainer = splitText(endContainer, endOffset).previousSibling;\r
7339                                         endOffset = endContainer.nodeValue.length;\r
7340                                 }\r
7341                         }\r
7342 \r
7343                         return {\r
7344                                 startContainer : startContainer,\r
7345                                 startOffset : startOffset,\r
7346                                 endContainer : endContainer,\r
7347                                 endOffset : endOffset\r
7348                         };\r
7349                 };\r
7350 */\r
7351         };\r
7352 \r
7353         tinymce.dom.RangeUtils.compareRanges = function(rng1, rng2) {\r
7354                 if (rng1 && rng2) {\r
7355                         // Compare native IE ranges\r
7356                         if (rng1.item || rng1.duplicate) {\r
7357                                 // Both are control ranges and the selected element matches\r
7358                                 if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))\r
7359                                         return true;\r
7360 \r
7361                                 // Both are text ranges and the range matches\r
7362                                 if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1))\r
7363                                         return true;\r
7364                         } else {\r
7365                                 // Compare w3c ranges\r
7366                                 return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;\r
7367                         }\r
7368                 }\r
7369 \r
7370                 return false;\r
7371         };\r
7372 })(tinymce);\r
7373 \r
7374 (function(tinymce) {\r
7375         // Shorten class names\r
7376         var DOM = tinymce.DOM, is = tinymce.is;\r
7377 \r
7378         tinymce.create('tinymce.ui.Control', {\r
7379                 Control : function(id, s) {\r
7380                         this.id = id;\r
7381                         this.settings = s = s || {};\r
7382                         this.rendered = false;\r
7383                         this.onRender = new tinymce.util.Dispatcher(this);\r
7384                         this.classPrefix = '';\r
7385                         this.scope = s.scope || this;\r
7386                         this.disabled = 0;\r
7387                         this.active = 0;\r
7388                 },\r
7389 \r
7390                 setDisabled : function(s) {\r
7391                         var e;\r
7392 \r
7393                         if (s != this.disabled) {\r
7394                                 e = DOM.get(this.id);\r
7395 \r
7396                                 // Add accessibility title for unavailable actions\r
7397                                 if (e && this.settings.unavailable_prefix) {\r
7398                                         if (s) {\r
7399                                                 this.prevTitle = e.title;\r
7400                                                 e.title = this.settings.unavailable_prefix + ": " + e.title;\r
7401                                         } else\r
7402                                                 e.title = this.prevTitle;\r
7403                                 }\r
7404 \r
7405                                 this.setState('Disabled', s);\r
7406                                 this.setState('Enabled', !s);\r
7407                                 this.disabled = s;\r
7408                         }\r
7409                 },\r
7410 \r
7411                 isDisabled : function() {\r
7412                         return this.disabled;\r
7413                 },\r
7414 \r
7415                 setActive : function(s) {\r
7416                         if (s != this.active) {\r
7417                                 this.setState('Active', s);\r
7418                                 this.active = s;\r
7419                         }\r
7420                 },\r
7421 \r
7422                 isActive : function() {\r
7423                         return this.active;\r
7424                 },\r
7425 \r
7426                 setState : function(c, s) {\r
7427                         var n = DOM.get(this.id);\r
7428 \r
7429                         c = this.classPrefix + c;\r
7430 \r
7431                         if (s)\r
7432                                 DOM.addClass(n, c);\r
7433                         else\r
7434                                 DOM.removeClass(n, c);\r
7435                 },\r
7436 \r
7437                 isRendered : function() {\r
7438                         return this.rendered;\r
7439                 },\r
7440 \r
7441                 renderHTML : function() {\r
7442                 },\r
7443 \r
7444                 renderTo : function(n) {\r
7445                         DOM.setHTML(n, this.renderHTML());\r
7446                 },\r
7447 \r
7448                 postRender : function() {\r
7449                         var t = this, b;\r
7450 \r
7451                         // Set pending states\r
7452                         if (is(t.disabled)) {\r
7453                                 b = t.disabled;\r
7454                                 t.disabled = -1;\r
7455                                 t.setDisabled(b);\r
7456                         }\r
7457 \r
7458                         if (is(t.active)) {\r
7459                                 b = t.active;\r
7460                                 t.active = -1;\r
7461                                 t.setActive(b);\r
7462                         }\r
7463                 },\r
7464 \r
7465                 remove : function() {\r
7466                         DOM.remove(this.id);\r
7467                         this.destroy();\r
7468                 },\r
7469 \r
7470                 destroy : function() {\r
7471                         tinymce.dom.Event.clear(this.id);\r
7472                 }\r
7473         });\r
7474 })(tinymce);\r
7475 tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {\r
7476         Container : function(id, s) {\r
7477                 this.parent(id, s);\r
7478 \r
7479                 this.controls = [];\r
7480 \r
7481                 this.lookup = {};\r
7482         },\r
7483 \r
7484         add : function(c) {\r
7485                 this.lookup[c.id] = c;\r
7486                 this.controls.push(c);\r
7487 \r
7488                 return c;\r
7489         },\r
7490 \r
7491         get : function(n) {\r
7492                 return this.lookup[n];\r
7493         }\r
7494 });\r
7495 \r
7496 \r
7497 tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {\r
7498         Separator : function(id, s) {\r
7499                 this.parent(id, s);\r
7500                 this.classPrefix = 'mceSeparator';\r
7501         },\r
7502 \r
7503         renderHTML : function() {\r
7504                 return tinymce.DOM.createHTML('span', {'class' : this.classPrefix});\r
7505         }\r
7506 });\r
7507 \r
7508 (function(tinymce) {\r
7509         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
7510 \r
7511         tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', {\r
7512                 MenuItem : function(id, s) {\r
7513                         this.parent(id, s);\r
7514                         this.classPrefix = 'mceMenuItem';\r
7515                 },\r
7516 \r
7517                 setSelected : function(s) {\r
7518                         this.setState('Selected', s);\r
7519                         this.selected = s;\r
7520                 },\r
7521 \r
7522                 isSelected : function() {\r
7523                         return this.selected;\r
7524                 },\r
7525 \r
7526                 postRender : function() {\r
7527                         var t = this;\r
7528                         \r
7529                         t.parent();\r
7530 \r
7531                         // Set pending state\r
7532                         if (is(t.selected))\r
7533                                 t.setSelected(t.selected);\r
7534                 }\r
7535         });\r
7536 })(tinymce);\r
7537 \r
7538 (function(tinymce) {\r
7539         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
7540 \r
7541         tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', {\r
7542                 Menu : function(id, s) {\r
7543                         var t = this;\r
7544 \r
7545                         t.parent(id, s);\r
7546                         t.items = {};\r
7547                         t.collapsed = false;\r
7548                         t.menuCount = 0;\r
7549                         t.onAddItem = new tinymce.util.Dispatcher(this);\r
7550                 },\r
7551 \r
7552                 expand : function(d) {\r
7553                         var t = this;\r
7554 \r
7555                         if (d) {\r
7556                                 walk(t, function(o) {\r
7557                                         if (o.expand)\r
7558                                                 o.expand();\r
7559                                 }, 'items', t);\r
7560                         }\r
7561 \r
7562                         t.collapsed = false;\r
7563                 },\r
7564 \r
7565                 collapse : function(d) {\r
7566                         var t = this;\r
7567 \r
7568                         if (d) {\r
7569                                 walk(t, function(o) {\r
7570                                         if (o.collapse)\r
7571                                                 o.collapse();\r
7572                                 }, 'items', t);\r
7573                         }\r
7574 \r
7575                         t.collapsed = true;\r
7576                 },\r
7577 \r
7578                 isCollapsed : function() {\r
7579                         return this.collapsed;\r
7580                 },\r
7581 \r
7582                 add : function(o) {\r
7583                         if (!o.settings)\r
7584                                 o = new tinymce.ui.MenuItem(o.id || DOM.uniqueId(), o);\r
7585 \r
7586                         this.onAddItem.dispatch(this, o);\r
7587 \r
7588                         return this.items[o.id] = o;\r
7589                 },\r
7590 \r
7591                 addSeparator : function() {\r
7592                         return this.add({separator : true});\r
7593                 },\r
7594 \r
7595                 addMenu : function(o) {\r
7596                         if (!o.collapse)\r
7597                                 o = this.createMenu(o);\r
7598 \r
7599                         this.menuCount++;\r
7600 \r
7601                         return this.add(o);\r
7602                 },\r
7603 \r
7604                 hasMenus : function() {\r
7605                         return this.menuCount !== 0;\r
7606                 },\r
7607 \r
7608                 remove : function(o) {\r
7609                         delete this.items[o.id];\r
7610                 },\r
7611 \r
7612                 removeAll : function() {\r
7613                         var t = this;\r
7614 \r
7615                         walk(t, function(o) {\r
7616                                 if (o.removeAll)\r
7617                                         o.removeAll();\r
7618                                 else\r
7619                                         o.remove();\r
7620 \r
7621                                 o.destroy();\r
7622                         }, 'items', t);\r
7623 \r
7624                         t.items = {};\r
7625                 },\r
7626 \r
7627                 createMenu : function(o) {\r
7628                         var m = new tinymce.ui.Menu(o.id || DOM.uniqueId(), o);\r
7629 \r
7630                         m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem);\r
7631 \r
7632                         return m;\r
7633                 }\r
7634         });\r
7635 })(tinymce);\r
7636 (function(tinymce) {\r
7637         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element;\r
7638 \r
7639         tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', {\r
7640                 DropMenu : function(id, s) {\r
7641                         s = s || {};\r
7642                         s.container = s.container || DOM.doc.body;\r
7643                         s.offset_x = s.offset_x || 0;\r
7644                         s.offset_y = s.offset_y || 0;\r
7645                         s.vp_offset_x = s.vp_offset_x || 0;\r
7646                         s.vp_offset_y = s.vp_offset_y || 0;\r
7647 \r
7648                         if (is(s.icons) && !s.icons)\r
7649                                 s['class'] += ' mceNoIcons';\r
7650 \r
7651                         this.parent(id, s);\r
7652                         this.onShowMenu = new tinymce.util.Dispatcher(this);\r
7653                         this.onHideMenu = new tinymce.util.Dispatcher(this);\r
7654                         this.classPrefix = 'mceMenu';\r
7655                 },\r
7656 \r
7657                 createMenu : function(s) {\r
7658                         var t = this, cs = t.settings, m;\r
7659 \r
7660                         s.container = s.container || cs.container;\r
7661                         s.parent = t;\r
7662                         s.constrain = s.constrain || cs.constrain;\r
7663                         s['class'] = s['class'] || cs['class'];\r
7664                         s.vp_offset_x = s.vp_offset_x || cs.vp_offset_x;\r
7665                         s.vp_offset_y = s.vp_offset_y || cs.vp_offset_y;\r
7666                         m = new tinymce.ui.DropMenu(s.id || DOM.uniqueId(), s);\r
7667 \r
7668                         m.onAddItem.add(t.onAddItem.dispatch, t.onAddItem);\r
7669 \r
7670                         return m;\r
7671                 },\r
7672 \r
7673                 update : function() {\r
7674                         var t = this, s = t.settings, tb = DOM.get('menu_' + t.id + '_tbl'), co = DOM.get('menu_' + t.id + '_co'), tw, th;\r
7675 \r
7676                         tw = s.max_width ? Math.min(tb.clientWidth, s.max_width) : tb.clientWidth;\r
7677                         th = s.max_height ? Math.min(tb.clientHeight, s.max_height) : tb.clientHeight;\r
7678 \r
7679                         if (!DOM.boxModel)\r
7680                                 t.element.setStyles({width : tw + 2, height : th + 2});\r
7681                         else\r
7682                                 t.element.setStyles({width : tw, height : th});\r
7683 \r
7684                         if (s.max_width)\r
7685                                 DOM.setStyle(co, 'width', tw);\r
7686 \r
7687                         if (s.max_height) {\r
7688                                 DOM.setStyle(co, 'height', th);\r
7689 \r
7690                                 if (tb.clientHeight < s.max_height)\r
7691                                         DOM.setStyle(co, 'overflow', 'hidden');\r
7692                         }\r
7693                 },\r
7694 \r
7695                 showMenu : function(x, y, px) {\r
7696                         var t = this, s = t.settings, co, vp = DOM.getViewPort(), w, h, mx, my, ot = 2, dm, tb, cp = t.classPrefix;\r
7697 \r
7698                         t.collapse(1);\r
7699 \r
7700                         if (t.isMenuVisible)\r
7701                                 return;\r
7702 \r
7703                         if (!t.rendered) {\r
7704                                 co = DOM.add(t.settings.container, t.renderNode());\r
7705 \r
7706                                 each(t.items, function(o) {\r
7707                                         o.postRender();\r
7708                                 });\r
7709 \r
7710                                 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
7711                         } else\r
7712                                 co = DOM.get('menu_' + t.id);\r
7713 \r
7714                         // Move layer out of sight unless it's Opera since it scrolls to top of page due to an bug\r
7715                         if (!tinymce.isOpera)\r
7716                                 DOM.setStyles(co, {left : -0xFFFF , top : -0xFFFF});\r
7717 \r
7718                         DOM.show(co);\r
7719                         t.update();\r
7720 \r
7721                         x += s.offset_x || 0;\r
7722                         y += s.offset_y || 0;\r
7723                         vp.w -= 4;\r
7724                         vp.h -= 4;\r
7725 \r
7726                         // Move inside viewport if not submenu\r
7727                         if (s.constrain) {\r
7728                                 w = co.clientWidth - ot;\r
7729                                 h = co.clientHeight - ot;\r
7730                                 mx = vp.x + vp.w;\r
7731                                 my = vp.y + vp.h;\r
7732 \r
7733                                 if ((x + s.vp_offset_x + w) > mx)\r
7734                                         x = px ? px - w : Math.max(0, (mx - s.vp_offset_x) - w);\r
7735 \r
7736                                 if ((y + s.vp_offset_y + h) > my)\r
7737                                         y = Math.max(0, (my - s.vp_offset_y) - h);\r
7738                         }\r
7739 \r
7740                         DOM.setStyles(co, {left : x , top : y});\r
7741                         t.element.update();\r
7742 \r
7743                         t.isMenuVisible = 1;\r
7744                         t.mouseClickFunc = Event.add(co, 'click', function(e) {\r
7745                                 var m;\r
7746 \r
7747                                 e = e.target;\r
7748 \r
7749                                 if (e && (e = DOM.getParent(e, 'tr')) && !DOM.hasClass(e, cp + 'ItemSub')) {\r
7750                                         m = t.items[e.id];\r
7751 \r
7752                                         if (m.isDisabled())\r
7753                                                 return;\r
7754 \r
7755                                         dm = t;\r
7756 \r
7757                                         while (dm) {\r
7758                                                 if (dm.hideMenu)\r
7759                                                         dm.hideMenu();\r
7760 \r
7761                                                 dm = dm.settings.parent;\r
7762                                         }\r
7763 \r
7764                                         if (m.settings.onclick)\r
7765                                                 m.settings.onclick(e);\r
7766 \r
7767                                         return Event.cancel(e); // Cancel to fix onbeforeunload problem\r
7768                                 }\r
7769                         });\r
7770 \r
7771                         if (t.hasMenus()) {\r
7772                                 t.mouseOverFunc = Event.add(co, 'mouseover', function(e) {\r
7773                                         var m, r, mi;\r
7774 \r
7775                                         e = e.target;\r
7776                                         if (e && (e = DOM.getParent(e, 'tr'))) {\r
7777                                                 m = t.items[e.id];\r
7778 \r
7779                                                 if (t.lastMenu)\r
7780                                                         t.lastMenu.collapse(1);\r
7781 \r
7782                                                 if (m.isDisabled())\r
7783                                                         return;\r
7784 \r
7785                                                 if (e && DOM.hasClass(e, cp + 'ItemSub')) {\r
7786                                                         //p = DOM.getPos(s.container);\r
7787                                                         r = DOM.getRect(e);\r
7788                                                         m.showMenu((r.x + r.w - ot), r.y - ot, r.x);\r
7789                                                         t.lastMenu = m;\r
7790                                                         DOM.addClass(DOM.get(m.id).firstChild, cp + 'ItemActive');\r
7791                                                 }\r
7792                                         }\r
7793                                 });\r
7794                         }\r
7795 \r
7796                         t.onShowMenu.dispatch(t);\r
7797 \r
7798                         if (s.keyboard_focus) {\r
7799                                 Event.add(co, 'keydown', t._keyHandler, t);\r
7800                                 DOM.select('a', 'menu_' + t.id)[0].focus(); // Select first link\r
7801                                 t._focusIdx = 0;\r
7802                         }\r
7803                 },\r
7804 \r
7805                 hideMenu : function(c) {\r
7806                         var t = this, co = DOM.get('menu_' + t.id), e;\r
7807 \r
7808                         if (!t.isMenuVisible)\r
7809                                 return;\r
7810 \r
7811                         Event.remove(co, 'mouseover', t.mouseOverFunc);\r
7812                         Event.remove(co, 'click', t.mouseClickFunc);\r
7813                         Event.remove(co, 'keydown', t._keyHandler);\r
7814                         DOM.hide(co);\r
7815                         t.isMenuVisible = 0;\r
7816 \r
7817                         if (!c)\r
7818                                 t.collapse(1);\r
7819 \r
7820                         if (t.element)\r
7821                                 t.element.hide();\r
7822 \r
7823                         if (e = DOM.get(t.id))\r
7824                                 DOM.removeClass(e.firstChild, t.classPrefix + 'ItemActive');\r
7825 \r
7826                         t.onHideMenu.dispatch(t);\r
7827                 },\r
7828 \r
7829                 add : function(o) {\r
7830                         var t = this, co;\r
7831 \r
7832                         o = t.parent(o);\r
7833 \r
7834                         if (t.isRendered && (co = DOM.get('menu_' + t.id)))\r
7835                                 t._add(DOM.select('tbody', co)[0], o);\r
7836 \r
7837                         return o;\r
7838                 },\r
7839 \r
7840                 collapse : function(d) {\r
7841                         this.parent(d);\r
7842                         this.hideMenu(1);\r
7843                 },\r
7844 \r
7845                 remove : function(o) {\r
7846                         DOM.remove(o.id);\r
7847                         this.destroy();\r
7848 \r
7849                         return this.parent(o);\r
7850                 },\r
7851 \r
7852                 destroy : function() {\r
7853                         var t = this, co = DOM.get('menu_' + t.id);\r
7854 \r
7855                         Event.remove(co, 'mouseover', t.mouseOverFunc);\r
7856                         Event.remove(co, 'click', t.mouseClickFunc);\r
7857 \r
7858                         if (t.element)\r
7859                                 t.element.remove();\r
7860 \r
7861                         DOM.remove(co);\r
7862                 },\r
7863 \r
7864                 renderNode : function() {\r
7865                         var t = this, s = t.settings, n, tb, co, w;\r
7866 \r
7867                         w = DOM.create('div', {id : 'menu_' + t.id, 'class' : s['class'], 'style' : 'position:absolute;left:0;top:0;z-index:200000'});\r
7868                         co = DOM.add(w, 'div', {id : 'menu_' + t.id + '_co', 'class' : t.classPrefix + (s['class'] ? ' ' + s['class'] : '')});\r
7869                         t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
7870 \r
7871                         if (s.menu_line)\r
7872                                 DOM.add(co, 'span', {'class' : t.classPrefix + 'Line'});\r
7873 \r
7874 //                      n = DOM.add(co, 'div', {id : 'menu_' + t.id + '_co', 'class' : 'mceMenuContainer'});\r
7875                         n = DOM.add(co, 'table', {id : 'menu_' + t.id + '_tbl', border : 0, cellPadding : 0, cellSpacing : 0});\r
7876                         tb = DOM.add(n, 'tbody');\r
7877 \r
7878                         each(t.items, function(o) {\r
7879                                 t._add(tb, o);\r
7880                         });\r
7881 \r
7882                         t.rendered = true;\r
7883 \r
7884                         return w;\r
7885                 },\r
7886 \r
7887                 // Internal functions\r
7888 \r
7889                 _keyHandler : function(e) {\r
7890                         var t = this, kc = e.keyCode;\r
7891 \r
7892                         function focus(d) {\r
7893                                 var i = t._focusIdx + d, e = DOM.select('a', 'menu_' + t.id)[i];\r
7894 \r
7895                                 if (e) {\r
7896                                         t._focusIdx = i;\r
7897                                         e.focus();\r
7898                                 }\r
7899                         };\r
7900 \r
7901                         switch (kc) {\r
7902                                 case 38:\r
7903                                         focus(-1); // Select first link\r
7904                                         return;\r
7905 \r
7906                                 case 40:\r
7907                                         focus(1);\r
7908                                         return;\r
7909 \r
7910                                 case 13:\r
7911                                         return;\r
7912 \r
7913                                 case 27:\r
7914                                         return this.hideMenu();\r
7915                         }\r
7916                 },\r
7917 \r
7918                 _add : function(tb, o) {\r
7919                         var n, s = o.settings, a, ro, it, cp = this.classPrefix, ic;\r
7920 \r
7921                         if (s.separator) {\r
7922                                 ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'ItemSeparator'});\r
7923                                 DOM.add(ro, 'td', {'class' : cp + 'ItemSeparator'});\r
7924 \r
7925                                 if (n = ro.previousSibling)\r
7926                                         DOM.addClass(n, 'mceLast');\r
7927 \r
7928                                 return;\r
7929                         }\r
7930 \r
7931                         n = ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'Item ' + cp + 'ItemEnabled'});\r
7932                         n = it = DOM.add(n, 'td');\r
7933                         n = a = DOM.add(n, 'a', {href : 'javascript:;', onclick : "return false;", onmousedown : 'return false;'});\r
7934 \r
7935                         DOM.addClass(it, s['class']);\r
7936 //                      n = DOM.add(n, 'span', {'class' : 'item'});\r
7937 \r
7938                         ic = DOM.add(n, 'span', {'class' : 'mceIcon' + (s.icon ? ' mce_' + s.icon : '')});\r
7939 \r
7940                         if (s.icon_src)\r
7941                                 DOM.add(ic, 'img', {src : s.icon_src});\r
7942 \r
7943                         n = DOM.add(n, s.element || 'span', {'class' : 'mceText', title : o.settings.title}, o.settings.title);\r
7944 \r
7945                         if (o.settings.style)\r
7946                                 DOM.setAttrib(n, 'style', o.settings.style);\r
7947 \r
7948                         if (tb.childNodes.length == 1)\r
7949                                 DOM.addClass(ro, 'mceFirst');\r
7950 \r
7951                         if ((n = ro.previousSibling) && DOM.hasClass(n, cp + 'ItemSeparator'))\r
7952                                 DOM.addClass(ro, 'mceFirst');\r
7953 \r
7954                         if (o.collapse)\r
7955                                 DOM.addClass(ro, cp + 'ItemSub');\r
7956 \r
7957                         if (n = ro.previousSibling)\r
7958                                 DOM.removeClass(n, 'mceLast');\r
7959 \r
7960                         DOM.addClass(ro, 'mceLast');\r
7961                 }\r
7962         });\r
7963 })(tinymce);\r
7964 (function(tinymce) {\r
7965         var DOM = tinymce.DOM;\r
7966 \r
7967         tinymce.create('tinymce.ui.Button:tinymce.ui.Control', {\r
7968                 Button : function(id, s) {\r
7969                         this.parent(id, s);\r
7970                         this.classPrefix = 'mceButton';\r
7971                 },\r
7972 \r
7973                 renderHTML : function() {\r
7974                         var cp = this.classPrefix, s = this.settings, h, l;\r
7975 \r
7976                         l = DOM.encode(s.label || '');\r
7977                         h = '<a id="' + this.id + '" href="javascript:;" class="' + cp + ' ' + cp + 'Enabled ' + s['class'] + (l ? ' ' + cp + 'Labeled' : '') +'" onmousedown="return false;" onclick="return false;" title="' + DOM.encode(s.title) + '">';\r
7978 \r
7979                         if (s.image)\r
7980                                 h += '<img class="mceIcon" src="' + s.image + '" />' + l + '</a>';\r
7981                         else\r
7982                                 h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '') + '</a>';\r
7983 \r
7984                         return h;\r
7985                 },\r
7986 \r
7987                 postRender : function() {\r
7988                         var t = this, s = t.settings;\r
7989 \r
7990                         tinymce.dom.Event.add(t.id, 'click', function(e) {\r
7991                                 if (!t.isDisabled())\r
7992                                         return s.onclick.call(s.scope, e);\r
7993                         });\r
7994                 }\r
7995         });\r
7996 })(tinymce);\r
7997 \r
7998 (function(tinymce) {\r
7999         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
8000 \r
8001         tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {\r
8002                 ListBox : function(id, s) {\r
8003                         var t = this;\r
8004 \r
8005                         t.parent(id, s);\r
8006 \r
8007                         t.items = [];\r
8008 \r
8009                         t.onChange = new Dispatcher(t);\r
8010 \r
8011                         t.onPostRender = new Dispatcher(t);\r
8012 \r
8013                         t.onAdd = new Dispatcher(t);\r
8014 \r
8015                         t.onRenderMenu = new tinymce.util.Dispatcher(this);\r
8016 \r
8017                         t.classPrefix = 'mceListBox';\r
8018                 },\r
8019 \r
8020                 select : function(va) {\r
8021                         var t = this, fv, f;\r
8022 \r
8023                         if (va == undefined)\r
8024                                 return t.selectByIndex(-1);\r
8025 \r
8026                         // Is string or number make function selector\r
8027                         if (va && va.call)\r
8028                                 f = va;\r
8029                         else {\r
8030                                 f = function(v) {\r
8031                                         return v == va;\r
8032                                 };\r
8033                         }\r
8034 \r
8035                         // Do we need to do something?\r
8036                         if (va != t.selectedValue) {\r
8037                                 // Find item\r
8038                                 each(t.items, function(o, i) {\r
8039                                         if (f(o.value)) {\r
8040                                                 fv = 1;\r
8041                                                 t.selectByIndex(i);\r
8042                                                 return false;\r
8043                                         }\r
8044                                 });\r
8045 \r
8046                                 if (!fv)\r
8047                                         t.selectByIndex(-1);\r
8048                         }\r
8049                 },\r
8050 \r
8051                 selectByIndex : function(idx) {\r
8052                         var t = this, e, o;\r
8053 \r
8054                         if (idx != t.selectedIndex) {\r
8055                                 e = DOM.get(t.id + '_text');\r
8056                                 o = t.items[idx];\r
8057 \r
8058                                 if (o) {\r
8059                                         t.selectedValue = o.value;\r
8060                                         t.selectedIndex = idx;\r
8061                                         DOM.setHTML(e, DOM.encode(o.title));\r
8062                                         DOM.removeClass(e, 'mceTitle');\r
8063                                 } else {\r
8064                                         DOM.setHTML(e, DOM.encode(t.settings.title));\r
8065                                         DOM.addClass(e, 'mceTitle');\r
8066                                         t.selectedValue = t.selectedIndex = null;\r
8067                                 }\r
8068 \r
8069                                 e = 0;\r
8070                         }\r
8071                 },\r
8072 \r
8073                 add : function(n, v, o) {\r
8074                         var t = this;\r
8075 \r
8076                         o = o || {};\r
8077                         o = tinymce.extend(o, {\r
8078                                 title : n,\r
8079                                 value : v\r
8080                         });\r
8081 \r
8082                         t.items.push(o);\r
8083                         t.onAdd.dispatch(t, o);\r
8084                 },\r
8085 \r
8086                 getLength : function() {\r
8087                         return this.items.length;\r
8088                 },\r
8089 \r
8090                 renderHTML : function() {\r
8091                         var h = '', t = this, s = t.settings, cp = t.classPrefix;\r
8092 \r
8093                         h = '<table id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>';\r
8094                         h += '<td>' + DOM.createHTML('a', {id : t.id + '_text', href : 'javascript:;', 'class' : 'mceText', onclick : "return false;", onmousedown : 'return false;'}, DOM.encode(t.settings.title)) + '</td>';\r
8095                         h += '<td>' + DOM.createHTML('a', {id : t.id + '_open', tabindex : -1, href : 'javascript:;', 'class' : 'mceOpen', onclick : "return false;", onmousedown : 'return false;'}, '<span></span>') + '</td>';\r
8096                         h += '</tr></tbody></table>';\r
8097 \r
8098                         return h;\r
8099                 },\r
8100 \r
8101                 showMenu : function() {\r
8102                         var t = this, p1, p2, e = DOM.get(this.id), m;\r
8103 \r
8104                         if (t.isDisabled() || t.items.length == 0)\r
8105                                 return;\r
8106 \r
8107                         if (t.menu && t.menu.isMenuVisible)\r
8108                                 return t.hideMenu();\r
8109 \r
8110                         if (!t.isMenuRendered) {\r
8111                                 t.renderMenu();\r
8112                                 t.isMenuRendered = true;\r
8113                         }\r
8114 \r
8115                         p1 = DOM.getPos(this.settings.menu_container);\r
8116                         p2 = DOM.getPos(e);\r
8117 \r
8118                         m = t.menu;\r
8119                         m.settings.offset_x = p2.x;\r
8120                         m.settings.offset_y = p2.y;\r
8121                         m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus\r
8122 \r
8123                         // Select in menu\r
8124                         if (t.oldID)\r
8125                                 m.items[t.oldID].setSelected(0);\r
8126 \r
8127                         each(t.items, function(o) {\r
8128                                 if (o.value === t.selectedValue) {\r
8129                                         m.items[o.id].setSelected(1);\r
8130                                         t.oldID = o.id;\r
8131                                 }\r
8132                         });\r
8133 \r
8134                         m.showMenu(0, e.clientHeight);\r
8135 \r
8136                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
8137                         DOM.addClass(t.id, t.classPrefix + 'Selected');\r
8138 \r
8139                         //DOM.get(t.id + '_text').focus();\r
8140                 },\r
8141 \r
8142                 hideMenu : function(e) {\r
8143                         var t = this;\r
8144 \r
8145                         if (t.menu && t.menu.isMenuVisible) {\r
8146                                 // Prevent double toogles by canceling the mouse click event to the button\r
8147                                 if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open'))\r
8148                                         return;\r
8149 \r
8150                                 if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
8151                                         DOM.removeClass(t.id, t.classPrefix + 'Selected');\r
8152                                         Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
8153                                         t.menu.hideMenu();\r
8154                                 }\r
8155                         }\r
8156                 },\r
8157 \r
8158                 renderMenu : function() {\r
8159                         var t = this, m;\r
8160 \r
8161                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
8162                                 menu_line : 1,\r
8163                                 'class' : t.classPrefix + 'Menu mceNoIcons',\r
8164                                 max_width : 150,\r
8165                                 max_height : 150\r
8166                         });\r
8167 \r
8168                         m.onHideMenu.add(t.hideMenu, t);\r
8169 \r
8170                         m.add({\r
8171                                 title : t.settings.title,\r
8172                                 'class' : 'mceMenuItemTitle',\r
8173                                 onclick : function() {\r
8174                                         if (t.settings.onselect('') !== false)\r
8175                                                 t.select(''); // Must be runned after\r
8176                                 }\r
8177                         });\r
8178 \r
8179                         each(t.items, function(o) {\r
8180                                 // No value then treat it as a title\r
8181                                 if (o.value === undefined) {\r
8182                                         m.add({\r
8183                                                 title : o.title,\r
8184                                                 'class' : 'mceMenuItemTitle',\r
8185                                                 onclick : function() {\r
8186                                                         if (t.settings.onselect('') !== false)\r
8187                                                                 t.select(''); // Must be runned after\r
8188                                                 }\r
8189                                         });\r
8190                                 } else {\r
8191                                         o.id = DOM.uniqueId();\r
8192                                         o.onclick = function() {\r
8193                                                 if (t.settings.onselect(o.value) !== false)\r
8194                                                         t.select(o.value); // Must be runned after\r
8195                                         };\r
8196 \r
8197                                         m.add(o);\r
8198                                 }\r
8199                         });\r
8200 \r
8201                         t.onRenderMenu.dispatch(t, m);\r
8202                         t.menu = m;\r
8203                 },\r
8204 \r
8205                 postRender : function() {\r
8206                         var t = this, cp = t.classPrefix;\r
8207 \r
8208                         Event.add(t.id, 'click', t.showMenu, t);\r
8209                         Event.add(t.id + '_text', 'focus', function() {\r
8210                                 if (!t._focused) {\r
8211                                         t.keyDownHandler = Event.add(t.id + '_text', 'keydown', function(e) {\r
8212                                                 var idx = -1, v, kc = e.keyCode;\r
8213 \r
8214                                                 // Find current index\r
8215                                                 each(t.items, function(v, i) {\r
8216                                                         if (t.selectedValue == v.value)\r
8217                                                                 idx = i;\r
8218                                                 });\r
8219 \r
8220                                                 // Move up/down\r
8221                                                 if (kc == 38)\r
8222                                                         v = t.items[idx - 1];\r
8223                                                 else if (kc == 40)\r
8224                                                         v = t.items[idx + 1];\r
8225                                                 else if (kc == 13) {\r
8226                                                         // Fake select on enter\r
8227                                                         v = t.selectedValue;\r
8228                                                         t.selectedValue = null; // Needs to be null to fake change\r
8229                                                         t.settings.onselect(v);\r
8230                                                         return Event.cancel(e);\r
8231                                                 }\r
8232 \r
8233                                                 if (v) {\r
8234                                                         t.hideMenu();\r
8235                                                         t.select(v.value);\r
8236                                                 }\r
8237                                         });\r
8238                                 }\r
8239 \r
8240                                 t._focused = 1;\r
8241                         });\r
8242                         Event.add(t.id + '_text', 'blur', function() {Event.remove(t.id + '_text', 'keydown', t.keyDownHandler); t._focused = 0;});\r
8243 \r
8244                         // Old IE doesn't have hover on all elements\r
8245                         if (tinymce.isIE6 || !DOM.boxModel) {\r
8246                                 Event.add(t.id, 'mouseover', function() {\r
8247                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
8248                                                 DOM.addClass(t.id, cp + 'Hover');\r
8249                                 });\r
8250 \r
8251                                 Event.add(t.id, 'mouseout', function() {\r
8252                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
8253                                                 DOM.removeClass(t.id, cp + 'Hover');\r
8254                                 });\r
8255                         }\r
8256 \r
8257                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
8258                 },\r
8259 \r
8260                 destroy : function() {\r
8261                         this.parent();\r
8262 \r
8263                         Event.clear(this.id + '_text');\r
8264                         Event.clear(this.id + '_open');\r
8265                 }\r
8266         });\r
8267 })(tinymce);\r
8268 (function(tinymce) {\r
8269         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
8270 \r
8271         tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {\r
8272                 NativeListBox : function(id, s) {\r
8273                         this.parent(id, s);\r
8274                         this.classPrefix = 'mceNativeListBox';\r
8275                 },\r
8276 \r
8277                 setDisabled : function(s) {\r
8278                         DOM.get(this.id).disabled = s;\r
8279                 },\r
8280 \r
8281                 isDisabled : function() {\r
8282                         return DOM.get(this.id).disabled;\r
8283                 },\r
8284 \r
8285                 select : function(va) {\r
8286                         var t = this, fv, f;\r
8287 \r
8288                         if (va == undefined)\r
8289                                 return t.selectByIndex(-1);\r
8290 \r
8291                         // Is string or number make function selector\r
8292                         if (va && va.call)\r
8293                                 f = va;\r
8294                         else {\r
8295                                 f = function(v) {\r
8296                                         return v == va;\r
8297                                 };\r
8298                         }\r
8299 \r
8300                         // Do we need to do something?\r
8301                         if (va != t.selectedValue) {\r
8302                                 // Find item\r
8303                                 each(t.items, function(o, i) {\r
8304                                         if (f(o.value)) {\r
8305                                                 fv = 1;\r
8306                                                 t.selectByIndex(i);\r
8307                                                 return false;\r
8308                                         }\r
8309                                 });\r
8310 \r
8311                                 if (!fv)\r
8312                                         t.selectByIndex(-1);\r
8313                         }\r
8314                 },\r
8315 \r
8316                 selectByIndex : function(idx) {\r
8317                         DOM.get(this.id).selectedIndex = idx + 1;\r
8318                         this.selectedValue = this.items[idx] ? this.items[idx].value : null;\r
8319                 },\r
8320 \r
8321                 add : function(n, v, a) {\r
8322                         var o, t = this;\r
8323 \r
8324                         a = a || {};\r
8325                         a.value = v;\r
8326 \r
8327                         if (t.isRendered())\r
8328                                 DOM.add(DOM.get(this.id), 'option', a, n);\r
8329 \r
8330                         o = {\r
8331                                 title : n,\r
8332                                 value : v,\r
8333                                 attribs : a\r
8334                         };\r
8335 \r
8336                         t.items.push(o);\r
8337                         t.onAdd.dispatch(t, o);\r
8338                 },\r
8339 \r
8340                 getLength : function() {\r
8341                         return this.items.length;\r
8342                 },\r
8343 \r
8344                 renderHTML : function() {\r
8345                         var h, t = this;\r
8346 \r
8347                         h = DOM.createHTML('option', {value : ''}, '-- ' + t.settings.title + ' --');\r
8348 \r
8349                         each(t.items, function(it) {\r
8350                                 h += DOM.createHTML('option', {value : it.value}, it.title);\r
8351                         });\r
8352 \r
8353                         h = DOM.createHTML('select', {id : t.id, 'class' : 'mceNativeListBox'}, h);\r
8354 \r
8355                         return h;\r
8356                 },\r
8357 \r
8358                 postRender : function() {\r
8359                         var t = this, ch;\r
8360 \r
8361                         t.rendered = true;\r
8362 \r
8363                         function onChange(e) {\r
8364                                 var v = t.items[e.target.selectedIndex - 1];\r
8365 \r
8366                                 if (v && (v = v.value)) {\r
8367                                         t.onChange.dispatch(t, v);\r
8368 \r
8369                                         if (t.settings.onselect)\r
8370                                                 t.settings.onselect(v);\r
8371                                 }\r
8372                         };\r
8373 \r
8374                         Event.add(t.id, 'change', onChange);\r
8375 \r
8376                         // Accessibility keyhandler\r
8377                         Event.add(t.id, 'keydown', function(e) {\r
8378                                 var bf;\r
8379 \r
8380                                 Event.remove(t.id, 'change', ch);\r
8381 \r
8382                                 bf = Event.add(t.id, 'blur', function() {\r
8383                                         Event.add(t.id, 'change', onChange);\r
8384                                         Event.remove(t.id, 'blur', bf);\r
8385                                 });\r
8386 \r
8387                                 if (e.keyCode == 13 || e.keyCode == 32) {\r
8388                                         onChange(e);\r
8389                                         return Event.cancel(e);\r
8390                                 }\r
8391                         });\r
8392 \r
8393                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
8394                 }\r
8395         });\r
8396 })(tinymce);\r
8397 (function(tinymce) {\r
8398         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
8399 \r
8400         tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', {\r
8401                 MenuButton : function(id, s) {\r
8402                         this.parent(id, s);\r
8403 \r
8404                         this.onRenderMenu = new tinymce.util.Dispatcher(this);\r
8405 \r
8406                         s.menu_container = s.menu_container || DOM.doc.body;\r
8407                 },\r
8408 \r
8409                 showMenu : function() {\r
8410                         var t = this, p1, p2, e = DOM.get(t.id), m;\r
8411 \r
8412                         if (t.isDisabled())\r
8413                                 return;\r
8414 \r
8415                         if (!t.isMenuRendered) {\r
8416                                 t.renderMenu();\r
8417                                 t.isMenuRendered = true;\r
8418                         }\r
8419 \r
8420                         if (t.isMenuVisible)\r
8421                                 return t.hideMenu();\r
8422 \r
8423                         p1 = DOM.getPos(t.settings.menu_container);\r
8424                         p2 = DOM.getPos(e);\r
8425 \r
8426                         m = t.menu;\r
8427                         m.settings.offset_x = p2.x;\r
8428                         m.settings.offset_y = p2.y;\r
8429                         m.settings.vp_offset_x = p2.x;\r
8430                         m.settings.vp_offset_y = p2.y;\r
8431                         m.settings.keyboard_focus = t._focused;\r
8432                         m.showMenu(0, e.clientHeight);\r
8433 \r
8434                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
8435                         t.setState('Selected', 1);\r
8436 \r
8437                         t.isMenuVisible = 1;\r
8438                 },\r
8439 \r
8440                 renderMenu : function() {\r
8441                         var t = this, m;\r
8442 \r
8443                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
8444                                 menu_line : 1,\r
8445                                 'class' : this.classPrefix + 'Menu',\r
8446                                 icons : t.settings.icons\r
8447                         });\r
8448 \r
8449                         m.onHideMenu.add(t.hideMenu, t);\r
8450 \r
8451                         t.onRenderMenu.dispatch(t, m);\r
8452                         t.menu = m;\r
8453                 },\r
8454 \r
8455                 hideMenu : function(e) {\r
8456                         var t = this;\r
8457 \r
8458                         // Prevent double toogles by canceling the mouse click event to the button\r
8459                         if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id || e.id === t.id + '_open';}))\r
8460                                 return;\r
8461 \r
8462                         if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
8463                                 t.setState('Selected', 0);\r
8464                                 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
8465                                 if (t.menu)\r
8466                                         t.menu.hideMenu();\r
8467                         }\r
8468 \r
8469                         t.isMenuVisible = 0;\r
8470                 },\r
8471 \r
8472                 postRender : function() {\r
8473                         var t = this, s = t.settings;\r
8474 \r
8475                         Event.add(t.id, 'click', function() {\r
8476                                 if (!t.isDisabled()) {\r
8477                                         if (s.onclick)\r
8478                                                 s.onclick(t.value);\r
8479 \r
8480                                         t.showMenu();\r
8481                                 }\r
8482                         });\r
8483                 }\r
8484         });\r
8485 })(tinymce);\r
8486 \r
8487 (function(tinymce) {\r
8488         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
8489 \r
8490         tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', {\r
8491                 SplitButton : function(id, s) {\r
8492                         this.parent(id, s);\r
8493                         this.classPrefix = 'mceSplitButton';\r
8494                 },\r
8495 \r
8496                 renderHTML : function() {\r
8497                         var h, t = this, s = t.settings, h1;\r
8498 \r
8499                         h = '<tbody><tr>';\r
8500 \r
8501                         if (s.image)\r
8502                                 h1 = DOM.createHTML('img ', {src : s.image, 'class' : 'mceAction ' + s['class']});\r
8503                         else\r
8504                                 h1 = DOM.createHTML('span', {'class' : 'mceAction ' + s['class']}, '');\r
8505 \r
8506                         h += '<td>' + DOM.createHTML('a', {id : t.id + '_action', href : 'javascript:;', 'class' : 'mceAction ' + s['class'], onclick : "return false;", onmousedown : 'return false;', title : s.title}, h1) + '</td>';\r
8507         \r
8508                         h1 = DOM.createHTML('span', {'class' : 'mceOpen ' + s['class']});\r
8509                         h += '<td>' + DOM.createHTML('a', {id : t.id + '_open', href : 'javascript:;', 'class' : 'mceOpen ' + s['class'], onclick : "return false;", onmousedown : 'return false;', title : s.title}, h1) + '</td>';\r
8510 \r
8511                         h += '</tr></tbody>';\r
8512 \r
8513                         return DOM.createHTML('table', {id : t.id, 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', onmousedown : 'return false;', title : s.title}, h);\r
8514                 },\r
8515 \r
8516                 postRender : function() {\r
8517                         var t = this, s = t.settings;\r
8518 \r
8519                         if (s.onclick) {\r
8520                                 Event.add(t.id + '_action', 'click', function() {\r
8521                                         if (!t.isDisabled())\r
8522                                                 s.onclick(t.value);\r
8523                                 });\r
8524                         }\r
8525 \r
8526                         Event.add(t.id + '_open', 'click', t.showMenu, t);\r
8527                         Event.add(t.id + '_open', 'focus', function() {t._focused = 1;});\r
8528                         Event.add(t.id + '_open', 'blur', function() {t._focused = 0;});\r
8529 \r
8530                         // Old IE doesn't have hover on all elements\r
8531                         if (tinymce.isIE6 || !DOM.boxModel) {\r
8532                                 Event.add(t.id, 'mouseover', function() {\r
8533                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
8534                                                 DOM.addClass(t.id, 'mceSplitButtonHover');\r
8535                                 });\r
8536 \r
8537                                 Event.add(t.id, 'mouseout', function() {\r
8538                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
8539                                                 DOM.removeClass(t.id, 'mceSplitButtonHover');\r
8540                                 });\r
8541                         }\r
8542                 },\r
8543 \r
8544                 destroy : function() {\r
8545                         this.parent();\r
8546 \r
8547                         Event.clear(this.id + '_action');\r
8548                         Event.clear(this.id + '_open');\r
8549                 }\r
8550         });\r
8551 })(tinymce);\r
8552 \r
8553 (function(tinymce) {\r
8554         var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each;\r
8555 \r
8556         tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', {\r
8557                 ColorSplitButton : function(id, s) {\r
8558                         var t = this;\r
8559 \r
8560                         t.parent(id, s);\r
8561 \r
8562                         t.settings = s = tinymce.extend({\r
8563                                 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
8564                                 grid_width : 8,\r
8565                                 default_color : '#888888'\r
8566                         }, t.settings);\r
8567 \r
8568                         t.onShowMenu = new tinymce.util.Dispatcher(t);\r
8569 \r
8570                         t.onHideMenu = new tinymce.util.Dispatcher(t);\r
8571 \r
8572                         t.value = s.default_color;\r
8573                 },\r
8574 \r
8575                 showMenu : function() {\r
8576                         var t = this, r, p, e, p2;\r
8577 \r
8578                         if (t.isDisabled())\r
8579                                 return;\r
8580 \r
8581                         if (!t.isMenuRendered) {\r
8582                                 t.renderMenu();\r
8583                                 t.isMenuRendered = true;\r
8584                         }\r
8585 \r
8586                         if (t.isMenuVisible)\r
8587                                 return t.hideMenu();\r
8588 \r
8589                         e = DOM.get(t.id);\r
8590                         DOM.show(t.id + '_menu');\r
8591                         DOM.addClass(e, 'mceSplitButtonSelected');\r
8592                         p2 = DOM.getPos(e);\r
8593                         DOM.setStyles(t.id + '_menu', {\r
8594                                 left : p2.x,\r
8595                                 top : p2.y + e.clientHeight,\r
8596                                 zIndex : 200000\r
8597                         });\r
8598                         e = 0;\r
8599 \r
8600                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
8601                         t.onShowMenu.dispatch(t);\r
8602 \r
8603                         if (t._focused) {\r
8604                                 t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) {\r
8605                                         if (e.keyCode == 27)\r
8606                                                 t.hideMenu();\r
8607                                 });\r
8608 \r
8609                                 DOM.select('a', t.id + '_menu')[0].focus(); // Select first link\r
8610                         }\r
8611 \r
8612                         t.isMenuVisible = 1;\r
8613                 },\r
8614 \r
8615                 hideMenu : function(e) {\r
8616                         var t = this;\r
8617 \r
8618                         // Prevent double toogles by canceling the mouse click event to the button\r
8619                         if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';}))\r
8620                                 return;\r
8621 \r
8622                         if (!e || !DOM.getParent(e.target, '.mceSplitButtonMenu')) {\r
8623                                 DOM.removeClass(t.id, 'mceSplitButtonSelected');\r
8624                                 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
8625                                 Event.remove(t.id + '_menu', 'keydown', t._keyHandler);\r
8626                                 DOM.hide(t.id + '_menu');\r
8627                         }\r
8628 \r
8629                         t.onHideMenu.dispatch(t);\r
8630 \r
8631                         t.isMenuVisible = 0;\r
8632                 },\r
8633 \r
8634                 renderMenu : function() {\r
8635                         var t = this, m, i = 0, s = t.settings, n, tb, tr, w;\r
8636 \r
8637                         w = DOM.add(s.menu_container, 'div', {id : t.id + '_menu', 'class' : s['menu_class'] + ' ' + s['class'], style : 'position:absolute;left:0;top:-1000px;'});\r
8638                         m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'});\r
8639                         DOM.add(m, 'span', {'class' : 'mceMenuLine'});\r
8640 \r
8641                         n = DOM.add(m, 'table', {'class' : 'mceColorSplitMenu'});\r
8642                         tb = DOM.add(n, 'tbody');\r
8643 \r
8644                         // Generate color grid\r
8645                         i = 0;\r
8646                         each(is(s.colors, 'array') ? s.colors : s.colors.split(','), function(c) {\r
8647                                 c = c.replace(/^#/, '');\r
8648 \r
8649                                 if (!i--) {\r
8650                                         tr = DOM.add(tb, 'tr');\r
8651                                         i = s.grid_width - 1;\r
8652                                 }\r
8653 \r
8654                                 n = DOM.add(tr, 'td');\r
8655 \r
8656                                 n = DOM.add(n, 'a', {\r
8657                                         href : 'javascript:;',\r
8658                                         style : {\r
8659                                                 backgroundColor : '#' + c\r
8660                                         },\r
8661                                         _mce_color : '#' + c\r
8662                                 });\r
8663                         });\r
8664 \r
8665                         if (s.more_colors_func) {\r
8666                                 n = DOM.add(tb, 'tr');\r
8667                                 n = DOM.add(n, 'td', {colspan : s.grid_width, 'class' : 'mceMoreColors'});\r
8668                                 n = DOM.add(n, 'a', {id : t.id + '_more', href : 'javascript:;', onclick : 'return false;', 'class' : 'mceMoreColors'}, s.more_colors_title);\r
8669 \r
8670                                 Event.add(n, 'click', function(e) {\r
8671                                         s.more_colors_func.call(s.more_colors_scope || this);\r
8672                                         return Event.cancel(e); // Cancel to fix onbeforeunload problem\r
8673                                 });\r
8674                         }\r
8675 \r
8676                         DOM.addClass(m, 'mceColorSplitMenu');\r
8677 \r
8678                         Event.add(t.id + '_menu', 'click', function(e) {\r
8679                                 var c;\r
8680 \r
8681                                 e = e.target;\r
8682 \r
8683                                 if (e.nodeName == 'A' && (c = e.getAttribute('_mce_color')))\r
8684                                         t.setColor(c);\r
8685 \r
8686                                 return Event.cancel(e); // Prevent IE auto save warning\r
8687                         });\r
8688 \r
8689                         return w;\r
8690                 },\r
8691 \r
8692                 setColor : function(c) {\r
8693                         var t = this;\r
8694 \r
8695                         DOM.setStyle(t.id + '_preview', 'backgroundColor', c);\r
8696 \r
8697                         t.value = c;\r
8698                         t.hideMenu();\r
8699                         t.settings.onselect(c);\r
8700                 },\r
8701 \r
8702                 postRender : function() {\r
8703                         var t = this, id = t.id;\r
8704 \r
8705                         t.parent();\r
8706                         DOM.add(id + '_action', 'div', {id : id + '_preview', 'class' : 'mceColorPreview'});\r
8707                         DOM.setStyle(t.id + '_preview', 'backgroundColor', t.value);\r
8708                 },\r
8709 \r
8710                 destroy : function() {\r
8711                         this.parent();\r
8712 \r
8713                         Event.clear(this.id + '_menu');\r
8714                         Event.clear(this.id + '_more');\r
8715                         DOM.remove(this.id + '_menu');\r
8716                 }\r
8717         });\r
8718 })(tinymce);\r
8719 \r
8720 tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {\r
8721         renderHTML : function() {\r
8722                 var t = this, h = '', c, co, dom = tinymce.DOM, s = t.settings, i, pr, nx, cl;\r
8723 \r
8724                 cl = t.controls;\r
8725                 for (i=0; i<cl.length; i++) {\r
8726                         // Get current control, prev control, next control and if the control is a list box or not\r
8727                         co = cl[i];\r
8728                         pr = cl[i - 1];\r
8729                         nx = cl[i + 1];\r
8730 \r
8731                         // Add toolbar start\r
8732                         if (i === 0) {\r
8733                                 c = 'mceToolbarStart';\r
8734 \r
8735                                 if (co.Button)\r
8736                                         c += ' mceToolbarStartButton';\r
8737                                 else if (co.SplitButton)\r
8738                                         c += ' mceToolbarStartSplitButton';\r
8739                                 else if (co.ListBox)\r
8740                                         c += ' mceToolbarStartListBox';\r
8741 \r
8742                                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
8743                         }\r
8744 \r
8745                         // Add toolbar end before list box and after the previous button\r
8746                         // This is to fix the o2k7 editor skins\r
8747                         if (pr && co.ListBox) {\r
8748                                 if (pr.Button || pr.SplitButton)\r
8749                                         h += dom.createHTML('td', {'class' : 'mceToolbarEnd'}, dom.createHTML('span', null, '<!-- IE -->'));\r
8750                         }\r
8751 \r
8752                         // Render control HTML\r
8753 \r
8754                         // IE 8 quick fix, needed to propertly generate a hit area for anchors\r
8755                         if (dom.stdMode)\r
8756                                 h += '<td style="position: relative">' + co.renderHTML() + '</td>';\r
8757                         else\r
8758                                 h += '<td>' + co.renderHTML() + '</td>';\r
8759 \r
8760                         // Add toolbar start after list box and before the next button\r
8761                         // This is to fix the o2k7 editor skins\r
8762                         if (nx && co.ListBox) {\r
8763                                 if (nx.Button || nx.SplitButton)\r
8764                                         h += dom.createHTML('td', {'class' : 'mceToolbarStart'}, dom.createHTML('span', null, '<!-- IE -->'));\r
8765                         }\r
8766                 }\r
8767 \r
8768                 c = 'mceToolbarEnd';\r
8769 \r
8770                 if (co.Button)\r
8771                         c += ' mceToolbarEndButton';\r
8772                 else if (co.SplitButton)\r
8773                         c += ' mceToolbarEndSplitButton';\r
8774                 else if (co.ListBox)\r
8775                         c += ' mceToolbarEndListBox';\r
8776 \r
8777                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
8778 \r
8779                 return dom.createHTML('table', {id : t.id, 'class' : 'mceToolbar' + (s['class'] ? ' ' + s['class'] : ''), cellpadding : '0', cellspacing : '0', align : t.settings.align || ''}, '<tbody><tr>' + h + '</tr></tbody>');\r
8780         }\r
8781 });\r
8782 \r
8783 (function(tinymce) {\r
8784         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;\r
8785 \r
8786         tinymce.create('tinymce.AddOnManager', {\r
8787                 AddOnManager : function() {\r
8788                         var self = this;\r
8789 \r
8790                         self.items = [];\r
8791                         self.urls = {};\r
8792                         self.lookup = {};\r
8793                         self.onAdd = new Dispatcher(self);\r
8794                 },\r
8795 \r
8796                 get : function(n) {\r
8797                         return this.lookup[n];\r
8798                 },\r
8799 \r
8800                 requireLangPack : function(n) {\r
8801                         var s = tinymce.settings;\r
8802 \r
8803                         if (s && s.language)\r
8804                                 tinymce.ScriptLoader.add(this.urls[n] + '/langs/' + s.language + '.js');\r
8805                 },\r
8806 \r
8807                 add : function(id, o) {\r
8808                         this.items.push(o);\r
8809                         this.lookup[id] = o;\r
8810                         this.onAdd.dispatch(this, id, o);\r
8811 \r
8812                         return o;\r
8813                 },\r
8814 \r
8815                 load : function(n, u, cb, s) {\r
8816                         var t = this;\r
8817 \r
8818                         if (t.urls[n])\r
8819                                 return;\r
8820 \r
8821                         if (u.indexOf('/') != 0 && u.indexOf('://') == -1)\r
8822                                 u = tinymce.baseURL + '/' +  u;\r
8823 \r
8824                         t.urls[n] = u.substring(0, u.lastIndexOf('/'));\r
8825 \r
8826                         if (!t.lookup[n])\r
8827                                 tinymce.ScriptLoader.add(u, cb, s);\r
8828                 }\r
8829         });\r
8830 \r
8831         // Create plugin and theme managers\r
8832         tinymce.PluginManager = new tinymce.AddOnManager();\r
8833         tinymce.ThemeManager = new tinymce.AddOnManager();\r
8834 }(tinymce));\r
8835 \r
8836 (function(tinymce) {\r
8837         // Shorten names\r
8838         var each = tinymce.each, extend = tinymce.extend,\r
8839                 DOM = tinymce.DOM, Event = tinymce.dom.Event,\r
8840                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
8841                 explode = tinymce.explode,\r
8842                 Dispatcher = tinymce.util.Dispatcher, undefined, instanceCounter = 0;\r
8843 \r
8844         // Setup some URLs where the editor API is located and where the document is\r
8845         tinymce.documentBaseURL = window.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');\r
8846         if (!/[\/\\]$/.test(tinymce.documentBaseURL))\r
8847                 tinymce.documentBaseURL += '/';\r
8848 \r
8849         tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL);\r
8850 \r
8851         tinymce.baseURI = new tinymce.util.URI(tinymce.baseURL);\r
8852 \r
8853         // Add before unload listener\r
8854         // This was required since IE was leaking memory if you added and removed beforeunload listeners\r
8855         // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event\r
8856         tinymce.onBeforeUnload = new Dispatcher(tinymce);\r
8857 \r
8858         // Must be on window or IE will leak if the editor is placed in frame or iframe\r
8859         Event.add(window, 'beforeunload', function(e) {\r
8860                 tinymce.onBeforeUnload.dispatch(tinymce, e);\r
8861         });\r
8862 \r
8863         tinymce.onAddEditor = new Dispatcher(tinymce);\r
8864 \r
8865         tinymce.onRemoveEditor = new Dispatcher(tinymce);\r
8866 \r
8867         tinymce.EditorManager = extend(tinymce, {\r
8868                 editors : [],\r
8869 \r
8870                 i18n : {},\r
8871 \r
8872                 activeEditor : null,\r
8873 \r
8874                 init : function(s) {\r
8875                         var t = this, pl, sl = tinymce.ScriptLoader, e, el = [], ed;\r
8876 \r
8877                         function execCallback(se, n, s) {\r
8878                                 var f = se[n];\r
8879 \r
8880                                 if (!f)\r
8881                                         return;\r
8882 \r
8883                                 if (tinymce.is(f, 'string')) {\r
8884                                         s = f.replace(/\.\w+$/, '');\r
8885                                         s = s ? tinymce.resolve(s) : 0;\r
8886                                         f = tinymce.resolve(f);\r
8887                                 }\r
8888 \r
8889                                 return f.apply(s || this, Array.prototype.slice.call(arguments, 2));\r
8890                         };\r
8891 \r
8892                         s = extend({\r
8893                                 theme : "simple",\r
8894                                 language : "en"\r
8895                         }, s);\r
8896 \r
8897                         t.settings = s;\r
8898 \r
8899                         // Legacy call\r
8900                         Event.add(document, 'init', function() {\r
8901                                 var l, co;\r
8902 \r
8903                                 execCallback(s, 'onpageload');\r
8904 \r
8905                                 switch (s.mode) {\r
8906                                         case "exact":\r
8907                                                 l = s.elements || '';\r
8908 \r
8909                                                 if(l.length > 0) {\r
8910                                                         each(explode(l), function(v) {\r
8911                                                                 if (DOM.get(v)) {\r
8912                                                                         ed = new tinymce.Editor(v, s);\r
8913                                                                         el.push(ed);\r
8914                                                                         ed.render(1);\r
8915                                                                 } else {\r
8916                                                                         each(document.forms, function(f) {\r
8917                                                                                 each(f.elements, function(e) {\r
8918                                                                                         if (e.name === v) {\r
8919                                                                                                 v = 'mce_editor_' + instanceCounter++;\r
8920                                                                                                 DOM.setAttrib(e, 'id', v);\r
8921 \r
8922                                                                                                 ed = new tinymce.Editor(v, s);\r
8923                                                                                                 el.push(ed);\r
8924                                                                                                 ed.render(1);\r
8925                                                                                         }\r
8926                                                                                 });\r
8927                                                                         });\r
8928                                                                 }\r
8929                                                         });\r
8930                                                 }\r
8931                                                 break;\r
8932 \r
8933                                         case "textareas":\r
8934                                         case "specific_textareas":\r
8935                                                 function hasClass(n, c) {\r
8936                                                         return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);\r
8937                                                 };\r
8938 \r
8939                                                 each(DOM.select('textarea'), function(v) {\r
8940                                                         if (s.editor_deselector && hasClass(v, s.editor_deselector))\r
8941                                                                 return;\r
8942 \r
8943                                                         if (!s.editor_selector || hasClass(v, s.editor_selector)) {\r
8944                                                                 // Can we use the name\r
8945                                                                 e = DOM.get(v.name);\r
8946                                                                 if (!v.id && !e)\r
8947                                                                         v.id = v.name;\r
8948 \r
8949                                                                 // Generate unique name if missing or already exists\r
8950                                                                 if (!v.id || t.get(v.id))\r
8951                                                                         v.id = DOM.uniqueId();\r
8952 \r
8953                                                                 ed = new tinymce.Editor(v.id, s);\r
8954                                                                 el.push(ed);\r
8955                                                                 ed.render(1);\r
8956                                                         }\r
8957                                                 });\r
8958                                                 break;\r
8959                                 }\r
8960 \r
8961                                 // Call onInit when all editors are initialized\r
8962                                 if (s.oninit) {\r
8963                                         l = co = 0;\r
8964 \r
8965                                         each(el, function(ed) {\r
8966                                                 co++;\r
8967 \r
8968                                                 if (!ed.initialized) {\r
8969                                                         // Wait for it\r
8970                                                         ed.onInit.add(function() {\r
8971                                                                 l++;\r
8972 \r
8973                                                                 // All done\r
8974                                                                 if (l == co)\r
8975                                                                         execCallback(s, 'oninit');\r
8976                                                         });\r
8977                                                 } else\r
8978                                                         l++;\r
8979 \r
8980                                                 // All done\r
8981                                                 if (l == co)\r
8982                                                         execCallback(s, 'oninit');                                      \r
8983                                         });\r
8984                                 }\r
8985                         });\r
8986                 },\r
8987 \r
8988                 get : function(id) {\r
8989                         if (id === undefined)\r
8990                                 return this.editors;\r
8991 \r
8992                         return this.editors[id];\r
8993                 },\r
8994 \r
8995                 getInstanceById : function(id) {\r
8996                         return this.get(id);\r
8997                 },\r
8998 \r
8999                 add : function(editor) {\r
9000                         var self = this, editors = self.editors;\r
9001 \r
9002                         // Add named and index editor instance\r
9003                         editors[editor.id] = editor;\r
9004                         editors.push(editor);\r
9005 \r
9006                         self._setActive(editor);\r
9007                         self.onAddEditor.dispatch(self, editor);\r
9008 \r
9009 \r
9010                         return editor;\r
9011                 },\r
9012 \r
9013                 remove : function(editor) {\r
9014                         var t = this, i, editors = t.editors;\r
9015 \r
9016                         // Not in the collection\r
9017                         if (!editors[editor.id])\r
9018                                 return null;\r
9019 \r
9020                         delete editors[editor.id];\r
9021 \r
9022                         for (i = 0; i < editors.length; i++) {\r
9023                                 if (editors[i] == editor) {\r
9024                                         editors.splice(i, 1);\r
9025                                         break;\r
9026                                 }\r
9027                         }\r
9028 \r
9029                         // Select another editor since the active one was removed\r
9030                         if (t.activeEditor == editor)\r
9031                                 t._setActive(editors[0]);\r
9032 \r
9033                         editor.destroy();\r
9034                         t.onRemoveEditor.dispatch(t, editor);\r
9035 \r
9036                         return editor;\r
9037                 },\r
9038 \r
9039                 execCommand : function(c, u, v) {\r
9040                         var t = this, ed = t.get(v), w;\r
9041 \r
9042                         // Manager commands\r
9043                         switch (c) {\r
9044                                 case "mceFocus":\r
9045                                         ed.focus();\r
9046                                         return true;\r
9047 \r
9048                                 case "mceAddEditor":\r
9049                                 case "mceAddControl":\r
9050                                         if (!t.get(v))\r
9051                                                 new tinymce.Editor(v, t.settings).render();\r
9052 \r
9053                                         return true;\r
9054 \r
9055                                 case "mceAddFrameControl":\r
9056                                         w = v.window;\r
9057 \r
9058                                         // Add tinyMCE global instance and tinymce namespace to specified window\r
9059                                         w.tinyMCE = tinyMCE;\r
9060                                         w.tinymce = tinymce;\r
9061 \r
9062                                         tinymce.DOM.doc = w.document;\r
9063                                         tinymce.DOM.win = w;\r
9064 \r
9065                                         ed = new tinymce.Editor(v.element_id, v);\r
9066                                         ed.render();\r
9067 \r
9068                                         // Fix IE memory leaks\r
9069                                         if (tinymce.isIE) {\r
9070                                                 function clr() {\r
9071                                                         ed.destroy();\r
9072                                                         w.detachEvent('onunload', clr);\r
9073                                                         w = w.tinyMCE = w.tinymce = null; // IE leak\r
9074                                                 };\r
9075 \r
9076                                                 w.attachEvent('onunload', clr);\r
9077                                         }\r
9078 \r
9079                                         v.page_window = null;\r
9080 \r
9081                                         return true;\r
9082 \r
9083                                 case "mceRemoveEditor":\r
9084                                 case "mceRemoveControl":\r
9085                                         if (ed)\r
9086                                                 ed.remove();\r
9087 \r
9088                                         return true;\r
9089 \r
9090                                 case 'mceToggleEditor':\r
9091                                         if (!ed) {\r
9092                                                 t.execCommand('mceAddControl', 0, v);\r
9093                                                 return true;\r
9094                                         }\r
9095 \r
9096                                         if (ed.isHidden())\r
9097                                                 ed.show();\r
9098                                         else\r
9099                                                 ed.hide();\r
9100 \r
9101                                         return true;\r
9102                         }\r
9103 \r
9104                         // Run command on active editor\r
9105                         if (t.activeEditor)\r
9106                                 return t.activeEditor.execCommand(c, u, v);\r
9107 \r
9108                         return false;\r
9109                 },\r
9110 \r
9111                 execInstanceCommand : function(id, c, u, v) {\r
9112                         var ed = this.get(id);\r
9113 \r
9114                         if (ed)\r
9115                                 return ed.execCommand(c, u, v);\r
9116 \r
9117                         return false;\r
9118                 },\r
9119 \r
9120                 triggerSave : function() {\r
9121                         each(this.editors, function(e) {\r
9122                                 e.save();\r
9123                         });\r
9124                 },\r
9125 \r
9126                 addI18n : function(p, o) {\r
9127                         var lo, i18n = this.i18n;\r
9128 \r
9129                         if (!tinymce.is(p, 'string')) {\r
9130                                 each(p, function(o, lc) {\r
9131                                         each(o, function(o, g) {\r
9132                                                 each(o, function(o, k) {\r
9133                                                         if (g === 'common')\r
9134                                                                 i18n[lc + '.' + k] = o;\r
9135                                                         else\r
9136                                                                 i18n[lc + '.' + g + '.' + k] = o;\r
9137                                                 });\r
9138                                         });\r
9139                                 });\r
9140                         } else {\r
9141                                 each(o, function(o, k) {\r
9142                                         i18n[p + '.' + k] = o;\r
9143                                 });\r
9144                         }\r
9145                 },\r
9146 \r
9147                 // Private methods\r
9148 \r
9149                 _setActive : function(editor) {\r
9150                         this.selectedInstance = this.activeEditor = editor;\r
9151                 }\r
9152         });\r
9153 })(tinymce);\r
9154 \r
9155 (function(tinymce) {\r
9156         // Shorten these names\r
9157         var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend,\r
9158                 Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isGecko = tinymce.isGecko,\r
9159                 isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, is = tinymce.is,\r
9160                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
9161                 inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;\r
9162 \r
9163         tinymce.create('tinymce.Editor', {\r
9164                 Editor : function(id, s) {\r
9165                         var t = this;\r
9166 \r
9167                         t.id = t.editorId = id;\r
9168 \r
9169                         t.execCommands = {};\r
9170                         t.queryStateCommands = {};\r
9171                         t.queryValueCommands = {};\r
9172 \r
9173                         t.isNotDirty = false;\r
9174 \r
9175                         t.plugins = {};\r
9176 \r
9177                         // Add events to the editor\r
9178                         each([\r
9179                                 'onPreInit',\r
9180 \r
9181                                 'onBeforeRenderUI',\r
9182 \r
9183                                 'onPostRender',\r
9184 \r
9185                                 'onInit',\r
9186 \r
9187                                 'onRemove',\r
9188 \r
9189                                 'onActivate',\r
9190 \r
9191                                 'onDeactivate',\r
9192 \r
9193                                 'onClick',\r
9194 \r
9195                                 'onEvent',\r
9196 \r
9197                                 'onMouseUp',\r
9198 \r
9199                                 'onMouseDown',\r
9200 \r
9201                                 'onDblClick',\r
9202 \r
9203                                 'onKeyDown',\r
9204 \r
9205                                 'onKeyUp',\r
9206 \r
9207                                 'onKeyPress',\r
9208 \r
9209                                 'onContextMenu',\r
9210 \r
9211                                 'onSubmit',\r
9212 \r
9213                                 'onReset',\r
9214 \r
9215                                 'onPaste',\r
9216 \r
9217                                 'onPreProcess',\r
9218 \r
9219                                 'onPostProcess',\r
9220 \r
9221                                 'onBeforeSetContent',\r
9222 \r
9223                                 'onBeforeGetContent',\r
9224 \r
9225                                 'onSetContent',\r
9226 \r
9227                                 'onGetContent',\r
9228 \r
9229                                 'onLoadContent',\r
9230 \r
9231                                 'onSaveContent',\r
9232 \r
9233                                 'onNodeChange',\r
9234 \r
9235                                 'onChange',\r
9236 \r
9237                                 'onBeforeExecCommand',\r
9238 \r
9239                                 'onExecCommand',\r
9240 \r
9241                                 'onUndo',\r
9242 \r
9243                                 'onRedo',\r
9244 \r
9245                                 'onVisualAid',\r
9246 \r
9247                                 'onSetProgressState'\r
9248                         ], function(e) {\r
9249                                 t[e] = new Dispatcher(t);\r
9250                         });\r
9251 \r
9252                         t.settings = s = extend({\r
9253                                 id : id,\r
9254                                 language : 'en',\r
9255                                 docs_language : 'en',\r
9256                                 theme : 'simple',\r
9257                                 skin : 'default',\r
9258                                 delta_width : 0,\r
9259                                 delta_height : 0,\r
9260                                 popup_css : '',\r
9261                                 plugins : '',\r
9262                                 document_base_url : tinymce.documentBaseURL,\r
9263                                 add_form_submit_trigger : 1,\r
9264                                 submit_patch : 1,\r
9265                                 add_unload_trigger : 1,\r
9266                                 convert_urls : 1,\r
9267                                 relative_urls : 1,\r
9268                                 remove_script_host : 1,\r
9269                                 table_inline_editing : 0,\r
9270                                 object_resizing : 1,\r
9271                                 cleanup : 1,\r
9272                                 accessibility_focus : 1,\r
9273                                 custom_shortcuts : 1,\r
9274                                 custom_undo_redo_keyboard_shortcuts : 1,\r
9275                                 custom_undo_redo_restore_selection : 1,\r
9276                                 custom_undo_redo : 1,\r
9277                                 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
9278                                 visual_table_class : 'mceItemTable',\r
9279                                 visual : 1,\r
9280                                 font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',\r
9281                                 apply_source_formatting : 1,\r
9282                                 directionality : 'ltr',\r
9283                                 forced_root_block : 'p',\r
9284                                 valid_elements : '@[id|class|style|title|dir<ltr?rtl|lang|xml::lang|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],a[rel|rev|charset|hreflang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur],strong/b,em/i,strike,u,#p,-ol[type|compact],-ul[type|compact],-li,br,img[longdesc|usemap|src|border|alt=|title|hspace|vspace|width|height|align],-sub,-sup,-blockquote[cite],-table[border|cellspacing|cellpadding|width|frame|rules|height|align|summary|bgcolor|background|bordercolor],-tr[rowspan|width|height|align|valign|bgcolor|background|bordercolor],tbody,thead,tfoot,#td[colspan|rowspan|width|height|align|valign|bgcolor|background|bordercolor|scope],#th[colspan|rowspan|width|height|align|valign|scope],caption,-div,-span,-code,-pre,address,-h1,-h2,-h3,-h4,-h5,-h6,hr[size|noshade],-font[face|size|color],dd,dl,dt,cite,abbr,acronym,del[datetime|cite],ins[datetime|cite],object[classid|width|height|codebase|*],param[name|value],embed[type|width|height|src|*],script[src|type],map[name],area[shape|coords|href|alt|target],bdo,button,col[align|char|charoff|span|valign|width],colgroup[align|char|charoff|span|valign|width],dfn,fieldset,form[action|accept|accept-charset|enctype|method],input[accept|alt|checked|disabled|maxlength|name|readonly|size|src|type|value|tabindex|accesskey],kbd,label[for],legend,noscript,optgroup[label|disabled],option[disabled|label|selected|value],q[cite],samp,select[disabled|multiple|name|size],small,textarea[cols|rows|disabled|name|readonly],tt,var,big',\r
9285                                 hidden_input : 1,\r
9286                                 padd_empty_editor : 1,\r
9287                                 render_ui : 1,\r
9288                                 init_theme : 1,\r
9289                                 force_p_newlines : 1,\r
9290                                 indentation : '30px',\r
9291                                 keep_styles : 1,\r
9292                                 fix_table_elements : 1,\r
9293                                 inline_styles : 1,\r
9294                                 convert_fonts_to_spans : true\r
9295                         }, s);\r
9296 \r
9297                         t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {\r
9298                                 base_uri : tinyMCE.baseURI\r
9299                         });\r
9300 \r
9301                         t.baseURI = tinymce.baseURI;\r
9302 \r
9303                         // Call setup\r
9304                         t.execCallback('setup', t);\r
9305                 },\r
9306 \r
9307                 render : function(nst) {\r
9308                         var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader;\r
9309 \r
9310                         // Page is not loaded yet, wait for it\r
9311                         if (!Event.domLoaded) {\r
9312                                 Event.add(document, 'init', function() {\r
9313                                         t.render();\r
9314                                 });\r
9315                                 return;\r
9316                         }\r
9317 \r
9318                         tinyMCE.settings = s;\r
9319 \r
9320                         // Element not found, then skip initialization\r
9321                         if (!t.getElement())\r
9322                                 return;\r
9323 \r
9324                         // Is a iPad/iPhone, then skip initialization. We need to sniff here since the\r
9325                         // browser says it has contentEditable support but there is no visible caret\r
9326                         // We will remove this check ones Apple implements full contentEditable support\r
9327                         if (tinymce.isIDevice)\r
9328                                 return;\r
9329 \r
9330                         // Add hidden input for non input elements inside form elements\r
9331                         if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))\r
9332                                 DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);\r
9333 \r
9334                         if (tinymce.WindowManager)\r
9335                                 t.windowManager = new tinymce.WindowManager(t);\r
9336 \r
9337                         if (s.encoding == 'xml') {\r
9338                                 t.onGetContent.add(function(ed, o) {\r
9339                                         if (o.save)\r
9340                                                 o.content = DOM.encode(o.content);\r
9341                                 });\r
9342                         }\r
9343 \r
9344                         if (s.add_form_submit_trigger) {\r
9345                                 t.onSubmit.addToTop(function() {\r
9346                                         if (t.initialized) {\r
9347                                                 t.save();\r
9348                                                 t.isNotDirty = 1;\r
9349                                         }\r
9350                                 });\r
9351                         }\r
9352 \r
9353                         if (s.add_unload_trigger) {\r
9354                                 t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {\r
9355                                         if (t.initialized && !t.destroyed && !t.isHidden())\r
9356                                                 t.save({format : 'raw', no_events : true});\r
9357                                 });\r
9358                         }\r
9359 \r
9360                         tinymce.addUnload(t.destroy, t);\r
9361 \r
9362                         if (s.submit_patch) {\r
9363                                 t.onBeforeRenderUI.add(function() {\r
9364                                         var n = t.getElement().form;\r
9365 \r
9366                                         if (!n)\r
9367                                                 return;\r
9368 \r
9369                                         // Already patched\r
9370                                         if (n._mceOldSubmit)\r
9371                                                 return;\r
9372 \r
9373                                         // Check page uses id="submit" or name="submit" for it's submit button\r
9374                                         if (!n.submit.nodeType && !n.submit.length) {\r
9375                                                 t.formElement = n;\r
9376                                                 n._mceOldSubmit = n.submit;\r
9377                                                 n.submit = function() {\r
9378                                                         // Save all instances\r
9379                                                         tinymce.triggerSave();\r
9380                                                         t.isNotDirty = 1;\r
9381 \r
9382                                                         return t.formElement._mceOldSubmit(t.formElement);\r
9383                                                 };\r
9384                                         }\r
9385 \r
9386                                         n = null;\r
9387                                 });\r
9388                         }\r
9389 \r
9390                         // Load scripts\r
9391                         function loadScripts() {\r
9392                                 if (s.language)\r
9393                                         sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');\r
9394 \r
9395                                 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])\r
9396                                         ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');\r
9397 \r
9398                                 each(explode(s.plugins), function(p) {\r
9399                                         if (p && p.charAt(0) != '-' && !PluginManager.urls[p]) {\r
9400                                                 // Skip safari plugin, since it is removed as of 3.3b1\r
9401                                                 if (p == 'safari')\r
9402                                                         return;\r
9403 \r
9404                                                 PluginManager.load(p, 'plugins/' + p + '/editor_plugin' + tinymce.suffix + '.js');\r
9405                                         }\r
9406                                 });\r
9407 \r
9408                                 // Init when que is loaded\r
9409                                 sl.loadQueue(function() {\r
9410                                         if (!t.removed)\r
9411                                                 t.init();\r
9412                                 });\r
9413                         };\r
9414 \r
9415                         loadScripts();\r
9416                 },\r
9417 \r
9418                 init : function() {\r
9419                         var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re;\r
9420 \r
9421                         tinymce.add(t);\r
9422 \r
9423                         if (s.theme) {\r
9424                                 s.theme = s.theme.replace(/-/, '');\r
9425                                 o = ThemeManager.get(s.theme);\r
9426                                 t.theme = new o();\r
9427 \r
9428                                 if (t.theme.init && s.init_theme)\r
9429                                         t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));\r
9430                         }\r
9431 \r
9432                         // Create all plugins\r
9433                         each(explode(s.plugins.replace(/\-/g, '')), function(p) {\r
9434                                 var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;\r
9435 \r
9436                                 if (c) {\r
9437                                         po = new c(t, u);\r
9438 \r
9439                                         t.plugins[p] = po;\r
9440 \r
9441                                         if (po.init)\r
9442                                                 po.init(t, u);\r
9443                                 }\r
9444                         });\r
9445 \r
9446                         // Setup popup CSS path(s)\r
9447                         if (s.popup_css !== false) {\r
9448                                 if (s.popup_css)\r
9449                                         s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);\r
9450                                 else\r
9451                                         s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");\r
9452                         }\r
9453 \r
9454                         if (s.popup_css_add)\r
9455                                 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);\r
9456 \r
9457                         t.controlManager = new tinymce.ControlManager(t);\r
9458 \r
9459                         if (s.custom_undo_redo) {\r
9460                                 // Add initial undo level\r
9461                                 t.onBeforeExecCommand.add(function(ed, cmd, ui, val, a) {\r
9462                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo)) {\r
9463                                                 if (!t.undoManager.hasUndo())\r
9464                                                         t.undoManager.add();\r
9465                                         }\r
9466                                 });\r
9467 \r
9468                                 t.onExecCommand.add(function(ed, cmd, ui, val, a) {\r
9469                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))\r
9470                                                 t.undoManager.add();\r
9471                                 });\r
9472                         }\r
9473 \r
9474                         t.onExecCommand.add(function(ed, c) {\r
9475                                 // Don't refresh the select lists until caret move\r
9476                                 if (!/^(FontName|FontSize)$/.test(c))\r
9477                                         t.nodeChanged();\r
9478                         });\r
9479 \r
9480                         // Remove ghost selections on images and tables in Gecko\r
9481                         if (isGecko) {\r
9482                                 function repaint(a, o) {\r
9483                                         if (!o || !o.initial)\r
9484                                                 t.execCommand('mceRepaint');\r
9485                                 };\r
9486 \r
9487                                 t.onUndo.add(repaint);\r
9488                                 t.onRedo.add(repaint);\r
9489                                 t.onSetContent.add(repaint);\r
9490                         }\r
9491 \r
9492                         // Enables users to override the control factory\r
9493                         t.onBeforeRenderUI.dispatch(t, t.controlManager);\r
9494 \r
9495                         // Measure box\r
9496                         if (s.render_ui) {\r
9497                                 w = s.width || e.style.width || e.offsetWidth;\r
9498                                 h = s.height || e.style.height || e.offsetHeight;\r
9499                                 t.orgDisplay = e.style.display;\r
9500                                 re = /^[0-9\.]+(|px)$/i;\r
9501 \r
9502                                 if (re.test('' + w))\r
9503                                         w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);\r
9504 \r
9505                                 if (re.test('' + h))\r
9506                                         h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);\r
9507 \r
9508                                 // Render UI\r
9509                                 o = t.theme.renderUI({\r
9510                                         targetNode : e,\r
9511                                         width : w,\r
9512                                         height : h,\r
9513                                         deltaWidth : s.delta_width,\r
9514                                         deltaHeight : s.delta_height\r
9515                                 });\r
9516 \r
9517                                 t.editorContainer = o.editorContainer;\r
9518                         }\r
9519 \r
9520 \r
9521                         // User specified a document.domain value\r
9522                         if (document.domain && location.hostname != document.domain)\r
9523                                 tinymce.relaxedDomain = document.domain;\r
9524 \r
9525                         // Resize editor\r
9526                         DOM.setStyles(o.sizeContainer || o.editorContainer, {\r
9527                                 width : w,\r
9528                                 height : h\r
9529                         });\r
9530 \r
9531                         h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');\r
9532                         if (h < 100)\r
9533                                 h = 100;\r
9534 \r
9535                         t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">';\r
9536 \r
9537                         // We only need to override paths if we have to\r
9538                         // IE has a bug where it remove site absolute urls to relative ones if this is specified\r
9539                         if (s.document_base_url != tinymce.documentBaseURL)\r
9540                                 t.iframeHTML += '<base href="' + t.documentBaseURI.getURI() + '" />';\r
9541 \r
9542                         t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" /><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';\r
9543 \r
9544                         if (tinymce.relaxedDomain)\r
9545                                 t.iframeHTML += '<script type="text/javascript">document.domain = "' + tinymce.relaxedDomain + '";</script>';\r
9546 \r
9547                         bi = s.body_id || 'tinymce';\r
9548                         if (bi.indexOf('=') != -1) {\r
9549                                 bi = t.getParam('body_id', '', 'hash');\r
9550                                 bi = bi[t.id] || bi;\r
9551                         }\r
9552 \r
9553                         bc = s.body_class || '';\r
9554                         if (bc.indexOf('=') != -1) {\r
9555                                 bc = t.getParam('body_class', '', 'hash');\r
9556                                 bc = bc[t.id] || '';\r
9557                         }\r
9558 \r
9559                         t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"></body></html>';\r
9560 \r
9561                         // Domain relaxing enabled, then set document domain\r
9562                         if (tinymce.relaxedDomain) {\r
9563                                 // We need to write the contents here in IE since multiple writes messes up refresh button and back button\r
9564                                 if (isIE || (tinymce.isOpera && parseFloat(opera.version()) >= 9.5))\r
9565                                         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
9566                                 else if (tinymce.isOpera)\r
9567                                         u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";document.close();ed.setupIframe();})()';                                  \r
9568                         }\r
9569 \r
9570                         // Create iframe\r
9571                         n = DOM.add(o.iframeContainer, 'iframe', {\r
9572                                 id : t.id + "_ifr",\r
9573                                 src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7\r
9574                                 frameBorder : '0',\r
9575                                 style : {\r
9576                                         width : '100%',\r
9577                                         height : h\r
9578                                 }\r
9579                         });\r
9580 \r
9581                         t.contentAreaContainer = o.iframeContainer;\r
9582                         DOM.get(o.editorContainer).style.display = t.orgDisplay;\r
9583                         DOM.get(t.id).style.display = 'none';\r
9584 \r
9585                         if (!isIE || !tinymce.relaxedDomain)\r
9586                                 t.setupIframe();\r
9587 \r
9588                         e = n = o = null; // Cleanup\r
9589                 },\r
9590 \r
9591                 setupIframe : function() {\r
9592                         var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;\r
9593 \r
9594                         // Setup iframe body\r
9595                         if (!isIE || !tinymce.relaxedDomain) {\r
9596                                 d.open();\r
9597                                 d.write(t.iframeHTML);\r
9598                                 d.close();\r
9599                         }\r
9600 \r
9601                         // Design mode needs to be added here Ctrl+A will fail otherwise\r
9602                         if (!isIE) {\r
9603                                 try {\r
9604                                         if (!s.readonly)\r
9605                                                 d.designMode = 'On';\r
9606                                 } catch (ex) {\r
9607                                         // Will fail on Gecko if the editor is placed in an hidden container element\r
9608                                         // The design mode will be set ones the editor is focused\r
9609                                 }\r
9610                         }\r
9611 \r
9612                         // IE needs to use contentEditable or it will display non secure items for HTTPS\r
9613                         if (isIE) {\r
9614                                 // It will not steal focus if we hide it while setting contentEditable\r
9615                                 b = t.getBody();\r
9616                                 DOM.hide(b);\r
9617 \r
9618                                 if (!s.readonly)\r
9619                                         b.contentEditable = true;\r
9620 \r
9621                                 DOM.show(b);\r
9622                         }\r
9623 \r
9624                         t.dom = new tinymce.dom.DOMUtils(t.getDoc(), {\r
9625                                 keep_values : true,\r
9626                                 url_converter : t.convertURL,\r
9627                                 url_converter_scope : t,\r
9628                                 hex_colors : s.force_hex_style_colors,\r
9629                                 class_filter : s.class_filter,\r
9630                                 update_styles : 1,\r
9631                                 fix_ie_paragraphs : 1,\r
9632                                 valid_styles : s.valid_styles\r
9633                         });\r
9634 \r
9635                         t.schema = new tinymce.dom.Schema();\r
9636 \r
9637                         t.serializer = new tinymce.dom.Serializer(extend(s, {\r
9638                                 valid_elements : s.verify_html === false ? '*[*]' : s.valid_elements,\r
9639                                 dom : t.dom,\r
9640                                 schema : t.schema\r
9641                         }));\r
9642 \r
9643                         t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);\r
9644 \r
9645                         t.formatter = new tinymce.Formatter(this);\r
9646 \r
9647                         // Register default formats\r
9648                         t.formatter.register({\r
9649                                 alignleft : [\r
9650                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}},\r
9651                                         {selector : 'img,table', styles : {'float' : 'left'}}\r
9652                                 ],\r
9653 \r
9654                                 aligncenter : [\r
9655                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}},\r
9656                                         {selector : 'img', styles : {display : 'block', marginLeft : 'auto', marginRight : 'auto'}},\r
9657                                         {selector : 'table', styles : {marginLeft : 'auto', marginRight : 'auto'}}\r
9658                                 ],\r
9659 \r
9660                                 alignright : [\r
9661                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}},\r
9662                                         {selector : 'img,table', styles : {'float' : 'right'}}\r
9663                                 ],\r
9664 \r
9665                                 alignfull : [\r
9666                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'justify'}}\r
9667                                 ],\r
9668 \r
9669                                 bold : [\r
9670                                         {inline : 'strong'},\r
9671                                         {inline : 'span', styles : {fontWeight : 'bold'}},\r
9672                                         {inline : 'b'}\r
9673                                 ],\r
9674 \r
9675                                 italic : [\r
9676                                         {inline : 'em'},\r
9677                                         {inline : 'span', styles : {fontStyle : 'italic'}},\r
9678                                         {inline : 'i'}\r
9679                                 ],\r
9680 \r
9681                                 underline : [\r
9682                                         {inline : 'span', styles : {textDecoration : 'underline'}, exact : true},\r
9683                                         {inline : 'u'}\r
9684                                 ],\r
9685 \r
9686                                 strikethrough : [\r
9687                                         {inline : 'span', styles : {textDecoration : 'line-through'}, exact : true},\r
9688                                         {inline : 'u'}\r
9689                                 ],\r
9690 \r
9691                                 forecolor : {inline : 'span', styles : {color : '%value'}},\r
9692                                 hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}},\r
9693                                 fontname : {inline : 'span', styles : {fontFamily : '%value'}},\r
9694                                 fontsize : {inline : 'span', styles : {fontSize : '%value'}},\r
9695                                 fontsize_class : {inline : 'span', attributes : {'class' : '%value'}},\r
9696                                 blockquote : {block : 'blockquote', wrapper : 1, remove : 'all'},\r
9697 \r
9698                                 removeformat : [\r
9699                                         {selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},\r
9700                                         {selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},\r
9701                                         {selector : '*', attributes : ['style', 'class'], split : false, expand : false, deep : true}\r
9702                                 ]\r
9703                         });\r
9704 \r
9705                         // Register default block formats\r
9706                         each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) {\r
9707                                 t.formatter.register(name, {block : name, remove : 'all'});\r
9708                         });\r
9709 \r
9710                         // Register user defined formats\r
9711                         t.formatter.register(t.settings.formats);\r
9712 \r
9713                         t.undoManager = new tinymce.UndoManager(t);\r
9714 \r
9715                         // Pass through\r
9716                         t.undoManager.onAdd.add(function(um, l) {\r
9717                                 if (!l.initial)\r
9718                                         return t.onChange.dispatch(t, l, um);\r
9719                         });\r
9720 \r
9721                         t.undoManager.onUndo.add(function(um, l) {\r
9722                                 return t.onUndo.dispatch(t, l, um);\r
9723                         });\r
9724 \r
9725                         t.undoManager.onRedo.add(function(um, l) {\r
9726                                 return t.onRedo.dispatch(t, l, um);\r
9727                         });\r
9728 \r
9729                         t.forceBlocks = new tinymce.ForceBlocks(t, {\r
9730                                 forced_root_block : s.forced_root_block\r
9731                         });\r
9732 \r
9733                         t.editorCommands = new tinymce.EditorCommands(t);\r
9734 \r
9735                         // Pass through\r
9736                         t.serializer.onPreProcess.add(function(se, o) {\r
9737                                 return t.onPreProcess.dispatch(t, o, se);\r
9738                         });\r
9739 \r
9740                         t.serializer.onPostProcess.add(function(se, o) {\r
9741                                 return t.onPostProcess.dispatch(t, o, se);\r
9742                         });\r
9743 \r
9744                         t.onPreInit.dispatch(t);\r
9745 \r
9746                         if (!s.gecko_spellcheck)\r
9747                                 t.getBody().spellcheck = 0;\r
9748 \r
9749                         if (!s.readonly)\r
9750                                 t._addEvents();\r
9751 \r
9752                         t.controlManager.onPostRender.dispatch(t, t.controlManager);\r
9753                         t.onPostRender.dispatch(t);\r
9754 \r
9755                         if (s.directionality)\r
9756                                 t.getBody().dir = s.directionality;\r
9757 \r
9758                         if (s.nowrap)\r
9759                                 t.getBody().style.whiteSpace = "nowrap";\r
9760 \r
9761                         if (s.custom_elements) {\r
9762                                 function handleCustom(ed, o) {\r
9763                                         each(explode(s.custom_elements), function(v) {\r
9764                                                 var n;\r
9765 \r
9766                                                 if (v.indexOf('~') === 0) {\r
9767                                                         v = v.substring(1);\r
9768                                                         n = 'span';\r
9769                                                 } else\r
9770                                                         n = 'div';\r
9771 \r
9772                                                 o.content = o.content.replace(new RegExp('<(' + v + ')([^>]*)>', 'g'), '<' + n + ' _mce_name="$1"$2>');\r
9773                                                 o.content = o.content.replace(new RegExp('</(' + v + ')>', 'g'), '</' + n + '>');\r
9774                                         });\r
9775                                 };\r
9776 \r
9777                                 t.onBeforeSetContent.add(handleCustom);\r
9778                                 t.onPostProcess.add(function(ed, o) {\r
9779                                         if (o.set)\r
9780                                                 handleCustom(ed, o);\r
9781                                 });\r
9782                         }\r
9783 \r
9784                         if (s.handle_node_change_callback) {\r
9785                                 t.onNodeChange.add(function(ed, cm, n) {\r
9786                                         t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());\r
9787                                 });\r
9788                         }\r
9789 \r
9790                         if (s.save_callback) {\r
9791                                 t.onSaveContent.add(function(ed, o) {\r
9792                                         var h = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
9793 \r
9794                                         if (h)\r
9795                                                 o.content = h;\r
9796                                 });\r
9797                         }\r
9798 \r
9799                         if (s.onchange_callback) {\r
9800                                 t.onChange.add(function(ed, l) {\r
9801                                         t.execCallback('onchange_callback', t, l);\r
9802                                 });\r
9803                         }\r
9804 \r
9805                         if (s.convert_newlines_to_brs) {\r
9806                                 t.onBeforeSetContent.add(function(ed, o) {\r
9807                                         if (o.initial)\r
9808                                                 o.content = o.content.replace(/\r?\n/g, '<br />');\r
9809                                 });\r
9810                         }\r
9811 \r
9812                         if (s.fix_nesting && isIE) {\r
9813                                 t.onBeforeSetContent.add(function(ed, o) {\r
9814                                         o.content = t._fixNesting(o.content);\r
9815                                 });\r
9816                         }\r
9817 \r
9818                         if (s.preformatted) {\r
9819                                 t.onPostProcess.add(function(ed, o) {\r
9820                                         o.content = o.content.replace(/^\s*<pre.*?>/, '');\r
9821                                         o.content = o.content.replace(/<\/pre>\s*$/, '');\r
9822 \r
9823                                         if (o.set)\r
9824                                                 o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';\r
9825                                 });\r
9826                         }\r
9827 \r
9828                         if (s.verify_css_classes) {\r
9829                                 t.serializer.attribValueFilter = function(n, v) {\r
9830                                         var s, cl;\r
9831 \r
9832                                         if (n == 'class') {\r
9833                                                 // Build regexp for classes\r
9834                                                 if (!t.classesRE) {\r
9835                                                         cl = t.dom.getClasses();\r
9836 \r
9837                                                         if (cl.length > 0) {\r
9838                                                                 s = '';\r
9839 \r
9840                                                                 each (cl, function(o) {\r
9841                                                                         s += (s ? '|' : '') + o['class'];\r
9842                                                                 });\r
9843 \r
9844                                                                 t.classesRE = new RegExp('(' + s + ')', 'gi');\r
9845                                                         }\r
9846                                                 }\r
9847 \r
9848                                                 return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';\r
9849                                         }\r
9850 \r
9851                                         return v;\r
9852                                 };\r
9853                         }\r
9854 \r
9855                         if (s.cleanup_callback) {\r
9856                                 t.onBeforeSetContent.add(function(ed, o) {\r
9857                                         o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
9858                                 });\r
9859 \r
9860                                 t.onPreProcess.add(function(ed, o) {\r
9861                                         if (o.set)\r
9862                                                 t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);\r
9863 \r
9864                                         if (o.get)\r
9865                                                 t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);\r
9866                                 });\r
9867 \r
9868                                 t.onPostProcess.add(function(ed, o) {\r
9869                                         if (o.set)\r
9870                                                 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
9871 \r
9872                                         if (o.get)                                              \r
9873                                                 o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);\r
9874                                 });\r
9875                         }\r
9876 \r
9877                         if (s.save_callback) {\r
9878                                 t.onGetContent.add(function(ed, o) {\r
9879                                         if (o.save)\r
9880                                                 o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
9881                                 });\r
9882                         }\r
9883 \r
9884                         if (s.handle_event_callback) {\r
9885                                 t.onEvent.add(function(ed, e, o) {\r
9886                                         if (t.execCallback('handle_event_callback', e, ed, o) === false)\r
9887                                                 Event.cancel(e);\r
9888                                 });\r
9889                         }\r
9890 \r
9891                         // Add visual aids when new contents is added\r
9892                         t.onSetContent.add(function() {\r
9893                                 t.addVisual(t.getBody());\r
9894                         });\r
9895 \r
9896                         // Remove empty contents\r
9897                         if (s.padd_empty_editor) {\r
9898                                 t.onPostProcess.add(function(ed, o) {\r
9899                                         o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');\r
9900                                 });\r
9901                         }\r
9902 \r
9903                         if (isGecko) {\r
9904                                 // Fix gecko link bug, when a link is placed at the end of block elements there is\r
9905                                 // no way to move the caret behind the link. This fix adds a bogus br element after the link\r
9906                                 function fixLinks(ed, o) {\r
9907                                         each(ed.dom.select('a'), function(n) {\r
9908                                                 var pn = n.parentNode;\r
9909 \r
9910                                                 if (ed.dom.isBlock(pn) && pn.lastChild === n)\r
9911                                                         ed.dom.add(pn, 'br', {'_mce_bogus' : 1});\r
9912                                         });\r
9913                                 };\r
9914 \r
9915                                 t.onExecCommand.add(function(ed, cmd) {\r
9916                                         if (cmd === 'CreateLink')\r
9917                                                 fixLinks(ed);\r
9918                                 });\r
9919 \r
9920                                 t.onSetContent.add(t.selection.onSetContent.add(fixLinks));\r
9921 \r
9922                                 if (!s.readonly) {\r
9923                                         try {\r
9924                                                 // Design mode must be set here once again to fix a bug where\r
9925                                                 // Ctrl+A/Delete/Backspace didn't work if the editor was added using mceAddControl then removed then added again\r
9926                                                 d.designMode = 'Off';\r
9927                                                 d.designMode = 'On';\r
9928                                         } catch (ex) {\r
9929                                                 // Will fail on Gecko if the editor is placed in an hidden container element\r
9930                                                 // The design mode will be set ones the editor is focused\r
9931                                         }\r
9932                                 }\r
9933                         }\r
9934 \r
9935                         // A small timeout was needed since firefox will remove. Bug: #1838304\r
9936                         setTimeout(function () {\r
9937                                 if (t.removed)\r
9938                                         return;\r
9939 \r
9940                                 t.load({initial : true, format : (s.cleanup_on_startup ? 'html' : 'raw')});\r
9941                                 t.startContent = t.getContent({format : 'raw'});\r
9942                                 t.initialized = true;\r
9943 \r
9944                                 t.onInit.dispatch(t);\r
9945                                 t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());\r
9946                                 t.execCallback('init_instance_callback', t);\r
9947                                 t.focus(true);\r
9948                                 t.nodeChanged({initial : 1});\r
9949 \r
9950                                 // Load specified content CSS last\r
9951                                 if (s.content_css) {\r
9952                                         tinymce.each(explode(s.content_css), function(u) {\r
9953                                                 t.dom.loadCSS(t.documentBaseURI.toAbsolute(u));\r
9954                                         });\r
9955                                 }\r
9956 \r
9957                                 // Handle auto focus\r
9958                                 if (s.auto_focus) {\r
9959                                         setTimeout(function () {\r
9960                                                 var ed = tinymce.get(s.auto_focus);\r
9961 \r
9962                                                 ed.selection.select(ed.getBody(), 1);\r
9963                                                 ed.selection.collapse(1);\r
9964                                                 ed.getWin().focus();\r
9965                                         }, 100);\r
9966                                 }\r
9967                         }, 1);\r
9968         \r
9969                         e = null;\r
9970                 },\r
9971 \r
9972 \r
9973                 focus : function(sf) {\r
9974                         var oed, t = this, ce = t.settings.content_editable, ieRng, controlElm, doc = t.getDoc();\r
9975 \r
9976                         if (!sf) {\r
9977                                 // Get selected control element\r
9978                                 ieRng = t.selection.getRng();\r
9979                                 if (ieRng.item) {\r
9980                                         controlElm = ieRng.item(0);\r
9981                                 }\r
9982 \r
9983                                 // Is not content editable\r
9984                                 if (!ce)\r
9985                                         t.getWin().focus();\r
9986 \r
9987                                 // Restore selected control element\r
9988                                 // This is needed when for example an image is selected within a\r
9989                                 // layer a call to focus will then remove the control selection\r
9990                                 if (controlElm && controlElm.ownerDocument == doc) {\r
9991                                         ieRng = doc.body.createControlRange();\r
9992                                         ieRng.addElement(controlElm);\r
9993                                         ieRng.select();\r
9994                                 }\r
9995 \r
9996                         }\r
9997 \r
9998                         if (tinymce.activeEditor != t) {\r
9999                                 if ((oed = tinymce.activeEditor) != null)\r
10000                                         oed.onDeactivate.dispatch(oed, t);\r
10001 \r
10002                                 t.onActivate.dispatch(t, oed);\r
10003                         }\r
10004 \r
10005                         tinymce._setActive(t);\r
10006                 },\r
10007 \r
10008                 execCallback : function(n) {\r
10009                         var t = this, f = t.settings[n], s;\r
10010 \r
10011                         if (!f)\r
10012                                 return;\r
10013 \r
10014                         // Look through lookup\r
10015                         if (t.callbackLookup && (s = t.callbackLookup[n])) {\r
10016                                 f = s.func;\r
10017                                 s = s.scope;\r
10018                         }\r
10019 \r
10020                         if (is(f, 'string')) {\r
10021                                 s = f.replace(/\.\w+$/, '');\r
10022                                 s = s ? tinymce.resolve(s) : 0;\r
10023                                 f = tinymce.resolve(f);\r
10024                                 t.callbackLookup = t.callbackLookup || {};\r
10025                                 t.callbackLookup[n] = {func : f, scope : s};\r
10026                         }\r
10027 \r
10028                         return f.apply(s || t, Array.prototype.slice.call(arguments, 1));\r
10029                 },\r
10030 \r
10031                 translate : function(s) {\r
10032                         var c = this.settings.language || 'en', i18n = tinymce.i18n;\r
10033 \r
10034                         if (!s)\r
10035                                 return '';\r
10036 \r
10037                         return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {\r
10038                                 return i18n[c + '.' + b] || '{#' + b + '}';\r
10039                         });\r
10040                 },\r
10041 \r
10042                 getLang : function(n, dv) {\r
10043                         return tinymce.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}');\r
10044                 },\r
10045 \r
10046                 getParam : function(n, dv, ty) {\r
10047                         var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;\r
10048 \r
10049                         if (ty === 'hash') {\r
10050                                 o = {};\r
10051 \r
10052                                 if (is(v, 'string')) {\r
10053                                         each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {\r
10054                                                 v = v.split('=');\r
10055 \r
10056                                                 if (v.length > 1)\r
10057                                                         o[tr(v[0])] = tr(v[1]);\r
10058                                                 else\r
10059                                                         o[tr(v[0])] = tr(v);\r
10060                                         });\r
10061                                 } else\r
10062                                         o = v;\r
10063 \r
10064                                 return o;\r
10065                         }\r
10066 \r
10067                         return v;\r
10068                 },\r
10069 \r
10070                 nodeChanged : function(o) {\r
10071                         var t = this, s = t.selection, n = (isIE ? s.getNode() : s.getStart()) || t.getBody();\r
10072 \r
10073                         // Fix for bug #1896577 it seems that this can not be fired while the editor is loading\r
10074                         if (t.initialized) {\r
10075                                 o = o || {};\r
10076                                 n = isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n; // Fix for IE initial state\r
10077 \r
10078                                 // Get parents and add them to object\r
10079                                 o.parents = [];\r
10080                                 t.dom.getParent(n, function(node) {\r
10081                                         if (node.nodeName == 'BODY')\r
10082                                                 return true;\r
10083 \r
10084                                         o.parents.push(node);\r
10085                                 });\r
10086 \r
10087                                 t.onNodeChange.dispatch(\r
10088                                         t,\r
10089                                         o ? o.controlManager || t.controlManager : t.controlManager,\r
10090                                         n,\r
10091                                         s.isCollapsed(),\r
10092                                         o\r
10093                                 );\r
10094                         }\r
10095                 },\r
10096 \r
10097                 addButton : function(n, s) {\r
10098                         var t = this;\r
10099 \r
10100                         t.buttons = t.buttons || {};\r
10101                         t.buttons[n] = s;\r
10102                 },\r
10103 \r
10104                 addCommand : function(n, f, s) {\r
10105                         this.execCommands[n] = {func : f, scope : s || this};\r
10106                 },\r
10107 \r
10108                 addQueryStateHandler : function(n, f, s) {\r
10109                         this.queryStateCommands[n] = {func : f, scope : s || this};\r
10110                 },\r
10111 \r
10112                 addQueryValueHandler : function(n, f, s) {\r
10113                         this.queryValueCommands[n] = {func : f, scope : s || this};\r
10114                 },\r
10115 \r
10116                 addShortcut : function(pa, desc, cmd_func, sc) {\r
10117                         var t = this, c;\r
10118 \r
10119                         if (!t.settings.custom_shortcuts)\r
10120                                 return false;\r
10121 \r
10122                         t.shortcuts = t.shortcuts || {};\r
10123 \r
10124                         if (is(cmd_func, 'string')) {\r
10125                                 c = cmd_func;\r
10126 \r
10127                                 cmd_func = function() {\r
10128                                         t.execCommand(c, false, null);\r
10129                                 };\r
10130                         }\r
10131 \r
10132                         if (is(cmd_func, 'object')) {\r
10133                                 c = cmd_func;\r
10134 \r
10135                                 cmd_func = function() {\r
10136                                         t.execCommand(c[0], c[1], c[2]);\r
10137                                 };\r
10138                         }\r
10139 \r
10140                         each(explode(pa), function(pa) {\r
10141                                 var o = {\r
10142                                         func : cmd_func,\r
10143                                         scope : sc || this,\r
10144                                         desc : desc,\r
10145                                         alt : false,\r
10146                                         ctrl : false,\r
10147                                         shift : false\r
10148                                 };\r
10149 \r
10150                                 each(explode(pa, '+'), function(v) {\r
10151                                         switch (v) {\r
10152                                                 case 'alt':\r
10153                                                 case 'ctrl':\r
10154                                                 case 'shift':\r
10155                                                         o[v] = true;\r
10156                                                         break;\r
10157 \r
10158                                                 default:\r
10159                                                         o.charCode = v.charCodeAt(0);\r
10160                                                         o.keyCode = v.toUpperCase().charCodeAt(0);\r
10161                                         }\r
10162                                 });\r
10163 \r
10164                                 t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;\r
10165                         });\r
10166 \r
10167                         return true;\r
10168                 },\r
10169 \r
10170                 execCommand : function(cmd, ui, val, a) {\r
10171                         var t = this, s = 0, o, st;\r
10172 \r
10173                         if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))\r
10174                                 t.focus();\r
10175 \r
10176                         o = {};\r
10177                         t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);\r
10178                         if (o.terminate)\r
10179                                 return false;\r
10180 \r
10181                         // Command callback\r
10182                         if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {\r
10183                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10184                                 return true;\r
10185                         }\r
10186 \r
10187                         // Registred commands\r
10188                         if (o = t.execCommands[cmd]) {\r
10189                                 st = o.func.call(o.scope, ui, val);\r
10190 \r
10191                                 // Fall through on true\r
10192                                 if (st !== true) {\r
10193                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10194                                         return st;\r
10195                                 }\r
10196                         }\r
10197 \r
10198                         // Plugin commands\r
10199                         each(t.plugins, function(p) {\r
10200                                 if (p.execCommand && p.execCommand(cmd, ui, val)) {\r
10201                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10202                                         s = 1;\r
10203                                         return false;\r
10204                                 }\r
10205                         });\r
10206 \r
10207                         if (s)\r
10208                                 return true;\r
10209 \r
10210                         // Theme commands\r
10211                         if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {\r
10212                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10213                                 return true;\r
10214                         }\r
10215 \r
10216                         // Execute global commands\r
10217                         if (tinymce.GlobalCommands.execCommand(t, cmd, ui, val)) {\r
10218                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10219                                 return true;\r
10220                         }\r
10221 \r
10222                         // Editor commands\r
10223                         if (t.editorCommands.execCommand(cmd, ui, val)) {\r
10224                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10225                                 return true;\r
10226                         }\r
10227 \r
10228                         // Browser commands\r
10229                         t.getDoc().execCommand(cmd, ui, val);\r
10230                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10231                 },\r
10232 \r
10233                 queryCommandState : function(cmd) {\r
10234                         var t = this, o, s;\r
10235 \r
10236                         // Is hidden then return undefined\r
10237                         if (t._isHidden())\r
10238                                 return;\r
10239 \r
10240                         // Registred commands\r
10241                         if (o = t.queryStateCommands[cmd]) {\r
10242                                 s = o.func.call(o.scope);\r
10243 \r
10244                                 // Fall though on true\r
10245                                 if (s !== true)\r
10246                                         return s;\r
10247                         }\r
10248 \r
10249                         // Registred commands\r
10250                         o = t.editorCommands.queryCommandState(cmd);\r
10251                         if (o !== -1)\r
10252                                 return o;\r
10253 \r
10254                         // Browser commands\r
10255                         try {\r
10256                                 return this.getDoc().queryCommandState(cmd);\r
10257                         } catch (ex) {\r
10258                                 // Fails sometimes see bug: 1896577\r
10259                         }\r
10260                 },\r
10261 \r
10262                 queryCommandValue : function(c) {\r
10263                         var t = this, o, s;\r
10264 \r
10265                         // Is hidden then return undefined\r
10266                         if (t._isHidden())\r
10267                                 return;\r
10268 \r
10269                         // Registred commands\r
10270                         if (o = t.queryValueCommands[c]) {\r
10271                                 s = o.func.call(o.scope);\r
10272 \r
10273                                 // Fall though on true\r
10274                                 if (s !== true)\r
10275                                         return s;\r
10276                         }\r
10277 \r
10278                         // Registred commands\r
10279                         o = t.editorCommands.queryCommandValue(c);\r
10280                         if (is(o))\r
10281                                 return o;\r
10282 \r
10283                         // Browser commands\r
10284                         try {\r
10285                                 return this.getDoc().queryCommandValue(c);\r
10286                         } catch (ex) {\r
10287                                 // Fails sometimes see bug: 1896577\r
10288                         }\r
10289                 },\r
10290 \r
10291                 show : function() {\r
10292                         var t = this;\r
10293 \r
10294                         DOM.show(t.getContainer());\r
10295                         DOM.hide(t.id);\r
10296                         t.load();\r
10297                 },\r
10298 \r
10299                 hide : function() {\r
10300                         var t = this, d = t.getDoc();\r
10301 \r
10302                         // Fixed bug where IE has a blinking cursor left from the editor\r
10303                         if (isIE && d)\r
10304                                 d.execCommand('SelectAll');\r
10305 \r
10306                         // We must save before we hide so Safari doesn't crash\r
10307                         t.save();\r
10308                         DOM.hide(t.getContainer());\r
10309                         DOM.setStyle(t.id, 'display', t.orgDisplay);\r
10310                 },\r
10311 \r
10312                 isHidden : function() {\r
10313                         return !DOM.isHidden(this.id);\r
10314                 },\r
10315 \r
10316                 setProgressState : function(b, ti, o) {\r
10317                         this.onSetProgressState.dispatch(this, b, ti, o);\r
10318 \r
10319                         return b;\r
10320                 },\r
10321 \r
10322                 load : function(o) {\r
10323                         var t = this, e = t.getElement(), h;\r
10324 \r
10325                         if (e) {\r
10326                                 o = o || {};\r
10327                                 o.load = true;\r
10328 \r
10329                                 // Double encode existing entities in the value\r
10330                                 h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);\r
10331                                 o.element = e;\r
10332 \r
10333                                 if (!o.no_events)\r
10334                                         t.onLoadContent.dispatch(t, o);\r
10335 \r
10336                                 o.element = e = null;\r
10337 \r
10338                                 return h;\r
10339                         }\r
10340                 },\r
10341 \r
10342                 save : function(o) {\r
10343                         var t = this, e = t.getElement(), h, f;\r
10344 \r
10345                         if (!e || !t.initialized)\r
10346                                 return;\r
10347 \r
10348                         o = o || {};\r
10349                         o.save = true;\r
10350 \r
10351                         // Add undo level will trigger onchange event\r
10352                         if (!o.no_events) {\r
10353                                 t.undoManager.typing = 0;\r
10354                                 t.undoManager.add();\r
10355                         }\r
10356 \r
10357                         o.element = e;\r
10358                         h = o.content = t.getContent(o);\r
10359 \r
10360                         if (!o.no_events)\r
10361                                 t.onSaveContent.dispatch(t, o);\r
10362 \r
10363                         h = o.content;\r
10364 \r
10365                         if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {\r
10366                                 e.innerHTML = h;\r
10367 \r
10368                                 // Update hidden form element\r
10369                                 if (f = DOM.getParent(t.id, 'form')) {\r
10370                                         each(f.elements, function(e) {\r
10371                                                 if (e.name == t.id) {\r
10372                                                         e.value = h;\r
10373                                                         return false;\r
10374                                                 }\r
10375                                         });\r
10376                                 }\r
10377                         } else\r
10378                                 e.value = h;\r
10379 \r
10380                         o.element = e = null;\r
10381 \r
10382                         return h;\r
10383                 },\r
10384 \r
10385                 setContent : function(h, o) {\r
10386                         var t = this;\r
10387 \r
10388                         o = o || {};\r
10389                         o.format = o.format || 'html';\r
10390                         o.set = true;\r
10391                         o.content = h;\r
10392 \r
10393                         if (!o.no_events)\r
10394                                 t.onBeforeSetContent.dispatch(t, o);\r
10395 \r
10396                         // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content\r
10397                         // It will also be impossible to place the caret in the editor unless there is a BR element present\r
10398                         if (!tinymce.isIE && (h.length === 0 || /^\s+$/.test(h))) {\r
10399                                 o.content = t.dom.setHTML(t.getBody(), '<br _mce_bogus="1" />');\r
10400                                 o.format = 'raw';\r
10401                         }\r
10402 \r
10403                         o.content = t.dom.setHTML(t.getBody(), tinymce.trim(o.content));\r
10404 \r
10405                         if (o.format != 'raw' && t.settings.cleanup) {\r
10406                                 o.getInner = true;\r
10407                                 o.content = t.dom.setHTML(t.getBody(), t.serializer.serialize(t.getBody(), o));\r
10408                         }\r
10409 \r
10410                         if (!o.no_events)\r
10411                                 t.onSetContent.dispatch(t, o);\r
10412 \r
10413                         return o.content;\r
10414                 },\r
10415 \r
10416                 getContent : function(o) {\r
10417                         var t = this, h;\r
10418 \r
10419                         o = o || {};\r
10420                         o.format = o.format || 'html';\r
10421                         o.get = true;\r
10422 \r
10423                         if (!o.no_events)\r
10424                                 t.onBeforeGetContent.dispatch(t, o);\r
10425 \r
10426                         if (o.format != 'raw' && t.settings.cleanup) {\r
10427                                 o.getInner = true;\r
10428                                 h = t.serializer.serialize(t.getBody(), o);\r
10429                         } else\r
10430                                 h = t.getBody().innerHTML;\r
10431 \r
10432                         h = h.replace(/^\s*|\s*$/g, '');\r
10433                         o.content = h;\r
10434 \r
10435                         if (!o.no_events)\r
10436                                 t.onGetContent.dispatch(t, o);\r
10437 \r
10438                         return o.content;\r
10439                 },\r
10440 \r
10441                 isDirty : function() {\r
10442                         var t = this;\r
10443 \r
10444                         return tinymce.trim(t.startContent) != tinymce.trim(t.getContent({format : 'raw', no_events : 1})) && !t.isNotDirty;\r
10445                 },\r
10446 \r
10447                 getContainer : function() {\r
10448                         var t = this;\r
10449 \r
10450                         if (!t.container)\r
10451                                 t.container = DOM.get(t.editorContainer || t.id + '_parent');\r
10452 \r
10453                         return t.container;\r
10454                 },\r
10455 \r
10456                 getContentAreaContainer : function() {\r
10457                         return this.contentAreaContainer;\r
10458                 },\r
10459 \r
10460                 getElement : function() {\r
10461                         return DOM.get(this.settings.content_element || this.id);\r
10462                 },\r
10463 \r
10464                 getWin : function() {\r
10465                         var t = this, e;\r
10466 \r
10467                         if (!t.contentWindow) {\r
10468                                 e = DOM.get(t.id + "_ifr");\r
10469 \r
10470                                 if (e)\r
10471                                         t.contentWindow = e.contentWindow;\r
10472                         }\r
10473 \r
10474                         return t.contentWindow;\r
10475                 },\r
10476 \r
10477                 getDoc : function() {\r
10478                         var t = this, w;\r
10479 \r
10480                         if (!t.contentDocument) {\r
10481                                 w = t.getWin();\r
10482 \r
10483                                 if (w)\r
10484                                         t.contentDocument = w.document;\r
10485                         }\r
10486 \r
10487                         return t.contentDocument;\r
10488                 },\r
10489 \r
10490                 getBody : function() {\r
10491                         return this.bodyElement || this.getDoc().body;\r
10492                 },\r
10493 \r
10494                 convertURL : function(u, n, e) {\r
10495                         var t = this, s = t.settings;\r
10496 \r
10497                         // Use callback instead\r
10498                         if (s.urlconverter_callback)\r
10499                                 return t.execCallback('urlconverter_callback', u, e, true, n);\r
10500 \r
10501                         // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs\r
10502                         if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)\r
10503                                 return u;\r
10504 \r
10505                         // Convert to relative\r
10506                         if (s.relative_urls)\r
10507                                 return t.documentBaseURI.toRelative(u);\r
10508 \r
10509                         // Convert to absolute\r
10510                         u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);\r
10511 \r
10512                         return u;\r
10513                 },\r
10514 \r
10515                 addVisual : function(e) {\r
10516                         var t = this, s = t.settings;\r
10517 \r
10518                         e = e || t.getBody();\r
10519 \r
10520                         if (!is(t.hasVisual))\r
10521                                 t.hasVisual = s.visual;\r
10522 \r
10523                         each(t.dom.select('table,a', e), function(e) {\r
10524                                 var v;\r
10525 \r
10526                                 switch (e.nodeName) {\r
10527                                         case 'TABLE':\r
10528                                                 v = t.dom.getAttrib(e, 'border');\r
10529 \r
10530                                                 if (!v || v == '0') {\r
10531                                                         if (t.hasVisual)\r
10532                                                                 t.dom.addClass(e, s.visual_table_class);\r
10533                                                         else\r
10534                                                                 t.dom.removeClass(e, s.visual_table_class);\r
10535                                                 }\r
10536 \r
10537                                                 return;\r
10538 \r
10539                                         case 'A':\r
10540                                                 v = t.dom.getAttrib(e, 'name');\r
10541 \r
10542                                                 if (v) {\r
10543                                                         if (t.hasVisual)\r
10544                                                                 t.dom.addClass(e, 'mceItemAnchor');\r
10545                                                         else\r
10546                                                                 t.dom.removeClass(e, 'mceItemAnchor');\r
10547                                                 }\r
10548 \r
10549                                                 return;\r
10550                                 }\r
10551                         });\r
10552 \r
10553                         t.onVisualAid.dispatch(t, e, t.hasVisual);\r
10554                 },\r
10555 \r
10556                 remove : function() {\r
10557                         var t = this, e = t.getContainer();\r
10558 \r
10559                         t.removed = 1; // Cancels post remove event execution\r
10560                         t.hide();\r
10561 \r
10562                         t.execCallback('remove_instance_callback', t);\r
10563                         t.onRemove.dispatch(t);\r
10564 \r
10565                         // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command\r
10566                         t.onExecCommand.listeners = [];\r
10567 \r
10568                         tinymce.remove(t);\r
10569                         DOM.remove(e);\r
10570                 },\r
10571 \r
10572                 destroy : function(s) {\r
10573                         var t = this;\r
10574 \r
10575                         // One time is enough\r
10576                         if (t.destroyed)\r
10577                                 return;\r
10578 \r
10579                         if (!s) {\r
10580                                 tinymce.removeUnload(t.destroy);\r
10581                                 tinyMCE.onBeforeUnload.remove(t._beforeUnload);\r
10582 \r
10583                                 // Manual destroy\r
10584                                 if (t.theme && t.theme.destroy)\r
10585                                         t.theme.destroy();\r
10586 \r
10587                                 // Destroy controls, selection and dom\r
10588                                 t.controlManager.destroy();\r
10589                                 t.selection.destroy();\r
10590                                 t.dom.destroy();\r
10591 \r
10592                                 // Remove all events\r
10593 \r
10594                                 // Don't clear the window or document if content editable\r
10595                                 // is enabled since other instances might still be present\r
10596                                 if (!t.settings.content_editable) {\r
10597                                         Event.clear(t.getWin());\r
10598                                         Event.clear(t.getDoc());\r
10599                                 }\r
10600 \r
10601                                 Event.clear(t.getBody());\r
10602                                 Event.clear(t.formElement);\r
10603                         }\r
10604 \r
10605                         if (t.formElement) {\r
10606                                 t.formElement.submit = t.formElement._mceOldSubmit;\r
10607                                 t.formElement._mceOldSubmit = null;\r
10608                         }\r
10609 \r
10610                         t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;\r
10611 \r
10612                         if (t.selection)\r
10613                                 t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;\r
10614 \r
10615                         t.destroyed = 1;\r
10616                 },\r
10617 \r
10618                 // Internal functions\r
10619 \r
10620                 _addEvents : function() {\r
10621                         // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset\r
10622                         var t = this, i, s = t.settings, dom = t.dom, lo = {\r
10623                                 mouseup : 'onMouseUp',\r
10624                                 mousedown : 'onMouseDown',\r
10625                                 click : 'onClick',\r
10626                                 keyup : 'onKeyUp',\r
10627                                 keydown : 'onKeyDown',\r
10628                                 keypress : 'onKeyPress',\r
10629                                 submit : 'onSubmit',\r
10630                                 reset : 'onReset',\r
10631                                 contextmenu : 'onContextMenu',\r
10632                                 dblclick : 'onDblClick',\r
10633                                 paste : 'onPaste' // Doesn't work in all browsers yet\r
10634                         };\r
10635 \r
10636                         function eventHandler(e, o) {\r
10637                                 var ty = e.type;\r
10638 \r
10639                                 // Don't fire events when it's removed\r
10640                                 if (t.removed)\r
10641                                         return;\r
10642 \r
10643                                 // Generic event handler\r
10644                                 if (t.onEvent.dispatch(t, e, o) !== false) {\r
10645                                         // Specific event handler\r
10646                                         t[lo[e.fakeType || e.type]].dispatch(t, e, o);\r
10647                                 }\r
10648                         };\r
10649 \r
10650                         // Add DOM events\r
10651                         each(lo, function(v, k) {\r
10652                                 switch (k) {\r
10653                                         case 'contextmenu':\r
10654                                                 if (tinymce.isOpera) {\r
10655                                                         // Fake contextmenu on Opera\r
10656                                                         dom.bind(t.getBody(), 'mousedown', function(e) {\r
10657                                                                 if (e.ctrlKey) {\r
10658                                                                         e.fakeType = 'contextmenu';\r
10659                                                                         eventHandler(e);\r
10660                                                                 }\r
10661                                                         });\r
10662                                                 } else\r
10663                                                         dom.bind(t.getBody(), k, eventHandler);\r
10664                                                 break;\r
10665 \r
10666                                         case 'paste':\r
10667                                                 dom.bind(t.getBody(), k, function(e) {\r
10668                                                         eventHandler(e);\r
10669                                                 });\r
10670                                                 break;\r
10671 \r
10672                                         case 'submit':\r
10673                                         case 'reset':\r
10674                                                 dom.bind(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);\r
10675                                                 break;\r
10676 \r
10677                                         default:\r
10678                                                 dom.bind(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler);\r
10679                                 }\r
10680                         });\r
10681 \r
10682                         dom.bind(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) {\r
10683                                 t.focus(true);\r
10684                         });\r
10685 \r
10686 \r
10687                         // Fixes bug where a specified document_base_uri could result in broken images\r
10688                         // This will also fix drag drop of images in Gecko\r
10689                         if (tinymce.isGecko) {\r
10690                                 dom.bind(t.getDoc(), 'DOMNodeInserted', function(e) {\r
10691                                         var v;\r
10692 \r
10693                                         e = e.target;\r
10694 \r
10695                                         if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('_mce_src')))\r
10696                                                 e.src = t.documentBaseURI.toAbsolute(v);\r
10697                                 });\r
10698                         }\r
10699 \r
10700                         // Set various midas options in Gecko\r
10701                         if (isGecko) {\r
10702                                 function setOpts() {\r
10703                                         var t = this, d = t.getDoc(), s = t.settings;\r
10704 \r
10705                                         if (isGecko && !s.readonly) {\r
10706                                                 if (t._isHidden()) {\r
10707                                                         try {\r
10708                                                                 if (!s.content_editable)\r
10709                                                                         d.designMode = 'On';\r
10710                                                         } catch (ex) {\r
10711                                                                 // Fails if it's hidden\r
10712                                                         }\r
10713                                                 }\r
10714 \r
10715                                                 try {\r
10716                                                         // Try new Gecko method\r
10717                                                         d.execCommand("styleWithCSS", 0, false);\r
10718                                                 } catch (ex) {\r
10719                                                         // Use old method\r
10720                                                         if (!t._isHidden())\r
10721                                                                 try {d.execCommand("useCSS", 0, true);} catch (ex) {}\r
10722                                                 }\r
10723 \r
10724                                                 if (!s.table_inline_editing)\r
10725                                                         try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {}\r
10726 \r
10727                                                 if (!s.object_resizing)\r
10728                                                         try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {}\r
10729                                         }\r
10730                                 };\r
10731 \r
10732                                 t.onBeforeExecCommand.add(setOpts);\r
10733                                 t.onMouseDown.add(setOpts);\r
10734                         }\r
10735 \r
10736                         // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250\r
10737                         // WebKit can't even do simple things like selecting an image\r
10738                         // This also fixes so it's possible to select mceItemAnchors\r
10739                         if (tinymce.isWebKit) {\r
10740                                 t.onClick.add(function(ed, e) {\r
10741                                         e = e.target;\r
10742 \r
10743                                         // Needs tobe the setBaseAndExtend or it will fail to select floated images\r
10744                                         if (e.nodeName == 'IMG' || (e.nodeName == 'A' && dom.hasClass(e, 'mceItemAnchor'))) {\r
10745                                                 t.selection.getSel().setBaseAndExtent(e, 0, e, 1);\r
10746                                                 t.nodeChanged();\r
10747                                         }\r
10748                                 });\r
10749                         }\r
10750 \r
10751                         // Add node change handlers\r
10752                         t.onMouseUp.add(t.nodeChanged);\r
10753                         //t.onClick.add(t.nodeChanged);\r
10754                         t.onKeyUp.add(function(ed, e) {\r
10755                                 var c = e.keyCode;\r
10756 \r
10757                                 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
10758                                         t.nodeChanged();\r
10759                         });\r
10760 \r
10761                         // Add reset handler\r
10762                         t.onReset.add(function() {\r
10763                                 t.setContent(t.startContent, {format : 'raw'});\r
10764                         });\r
10765 \r
10766                         // Add shortcuts\r
10767                         if (s.custom_shortcuts) {\r
10768                                 if (s.custom_undo_redo_keyboard_shortcuts) {\r
10769                                         t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo');\r
10770                                         t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo');\r
10771                                 }\r
10772 \r
10773                                 // Add default shortcuts for gecko\r
10774                                 t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');\r
10775                                 t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');\r
10776                                 t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');\r
10777 \r
10778                                 // BlockFormat shortcuts keys\r
10779                                 for (i=1; i<=6; i++)\r
10780                                         t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);\r
10781 \r
10782                                 t.addShortcut('ctrl+7', '', ['FormatBlock', false, '<p>']);\r
10783                                 t.addShortcut('ctrl+8', '', ['FormatBlock', false, '<div>']);\r
10784                                 t.addShortcut('ctrl+9', '', ['FormatBlock', false, '<address>']);\r
10785 \r
10786                                 function find(e) {\r
10787                                         var v = null;\r
10788 \r
10789                                         if (!e.altKey && !e.ctrlKey && !e.metaKey)\r
10790                                                 return v;\r
10791 \r
10792                                         each(t.shortcuts, function(o) {\r
10793                                                 if (tinymce.isMac && o.ctrl != e.metaKey)\r
10794                                                         return;\r
10795                                                 else if (!tinymce.isMac && o.ctrl != e.ctrlKey)\r
10796                                                         return;\r
10797 \r
10798                                                 if (o.alt != e.altKey)\r
10799                                                         return;\r
10800 \r
10801                                                 if (o.shift != e.shiftKey)\r
10802                                                         return;\r
10803 \r
10804                                                 if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {\r
10805                                                         v = o;\r
10806                                                         return false;\r
10807                                                 }\r
10808                                         });\r
10809 \r
10810                                         return v;\r
10811                                 };\r
10812 \r
10813                                 t.onKeyUp.add(function(ed, e) {\r
10814                                         var o = find(e);\r
10815 \r
10816                                         if (o)\r
10817                                                 return Event.cancel(e);\r
10818                                 });\r
10819 \r
10820                                 t.onKeyPress.add(function(ed, e) {\r
10821                                         var o = find(e);\r
10822 \r
10823                                         if (o)\r
10824                                                 return Event.cancel(e);\r
10825                                 });\r
10826 \r
10827                                 t.onKeyDown.add(function(ed, e) {\r
10828                                         var o = find(e);\r
10829 \r
10830                                         if (o) {\r
10831                                                 o.func.call(o.scope);\r
10832                                                 return Event.cancel(e);\r
10833                                         }\r
10834                                 });\r
10835                         }\r
10836 \r
10837                         if (tinymce.isIE) {\r
10838                                 // Fix so resize will only update the width and height attributes not the styles of an image\r
10839                                 // It will also block mceItemNoResize items\r
10840                                 dom.bind(t.getDoc(), 'controlselect', function(e) {\r
10841                                         var re = t.resizeInfo, cb;\r
10842 \r
10843                                         e = e.target;\r
10844 \r
10845                                         // Don't do this action for non image elements\r
10846                                         if (e.nodeName !== 'IMG')\r
10847                                                 return;\r
10848 \r
10849                                         if (re)\r
10850                                                 dom.unbind(re.node, re.ev, re.cb);\r
10851 \r
10852                                         if (!dom.hasClass(e, 'mceItemNoResize')) {\r
10853                                                 ev = 'resizeend';\r
10854                                                 cb = dom.bind(e, ev, function(e) {\r
10855                                                         var v;\r
10856 \r
10857                                                         e = e.target;\r
10858 \r
10859                                                         if (v = dom.getStyle(e, 'width')) {\r
10860                                                                 dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, ''));\r
10861                                                                 dom.setStyle(e, 'width', '');\r
10862                                                         }\r
10863 \r
10864                                                         if (v = dom.getStyle(e, 'height')) {\r
10865                                                                 dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, ''));\r
10866                                                                 dom.setStyle(e, 'height', '');\r
10867                                                         }\r
10868                                                 });\r
10869                                         } else {\r
10870                                                 ev = 'resizestart';\r
10871                                                 cb = dom.bind(e, 'resizestart', Event.cancel, Event);\r
10872                                         }\r
10873 \r
10874                                         re = t.resizeInfo = {\r
10875                                                 node : e,\r
10876                                                 ev : ev,\r
10877                                                 cb : cb\r
10878                                         };\r
10879                                 });\r
10880 \r
10881                                 t.onKeyDown.add(function(ed, e) {\r
10882                                         switch (e.keyCode) {\r
10883                                                 case 8:\r
10884                                                         // Fix IE control + backspace browser bug\r
10885                                                         if (t.selection.getRng().item) {\r
10886                                                                 ed.dom.remove(t.selection.getRng().item(0));\r
10887                                                                 return Event.cancel(e);\r
10888                                                         }\r
10889                                         }\r
10890                                 });\r
10891 \r
10892                                 /*if (t.dom.boxModel) {\r
10893                                         t.getBody().style.height = '100%';\r
10894 \r
10895                                         Event.add(t.getWin(), 'resize', function(e) {\r
10896                                                 var docElm = t.getDoc().documentElement;\r
10897 \r
10898                                                 docElm.style.height = (docElm.offsetHeight - 10) + 'px';\r
10899                                         });\r
10900                                 }*/\r
10901                         }\r
10902 \r
10903                         if (tinymce.isOpera) {\r
10904                                 t.onClick.add(function(ed, e) {\r
10905                                         Event.prevent(e);\r
10906                                 });\r
10907                         }\r
10908 \r
10909                         // Add custom undo/redo handlers\r
10910                         if (s.custom_undo_redo) {\r
10911                                 function addUndo() {\r
10912                                         t.undoManager.typing = 0;\r
10913                                         t.undoManager.add();\r
10914                                 };\r
10915 \r
10916                                 dom.bind(t.getDoc(), 'focusout', function(e) {\r
10917                                         if (!t.removed && t.undoManager.typing)\r
10918                                                 addUndo();\r
10919                                 });\r
10920 \r
10921                                 t.onKeyUp.add(function(ed, e) {\r
10922                                         if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45 || e.ctrlKey)\r
10923                                                 addUndo();\r
10924                                 });\r
10925 \r
10926                                 t.onKeyDown.add(function(ed, e) {\r
10927                                         var rng, parent, bookmark;\r
10928 \r
10929                                         // IE has a really odd bug where the DOM might include an node that doesn't have\r
10930                                         // a proper structure. If you try to access nodeValue it would throw an illegal value exception.\r
10931                                         // This seems to only happen when you delete contents and it seems to be avoidable if you refresh the element\r
10932                                         // after you delete contents from it. See: #3008923\r
10933                                         if (isIE && e.keyCode == 46) {\r
10934                                                 rng = t.selection.getRng();\r
10935 \r
10936                                                 if (rng.parentElement) {\r
10937                                                         parent = rng.parentElement();\r
10938 \r
10939                                                         // Select next word when ctrl key is used in combo with delete\r
10940                                                         if (e.ctrlKey) {\r
10941                                                                 rng.moveEnd('word', 1);\r
10942                                                                 rng.select();\r
10943                                                         }\r
10944 \r
10945                                                         // Delete contents\r
10946                                                         t.selection.getSel().clear();\r
10947 \r
10948                                                         // Check if we are within the same parent\r
10949                                                         if (rng.parentElement() == parent) {\r
10950                                                                 bookmark = t.selection.getBookmark();\r
10951 \r
10952                                                                 try {\r
10953                                                                         // Update the HTML and hopefully it will remove the artifacts\r
10954                                                                         parent.innerHTML = parent.innerHTML;\r
10955                                                                 } catch (ex) {\r
10956                                                                         // And since it's IE it can sometimes produce an unknown runtime error\r
10957                                                                 }\r
10958 \r
10959                                                                 // Restore the caret position\r
10960                                                                 t.selection.moveToBookmark(bookmark);\r
10961                                                         }\r
10962 \r
10963                                                         // Block the default delete behavior since it might be broken\r
10964                                                         e.preventDefault();\r
10965                                                         return;\r
10966                                                 }\r
10967                                         }\r
10968 \r
10969                                         // Is caracter positon keys\r
10970                                         if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) {\r
10971                                                 if (t.undoManager.typing)\r
10972                                                         addUndo();\r
10973 \r
10974                                                 return;\r
10975                                         }\r
10976 \r
10977                                         if (!t.undoManager.typing) {\r
10978                                                 t.undoManager.add();\r
10979                                                 t.undoManager.typing = 1;\r
10980                                         }\r
10981                                 });\r
10982 \r
10983                                 t.onMouseDown.add(function() {\r
10984                                         if (t.undoManager.typing)\r
10985                                                 addUndo();\r
10986                                 });\r
10987                         }\r
10988                 },\r
10989 \r
10990                 _isHidden : function() {\r
10991                         var s;\r
10992 \r
10993                         if (!isGecko)\r
10994                                 return 0;\r
10995 \r
10996                         // Weird, wheres that cursor selection?\r
10997                         s = this.selection.getSel();\r
10998                         return (!s || !s.rangeCount || s.rangeCount == 0);\r
10999                 },\r
11000 \r
11001                 // Fix for bug #1867292\r
11002                 _fixNesting : function(s) {\r
11003                         var d = [], i;\r
11004 \r
11005                         s = s.replace(/<(\/)?([^\s>]+)[^>]*?>/g, function(a, b, c) {\r
11006                                 var e;\r
11007 \r
11008                                 // Handle end element\r
11009                                 if (b === '/') {\r
11010                                         if (!d.length)\r
11011                                                 return '';\r
11012 \r
11013                                         if (c !== d[d.length - 1].tag) {\r
11014                                                 for (i=d.length - 1; i>=0; i--) {\r
11015                                                         if (d[i].tag === c) {\r
11016                                                                 d[i].close = 1;\r
11017                                                                 break;\r
11018                                                         }\r
11019                                                 }\r
11020 \r
11021                                                 return '';\r
11022                                         } else {\r
11023                                                 d.pop();\r
11024 \r
11025                                                 if (d.length && d[d.length - 1].close) {\r
11026                                                         a = a + '</' + d[d.length - 1].tag + '>';\r
11027                                                         d.pop();\r
11028                                                 }\r
11029                                         }\r
11030                                 } else {\r
11031                                         // Ignore these\r
11032                                         if (/^(br|hr|input|meta|img|link|param)$/i.test(c))\r
11033                                                 return a;\r
11034 \r
11035                                         // Ignore closed ones\r
11036                                         if (/\/>$/.test(a))\r
11037                                                 return a;\r
11038 \r
11039                                         d.push({tag : c}); // Push start element\r
11040                                 }\r
11041 \r
11042                                 return a;\r
11043                         });\r
11044 \r
11045                         // End all open tags\r
11046                         for (i=d.length - 1; i>=0; i--)\r
11047                                 s += '</' + d[i].tag + '>';\r
11048 \r
11049                         return s;\r
11050                 }\r
11051         });\r
11052 })(tinymce);\r
11053 \r
11054 (function(tinymce) {\r
11055         // Added for compression purposes\r
11056         var each = tinymce.each, undefined, TRUE = true, FALSE = false;\r
11057 \r
11058         tinymce.EditorCommands = function(editor) {\r
11059                 var dom = editor.dom,\r
11060                         selection = editor.selection,\r
11061                         commands = {state: {}, exec : {}, value : {}},\r
11062                         settings = editor.settings,\r
11063                         bookmark;\r
11064 \r
11065                 function execCommand(command, ui, value) {\r
11066                         var func;\r
11067 \r
11068                         command = command.toLowerCase();\r
11069                         if (func = commands.exec[command]) {\r
11070                                 func(command, ui, value);\r
11071                                 return TRUE;\r
11072                         }\r
11073 \r
11074                         return FALSE;\r
11075                 };\r
11076 \r
11077                 function queryCommandState(command) {\r
11078                         var func;\r
11079 \r
11080                         command = command.toLowerCase();\r
11081                         if (func = commands.state[command])\r
11082                                 return func(command);\r
11083 \r
11084                         return -1;\r
11085                 };\r
11086 \r
11087                 function queryCommandValue(command) {\r
11088                         var func;\r
11089 \r
11090                         command = command.toLowerCase();\r
11091                         if (func = commands.value[command])\r
11092                                 return func(command);\r
11093 \r
11094                         return FALSE;\r
11095                 };\r
11096 \r
11097                 function addCommands(command_list, type) {\r
11098                         type = type || 'exec';\r
11099 \r
11100                         each(command_list, function(callback, command) {\r
11101                                 each(command.toLowerCase().split(','), function(command) {\r
11102                                         commands[type][command] = callback;\r
11103                                 });\r
11104                         });\r
11105                 };\r
11106 \r
11107                 // Expose public methods\r
11108                 tinymce.extend(this, {\r
11109                         execCommand : execCommand,\r
11110                         queryCommandState : queryCommandState,\r
11111                         queryCommandValue : queryCommandValue,\r
11112                         addCommands : addCommands\r
11113                 });\r
11114 \r
11115                 // Private methods\r
11116 \r
11117                 function execNativeCommand(command, ui, value) {\r
11118                         if (ui === undefined)\r
11119                                 ui = FALSE;\r
11120 \r
11121                         if (value === undefined)\r
11122                                 value = null;\r
11123 \r
11124                         return editor.getDoc().execCommand(command, ui, value);\r
11125                 };\r
11126 \r
11127                 function isFormatMatch(name) {\r
11128                         return editor.formatter.match(name);\r
11129                 };\r
11130 \r
11131                 function toggleFormat(name, value) {\r
11132                         editor.formatter.toggle(name, value ? {value : value} : undefined);\r
11133                 };\r
11134 \r
11135                 function storeSelection(type) {\r
11136                         bookmark = selection.getBookmark(type);\r
11137                 };\r
11138 \r
11139                 function restoreSelection() {\r
11140                         selection.moveToBookmark(bookmark);\r
11141                 };\r
11142 \r
11143                 // Add execCommand overrides\r
11144                 addCommands({\r
11145                         // Ignore these, added for compatibility\r
11146                         'mceResetDesignMode,mceBeginUndoLevel' : function() {},\r
11147 \r
11148                         // Add undo manager logic\r
11149                         'mceEndUndoLevel,mceAddUndoLevel' : function() {\r
11150                                 editor.undoManager.add();\r
11151                         },\r
11152 \r
11153                         'Cut,Copy,Paste' : function(command) {\r
11154                                 var doc = editor.getDoc(), failed;\r
11155 \r
11156                                 // Try executing the native command\r
11157                                 try {\r
11158                                         execNativeCommand(command);\r
11159                                 } catch (ex) {\r
11160                                         // Command failed\r
11161                                         failed = TRUE;\r
11162                                 }\r
11163 \r
11164                                 // Present alert message about clipboard access not being available\r
11165                                 if (failed || !doc.queryCommandSupported(command)) {\r
11166                                         if (tinymce.isGecko) {\r
11167                                                 editor.windowManager.confirm(editor.getLang('clipboard_msg'), function(state) {\r
11168                                                         if (state)\r
11169                                                                 open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', '_blank');\r
11170                                                 });\r
11171                                         } else\r
11172                                                 editor.windowManager.alert(editor.getLang('clipboard_no_support'));\r
11173                                 }\r
11174                         },\r
11175 \r
11176                         // Override unlink command\r
11177                         unlink : function(command) {\r
11178                                 if (selection.isCollapsed())\r
11179                                         selection.select(selection.getNode());\r
11180 \r
11181                                 execNativeCommand(command);\r
11182                                 selection.collapse(FALSE);\r
11183                         },\r
11184 \r
11185                         // Override justify commands to use the text formatter engine\r
11186                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
11187                                 var align = command.substring(7);\r
11188 \r
11189                                 // Remove all other alignments first\r
11190                                 each('left,center,right,full'.split(','), function(name) {\r
11191                                         if (align != name)\r
11192                                                 editor.formatter.remove('align' + name);\r
11193                                 });\r
11194 \r
11195                                 toggleFormat('align' + align);\r
11196                         },\r
11197 \r
11198                         // Override list commands to fix WebKit bug\r
11199                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
11200                                 var listElm, listParent;\r
11201 \r
11202                                 execNativeCommand(command);\r
11203 \r
11204                                 // WebKit produces lists within block elements so we need to split them\r
11205                                 // we will replace the native list creation logic to custom logic later on\r
11206                                 // TODO: Remove this when the list creation logic is removed\r
11207                                 listElm = dom.getParent(selection.getNode(), 'ol,ul');\r
11208                                 if (listElm) {\r
11209                                         listParent = listElm.parentNode;\r
11210 \r
11211                                         // If list is within a text block then split that block\r
11212                                         if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {\r
11213                                                 storeSelection();\r
11214                                                 dom.split(listParent, listElm);\r
11215                                                 restoreSelection();\r
11216                                         }\r
11217                                 }\r
11218                         },\r
11219 \r
11220                         // Override commands to use the text formatter engine\r
11221                         'Bold,Italic,Underline,Strikethrough' : function(command) {\r
11222                                 toggleFormat(command);\r
11223                         },\r
11224 \r
11225                         // Override commands to use the text formatter engine\r
11226                         'ForeColor,HiliteColor,FontName' : function(command, ui, value) {\r
11227                                 toggleFormat(command, value);\r
11228                         },\r
11229 \r
11230                         FontSize : function(command, ui, value) {\r
11231                                 var fontClasses, fontSizes;\r
11232 \r
11233                                 // Convert font size 1-7 to styles\r
11234                                 if (value >= 1 && value <= 7) {\r
11235                                         fontSizes = tinymce.explode(settings.font_size_style_values);\r
11236                                         fontClasses = tinymce.explode(settings.font_size_classes);\r
11237 \r
11238                                         if (fontClasses)\r
11239                                                 value = fontClasses[value - 1] || value;\r
11240                                         else\r
11241                                                 value = fontSizes[value - 1] || value;\r
11242                                 }\r
11243 \r
11244                                 toggleFormat(command, value);\r
11245                         },\r
11246 \r
11247                         RemoveFormat : function(command) {\r
11248                                 editor.formatter.remove(command);\r
11249                         },\r
11250 \r
11251                         mceBlockQuote : function(command) {\r
11252                                 toggleFormat('blockquote');\r
11253                         },\r
11254 \r
11255                         FormatBlock : function(command, ui, value) {\r
11256                                 return toggleFormat(value || 'p');\r
11257                         },\r
11258 \r
11259                         mceCleanup : function() {\r
11260                                 var bookmark = selection.getBookmark();\r
11261 \r
11262                                 editor.setContent(editor.getContent({cleanup : TRUE}), {cleanup : TRUE});\r
11263 \r
11264                                 selection.moveToBookmark(bookmark);\r
11265                         },\r
11266 \r
11267                         mceRemoveNode : function(command, ui, value) {\r
11268                                 var node = value || selection.getNode();\r
11269 \r
11270                                 // Make sure that the body node isn't removed\r
11271                                 if (node != editor.getBody()) {\r
11272                                         storeSelection();\r
11273                                         editor.dom.remove(node, TRUE);\r
11274                                         restoreSelection();\r
11275                                 }\r
11276                         },\r
11277 \r
11278                         mceSelectNodeDepth : function(command, ui, value) {\r
11279                                 var counter = 0;\r
11280 \r
11281                                 dom.getParent(selection.getNode(), function(node) {\r
11282                                         if (node.nodeType == 1 && counter++ == value) {\r
11283                                                 selection.select(node);\r
11284                                                 return FALSE;\r
11285                                         }\r
11286                                 }, editor.getBody());\r
11287                         },\r
11288 \r
11289                         mceSelectNode : function(command, ui, value) {\r
11290                                 selection.select(value);\r
11291                         },\r
11292 \r
11293                         mceInsertContent : function(command, ui, value) {\r
11294                                 selection.setContent(value);\r
11295                         },\r
11296 \r
11297                         mceInsertRawHTML : function(command, ui, value) {\r
11298                                 selection.setContent('tiny_mce_marker');\r
11299                                 editor.setContent(editor.getContent().replace(/tiny_mce_marker/g, function() { return value }));\r
11300                         },\r
11301 \r
11302                         mceSetContent : function(command, ui, value) {\r
11303                                 editor.setContent(value);\r
11304                         },\r
11305 \r
11306                         'Indent,Outdent' : function(command) {\r
11307                                 var intentValue, indentUnit, value;\r
11308 \r
11309                                 // Setup indent level\r
11310                                 intentValue = settings.indentation;\r
11311                                 indentUnit = /[a-z%]+$/i.exec(intentValue);\r
11312                                 intentValue = parseInt(intentValue);\r
11313 \r
11314                                 if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) {\r
11315                                         each(selection.getSelectedBlocks(), function(element) {\r
11316                                                 if (command == 'outdent') {\r
11317                                                         value = Math.max(0, parseInt(element.style.paddingLeft || 0) - intentValue);\r
11318                                                         dom.setStyle(element, 'paddingLeft', value ? value + indentUnit : '');\r
11319                                                 } else\r
11320                                                         dom.setStyle(element, 'paddingLeft', (parseInt(element.style.paddingLeft || 0) + intentValue) + indentUnit);\r
11321                                         });\r
11322                                 } else\r
11323                                         execNativeCommand(command);\r
11324                         },\r
11325 \r
11326                         mceRepaint : function() {\r
11327                                 var bookmark;\r
11328 \r
11329                                 if (tinymce.isGecko) {\r
11330                                         try {\r
11331                                                 storeSelection(TRUE);\r
11332 \r
11333                                                 if (selection.getSel())\r
11334                                                         selection.getSel().selectAllChildren(editor.getBody());\r
11335 \r
11336                                                 selection.collapse(TRUE);\r
11337                                                 restoreSelection();\r
11338                                         } catch (ex) {\r
11339                                                 // Ignore\r
11340                                         }\r
11341                                 }\r
11342                         },\r
11343 \r
11344                         mceToggleFormat : function(command, ui, value) {\r
11345                                 editor.formatter.toggle(value);\r
11346                         },\r
11347 \r
11348                         InsertHorizontalRule : function() {\r
11349                                 selection.setContent('<hr />');\r
11350                         },\r
11351 \r
11352                         mceToggleVisualAid : function() {\r
11353                                 editor.hasVisual = !editor.hasVisual;\r
11354                                 editor.addVisual();\r
11355                         },\r
11356 \r
11357                         mceReplaceContent : function(command, ui, value) {\r
11358                                 selection.setContent(value.replace(/\{\$selection\}/g, selection.getContent({format : 'text'})));\r
11359                         },\r
11360 \r
11361                         mceInsertLink : function(command, ui, value) {\r
11362                                 var link = dom.getParent(selection.getNode(), 'a');\r
11363 \r
11364                                 if (tinymce.is(value, 'string'))\r
11365                                         value = {href : value};\r
11366 \r
11367                                 if (!link) {\r
11368                                         execNativeCommand('CreateLink', FALSE, 'javascript:mctmp(0);');\r
11369                                         each(dom.select('a[href=javascript:mctmp(0);]'), function(link) {\r
11370                                                 dom.setAttribs(link, value);\r
11371                                         });\r
11372                                 } else {\r
11373                                         if (value.href)\r
11374                                                 dom.setAttribs(link, value);\r
11375                                         else\r
11376                                                 editor.dom.remove(link, TRUE);\r
11377                                 }\r
11378                         },\r
11379                         \r
11380                         selectAll : function() {\r
11381                                 var root = dom.getRoot(), rng = dom.createRng();\r
11382 \r
11383                                 rng.setStart(root, 0);\r
11384                                 rng.setEnd(root, root.childNodes.length);\r
11385 \r
11386                                 editor.selection.setRng(rng);\r
11387                         }\r
11388                 });\r
11389 \r
11390                 // Add queryCommandState overrides\r
11391                 addCommands({\r
11392                         // Override justify commands\r
11393                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
11394                                 return isFormatMatch('align' + command.substring(7));\r
11395                         },\r
11396 \r
11397                         'Bold,Italic,Underline,Strikethrough' : function(command) {\r
11398                                 return isFormatMatch(command);\r
11399                         },\r
11400 \r
11401                         mceBlockQuote : function() {\r
11402                                 return isFormatMatch('blockquote');\r
11403                         },\r
11404 \r
11405                         Outdent : function() {\r
11406                                 var node;\r
11407 \r
11408                                 if (settings.inline_styles) {\r
11409                                         if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
11410                                                 return TRUE;\r
11411 \r
11412                                         if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
11413                                                 return TRUE;\r
11414                                 }\r
11415 \r
11416                                 return queryCommandState('InsertUnorderedList') || queryCommandState('InsertOrderedList') || (!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE'));\r
11417                         },\r
11418 \r
11419                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
11420                                 return dom.getParent(selection.getNode(), command == 'insertunorderedlist' ? 'UL' : 'OL');\r
11421                         }\r
11422                 }, 'state');\r
11423 \r
11424                 // Add queryCommandValue overrides\r
11425                 addCommands({\r
11426                         'FontSize,FontName' : function(command) {\r
11427                                 var value = 0, parent;\r
11428 \r
11429                                 if (parent = dom.getParent(selection.getNode(), 'span')) {\r
11430                                         if (command == 'fontsize')\r
11431                                                 value = parent.style.fontSize;\r
11432                                         else\r
11433                                                 value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();\r
11434                                 }\r
11435 \r
11436                                 return value;\r
11437                         }\r
11438                 }, 'value');\r
11439 \r
11440                 // Add undo manager logic\r
11441                 if (settings.custom_undo_redo) {\r
11442                         addCommands({\r
11443                                 Undo : function() {\r
11444                                         editor.undoManager.undo();\r
11445                                 },\r
11446 \r
11447                                 Redo : function() {\r
11448                                         editor.undoManager.redo();\r
11449                                 }\r
11450                         });\r
11451                 }\r
11452         };\r
11453 })(tinymce);\r
11454 (function(tinymce) {\r
11455         var Dispatcher = tinymce.util.Dispatcher;\r
11456 \r
11457         tinymce.UndoManager = function(editor) {\r
11458                 var self, index = 0, data = [];\r
11459 \r
11460                 function getContent() {\r
11461                         return tinymce.trim(editor.getContent({format : 'raw', no_events : 1}));\r
11462                 };\r
11463 \r
11464                 return self = {\r
11465                         typing : 0,\r
11466 \r
11467                         onAdd : new Dispatcher(self),\r
11468                         onUndo : new Dispatcher(self),\r
11469                         onRedo : new Dispatcher(self),\r
11470 \r
11471                         add : function(level) {\r
11472                                 var i, settings = editor.settings, lastLevel;\r
11473 \r
11474                                 level = level || {};\r
11475                                 level.content = getContent();\r
11476 \r
11477                                 // Add undo level if needed\r
11478                                 lastLevel = data[index];\r
11479                                 if (lastLevel && lastLevel.content == level.content) {\r
11480                                         if (index > 0 || data.length == 1)\r
11481                                                 return null;\r
11482                                 }\r
11483 \r
11484                                 // Time to compress\r
11485                                 if (settings.custom_undo_redo_levels) {\r
11486                                         if (data.length > settings.custom_undo_redo_levels) {\r
11487                                                 for (i = 0; i < data.length - 1; i++)\r
11488                                                         data[i] = data[i + 1];\r
11489 \r
11490                                                 data.length--;\r
11491                                                 index = data.length;\r
11492                                         }\r
11493                                 }\r
11494 \r
11495                                 // Get a non intrusive normalized bookmark\r
11496                                 level.bookmark = editor.selection.getBookmark(2, true);\r
11497 \r
11498                                 // Crop array if needed\r
11499                                 if (index < data.length - 1) {\r
11500                                         // Treat first level as initial\r
11501                                         if (index == 0)\r
11502                                                 data = [];\r
11503                                         else\r
11504                                                 data.length = index + 1;\r
11505                                 }\r
11506 \r
11507                                 data.push(level);\r
11508                                 index = data.length - 1;\r
11509 \r
11510                                 self.onAdd.dispatch(self, level);\r
11511                                 editor.isNotDirty = 0;\r
11512 \r
11513                                 return level;\r
11514                         },\r
11515 \r
11516                         undo : function() {\r
11517                                 var level, i;\r
11518 \r
11519                                 if (self.typing) {\r
11520                                         self.add();\r
11521                                         self.typing = 0;\r
11522                                 }\r
11523 \r
11524                                 if (index > 0) {\r
11525                                         level = data[--index];\r
11526 \r
11527                                         editor.setContent(level.content, {format : 'raw'});\r
11528                                         editor.selection.moveToBookmark(level.bookmark);\r
11529 \r
11530                                         self.onUndo.dispatch(self, level);\r
11531                                 }\r
11532 \r
11533                                 return level;\r
11534                         },\r
11535 \r
11536                         redo : function() {\r
11537                                 var level;\r
11538 \r
11539                                 if (index < data.length - 1) {\r
11540                                         level = data[++index];\r
11541 \r
11542                                         editor.setContent(level.content, {format : 'raw'});\r
11543                                         editor.selection.moveToBookmark(level.bookmark);\r
11544 \r
11545                                         self.onRedo.dispatch(self, level);\r
11546                                 }\r
11547 \r
11548                                 return level;\r
11549                         },\r
11550 \r
11551                         clear : function() {\r
11552                                 data = [];\r
11553                                 index = self.typing = 0;\r
11554                         },\r
11555 \r
11556                         hasUndo : function() {\r
11557                                 return index > 0 || self.typing;\r
11558                         },\r
11559 \r
11560                         hasRedo : function() {\r
11561                                 return index < data.length - 1;\r
11562                         }\r
11563                 };\r
11564         };\r
11565 })(tinymce);\r
11566 \r
11567 (function(tinymce) {\r
11568         // Shorten names\r
11569         var Event = tinymce.dom.Event,\r
11570                 isIE = tinymce.isIE,\r
11571                 isGecko = tinymce.isGecko,\r
11572                 isOpera = tinymce.isOpera,\r
11573                 each = tinymce.each,\r
11574                 extend = tinymce.extend,\r
11575                 TRUE = true,\r
11576                 FALSE = false;\r
11577 \r
11578         function cloneFormats(node) {\r
11579                 var clone, temp, inner;\r
11580 \r
11581                 do {\r
11582                         if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) {\r
11583                                 if (clone) {\r
11584                                         temp = node.cloneNode(false);\r
11585                                         temp.appendChild(clone);\r
11586                                         clone = temp;\r
11587                                 } else {\r
11588                                         clone = inner = node.cloneNode(false);\r
11589                                 }\r
11590 \r
11591                                 clone.removeAttribute('id');\r
11592                         }\r
11593                 } while (node = node.parentNode);\r
11594 \r
11595                 if (clone)\r
11596                         return {wrapper : clone, inner : inner};\r
11597         };\r
11598 \r
11599         // Checks if the selection/caret is at the end of the specified block element\r
11600         function isAtEnd(rng, par) {\r
11601                 var rng2 = par.ownerDocument.createRange();\r
11602 \r
11603                 rng2.setStart(rng.endContainer, rng.endOffset);\r
11604                 rng2.setEndAfter(par);\r
11605 \r
11606                 // 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
11607                 return rng2.cloneContents().textContent.length == 0;\r
11608         };\r
11609 \r
11610         function isEmpty(n) {\r
11611                 n = n.innerHTML;\r
11612 \r
11613                 n = n.replace(/<(img|hr|table|input|select|textarea)[ \>]/gi, '-'); // Keep these convert them to - chars\r
11614                 n = n.replace(/<[^>]+>/g, ''); // Remove all tags\r
11615 \r
11616                 return n.replace(/[ \u00a0\t\r\n]+/g, '') == '';\r
11617         };\r
11618 \r
11619         function splitList(selection, dom, li) {\r
11620                 var listBlock, block;\r
11621 \r
11622                 if (isEmpty(li)) {\r
11623                         listBlock = dom.getParent(li, 'ul,ol');\r
11624 \r
11625                         if (!dom.getParent(listBlock.parentNode, 'ul,ol')) {\r
11626                                 dom.split(listBlock, li);\r
11627                                 block = dom.create('p', 0, '<br _mce_bogus="1" />');\r
11628                                 dom.replace(block, li);\r
11629                                 selection.select(block, 1);\r
11630                         }\r
11631 \r
11632                         return FALSE;\r
11633                 }\r
11634 \r
11635                 return TRUE;\r
11636         };\r
11637 \r
11638         tinymce.create('tinymce.ForceBlocks', {\r
11639                 ForceBlocks : function(ed) {\r
11640                         var t = this, s = ed.settings, elm;\r
11641 \r
11642                         t.editor = ed;\r
11643                         t.dom = ed.dom;\r
11644                         elm = (s.forced_root_block || 'p').toLowerCase();\r
11645                         s.element = elm.toUpperCase();\r
11646 \r
11647                         ed.onPreInit.add(t.setup, t);\r
11648 \r
11649                         t.reOpera = new RegExp('(\\u00a0|&#160;|&nbsp;)<\/' + elm + '>', 'gi');\r
11650                         t.rePadd = new RegExp('<p( )([^>]+)><\\\/p>|<p( )([^>]+)\\\/>|<p( )([^>]+)>\\s+<\\\/p>|<p><\\\/p>|<p\\\/>|<p>\\s+<\\\/p>'.replace(/p/g, elm), 'gi');\r
11651                         t.reNbsp2BR1 = new RegExp('<p( )([^>]+)>[\\s\\u00a0]+<\\\/p>|<p>[\\s\\u00a0]+<\\\/p>'.replace(/p/g, elm), 'gi');\r
11652                         t.reNbsp2BR2 = new RegExp('<%p()([^>]+)>(&nbsp;|&#160;)<\\\/%p>|<%p>(&nbsp;|&#160;)<\\\/%p>'.replace(/%p/g, elm), 'gi');\r
11653                         t.reBR2Nbsp = new RegExp('<p( )([^>]+)>\\s*<br \\\/>\\s*<\\\/p>|<p>\\s*<br \\\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi');\r
11654 \r
11655                         function padd(ed, o) {\r
11656                                 if (isOpera)\r
11657                                         o.content = o.content.replace(t.reOpera, '</' + elm + '>');\r
11658 \r
11659                                 o.content = tinymce._replace(t.rePadd, '<' + elm + '$1$2$3$4$5$6>\u00a0</' + elm + '>', o.content);\r
11660 \r
11661                                 if (!isIE && !isOpera && o.set) {\r
11662                                         // Use &nbsp; instead of BR in padded paragraphs\r
11663                                         o.content = o.content.replace(t.reNbsp2BR1, '<' + elm + '$1$2><br /></' + elm + '>');\r
11664                                         o.content = o.content.replace(t.reNbsp2BR2, '<' + elm + '$1$2><br /></' + elm + '>');\r
11665                                 } else\r
11666                                         o.content = tinymce._replace(t.reBR2Nbsp, '<' + elm + '$1$2>\u00a0</' + elm + '>', o.content);\r
11667                         };\r
11668 \r
11669                         ed.onBeforeSetContent.add(padd);\r
11670                         ed.onPostProcess.add(padd);\r
11671 \r
11672                         if (s.forced_root_block) {\r
11673                                 ed.onInit.add(t.forceRoots, t);\r
11674                                 ed.onSetContent.add(t.forceRoots, t);\r
11675                                 ed.onBeforeGetContent.add(t.forceRoots, t);\r
11676                         }\r
11677                 },\r
11678 \r
11679                 setup : function() {\r
11680                         var t = this, ed = t.editor, s = ed.settings, dom = ed.dom, selection = ed.selection;\r
11681 \r
11682                         // Force root blocks when typing and when getting output\r
11683                         if (s.forced_root_block) {\r
11684                                 ed.onBeforeExecCommand.add(t.forceRoots, t);\r
11685                                 ed.onKeyUp.add(t.forceRoots, t);\r
11686                                 ed.onPreProcess.add(t.forceRoots, t);\r
11687                         }\r
11688 \r
11689                         if (s.force_br_newlines) {\r
11690                                 // Force IE to produce BRs on enter\r
11691                                 if (isIE) {\r
11692                                         ed.onKeyPress.add(function(ed, e) {\r
11693                                                 var n;\r
11694 \r
11695                                                 if (e.keyCode == 13 && selection.getNode().nodeName != 'LI') {\r
11696                                                         selection.setContent('<br id="__" /> ', {format : 'raw'});\r
11697                                                         n = dom.get('__');\r
11698                                                         n.removeAttribute('id');\r
11699                                                         selection.select(n);\r
11700                                                         selection.collapse();\r
11701                                                         return Event.cancel(e);\r
11702                                                 }\r
11703                                         });\r
11704                                 }\r
11705                         }\r
11706 \r
11707                         if (s.force_p_newlines) {\r
11708                                 if (!isIE) {\r
11709                                         ed.onKeyPress.add(function(ed, e) {\r
11710                                                 if (e.keyCode == 13 && !e.shiftKey && !t.insertPara(e))\r
11711                                                         Event.cancel(e);\r
11712                                         });\r
11713                                 } else {\r
11714                                         // Ungly hack to for IE to preserve the formatting when you press\r
11715                                         // enter at the end of a block element with formatted contents\r
11716                                         // This logic overrides the browsers default logic with\r
11717                                         // custom logic that enables us to control the output\r
11718                                         tinymce.addUnload(function() {\r
11719                                                 t._previousFormats = 0; // Fix IE leak\r
11720                                         });\r
11721 \r
11722                                         ed.onKeyPress.add(function(ed, e) {\r
11723                                                 t._previousFormats = 0;\r
11724 \r
11725                                                 // Clone the current formats, this will later be applied to the new block contents\r
11726                                                 if (e.keyCode == 13 && !e.shiftKey && ed.selection.isCollapsed() && s.keep_styles)\r
11727                                                         t._previousFormats = cloneFormats(ed.selection.getStart());\r
11728                                         });\r
11729 \r
11730                                         ed.onKeyUp.add(function(ed, e) {\r
11731                                                 // Let IE break the element and the wrap the new caret location in the previous formats\r
11732                                                 if (e.keyCode == 13 && !e.shiftKey) {\r
11733                                                         var parent = ed.selection.getStart(), fmt = t._previousFormats;\r
11734 \r
11735                                                         // Parent is an empty block\r
11736                                                         if (!parent.hasChildNodes() && fmt) {\r
11737                                                                 parent = dom.getParent(parent, dom.isBlock);\r
11738 \r
11739                                                                 if (parent && parent.nodeName != 'LI') {\r
11740                                                                         parent.innerHTML = '';\r
11741 \r
11742                                                                         if (t._previousFormats) {\r
11743                                                                                 parent.appendChild(fmt.wrapper);\r
11744                                                                                 fmt.inner.innerHTML = '\uFEFF';\r
11745                                                                         } else\r
11746                                                                                 parent.innerHTML = '\uFEFF';\r
11747 \r
11748                                                                         selection.select(parent, 1);\r
11749                                                                         ed.getDoc().execCommand('Delete', false, null);\r
11750                                                                         t._previousFormats = 0;\r
11751                                                                 }\r
11752                                                         }\r
11753                                                 }\r
11754                                         });\r
11755                                 }\r
11756 \r
11757                                 if (isGecko) {\r
11758                                         ed.onKeyDown.add(function(ed, e) {\r
11759                                                 if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey)\r
11760                                                         t.backspaceDelete(e, e.keyCode == 8);\r
11761                                         });\r
11762                                 }\r
11763                         }\r
11764 \r
11765                         // Workaround for missing shift+enter support, http://bugs.webkit.org/show_bug.cgi?id=16973\r
11766                         if (tinymce.isWebKit) {\r
11767                                 function insertBr(ed) {\r
11768                                         var rng = selection.getRng(), br, div = dom.create('div', null, ' '), divYPos, vpHeight = dom.getViewPort(ed.getWin()).h;\r
11769 \r
11770                                         // Insert BR element\r
11771                                         rng.insertNode(br = dom.create('br'));\r
11772 \r
11773                                         // Place caret after BR\r
11774                                         rng.setStartAfter(br);\r
11775                                         rng.setEndAfter(br);\r
11776                                         selection.setRng(rng);\r
11777 \r
11778                                         // Could not place caret after BR then insert an nbsp entity and move the caret\r
11779                                         if (selection.getSel().focusNode == br.previousSibling) {\r
11780                                                 selection.select(dom.insertAfter(dom.doc.createTextNode('\u00a0'), br));\r
11781                                                 selection.collapse(TRUE);\r
11782                                         }\r
11783 \r
11784                                         // Create a temporary DIV after the BR and get the position as it\r
11785                                         // seems like getPos() returns 0 for text nodes and BR elements.\r
11786                                         dom.insertAfter(div, br);\r
11787                                         divYPos = dom.getPos(div).y;\r
11788                                         dom.remove(div);\r
11789 \r
11790                                         // Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117\r
11791                                         if (divYPos > vpHeight) // It is not necessary to scroll if the DIV is inside the view port.\r
11792                                                 ed.getWin().scrollTo(0, divYPos);\r
11793                                 };\r
11794 \r
11795                                 ed.onKeyPress.add(function(ed, e) {\r
11796                                         if (e.keyCode == 13 && (e.shiftKey || (s.force_br_newlines && !dom.getParent(selection.getNode(), 'h1,h2,h3,h4,h5,h6,ol,ul')))) {\r
11797                                                 insertBr(ed);\r
11798                                                 Event.cancel(e);\r
11799                                         }\r
11800                                 });\r
11801                         }\r
11802 \r
11803                         // Padd empty inline elements within block elements\r
11804                         // For example: <p><strong><em></em></strong></p> becomes <p><strong><em>&nbsp;</em></strong></p>\r
11805                         ed.onPreProcess.add(function(ed, o) {\r
11806                                 each(dom.select('p,h1,h2,h3,h4,h5,h6,div', o.node), function(p) {\r
11807                                         if (isEmpty(p)) {\r
11808                                                 each(dom.select('span,em,strong,b,i', o.node), function(n) {\r
11809                                                         if (!n.hasChildNodes()) {\r
11810                                                                 n.appendChild(ed.getDoc().createTextNode('\u00a0'));\r
11811                                                                 return FALSE; // Break the loop one padding is enough\r
11812                                                         }\r
11813                                                 });\r
11814                                         }\r
11815                                 });\r
11816                         });\r
11817 \r
11818                         // IE specific fixes\r
11819                         if (isIE) {\r
11820                                 // Replaces IE:s auto generated paragraphs with the specified element name\r
11821                                 if (s.element != 'P') {\r
11822                                         ed.onKeyPress.add(function(ed, e) {\r
11823                                                 t.lastElm = selection.getNode().nodeName;\r
11824                                         });\r
11825 \r
11826                                         ed.onKeyUp.add(function(ed, e) {\r
11827                                                 var bl, n = selection.getNode(), b = ed.getBody();\r
11828 \r
11829                                                 if (b.childNodes.length === 1 && n.nodeName == 'P') {\r
11830                                                         n = dom.rename(n, s.element);\r
11831                                                         selection.select(n);\r
11832                                                         selection.collapse();\r
11833                                                         ed.nodeChanged();\r
11834                                                 } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') {\r
11835                                                         bl = dom.getParent(n, 'p');\r
11836 \r
11837                                                         if (bl) {\r
11838                                                                 dom.rename(bl, s.element);\r
11839                                                                 ed.nodeChanged();\r
11840                                                         }\r
11841                                                 }\r
11842                                         });\r
11843                                 }\r
11844                         }\r
11845                 },\r
11846 \r
11847                 find : function(n, t, s) {\r
11848                         var ed = this.editor, w = ed.getDoc().createTreeWalker(n, 4, null, FALSE), c = -1;\r
11849 \r
11850                         while (n = w.nextNode()) {\r
11851                                 c++;\r
11852 \r
11853                                 // Index by node\r
11854                                 if (t == 0 && n == s)\r
11855                                         return c;\r
11856 \r
11857                                 // Node by index\r
11858                                 if (t == 1 && c == s)\r
11859                                         return n;\r
11860                         }\r
11861 \r
11862                         return -1;\r
11863                 },\r
11864 \r
11865                 forceRoots : function(ed, e) {\r
11866                         var t = this, ed = t.editor, b = ed.getBody(), d = ed.getDoc(), se = ed.selection, s = se.getSel(), r = se.getRng(), si = -2, ei, so, eo, tr, c = -0xFFFFFF;\r
11867                         var nx, bl, bp, sp, le, nl = b.childNodes, i, n, eid;\r
11868 \r
11869                         // Fix for bug #1863847\r
11870                         //if (e && e.keyCode == 13)\r
11871                         //      return TRUE;\r
11872 \r
11873                         // Wrap non blocks into blocks\r
11874                         for (i = nl.length - 1; i >= 0; i--) {\r
11875                                 nx = nl[i];\r
11876 \r
11877                                 // Ignore internal elements\r
11878                                 if (nx.nodeType === 1 && nx.getAttribute('_mce_type')) {\r
11879                                         bl = null;\r
11880                                         continue;\r
11881                                 }\r
11882 \r
11883                                 // Is text or non block element\r
11884                                 if (nx.nodeType === 3 || (!t.dom.isBlock(nx) && nx.nodeType !== 8 && !/^(script|mce:script|style|mce:style)$/i.test(nx.nodeName))) {\r
11885                                         if (!bl) {\r
11886                                                 // Create new block but ignore whitespace\r
11887                                                 if (nx.nodeType != 3 || /[^\s]/g.test(nx.nodeValue)) {\r
11888                                                         // Store selection\r
11889                                                         if (si == -2 && r) {\r
11890                                                                 if (!isIE || r.setStart) {\r
11891                                                                         // If selection is element then mark it\r
11892                                                                         if (r.startContainer.nodeType == 1 && (n = r.startContainer.childNodes[r.startOffset]) && n.nodeType == 1) {\r
11893                                                                                 // Save the id of the selected element\r
11894                                                                                 eid = n.getAttribute("id");\r
11895                                                                                 n.setAttribute("id", "__mce");\r
11896                                                                         } else {\r
11897                                                                                 // If element is inside body, might not be the case in contentEdiable mode\r
11898                                                                                 if (ed.dom.getParent(r.startContainer, function(e) {return e === b;})) {\r
11899                                                                                         so = r.startOffset;\r
11900                                                                                         eo = r.endOffset;\r
11901                                                                                         si = t.find(b, 0, r.startContainer);\r
11902                                                                                         ei = t.find(b, 0, r.endContainer);\r
11903                                                                                 }\r
11904                                                                         }\r
11905                                                                 } else {\r
11906                                                                         // Force control range into text range\r
11907                                                                         if (r.item) {\r
11908                                                                                 tr = d.body.createTextRange();\r
11909                                                                                 tr.moveToElementText(r.item(0));\r
11910                                                                                 r = tr;\r
11911                                                                         }\r
11912 \r
11913                                                                         tr = d.body.createTextRange();\r
11914                                                                         tr.moveToElementText(b);\r
11915                                                                         tr.collapse(1);\r
11916                                                                         bp = tr.move('character', c) * -1;\r
11917 \r
11918                                                                         tr = r.duplicate();\r
11919                                                                         tr.collapse(1);\r
11920                                                                         sp = tr.move('character', c) * -1;\r
11921 \r
11922                                                                         tr = r.duplicate();\r
11923                                                                         tr.collapse(0);\r
11924                                                                         le = (tr.move('character', c) * -1) - sp;\r
11925 \r
11926                                                                         si = sp - bp;\r
11927                                                                         ei = le;\r
11928                                                                 }\r
11929                                                         }\r
11930 \r
11931                                                         // Uses replaceChild instead of cloneNode since it removes selected attribute from option elements on IE\r
11932                                                         // See: http://support.microsoft.com/kb/829907\r
11933                                                         bl = ed.dom.create(ed.settings.forced_root_block);\r
11934                                                         nx.parentNode.replaceChild(bl, nx);\r
11935                                                         bl.appendChild(nx);\r
11936                                                 }\r
11937                                         } else {\r
11938                                                 if (bl.hasChildNodes())\r
11939                                                         bl.insertBefore(nx, bl.firstChild);\r
11940                                                 else\r
11941                                                         bl.appendChild(nx);\r
11942                                         }\r
11943                                 } else\r
11944                                         bl = null; // Time to create new block\r
11945                         }\r
11946 \r
11947                         // Restore selection\r
11948                         if (si != -2) {\r
11949                                 if (!isIE || r.setStart) {\r
11950                                         bl = b.getElementsByTagName(ed.settings.element)[0];\r
11951                                         r = d.createRange();\r
11952 \r
11953                                         // Select last location or generated block\r
11954                                         if (si != -1)\r
11955                                                 r.setStart(t.find(b, 1, si), so);\r
11956                                         else\r
11957                                                 r.setStart(bl, 0);\r
11958 \r
11959                                         // Select last location or generated block\r
11960                                         if (ei != -1)\r
11961                                                 r.setEnd(t.find(b, 1, ei), eo);\r
11962                                         else\r
11963                                                 r.setEnd(bl, 0);\r
11964 \r
11965                                         if (s) {\r
11966                                                 s.removeAllRanges();\r
11967                                                 s.addRange(r);\r
11968                                         }\r
11969                                 } else {\r
11970                                         try {\r
11971                                                 r = s.createRange();\r
11972                                                 r.moveToElementText(b);\r
11973                                                 r.collapse(1);\r
11974                                                 r.moveStart('character', si);\r
11975                                                 r.moveEnd('character', ei);\r
11976                                                 r.select();\r
11977                                         } catch (ex) {\r
11978                                                 // Ignore\r
11979                                         }\r
11980                                 }\r
11981                         } else if ((!isIE || r.setStart) && (n = ed.dom.get('__mce'))) {\r
11982                                 // Restore the id of the selected element\r
11983                                 if (eid)\r
11984                                         n.setAttribute('id', eid);\r
11985                                 else\r
11986                                         n.removeAttribute('id');\r
11987 \r
11988                                 // Move caret before selected element\r
11989                                 r = d.createRange();\r
11990                                 r.setStartBefore(n);\r
11991                                 r.setEndBefore(n);\r
11992                                 se.setRng(r);\r
11993                         }\r
11994                 },\r
11995 \r
11996                 getParentBlock : function(n) {\r
11997                         var d = this.dom;\r
11998 \r
11999                         return d.getParent(n, d.isBlock);\r
12000                 },\r
12001 \r
12002                 insertPara : function(e) {\r
12003                         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
12004                         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
12005 \r
12006                         // If root blocks are forced then use Operas default behavior since it's really good\r
12007 // Removed due to bug: #1853816\r
12008 //                      if (se.forced_root_block && isOpera)\r
12009 //                              return TRUE;\r
12010 \r
12011                         // Setup before range\r
12012                         rb = d.createRange();\r
12013 \r
12014                         // If is before the first block element and in body, then move it into first block element\r
12015                         rb.setStart(s.anchorNode, s.anchorOffset);\r
12016                         rb.collapse(TRUE);\r
12017 \r
12018                         // Setup after range\r
12019                         ra = d.createRange();\r
12020 \r
12021                         // If is before the first block element and in body, then move it into first block element\r
12022                         ra.setStart(s.focusNode, s.focusOffset);\r
12023                         ra.collapse(TRUE);\r
12024 \r
12025                         // Setup start/end points\r
12026                         dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0;\r
12027                         sn = dir ? s.anchorNode : s.focusNode;\r
12028                         so = dir ? s.anchorOffset : s.focusOffset;\r
12029                         en = dir ? s.focusNode : s.anchorNode;\r
12030                         eo = dir ? s.focusOffset : s.anchorOffset;\r
12031 \r
12032                         // If selection is in empty table cell\r
12033                         if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) {\r
12034                                 if (sn.firstChild.nodeName == 'BR')\r
12035                                         dom.remove(sn.firstChild); // Remove BR\r
12036 \r
12037                                 // Create two new block elements\r
12038                                 if (sn.childNodes.length == 0) {\r
12039                                         ed.dom.add(sn, se.element, null, '<br />');\r
12040                                         aft = ed.dom.add(sn, se.element, null, '<br />');\r
12041                                 } else {\r
12042                                         n = sn.innerHTML;\r
12043                                         sn.innerHTML = '';\r
12044                                         ed.dom.add(sn, se.element, null, n);\r
12045                                         aft = ed.dom.add(sn, se.element, null, '<br />');\r
12046                                 }\r
12047 \r
12048                                 // Move caret into the last one\r
12049                                 r = d.createRange();\r
12050                                 r.selectNodeContents(aft);\r
12051                                 r.collapse(1);\r
12052                                 ed.selection.setRng(r);\r
12053 \r
12054                                 return FALSE;\r
12055                         }\r
12056 \r
12057                         // If the caret is in an invalid location in FF we need to move it into the first block\r
12058                         if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) {\r
12059                                 sn = en = sn.firstChild;\r
12060                                 so = eo = 0;\r
12061                                 rb = d.createRange();\r
12062                                 rb.setStart(sn, 0);\r
12063                                 ra = d.createRange();\r
12064                                 ra.setStart(en, 0);\r
12065                         }\r
12066 \r
12067                         // Never use body as start or end node\r
12068                         sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes\r
12069                         sn = sn.nodeName == "BODY" ? sn.firstChild : sn;\r
12070                         en = en.nodeName == "HTML" ? d.body : en; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes\r
12071                         en = en.nodeName == "BODY" ? en.firstChild : en;\r
12072 \r
12073                         // Get start and end blocks\r
12074                         sb = t.getParentBlock(sn);\r
12075                         eb = t.getParentBlock(en);\r
12076                         bn = sb ? sb.nodeName : se.element; // Get block name to create\r
12077 \r
12078                         // Return inside list use default browser behavior\r
12079                         if (n = t.dom.getParent(sb, 'li,pre')) {\r
12080                                 if (n.nodeName == 'LI')\r
12081                                         return splitList(ed.selection, t.dom, n);\r
12082 \r
12083                                 return TRUE;\r
12084                         }\r
12085 \r
12086                         // If caption or absolute layers then always generate new blocks within\r
12087                         if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {\r
12088                                 bn = se.element;\r
12089                                 sb = null;\r
12090                         }\r
12091 \r
12092                         // If caption or absolute layers then always generate new blocks within\r
12093                         if (eb && (eb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {\r
12094                                 bn = se.element;\r
12095                                 eb = null;\r
12096                         }\r
12097 \r
12098                         // Use P instead\r
12099                         if (/(TD|TABLE|TH|CAPTION)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(dom.getStyle(sb, 'float', 1)))) {\r
12100                                 bn = se.element;\r
12101                                 sb = eb = null;\r
12102                         }\r
12103 \r
12104                         // Setup new before and after blocks\r
12105                         bef = (sb && sb.nodeName == bn) ? sb.cloneNode(0) : ed.dom.create(bn);\r
12106                         aft = (eb && eb.nodeName == bn) ? eb.cloneNode(0) : ed.dom.create(bn);\r
12107 \r
12108                         // Remove id from after clone\r
12109                         aft.removeAttribute('id');\r
12110 \r
12111                         // Is header and cursor is at the end, then force paragraph under\r
12112                         if (/^(H[1-6])$/.test(bn) && isAtEnd(r, sb))\r
12113                                 aft = ed.dom.create(se.element);\r
12114 \r
12115                         // Find start chop node\r
12116                         n = sc = sn;\r
12117                         do {\r
12118                                 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))\r
12119                                         break;\r
12120 \r
12121                                 sc = n;\r
12122                         } while ((n = n.previousSibling ? n.previousSibling : n.parentNode));\r
12123 \r
12124                         // Find end chop node\r
12125                         n = ec = en;\r
12126                         do {\r
12127                                 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))\r
12128                                         break;\r
12129 \r
12130                                 ec = n;\r
12131                         } while ((n = n.nextSibling ? n.nextSibling : n.parentNode));\r
12132 \r
12133                         // Place first chop part into before block element\r
12134                         if (sc.nodeName == bn)\r
12135                                 rb.setStart(sc, 0);\r
12136                         else\r
12137                                 rb.setStartBefore(sc);\r
12138 \r
12139                         rb.setEnd(sn, so);\r
12140                         bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari\r
12141 \r
12142                         // Place secnd chop part within new block element\r
12143                         try {\r
12144                                 ra.setEndAfter(ec);\r
12145                         } catch(ex) {\r
12146                                 //console.debug(s.focusNode, s.focusOffset);\r
12147                         }\r
12148 \r
12149                         ra.setStart(en, eo);\r
12150                         aft.appendChild(ra.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari\r
12151 \r
12152                         // Create range around everything\r
12153                         r = d.createRange();\r
12154                         if (!sc.previousSibling && sc.parentNode.nodeName == bn) {\r
12155                                 r.setStartBefore(sc.parentNode);\r
12156                         } else {\r
12157                                 if (rb.startContainer.nodeName == bn && rb.startOffset == 0)\r
12158                                         r.setStartBefore(rb.startContainer);\r
12159                                 else\r
12160                                         r.setStart(rb.startContainer, rb.startOffset);\r
12161                         }\r
12162 \r
12163                         if (!ec.nextSibling && ec.parentNode.nodeName == bn)\r
12164                                 r.setEndAfter(ec.parentNode);\r
12165                         else\r
12166                                 r.setEnd(ra.endContainer, ra.endOffset);\r
12167 \r
12168                         // Delete and replace it with new block elements\r
12169                         r.deleteContents();\r
12170 \r
12171                         if (isOpera)\r
12172                                 ed.getWin().scrollTo(0, vp.y);\r
12173 \r
12174                         // Never wrap blocks in blocks\r
12175                         if (bef.firstChild && bef.firstChild.nodeName == bn)\r
12176                                 bef.innerHTML = bef.firstChild.innerHTML;\r
12177 \r
12178                         if (aft.firstChild && aft.firstChild.nodeName == bn)\r
12179                                 aft.innerHTML = aft.firstChild.innerHTML;\r
12180 \r
12181                         // Padd empty blocks\r
12182                         if (isEmpty(bef))\r
12183                                 bef.innerHTML = '<br />';\r
12184 \r
12185                         function appendStyles(e, en) {\r
12186                                 var nl = [], nn, n, i;\r
12187 \r
12188                                 e.innerHTML = '';\r
12189 \r
12190                                 // Make clones of style elements\r
12191                                 if (se.keep_styles) {\r
12192                                         n = en;\r
12193                                         do {\r
12194                                                 // We only want style specific elements\r
12195                                                 if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) {\r
12196                                                         nn = n.cloneNode(FALSE);\r
12197                                                         dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique\r
12198                                                         nl.push(nn);\r
12199                                                 }\r
12200                                         } while (n = n.parentNode);\r
12201                                 }\r
12202 \r
12203                                 // Append style elements to aft\r
12204                                 if (nl.length > 0) {\r
12205                                         for (i = nl.length - 1, nn = e; i >= 0; i--)\r
12206                                                 nn = nn.appendChild(nl[i]);\r
12207 \r
12208                                         // Padd most inner style element\r
12209                                         nl[0].innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there\r
12210                                         return nl[0]; // Move caret to most inner element\r
12211                                 } else\r
12212                                         e.innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there\r
12213                         };\r
12214 \r
12215                         // Fill empty afterblook with current style\r
12216                         if (isEmpty(aft))\r
12217                                 car = appendStyles(aft, en);\r
12218 \r
12219                         // Opera needs this one backwards for older versions\r
12220                         if (isOpera && parseFloat(opera.version()) < 9.5) {\r
12221                                 r.insertNode(bef);\r
12222                                 r.insertNode(aft);\r
12223                         } else {\r
12224                                 r.insertNode(aft);\r
12225                                 r.insertNode(bef);\r
12226                         }\r
12227 \r
12228                         // Normalize\r
12229                         aft.normalize();\r
12230                         bef.normalize();\r
12231 \r
12232                         function first(n) {\r
12233                                 return d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE).nextNode() || n;\r
12234                         };\r
12235 \r
12236                         // Move cursor and scroll into view\r
12237                         r = d.createRange();\r
12238                         r.selectNodeContents(isGecko ? first(car || aft) : car || aft);\r
12239                         r.collapse(1);\r
12240                         s.removeAllRanges();\r
12241                         s.addRange(r);\r
12242 \r
12243                         // 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
12244                         y = ed.dom.getPos(aft).y;\r
12245                         ch = aft.clientHeight;\r
12246 \r
12247                         // Is element within viewport\r
12248                         if (y < vp.y || y + ch > vp.y + vp.h) {\r
12249                                 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
12250                                 //console.debug('SCROLL!', 'vp.y: ' + vp.y, 'y' + y, 'vp.h' + vp.h, 'clientHeight' + aft.clientHeight, 'yyy: ' + (y < vp.y ? y : y - vp.h + aft.clientHeight));\r
12251                         }\r
12252 \r
12253                         return FALSE;\r
12254                 },\r
12255 \r
12256                 backspaceDelete : function(e, bs) {\r
12257                         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
12258 \r
12259                         // Delete when caret is behind a element doesn't work correctly on Gecko see #3011651\r
12260                         if (!bs && r.collapsed && sc.nodeType == 1 && r.startOffset == sc.childNodes.length) {\r
12261                                 walker = new tinymce.dom.TreeWalker(sc.lastChild, sc);\r
12262 \r
12263                                 // Walk the dom backwards until we find a text node\r
12264                                 for (n = sc.lastChild; n; n = walker.prev()) {\r
12265                                         if (n.nodeType == 3) {\r
12266                                                 r.setStart(n, n.nodeValue.length);\r
12267                                                 r.collapse(true);\r
12268                                                 se.setRng(r);\r
12269                                                 return;\r
12270                                         }\r
12271                                 }\r
12272                         }\r
12273 \r
12274                         // The caret sometimes gets stuck in Gecko if you delete empty paragraphs\r
12275                         // This workaround removes the element by hand and moves the caret to the previous element\r
12276                         if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) {\r
12277                                 if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) {\r
12278                                         // Find previous block element\r
12279                                         n = sc;\r
12280                                         while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ;\r
12281 \r
12282                                         if (n) {\r
12283                                                 if (sc != b.firstChild) {\r
12284                                                         // Find last text node\r
12285                                                         w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE);\r
12286                                                         while (tn = w.nextNode())\r
12287                                                                 n = tn;\r
12288 \r
12289                                                         // Place caret at the end of last text node\r
12290                                                         r = ed.getDoc().createRange();\r
12291                                                         r.setStart(n, n.nodeValue ? n.nodeValue.length : 0);\r
12292                                                         r.setEnd(n, n.nodeValue ? n.nodeValue.length : 0);\r
12293                                                         se.setRng(r);\r
12294 \r
12295                                                         // Remove the target container\r
12296                                                         ed.dom.remove(sc);\r
12297                                                 }\r
12298 \r
12299                                                 return Event.cancel(e);\r
12300                                         }\r
12301                                 }\r
12302                         }\r
12303                 }\r
12304         });\r
12305 })(tinymce);\r
12306 \r
12307 (function(tinymce) {\r
12308         // Shorten names\r
12309         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;\r
12310 \r
12311         tinymce.create('tinymce.ControlManager', {\r
12312                 ControlManager : function(ed, s) {\r
12313                         var t = this, i;\r
12314 \r
12315                         s = s || {};\r
12316                         t.editor = ed;\r
12317                         t.controls = {};\r
12318                         t.onAdd = new tinymce.util.Dispatcher(t);\r
12319                         t.onPostRender = new tinymce.util.Dispatcher(t);\r
12320                         t.prefix = s.prefix || ed.id + '_';\r
12321                         t._cls = {};\r
12322 \r
12323                         t.onPostRender.add(function() {\r
12324                                 each(t.controls, function(c) {\r
12325                                         c.postRender();\r
12326                                 });\r
12327                         });\r
12328                 },\r
12329 \r
12330                 get : function(id) {\r
12331                         return this.controls[this.prefix + id] || this.controls[id];\r
12332                 },\r
12333 \r
12334                 setActive : function(id, s) {\r
12335                         var c = null;\r
12336 \r
12337                         if (c = this.get(id))\r
12338                                 c.setActive(s);\r
12339 \r
12340                         return c;\r
12341                 },\r
12342 \r
12343                 setDisabled : function(id, s) {\r
12344                         var c = null;\r
12345 \r
12346                         if (c = this.get(id))\r
12347                                 c.setDisabled(s);\r
12348 \r
12349                         return c;\r
12350                 },\r
12351 \r
12352                 add : function(c) {\r
12353                         var t = this;\r
12354 \r
12355                         if (c) {\r
12356                                 t.controls[c.id] = c;\r
12357                                 t.onAdd.dispatch(c, t);\r
12358                         }\r
12359 \r
12360                         return c;\r
12361                 },\r
12362 \r
12363                 createControl : function(n) {\r
12364                         var c, t = this, ed = t.editor;\r
12365 \r
12366                         each(ed.plugins, function(p) {\r
12367                                 if (p.createControl) {\r
12368                                         c = p.createControl(n, t);\r
12369 \r
12370                                         if (c)\r
12371                                                 return false;\r
12372                                 }\r
12373                         });\r
12374 \r
12375                         switch (n) {\r
12376                                 case "|":\r
12377                                 case "separator":\r
12378                                         return t.createSeparator();\r
12379                         }\r
12380 \r
12381                         if (!c && ed.buttons && (c = ed.buttons[n]))\r
12382                                 return t.createButton(n, c);\r
12383 \r
12384                         return t.add(c);\r
12385                 },\r
12386 \r
12387                 createDropMenu : function(id, s, cc) {\r
12388                         var t = this, ed = t.editor, c, bm, v, cls;\r
12389 \r
12390                         s = extend({\r
12391                                 'class' : 'mceDropDown',\r
12392                                 constrain : ed.settings.constrain_menus\r
12393                         }, s);\r
12394 \r
12395                         s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin';\r
12396                         if (v = ed.getParam('skin_variant'))\r
12397                                 s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);\r
12398 \r
12399                         id = t.prefix + id;\r
12400                         cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;\r
12401                         c = t.controls[id] = new cls(id, s);\r
12402                         c.onAddItem.add(function(c, o) {\r
12403                                 var s = o.settings;\r
12404 \r
12405                                 s.title = ed.getLang(s.title, s.title);\r
12406 \r
12407                                 if (!s.onclick) {\r
12408                                         s.onclick = function(v) {\r
12409                                                 if (s.cmd)\r
12410                                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
12411                                         };\r
12412                                 }\r
12413                         });\r
12414 \r
12415                         ed.onRemove.add(function() {\r
12416                                 c.destroy();\r
12417                         });\r
12418 \r
12419                         // Fix for bug #1897785, #1898007\r
12420                         if (tinymce.isIE) {\r
12421                                 c.onShowMenu.add(function() {\r
12422                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
12423                                         ed.focus();\r
12424 \r
12425                                         bm = ed.selection.getBookmark(1);\r
12426                                 });\r
12427 \r
12428                                 c.onHideMenu.add(function() {\r
12429                                         if (bm) {\r
12430                                                 ed.selection.moveToBookmark(bm);\r
12431                                                 bm = 0;\r
12432                                         }\r
12433                                 });\r
12434                         }\r
12435 \r
12436                         return t.add(c);\r
12437                 },\r
12438 \r
12439                 createListBox : function(id, s, cc) {\r
12440                         var t = this, ed = t.editor, cmd, c, cls;\r
12441 \r
12442                         if (t.get(id))\r
12443                                 return null;\r
12444 \r
12445                         s.title = ed.translate(s.title);\r
12446                         s.scope = s.scope || ed;\r
12447 \r
12448                         if (!s.onselect) {\r
12449                                 s.onselect = function(v) {\r
12450                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12451                                 };\r
12452                         }\r
12453 \r
12454                         s = extend({\r
12455                                 title : s.title,\r
12456                                 'class' : 'mce_' + id,\r
12457                                 scope : s.scope,\r
12458                                 control_manager : t\r
12459                         }, s);\r
12460 \r
12461                         id = t.prefix + id;\r
12462 \r
12463                         if (ed.settings.use_native_selects)\r
12464                                 c = new tinymce.ui.NativeListBox(id, s);\r
12465                         else {\r
12466                                 cls = cc || t._cls.listbox || tinymce.ui.ListBox;\r
12467                                 c = new cls(id, s);\r
12468                         }\r
12469 \r
12470                         t.controls[id] = c;\r
12471 \r
12472                         // Fix focus problem in Safari\r
12473                         if (tinymce.isWebKit) {\r
12474                                 c.onPostRender.add(function(c, n) {\r
12475                                         // Store bookmark on mousedown\r
12476                                         Event.add(n, 'mousedown', function() {\r
12477                                                 ed.bookmark = ed.selection.getBookmark(1);\r
12478                                         });\r
12479 \r
12480                                         // Restore on focus, since it might be lost\r
12481                                         Event.add(n, 'focus', function() {\r
12482                                                 ed.selection.moveToBookmark(ed.bookmark);\r
12483                                                 ed.bookmark = null;\r
12484                                         });\r
12485                                 });\r
12486                         }\r
12487 \r
12488                         if (c.hideMenu)\r
12489                                 ed.onMouseDown.add(c.hideMenu, c);\r
12490 \r
12491                         return t.add(c);\r
12492                 },\r
12493 \r
12494                 createButton : function(id, s, cc) {\r
12495                         var t = this, ed = t.editor, o, c, cls;\r
12496 \r
12497                         if (t.get(id))\r
12498                                 return null;\r
12499 \r
12500                         s.title = ed.translate(s.title);\r
12501                         s.label = ed.translate(s.label);\r
12502                         s.scope = s.scope || ed;\r
12503 \r
12504                         if (!s.onclick && !s.menu_button) {\r
12505                                 s.onclick = function() {\r
12506                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
12507                                 };\r
12508                         }\r
12509 \r
12510                         s = extend({\r
12511                                 title : s.title,\r
12512                                 'class' : 'mce_' + id,\r
12513                                 unavailable_prefix : ed.getLang('unavailable', ''),\r
12514                                 scope : s.scope,\r
12515                                 control_manager : t\r
12516                         }, s);\r
12517 \r
12518                         id = t.prefix + id;\r
12519 \r
12520                         if (s.menu_button) {\r
12521                                 cls = cc || t._cls.menubutton || tinymce.ui.MenuButton;\r
12522                                 c = new cls(id, s);\r
12523                                 ed.onMouseDown.add(c.hideMenu, c);\r
12524                         } else {\r
12525                                 cls = t._cls.button || tinymce.ui.Button;\r
12526                                 c = new cls(id, s);\r
12527                         }\r
12528 \r
12529                         return t.add(c);\r
12530                 },\r
12531 \r
12532                 createMenuButton : function(id, s, cc) {\r
12533                         s = s || {};\r
12534                         s.menu_button = 1;\r
12535 \r
12536                         return this.createButton(id, s, cc);\r
12537                 },\r
12538 \r
12539                 createSplitButton : function(id, s, cc) {\r
12540                         var t = this, ed = t.editor, cmd, c, cls;\r
12541 \r
12542                         if (t.get(id))\r
12543                                 return null;\r
12544 \r
12545                         s.title = ed.translate(s.title);\r
12546                         s.scope = s.scope || ed;\r
12547 \r
12548                         if (!s.onclick) {\r
12549                                 s.onclick = function(v) {\r
12550                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12551                                 };\r
12552                         }\r
12553 \r
12554                         if (!s.onselect) {\r
12555                                 s.onselect = function(v) {\r
12556                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12557                                 };\r
12558                         }\r
12559 \r
12560                         s = extend({\r
12561                                 title : s.title,\r
12562                                 'class' : 'mce_' + id,\r
12563                                 scope : s.scope,\r
12564                                 control_manager : t\r
12565                         }, s);\r
12566 \r
12567                         id = t.prefix + id;\r
12568                         cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton;\r
12569                         c = t.add(new cls(id, s));\r
12570                         ed.onMouseDown.add(c.hideMenu, c);\r
12571 \r
12572                         return c;\r
12573                 },\r
12574 \r
12575                 createColorSplitButton : function(id, s, cc) {\r
12576                         var t = this, ed = t.editor, cmd, c, cls, bm;\r
12577 \r
12578                         if (t.get(id))\r
12579                                 return null;\r
12580 \r
12581                         s.title = ed.translate(s.title);\r
12582                         s.scope = s.scope || ed;\r
12583 \r
12584                         if (!s.onclick) {\r
12585                                 s.onclick = function(v) {\r
12586                                         if (tinymce.isIE)\r
12587                                                 bm = ed.selection.getBookmark(1);\r
12588 \r
12589                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12590                                 };\r
12591                         }\r
12592 \r
12593                         if (!s.onselect) {\r
12594                                 s.onselect = function(v) {\r
12595                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12596                                 };\r
12597                         }\r
12598 \r
12599                         s = extend({\r
12600                                 title : s.title,\r
12601                                 'class' : 'mce_' + id,\r
12602                                 'menu_class' : ed.getParam('skin') + 'Skin',\r
12603                                 scope : s.scope,\r
12604                                 more_colors_title : ed.getLang('more_colors')\r
12605                         }, s);\r
12606 \r
12607                         id = t.prefix + id;\r
12608                         cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton;\r
12609                         c = new cls(id, s);\r
12610                         ed.onMouseDown.add(c.hideMenu, c);\r
12611 \r
12612                         // Remove the menu element when the editor is removed\r
12613                         ed.onRemove.add(function() {\r
12614                                 c.destroy();\r
12615                         });\r
12616 \r
12617                         // Fix for bug #1897785, #1898007\r
12618                         if (tinymce.isIE) {\r
12619                                 c.onShowMenu.add(function() {\r
12620                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
12621                                         ed.focus();\r
12622                                         bm = ed.selection.getBookmark(1);\r
12623                                 });\r
12624 \r
12625                                 c.onHideMenu.add(function() {\r
12626                                         if (bm) {\r
12627                                                 ed.selection.moveToBookmark(bm);\r
12628                                                 bm = 0;\r
12629                                         }\r
12630                                 });\r
12631                         }\r
12632 \r
12633                         return t.add(c);\r
12634                 },\r
12635 \r
12636                 createToolbar : function(id, s, cc) {\r
12637                         var c, t = this, cls;\r
12638 \r
12639                         id = t.prefix + id;\r
12640                         cls = cc || t._cls.toolbar || tinymce.ui.Toolbar;\r
12641                         c = new cls(id, s);\r
12642 \r
12643                         if (t.get(id))\r
12644                                 return null;\r
12645 \r
12646                         return t.add(c);\r
12647                 },\r
12648 \r
12649                 createSeparator : function(cc) {\r
12650                         var cls = cc || this._cls.separator || tinymce.ui.Separator;\r
12651 \r
12652                         return new cls();\r
12653                 },\r
12654 \r
12655                 setControlType : function(n, c) {\r
12656                         return this._cls[n.toLowerCase()] = c;\r
12657                 },\r
12658         \r
12659                 destroy : function() {\r
12660                         each(this.controls, function(c) {\r
12661                                 c.destroy();\r
12662                         });\r
12663 \r
12664                         this.controls = null;\r
12665                 }\r
12666         });\r
12667 })(tinymce);\r
12668 \r
12669 (function(tinymce) {\r
12670         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;\r
12671 \r
12672         tinymce.create('tinymce.WindowManager', {\r
12673                 WindowManager : function(ed) {\r
12674                         var t = this;\r
12675 \r
12676                         t.editor = ed;\r
12677                         t.onOpen = new Dispatcher(t);\r
12678                         t.onClose = new Dispatcher(t);\r
12679                         t.params = {};\r
12680                         t.features = {};\r
12681                 },\r
12682 \r
12683                 open : function(s, p) {\r
12684                         var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u;\r
12685 \r
12686                         // Default some options\r
12687                         s = s || {};\r
12688                         p = p || {};\r
12689                         sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window\r
12690                         sh = isOpera ? vp.h : screen.height;\r
12691                         s.name = s.name || 'mc_' + new Date().getTime();\r
12692                         s.width = parseInt(s.width || 320);\r
12693                         s.height = parseInt(s.height || 240);\r
12694                         s.resizable = true;\r
12695                         s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0);\r
12696                         s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0);\r
12697                         p.inline = false;\r
12698                         p.mce_width = s.width;\r
12699                         p.mce_height = s.height;\r
12700                         p.mce_auto_focus = s.auto_focus;\r
12701 \r
12702                         if (mo) {\r
12703                                 if (isIE) {\r
12704                                         s.center = true;\r
12705                                         s.help = false;\r
12706                                         s.dialogWidth = s.width + 'px';\r
12707                                         s.dialogHeight = s.height + 'px';\r
12708                                         s.scroll = s.scrollbars || false;\r
12709                                 }\r
12710                         }\r
12711 \r
12712                         // Build features string\r
12713                         each(s, function(v, k) {\r
12714                                 if (tinymce.is(v, 'boolean'))\r
12715                                         v = v ? 'yes' : 'no';\r
12716 \r
12717                                 if (!/^(name|url)$/.test(k)) {\r
12718                                         if (isIE && mo)\r
12719                                                 f += (f ? ';' : '') + k + ':' + v;\r
12720                                         else\r
12721                                                 f += (f ? ',' : '') + k + '=' + v;\r
12722                                 }\r
12723                         });\r
12724 \r
12725                         t.features = s;\r
12726                         t.params = p;\r
12727                         t.onOpen.dispatch(t, s, p);\r
12728 \r
12729                         u = s.url || s.file;\r
12730                         u = tinymce._addVer(u);\r
12731 \r
12732                         try {\r
12733                                 if (isIE && mo) {\r
12734                                         w = 1;\r
12735                                         window.showModalDialog(u, window, f);\r
12736                                 } else\r
12737                                         w = window.open(u, s.name, f);\r
12738                         } catch (ex) {\r
12739                                 // Ignore\r
12740                         }\r
12741 \r
12742                         if (!w)\r
12743                                 alert(t.editor.getLang('popup_blocked'));\r
12744                 },\r
12745 \r
12746                 close : function(w) {\r
12747                         w.close();\r
12748                         this.onClose.dispatch(this);\r
12749                 },\r
12750 \r
12751                 createInstance : function(cl, a, b, c, d, e) {\r
12752                         var f = tinymce.resolve(cl);\r
12753 \r
12754                         return new f(a, b, c, d, e);\r
12755                 },\r
12756 \r
12757                 confirm : function(t, cb, s, w) {\r
12758                         w = w || window;\r
12759 \r
12760                         cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t))));\r
12761                 },\r
12762 \r
12763                 alert : function(tx, cb, s, w) {\r
12764                         var t = this;\r
12765 \r
12766                         w = w || window;\r
12767                         w.alert(t._decode(t.editor.getLang(tx, tx)));\r
12768 \r
12769                         if (cb)\r
12770                                 cb.call(s || t);\r
12771                 },\r
12772 \r
12773                 resizeBy : function(dw, dh, win) {\r
12774                         win.resizeBy(dw, dh);\r
12775                 },\r
12776 \r
12777                 // Internal functions\r
12778 \r
12779                 _decode : function(s) {\r
12780                         return tinymce.DOM.decode(s).replace(/\\n/g, '\n');\r
12781                 }\r
12782         });\r
12783 }(tinymce));\r
12784 (function(tinymce) {\r
12785         function CommandManager() {\r
12786                 var execCommands = {}, queryStateCommands = {}, queryValueCommands = {};\r
12787 \r
12788                 function add(collection, cmd, func, scope) {\r
12789                         if (typeof(cmd) == 'string')\r
12790                                 cmd = [cmd];\r
12791 \r
12792                         tinymce.each(cmd, function(cmd) {\r
12793                                 collection[cmd.toLowerCase()] = {func : func, scope : scope};\r
12794                         });\r
12795                 };\r
12796 \r
12797                 tinymce.extend(this, {\r
12798                         add : function(cmd, func, scope) {\r
12799                                 add(execCommands, cmd, func, scope);\r
12800                         },\r
12801 \r
12802                         addQueryStateHandler : function(cmd, func, scope) {\r
12803                                 add(queryStateCommands, cmd, func, scope);\r
12804                         },\r
12805 \r
12806                         addQueryValueHandler : function(cmd, func, scope) {\r
12807                                 add(queryValueCommands, cmd, func, scope);\r
12808                         },\r
12809 \r
12810                         execCommand : function(scope, cmd, ui, value, args) {\r
12811                                 if (cmd = execCommands[cmd.toLowerCase()]) {\r
12812                                         if (cmd.func.call(scope || cmd.scope, ui, value, args) !== false)\r
12813                                                 return true;\r
12814                                 }\r
12815                         },\r
12816 \r
12817                         queryCommandValue : function() {\r
12818                                 if (cmd = queryValueCommands[cmd.toLowerCase()])\r
12819                                         return cmd.func.call(scope || cmd.scope, ui, value, args);\r
12820                         },\r
12821 \r
12822                         queryCommandState : function() {\r
12823                                 if (cmd = queryStateCommands[cmd.toLowerCase()])\r
12824                                         return cmd.func.call(scope || cmd.scope, ui, value, args);\r
12825                         }\r
12826                 });\r
12827         };\r
12828 \r
12829         tinymce.GlobalCommands = new CommandManager();\r
12830 })(tinymce);\r
12831 (function(tinymce) {\r
12832         tinymce.Formatter = function(ed) {\r
12833                 var formats = {},\r
12834                         each = tinymce.each,\r
12835                         dom = ed.dom,\r
12836                         selection = ed.selection,\r
12837                         TreeWalker = tinymce.dom.TreeWalker,\r
12838                         rangeUtils = new tinymce.dom.RangeUtils(dom),\r
12839                         isValid = ed.schema.isValid,\r
12840                         isBlock = dom.isBlock,\r
12841                         forcedRootBlock = ed.settings.forced_root_block,\r
12842                         nodeIndex = dom.nodeIndex,\r
12843                         INVISIBLE_CHAR = '\uFEFF',\r
12844                         MCE_ATTR_RE = /^(src|href|style)$/,\r
12845                         FALSE = false,\r
12846                         TRUE = true,\r
12847                         undefined,\r
12848                         pendingFormats = {apply : [], remove : []};\r
12849 \r
12850                 function isArray(obj) {\r
12851                         return obj instanceof Array;\r
12852                 };\r
12853 \r
12854                 function getParents(node, selector) {\r
12855                         return dom.getParents(node, selector, dom.getRoot());\r
12856                 };\r
12857 \r
12858                 function isCaretNode(node) {\r
12859                         return node.nodeType === 1 && (node.face === 'mceinline' || node.style.fontFamily === 'mceinline');\r
12860                 };\r
12861 \r
12862                 // Public functions\r
12863 \r
12864                 function get(name) {\r
12865                         return name ? formats[name] : formats;\r
12866                 };\r
12867 \r
12868                 function register(name, format) {\r
12869                         if (name) {\r
12870                                 if (typeof(name) !== 'string') {\r
12871                                         each(name, function(format, name) {\r
12872                                                 register(name, format);\r
12873                                         });\r
12874                                 } else {\r
12875                                         // Force format into array and add it to internal collection\r
12876                                         format = format.length ? format : [format];\r
12877 \r
12878                                         each(format, function(format) {\r
12879                                                 // Set deep to false by default on selector formats this to avoid removing\r
12880                                                 // alignment on images inside paragraphs when alignment is changed on paragraphs\r
12881                                                 if (format.deep === undefined)\r
12882                                                         format.deep = !format.selector;\r
12883 \r
12884                                                 // Default to true\r
12885                                                 if (format.split === undefined)\r
12886                                                         format.split = !format.selector || format.inline;\r
12887 \r
12888                                                 // Default to true\r
12889                                                 if (format.remove === undefined && format.selector && !format.inline)\r
12890                                                         format.remove = 'none';\r
12891 \r
12892                                                 // Mark format as a mixed format inline + block level\r
12893                                                 if (format.selector && format.inline) {\r
12894                                                         format.mixed = true;\r
12895                                                         format.block_expand = true;\r
12896                                                 }\r
12897 \r
12898                                                 // Split classes if needed\r
12899                                                 if (typeof(format.classes) === 'string')\r
12900                                                         format.classes = format.classes.split(/\s+/);\r
12901                                         });\r
12902 \r
12903                                         formats[name] = format;\r
12904                                 }\r
12905                         }\r
12906                 };\r
12907 \r
12908                 function apply(name, vars, node) {\r
12909                         var formatList = get(name), format = formatList[0], bookmark, rng, i;\r
12910 \r
12911                         function moveStart(rng) {\r
12912                                 var container = rng.startContainer,\r
12913                                         offset = rng.startOffset,\r
12914                                         walker, node;\r
12915 \r
12916                                 // Move startContainer/startOffset in to a suitable node\r
12917                                 if (container.nodeType == 1 || container.nodeValue === "") {\r
12918                                         container = container.nodeType == 1 ? container.childNodes[offset] : container;\r
12919 \r
12920                                         // Might fail if the offset is behind the last element in it's container\r
12921                                         if (container) {\r
12922                                                 walker = new TreeWalker(container, container.parentNode);\r
12923                                                 for (node = walker.current(); node; node = walker.next()) {\r
12924                                                         if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {\r
12925                                                                 rng.setStart(node, 0);\r
12926                                                                 break;\r
12927                                                         }\r
12928                                                 }\r
12929                                         }\r
12930                                 }\r
12931 \r
12932                                 return rng;\r
12933                         };\r
12934 \r
12935                         function setElementFormat(elm, fmt) {\r
12936                                 fmt = fmt || format;\r
12937 \r
12938                                 if (elm) {\r
12939                                         each(fmt.styles, function(value, name) {\r
12940                                                 dom.setStyle(elm, name, replaceVars(value, vars));\r
12941                                         });\r
12942 \r
12943                                         each(fmt.attributes, function(value, name) {\r
12944                                                 dom.setAttrib(elm, name, replaceVars(value, vars));\r
12945                                         });\r
12946 \r
12947                                         each(fmt.classes, function(value) {\r
12948                                                 value = replaceVars(value, vars);\r
12949 \r
12950                                                 if (!dom.hasClass(elm, value))\r
12951                                                         dom.addClass(elm, value);\r
12952                                         });\r
12953                                 }\r
12954                         };\r
12955 \r
12956                         function applyRngStyle(rng) {\r
12957                                 var newWrappers = [], wrapName, wrapElm;\r
12958 \r
12959                                 // Setup wrapper element\r
12960                                 wrapName = format.inline || format.block;\r
12961                                 wrapElm = dom.create(wrapName);\r
12962                                 setElementFormat(wrapElm);\r
12963 \r
12964                                 rangeUtils.walk(rng, function(nodes) {\r
12965                                         var currentWrapElm;\r
12966 \r
12967                                         function process(node) {\r
12968                                                 var nodeName = node.nodeName.toLowerCase(), parentName = node.parentNode.nodeName.toLowerCase(), found;\r
12969 \r
12970                                                 // Stop wrapping on br elements\r
12971                                                 if (isEq(nodeName, 'br')) {\r
12972                                                         currentWrapElm = 0;\r
12973 \r
12974                                                         // Remove any br elements when we wrap things\r
12975                                                         if (format.block)\r
12976                                                                 dom.remove(node);\r
12977 \r
12978                                                         return;\r
12979                                                 }\r
12980 \r
12981                                                 // If node is wrapper type\r
12982                                                 if (format.wrapper && matchNode(node, name, vars)) {\r
12983                                                         currentWrapElm = 0;\r
12984                                                         return;\r
12985                                                 }\r
12986 \r
12987                                                 // Can we rename the block\r
12988                                                 if (format.block && !format.wrapper && isTextBlock(nodeName)) {\r
12989                                                         node = dom.rename(node, wrapName);\r
12990                                                         setElementFormat(node);\r
12991                                                         newWrappers.push(node);\r
12992                                                         currentWrapElm = 0;\r
12993                                                         return;\r
12994                                                 }\r
12995 \r
12996                                                 // Handle selector patterns\r
12997                                                 if (format.selector) {\r
12998                                                         // Look for matching formats\r
12999                                                         each(formatList, function(format) {\r
13000                                                                 if (dom.is(node, format.selector) && !isCaretNode(node)) {\r
13001                                                                         setElementFormat(node, format);\r
13002                                                                         found = true;\r
13003                                                                 }\r
13004                                                         });\r
13005 \r
13006                                                         // Continue processing if a selector match wasn't found and a inline element is defined\r
13007                                                         if (!format.inline || found) {\r
13008                                                                 currentWrapElm = 0;\r
13009                                                                 return;\r
13010                                                         }\r
13011                                                 }\r
13012 \r
13013                                                 // Is it valid to wrap this item\r
13014                                                 if (isValid(wrapName, nodeName) && isValid(parentName, wrapName)) {\r
13015                                                         // Start wrapping\r
13016                                                         if (!currentWrapElm) {\r
13017                                                                 // Wrap the node\r
13018                                                                 currentWrapElm = wrapElm.cloneNode(FALSE);\r
13019                                                                 node.parentNode.insertBefore(currentWrapElm, node);\r
13020                                                                 newWrappers.push(currentWrapElm);\r
13021                                                         }\r
13022 \r
13023                                                         currentWrapElm.appendChild(node);\r
13024                                                 } else {\r
13025                                                         // Start a new wrapper for possible children\r
13026                                                         currentWrapElm = 0;\r
13027 \r
13028                                                         each(tinymce.grep(node.childNodes), process);\r
13029 \r
13030                                                         // End the last wrapper\r
13031                                                         currentWrapElm = 0;\r
13032                                                 }\r
13033                                         };\r
13034 \r
13035                                         // Process siblings from range\r
13036                                         each(nodes, process);\r
13037                                 });\r
13038 \r
13039                                 // Cleanup\r
13040                                 each(newWrappers, function(node) {\r
13041                                         var childCount;\r
13042 \r
13043                                         function getChildCount(node) {\r
13044                                                 var count = 0;\r
13045 \r
13046                                                 each(node.childNodes, function(node) {\r
13047                                                         if (!isWhiteSpaceNode(node) && !isBookmarkNode(node))\r
13048                                                                 count++;\r
13049                                                 });\r
13050 \r
13051                                                 return count;\r
13052                                         };\r
13053 \r
13054                                         function mergeStyles(node) {\r
13055                                                 var child, clone;\r
13056 \r
13057                                                 each(node.childNodes, function(node) {\r
13058                                                         if (node.nodeType == 1 && !isBookmarkNode(node) && !isCaretNode(node)) {\r
13059                                                                 child = node;\r
13060                                                                 return FALSE; // break loop\r
13061                                                         }\r
13062                                                 });\r
13063 \r
13064                                                 // If child was found and of the same type as the current node\r
13065                                                 if (child && matchName(child, format)) {\r
13066                                                         clone = child.cloneNode(FALSE);\r
13067                                                         setElementFormat(clone);\r
13068 \r
13069                                                         dom.replace(clone, node, TRUE);\r
13070                                                         dom.remove(child, 1);\r
13071                                                 }\r
13072 \r
13073                                                 return clone || node;\r
13074                                         };\r
13075 \r
13076                                         childCount = getChildCount(node);\r
13077 \r
13078                                         // Remove empty nodes\r
13079                                         if (childCount === 0) {\r
13080                                                 dom.remove(node, 1);\r
13081                                                 return;\r
13082                                         }\r
13083 \r
13084                                         if (format.inline || format.wrapper) {\r
13085                                                 // Merges the current node with it's children of similar type to reduce the number of elements\r
13086                                                 if (!format.exact && childCount === 1)\r
13087                                                         node = mergeStyles(node);\r
13088 \r
13089                                                 // Remove/merge children\r
13090                                                 each(formatList, function(format) {\r
13091                                                         // Merge all children of similar type will move styles from child to parent\r
13092                                                         // this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span>\r
13093                                                         // will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span>\r
13094                                                         each(dom.select(format.inline, node), function(child) {\r
13095                                                                 removeFormat(format, vars, child, format.exact ? child : null);\r
13096                                                         });\r
13097                                                 });\r
13098 \r
13099                                                 // Remove child if direct parent is of same type\r
13100                                                 if (matchNode(node.parentNode, name, vars)) {\r
13101                                                         dom.remove(node, 1);\r
13102                                                         node = 0;\r
13103                                                         return TRUE;\r
13104                                                 }\r
13105 \r
13106                                                 // Look for parent with similar style format\r
13107                                                 if (format.merge_with_parents) {\r
13108                                                         dom.getParent(node.parentNode, function(parent) {\r
13109                                                                 if (matchNode(parent, name, vars)) {\r
13110                                                                         dom.remove(node, 1);\r
13111                                                                         node = 0;\r
13112                                                                         return TRUE;\r
13113                                                                 }\r
13114                                                         });\r
13115                                                 }\r
13116 \r
13117                                                 // Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>\r
13118                                                 if (node) {\r
13119                                                         node = mergeSiblings(getNonWhiteSpaceSibling(node), node);\r
13120                                                         node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE));\r
13121                                                 }\r
13122                                         }\r
13123                                 });\r
13124                         };\r
13125 \r
13126                         if (format) {\r
13127                                 if (node) {\r
13128                                         rng = dom.createRng();\r
13129 \r
13130                                         rng.setStartBefore(node);\r
13131                                         rng.setEndAfter(node);\r
13132 \r
13133                                         applyRngStyle(expandRng(rng, formatList));\r
13134                                 } else {\r
13135                                         if (!selection.isCollapsed() || !format.inline) {\r
13136                                                 // Apply formatting to selection\r
13137                                                 bookmark = selection.getBookmark();\r
13138                                                 applyRngStyle(expandRng(selection.getRng(TRUE), formatList));\r
13139 \r
13140                                                 selection.moveToBookmark(bookmark);\r
13141                                                 selection.setRng(moveStart(selection.getRng(TRUE)));\r
13142                                                 ed.nodeChanged();\r
13143                                         } else\r
13144                                                 performCaretAction('apply', name, vars);\r
13145                                 }\r
13146                         }\r
13147                 };\r
13148 \r
13149                 function remove(name, vars, node) {\r
13150                         var formatList = get(name), format = formatList[0], bookmark, i, rng;\r
13151 \r
13152                         function moveStart(rng) {\r
13153                                 var container = rng.startContainer,\r
13154                                         offset = rng.startOffset,\r
13155                                         walker, node, nodes, tmpNode;\r
13156 \r
13157                                 // Convert text node into index if possible\r
13158                                 if (container.nodeType == 3 && offset >= container.nodeValue.length - 1) {\r
13159                                         container = container.parentNode;\r
13160                                         offset = nodeIndex(container) + 1;\r
13161                                 }\r
13162 \r
13163                                 // Move startContainer/startOffset in to a suitable node\r
13164                                 if (container.nodeType == 1) {\r
13165                                         nodes = container.childNodes;\r
13166                                         container = nodes[Math.min(offset, nodes.length - 1)];\r
13167                                         walker = new TreeWalker(container);\r
13168 \r
13169                                         // If offset is at end of the parent node walk to the next one\r
13170                                         if (offset > nodes.length - 1)\r
13171                                                 walker.next();\r
13172 \r
13173                                         for (node = walker.current(); node; node = walker.next()) {\r
13174                                                 if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {\r
13175                                                         // IE has a "neat" feature where it moves the start node into the closest element\r
13176                                                         // we can avoid this by inserting an element before it and then remove it after we set the selection\r
13177                                                         tmpNode = dom.create('a', null, INVISIBLE_CHAR);\r
13178                                                         node.parentNode.insertBefore(tmpNode, node);\r
13179 \r
13180                                                         // Set selection and remove tmpNode\r
13181                                                         rng.setStart(node, 0);\r
13182                                                         selection.setRng(rng);\r
13183                                                         dom.remove(tmpNode);\r
13184 \r
13185                                                         return;\r
13186                                                 }\r
13187                                         }\r
13188                                 }\r
13189                         };\r
13190 \r
13191                         // Merges the styles for each node\r
13192                         function process(node) {\r
13193                                 var children, i, l;\r
13194 \r
13195                                 // Grab the children first since the nodelist might be changed\r
13196                                 children = tinymce.grep(node.childNodes);\r
13197 \r
13198                                 // Process current node\r
13199                                 for (i = 0, l = formatList.length; i < l; i++) {\r
13200                                         if (removeFormat(formatList[i], vars, node, node))\r
13201                                                 break;\r
13202                                 }\r
13203 \r
13204                                 // Process the children\r
13205                                 if (format.deep) {\r
13206                                         for (i = 0, l = children.length; i < l; i++)\r
13207                                                 process(children[i]);\r
13208                                 }\r
13209                         };\r
13210 \r
13211                         function findFormatRoot(container) {\r
13212                                 var formatRoot;\r
13213 \r
13214                                 // Find format root\r
13215                                 each(getParents(container.parentNode).reverse(), function(parent) {\r
13216                                         var format;\r
13217 \r
13218                                         // Find format root element\r
13219                                         if (!formatRoot && parent.id != '_start' && parent.id != '_end') {\r
13220                                                 // Is the node matching the format we are looking for\r
13221                                                 format = matchNode(parent, name, vars);\r
13222                                                 if (format && format.split !== false)\r
13223                                                         formatRoot = parent;\r
13224                                         }\r
13225                                 });\r
13226 \r
13227                                 return formatRoot;\r
13228                         };\r
13229 \r
13230                         function wrapAndSplit(format_root, container, target, split) {\r
13231                                 var parent, clone, lastClone, firstClone, i, formatRootParent;\r
13232 \r
13233                                 // Format root found then clone formats and split it\r
13234                                 if (format_root) {\r
13235                                         formatRootParent = format_root.parentNode;\r
13236 \r
13237                                         for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) {\r
13238                                                 clone = parent.cloneNode(FALSE);\r
13239 \r
13240                                                 for (i = 0; i < formatList.length; i++) {\r
13241                                                         if (removeFormat(formatList[i], vars, clone, clone)) {\r
13242                                                                 clone = 0;\r
13243                                                                 break;\r
13244                                                         }\r
13245                                                 }\r
13246 \r
13247                                                 // Build wrapper node\r
13248                                                 if (clone) {\r
13249                                                         if (lastClone)\r
13250                                                                 clone.appendChild(lastClone);\r
13251 \r
13252                                                         if (!firstClone)\r
13253                                                                 firstClone = clone;\r
13254 \r
13255                                                         lastClone = clone;\r
13256                                                 }\r
13257                                         }\r
13258 \r
13259                                         // Never split block elements if the format is mixed\r
13260                                         if (split && (!format.mixed || !isBlock(format_root)))\r
13261                                                 container = dom.split(format_root, container);\r
13262 \r
13263                                         // Wrap container in cloned formats\r
13264                                         if (lastClone) {\r
13265                                                 target.parentNode.insertBefore(lastClone, target);\r
13266                                                 firstClone.appendChild(target);\r
13267                                         }\r
13268                                 }\r
13269 \r
13270                                 return container;\r
13271                         };\r
13272 \r
13273                         function splitToFormatRoot(container) {\r
13274                                 return wrapAndSplit(findFormatRoot(container), container, container, true);\r
13275                         };\r
13276 \r
13277                         function unwrap(start) {\r
13278                                 var node = dom.get(start ? '_start' : '_end'),\r
13279                                         out = node[start ? 'firstChild' : 'lastChild'];\r
13280 \r
13281                                 // If the end is placed within the start the result will be removed\r
13282                                 // So this checks if the out node is a bookmark node if it is it\r
13283                                 // checks for another more suitable node\r
13284                                 if (isBookmarkNode(out))\r
13285                                         out = out[start ? 'firstChild' : 'lastChild'];\r
13286 \r
13287                                 dom.remove(node, true);\r
13288 \r
13289                                 return out;\r
13290                         };\r
13291 \r
13292                         function removeRngStyle(rng) {\r
13293                                 var startContainer, endContainer;\r
13294 \r
13295                                 rng = expandRng(rng, formatList, TRUE);\r
13296 \r
13297                                 if (format.split) {\r
13298                                         startContainer = getContainer(rng, TRUE);\r
13299                                         endContainer = getContainer(rng);\r
13300 \r
13301                                         if (startContainer != endContainer) {\r
13302                                                 // Wrap start/end nodes in span element since these might be cloned/moved\r
13303                                                 startContainer = wrap(startContainer, 'span', {id : '_start', _mce_type : 'bookmark'});\r
13304                                                 endContainer = wrap(endContainer, 'span', {id : '_end', _mce_type : 'bookmark'});\r
13305 \r
13306                                                 // Split start/end\r
13307                                                 splitToFormatRoot(startContainer);\r
13308                                                 splitToFormatRoot(endContainer);\r
13309 \r
13310                                                 // Unwrap start/end to get real elements again\r
13311                                                 startContainer = unwrap(TRUE);\r
13312                                                 endContainer = unwrap();\r
13313                                         } else\r
13314                                                 startContainer = endContainer = splitToFormatRoot(startContainer);\r
13315 \r
13316                                         // Update range positions since they might have changed after the split operations\r
13317                                         rng.startContainer = startContainer.parentNode;\r
13318                                         rng.startOffset = nodeIndex(startContainer);\r
13319                                         rng.endContainer = endContainer.parentNode;\r
13320                                         rng.endOffset = nodeIndex(endContainer) + 1;\r
13321                                 }\r
13322 \r
13323                                 // Remove items between start/end\r
13324                                 rangeUtils.walk(rng, function(nodes) {\r
13325                                         each(nodes, function(node) {\r
13326                                                 process(node);\r
13327                                         });\r
13328                                 });\r
13329                         };\r
13330 \r
13331                         // Handle node\r
13332                         if (node) {\r
13333                                 rng = dom.createRng();\r
13334                                 rng.setStartBefore(node);\r
13335                                 rng.setEndAfter(node);\r
13336                                 removeRngStyle(rng);\r
13337                                 return;\r
13338                         }\r
13339 \r
13340                         if (!selection.isCollapsed() || !format.inline) {\r
13341                                 bookmark = selection.getBookmark();\r
13342                                 removeRngStyle(selection.getRng(TRUE));\r
13343                                 selection.moveToBookmark(bookmark);\r
13344 \r
13345                                 // 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
13346                                 if (match(name, vars, selection.getStart())) {\r
13347                                         moveStart(selection.getRng(true));\r
13348                                 }\r
13349 \r
13350                                 ed.nodeChanged();\r
13351                         } else\r
13352                                 performCaretAction('remove', name, vars);\r
13353                 };\r
13354 \r
13355                 function toggle(name, vars, node) {\r
13356                         if (match(name, vars, node))\r
13357                                 remove(name, vars, node);\r
13358                         else\r
13359                                 apply(name, vars, node);\r
13360                 };\r
13361 \r
13362                 function matchNode(node, name, vars, similar) {\r
13363                         var formatList = get(name), format, i, classes;\r
13364 \r
13365                         function matchItems(node, format, item_name) {\r
13366                                 var key, value, items = format[item_name], i;\r
13367 \r
13368                                 // Check all items\r
13369                                 if (items) {\r
13370                                         // Non indexed object\r
13371                                         if (items.length === undefined) {\r
13372                                                 for (key in items) {\r
13373                                                         if (items.hasOwnProperty(key)) {\r
13374                                                                 if (item_name === 'attributes')\r
13375                                                                         value = dom.getAttrib(node, key);\r
13376                                                                 else\r
13377                                                                         value = getStyle(node, key);\r
13378 \r
13379                                                                 if (similar && !value && !format.exact)\r
13380                                                                         return;\r
13381 \r
13382                                                                 if ((!similar || format.exact) && !isEq(value, replaceVars(items[key], vars)))\r
13383                                                                         return;\r
13384                                                         }\r
13385                                                 }\r
13386                                         } else {\r
13387                                                 // Only one match needed for indexed arrays\r
13388                                                 for (i = 0; i < items.length; i++) {\r
13389                                                         if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i]))\r
13390                                                                 return format;\r
13391                                                 }\r
13392                                         }\r
13393                                 }\r
13394 \r
13395                                 return format;\r
13396                         };\r
13397 \r
13398                         if (formatList && node) {\r
13399                                 // Check each format in list\r
13400                                 for (i = 0; i < formatList.length; i++) {\r
13401                                         format = formatList[i];\r
13402 \r
13403                                         // Name name, attributes, styles and classes\r
13404                                         if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) {\r
13405                                                 // Match classes\r
13406                                                 if (classes = format.classes) {\r
13407                                                         for (i = 0; i < classes.length; i++) {\r
13408                                                                 if (!dom.hasClass(node, classes[i]))\r
13409                                                                         return;\r
13410                                                         }\r
13411                                                 }\r
13412 \r
13413                                                 return format;\r
13414                                         }\r
13415                                 }\r
13416                         }\r
13417                 };\r
13418 \r
13419                 function match(name, vars, node) {\r
13420                         var startNode, i;\r
13421 \r
13422                         function matchParents(node) {\r
13423                                 // Find first node with similar format settings\r
13424                                 node = dom.getParent(node, function(node) {\r
13425                                         return !!matchNode(node, name, vars, true);\r
13426                                 });\r
13427 \r
13428                                 // Do an exact check on the similar format element\r
13429                                 return matchNode(node, name, vars);\r
13430                         };\r
13431 \r
13432                         // Check specified node\r
13433                         if (node)\r
13434                                 return matchParents(node);\r
13435 \r
13436                         // Check pending formats\r
13437                         if (selection.isCollapsed()) {\r
13438                                 for (i = pendingFormats.apply.length - 1; i >= 0; i--) {\r
13439                                         if (pendingFormats.apply[i].name == name)\r
13440                                                 return true;\r
13441                                 }\r
13442 \r
13443                                 for (i = pendingFormats.remove.length - 1; i >= 0; i--) {\r
13444                                         if (pendingFormats.remove[i].name == name)\r
13445                                                 return false;\r
13446                                 }\r
13447 \r
13448                                 return matchParents(selection.getNode());\r
13449                         }\r
13450 \r
13451                         // Check selected node\r
13452                         node = selection.getNode();\r
13453                         if (matchParents(node))\r
13454                                 return TRUE;\r
13455 \r
13456                         // Check start node if it's different\r
13457                         startNode = selection.getStart();\r
13458                         if (startNode != node) {\r
13459                                 if (matchParents(startNode))\r
13460                                         return TRUE;\r
13461                         }\r
13462 \r
13463                         return FALSE;\r
13464                 };\r
13465 \r
13466                 function matchAll(names, vars) {\r
13467                         var startElement, matchedFormatNames = [], checkedMap = {}, i, ni, name;\r
13468 \r
13469                         // If the selection is collapsed then check pending formats\r
13470                         if (selection.isCollapsed()) {\r
13471                                 for (ni = 0; ni < names.length; ni++) {\r
13472                                         // If the name is to be removed, then stop it from being added\r
13473                                         for (i = pendingFormats.remove.length - 1; i >= 0; i--) {\r
13474                                                 name = names[ni];\r
13475 \r
13476                                                 if (pendingFormats.remove[i].name == name) {\r
13477                                                         checkedMap[name] = true;\r
13478                                                         break;\r
13479                                                 }\r
13480                                         }\r
13481                                 }\r
13482 \r
13483                                 // If the format is to be applied\r
13484                                 for (i = pendingFormats.apply.length - 1; i >= 0; i--) {\r
13485                                         for (ni = 0; ni < names.length; ni++) {\r
13486                                                 name = names[ni];\r
13487 \r
13488                                                 if (!checkedMap[name] && pendingFormats.apply[i].name == name) {\r
13489                                                         checkedMap[name] = true;\r
13490                                                         matchedFormatNames.push(name);\r
13491                                                 }\r
13492                                         }\r
13493                                 }\r
13494                         }\r
13495 \r
13496                         // Check start of selection for formats\r
13497                         startElement = selection.getStart();\r
13498                         dom.getParent(startElement, function(node) {\r
13499                                 var i, name;\r
13500 \r
13501                                 for (i = 0; i < names.length; i++) {\r
13502                                         name = names[i];\r
13503 \r
13504                                         if (!checkedMap[name] && matchNode(node, name, vars)) {\r
13505                                                 checkedMap[name] = true;\r
13506                                                 matchedFormatNames.push(name);\r
13507                                         }\r
13508                                 }\r
13509                         });\r
13510 \r
13511                         return matchedFormatNames;\r
13512                 };\r
13513 \r
13514                 function canApply(name) {\r
13515                         var formatList = get(name), startNode, parents, i, x, selector;\r
13516 \r
13517                         if (formatList) {\r
13518                                 startNode = selection.getStart();\r
13519                                 parents = getParents(startNode);\r
13520 \r
13521                                 for (x = formatList.length - 1; x >= 0; x--) {\r
13522                                         selector = formatList[x].selector;\r
13523 \r
13524                                         // Format is not selector based, then always return TRUE\r
13525                                         if (!selector)\r
13526                                                 return TRUE;\r
13527 \r
13528                                         for (i = parents.length - 1; i >= 0; i--) {\r
13529                                                 if (dom.is(parents[i], selector))\r
13530                                                         return TRUE;\r
13531                                         }\r
13532                                 }\r
13533                         }\r
13534 \r
13535                         return FALSE;\r
13536                 };\r
13537 \r
13538                 // Expose to public\r
13539                 tinymce.extend(this, {\r
13540                         get : get,\r
13541                         register : register,\r
13542                         apply : apply,\r
13543                         remove : remove,\r
13544                         toggle : toggle,\r
13545                         match : match,\r
13546                         matchAll : matchAll,\r
13547                         matchNode : matchNode,\r
13548                         canApply : canApply\r
13549                 });\r
13550 \r
13551                 // Private functions\r
13552 \r
13553                 function matchName(node, format) {\r
13554                         // Check for inline match\r
13555                         if (isEq(node, format.inline))\r
13556                                 return TRUE;\r
13557 \r
13558                         // Check for block match\r
13559                         if (isEq(node, format.block))\r
13560                                 return TRUE;\r
13561 \r
13562                         // Check for selector match\r
13563                         if (format.selector)\r
13564                                 return dom.is(node, format.selector);\r
13565                 };\r
13566 \r
13567                 function isEq(str1, str2) {\r
13568                         str1 = str1 || '';\r
13569                         str2 = str2 || '';\r
13570 \r
13571                         str1 = '' + (str1.nodeName || str1);\r
13572                         str2 = '' + (str2.nodeName || str2);\r
13573 \r
13574                         return str1.toLowerCase() == str2.toLowerCase();\r
13575                 };\r
13576 \r
13577                 function getStyle(node, name) {\r
13578                         var styleVal = dom.getStyle(node, name);\r
13579 \r
13580                         // Force the format to hex\r
13581                         if (name == 'color' || name == 'backgroundColor')\r
13582                                 styleVal = dom.toHex(styleVal);\r
13583 \r
13584                         // Opera will return bold as 700\r
13585                         if (name == 'fontWeight' && styleVal == 700)\r
13586                                 styleVal = 'bold';\r
13587 \r
13588                         return '' + styleVal;\r
13589                 };\r
13590 \r
13591                 function replaceVars(value, vars) {\r
13592                         if (typeof(value) != "string")\r
13593                                 value = value(vars);\r
13594                         else if (vars) {\r
13595                                 value = value.replace(/%(\w+)/g, function(str, name) {\r
13596                                         return vars[name] || str;\r
13597                                 });\r
13598                         }\r
13599 \r
13600                         return value;\r
13601                 };\r
13602 \r
13603                 function isWhiteSpaceNode(node) {\r
13604                         return node && node.nodeType === 3 && /^([\s\r\n]+|)$/.test(node.nodeValue);\r
13605                 };\r
13606 \r
13607                 function wrap(node, name, attrs) {\r
13608                         var wrapper = dom.create(name, attrs);\r
13609 \r
13610                         node.parentNode.insertBefore(wrapper, node);\r
13611                         wrapper.appendChild(node);\r
13612 \r
13613                         return wrapper;\r
13614                 };\r
13615 \r
13616                 function expandRng(rng, format, remove) {\r
13617                         var startContainer = rng.startContainer,\r
13618                                 startOffset = rng.startOffset,\r
13619                                 endContainer = rng.endContainer,\r
13620                                 endOffset = rng.endOffset, sibling, lastIdx;\r
13621 \r
13622                         // This function walks up the tree if there is no siblings before/after the node\r
13623                         function findParentContainer(container, child_name, sibling_name, root) {\r
13624                                 var parent, child;\r
13625 \r
13626                                 root = root || dom.getRoot();\r
13627 \r
13628                                 for (;;) {\r
13629                                         // Check if we can move up are we at root level or body level\r
13630                                         parent = container.parentNode;\r
13631 \r
13632                                         // Stop expanding on block elements or root depending on format\r
13633                                         if (parent == root || (!format[0].block_expand && isBlock(parent)))\r
13634                                                 return container;\r
13635 \r
13636                                         for (sibling = parent[child_name]; sibling && sibling != container; sibling = sibling[sibling_name]) {\r
13637                                                 if (sibling.nodeType == 1 && !isBookmarkNode(sibling))\r
13638                                                         return container;\r
13639 \r
13640                                                 if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling))\r
13641                                                         return container;\r
13642                                         }\r
13643 \r
13644                                         container = container.parentNode;\r
13645                                 }\r
13646 \r
13647                                 return container;\r
13648                         };\r
13649 \r
13650                         // If index based start position then resolve it\r
13651                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {\r
13652                                 lastIdx = startContainer.childNodes.length - 1;\r
13653                                 startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset];\r
13654 \r
13655                                 if (startContainer.nodeType == 3)\r
13656                                         startOffset = 0;\r
13657                         }\r
13658 \r
13659                         // If index based end position then resolve it\r
13660                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {\r
13661                                 lastIdx = endContainer.childNodes.length - 1;\r
13662                                 endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1];\r
13663 \r
13664                                 if (endContainer.nodeType == 3)\r
13665                                         endOffset = endContainer.nodeValue.length;\r
13666                         }\r
13667 \r
13668                         // Exclude bookmark nodes if possible\r
13669                         if (isBookmarkNode(startContainer.parentNode))\r
13670                                 startContainer = startContainer.parentNode;\r
13671 \r
13672                         if (isBookmarkNode(startContainer))\r
13673                                 startContainer = startContainer.nextSibling || startContainer;\r
13674 \r
13675                         if (isBookmarkNode(endContainer.parentNode))\r
13676                                 endContainer = endContainer.parentNode;\r
13677 \r
13678                         if (isBookmarkNode(endContainer))\r
13679                                 endContainer = endContainer.previousSibling || endContainer;\r
13680 \r
13681                         // Move start/end point up the tree if the leaves are sharp and if we are in different containers\r
13682                         // Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!\r
13683                         // This will reduce the number of wrapper elements that needs to be created\r
13684                         // Move start point up the tree\r
13685                         if (format[0].inline || format[0].block_expand) {\r
13686                                 startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');\r
13687                                 endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');\r
13688                         }\r
13689 \r
13690                         // Expand start/end container to matching selector\r
13691                         if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) {\r
13692                                 function findSelectorEndPoint(container, sibling_name) {\r
13693                                         var parents, i, y;\r
13694 \r
13695                                         if (container.nodeType == 3 && container.nodeValue.length == 0 && container[sibling_name])\r
13696                                                 container = container[sibling_name];\r
13697 \r
13698                                         parents = getParents(container);\r
13699                                         for (i = 0; i < parents.length; i++) {\r
13700                                                 for (y = 0; y < format.length; y++) {\r
13701                                                         if (dom.is(parents[i], format[y].selector))\r
13702                                                                 return parents[i];\r
13703                                                 }\r
13704                                         }\r
13705 \r
13706                                         return container;\r
13707                                 };\r
13708 \r
13709                                 // Find new startContainer/endContainer if there is better one\r
13710                                 startContainer = findSelectorEndPoint(startContainer, 'previousSibling');\r
13711                                 endContainer = findSelectorEndPoint(endContainer, 'nextSibling');\r
13712                         }\r
13713 \r
13714                         // Expand start/end container to matching block element or text node\r
13715                         if (format[0].block || format[0].selector) {\r
13716                                 function findBlockEndPoint(container, sibling_name, sibling_name2) {\r
13717                                         var node;\r
13718 \r
13719                                         // Expand to block of similar type\r
13720                                         if (!format[0].wrapper)\r
13721                                                 node = dom.getParent(container, format[0].block);\r
13722 \r
13723                                         // Expand to first wrappable block element or any block element\r
13724                                         if (!node)\r
13725                                                 node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, isBlock);\r
13726 \r
13727                                         // Exclude inner lists from wrapping\r
13728                                         if (node && format[0].wrapper)\r
13729                                                 node = getParents(node, 'ul,ol').reverse()[0] || node;\r
13730 \r
13731                                         // Didn't find a block element look for first/last wrappable element\r
13732                                         if (!node) {\r
13733                                                 node = container;\r
13734 \r
13735                                                 while (node[sibling_name] && !isBlock(node[sibling_name])) {\r
13736                                                         node = node[sibling_name];\r
13737 \r
13738                                                         // Break on BR but include it will be removed later on\r
13739                                                         // we can't remove it now since we need to check if it can be wrapped\r
13740                                                         if (isEq(node, 'br'))\r
13741                                                                 break;\r
13742                                                 }\r
13743                                         }\r
13744 \r
13745                                         return node || container;\r
13746                                 };\r
13747 \r
13748                                 // Find new startContainer/endContainer if there is better one\r
13749                                 startContainer = findBlockEndPoint(startContainer, 'previousSibling');\r
13750                                 endContainer = findBlockEndPoint(endContainer, 'nextSibling');\r
13751 \r
13752                                 // Non block element then try to expand up the leaf\r
13753                                 if (format[0].block) {\r
13754                                         if (!isBlock(startContainer))\r
13755                                                 startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');\r
13756 \r
13757                                         if (!isBlock(endContainer))\r
13758                                                 endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');\r
13759                                 }\r
13760                         }\r
13761 \r
13762                         // Setup index for startContainer\r
13763                         if (startContainer.nodeType == 1) {\r
13764                                 startOffset = nodeIndex(startContainer);\r
13765                                 startContainer = startContainer.parentNode;\r
13766                         }\r
13767 \r
13768                         // Setup index for endContainer\r
13769                         if (endContainer.nodeType == 1) {\r
13770                                 endOffset = nodeIndex(endContainer) + 1;\r
13771                                 endContainer = endContainer.parentNode;\r
13772                         }\r
13773 \r
13774                         // Return new range like object\r
13775                         return {\r
13776                                 startContainer : startContainer,\r
13777                                 startOffset : startOffset,\r
13778                                 endContainer : endContainer,\r
13779                                 endOffset : endOffset\r
13780                         };\r
13781                 }\r
13782 \r
13783                 function removeFormat(format, vars, node, compare_node) {\r
13784                         var i, attrs, stylesModified;\r
13785 \r
13786                         // Check if node matches format\r
13787                         if (!matchName(node, format))\r
13788                                 return FALSE;\r
13789 \r
13790                         // Should we compare with format attribs and styles\r
13791                         if (format.remove != 'all') {\r
13792                                 // Remove styles\r
13793                                 each(format.styles, function(value, name) {\r
13794                                         value = replaceVars(value, vars);\r
13795 \r
13796                                         // Indexed array\r
13797                                         if (typeof(name) === 'number') {\r
13798                                                 name = value;\r
13799                                                 compare_node = 0;\r
13800                                         }\r
13801 \r
13802                                         if (!compare_node || isEq(getStyle(compare_node, name), value))\r
13803                                                 dom.setStyle(node, name, '');\r
13804 \r
13805                                         stylesModified = 1;\r
13806                                 });\r
13807 \r
13808                                 // Remove style attribute if it's empty\r
13809                                 if (stylesModified && dom.getAttrib(node, 'style') == '') {\r
13810                                         node.removeAttribute('style');\r
13811                                         node.removeAttribute('_mce_style');\r
13812                                 }\r
13813 \r
13814                                 // Remove attributes\r
13815                                 each(format.attributes, function(value, name) {\r
13816                                         var valueOut;\r
13817 \r
13818                                         value = replaceVars(value, vars);\r
13819 \r
13820                                         // Indexed array\r
13821                                         if (typeof(name) === 'number') {\r
13822                                                 name = value;\r
13823                                                 compare_node = 0;\r
13824                                         }\r
13825 \r
13826                                         if (!compare_node || isEq(dom.getAttrib(compare_node, name), value)) {\r
13827                                                 // Keep internal classes\r
13828                                                 if (name == 'class') {\r
13829                                                         value = dom.getAttrib(node, name);\r
13830                                                         if (value) {\r
13831                                                                 // Build new class value where everything is removed except the internal prefixed classes\r
13832                                                                 valueOut = '';\r
13833                                                                 each(value.split(/\s+/), function(cls) {\r
13834                                                                         if (/mce\w+/.test(cls))\r
13835                                                                                 valueOut += (valueOut ? ' ' : '') + cls;\r
13836                                                                 });\r
13837 \r
13838                                                                 // We got some internal classes left\r
13839                                                                 if (valueOut) {\r
13840                                                                         dom.setAttrib(node, name, valueOut);\r
13841                                                                         return;\r
13842                                                                 }\r
13843                                                         }\r
13844                                                 }\r
13845 \r
13846                                                 // IE6 has a bug where the attribute doesn't get removed correctly\r
13847                                                 if (name == "class")\r
13848                                                         node.removeAttribute('className');\r
13849 \r
13850                                                 // Remove mce prefixed attributes\r
13851                                                 if (MCE_ATTR_RE.test(name))\r
13852                                                         node.removeAttribute('_mce_' + name);\r
13853 \r
13854                                                 node.removeAttribute(name);\r
13855                                         }\r
13856                                 });\r
13857 \r
13858                                 // Remove classes\r
13859                                 each(format.classes, function(value) {\r
13860                                         value = replaceVars(value, vars);\r
13861 \r
13862                                         if (!compare_node || dom.hasClass(compare_node, value))\r
13863                                                 dom.removeClass(node, value);\r
13864                                 });\r
13865 \r
13866                                 // Check for non internal attributes\r
13867                                 attrs = dom.getAttribs(node);\r
13868                                 for (i = 0; i < attrs.length; i++) {\r
13869                                         if (attrs[i].nodeName.indexOf('_') !== 0)\r
13870                                                 return FALSE;\r
13871                                 }\r
13872                         }\r
13873 \r
13874                         // Remove the inline child if it's empty for example <b> or <span>\r
13875                         if (format.remove != 'none') {\r
13876                                 removeNode(node, format);\r
13877                                 return TRUE;\r
13878                         }\r
13879                 };\r
13880 \r
13881                 function removeNode(node, format) {\r
13882                         var parentNode = node.parentNode, rootBlockElm;\r
13883 \r
13884                         if (format.block) {\r
13885                                 if (!forcedRootBlock) {\r
13886                                         function find(node, next, inc) {\r
13887                                                 node = getNonWhiteSpaceSibling(node, next, inc);\r
13888 \r
13889                                                 return !node || (node.nodeName == 'BR' || isBlock(node));\r
13890                                         };\r
13891 \r
13892                                         // Append BR elements if needed before we remove the block\r
13893                                         if (isBlock(node) && !isBlock(parentNode)) {\r
13894                                                 if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1))\r
13895                                                         node.insertBefore(dom.create('br'), node.firstChild);\r
13896 \r
13897                                                 if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1))\r
13898                                                         node.appendChild(dom.create('br'));\r
13899                                         }\r
13900                                 } else {\r
13901                                         // Wrap the block in a forcedRootBlock if we are at the root of document\r
13902                                         if (parentNode == dom.getRoot()) {\r
13903                                                 if (!format.list_block || !isEq(node, format.list_block)) {\r
13904                                                         each(tinymce.grep(node.childNodes), function(node) {\r
13905                                                                 if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) {\r
13906                                                                         if (!rootBlockElm)\r
13907                                                                                 rootBlockElm = wrap(node, forcedRootBlock);\r
13908                                                                         else\r
13909                                                                                 rootBlockElm.appendChild(node);\r
13910                                                                 } else\r
13911                                                                         rootBlockElm = 0;\r
13912                                                         });\r
13913                                                 }\r
13914                                         }\r
13915                                 }\r
13916                         }\r
13917 \r
13918                         // Never remove nodes that isn't the specified inline element if a selector is specified too\r
13919                         if (format.selector && format.inline && !isEq(format.inline, node))\r
13920                                 return;\r
13921 \r
13922                         dom.remove(node, 1);\r
13923                 };\r
13924 \r
13925                 function getNonWhiteSpaceSibling(node, next, inc) {\r
13926                         if (node) {\r
13927                                 next = next ? 'nextSibling' : 'previousSibling';\r
13928 \r
13929                                 for (node = inc ? node : node[next]; node; node = node[next]) {\r
13930                                         if (node.nodeType == 1 || !isWhiteSpaceNode(node))\r
13931                                                 return node;\r
13932                                 }\r
13933                         }\r
13934                 };\r
13935 \r
13936                 function isBookmarkNode(node) {\r
13937                         return node && node.nodeType == 1 && node.getAttribute('_mce_type') == 'bookmark';\r
13938                 };\r
13939 \r
13940                 function mergeSiblings(prev, next) {\r
13941                         var marker, sibling, tmpSibling;\r
13942 \r
13943                         function compareElements(node1, node2) {\r
13944                                 // Not the same name\r
13945                                 if (node1.nodeName != node2.nodeName)\r
13946                                         return FALSE;\r
13947 \r
13948                                 function getAttribs(node) {\r
13949                                         var attribs = {};\r
13950 \r
13951                                         each(dom.getAttribs(node), function(attr) {\r
13952                                                 var name = attr.nodeName.toLowerCase();\r
13953 \r
13954                                                 // Don't compare internal attributes or style\r
13955                                                 if (name.indexOf('_') !== 0 && name !== 'style')\r
13956                                                         attribs[name] = dom.getAttrib(node, name);\r
13957                                         });\r
13958 \r
13959                                         return attribs;\r
13960                                 };\r
13961 \r
13962                                 function compareObjects(obj1, obj2) {\r
13963                                         var value, name;\r
13964 \r
13965                                         for (name in obj1) {\r
13966                                                 // Obj1 has item obj2 doesn't have\r
13967                                                 if (obj1.hasOwnProperty(name)) {\r
13968                                                         value = obj2[name];\r
13969 \r
13970                                                         // Obj2 doesn't have obj1 item\r
13971                                                         if (value === undefined)\r
13972                                                                 return FALSE;\r
13973 \r
13974                                                         // Obj2 item has a different value\r
13975                                                         if (obj1[name] != value)\r
13976                                                                 return FALSE;\r
13977 \r
13978                                                         // Delete similar value\r
13979                                                         delete obj2[name];\r
13980                                                 }\r
13981                                         }\r
13982 \r
13983                                         // Check if obj 2 has something obj 1 doesn't have\r
13984                                         for (name in obj2) {\r
13985                                                 // Obj2 has item obj1 doesn't have\r
13986                                                 if (obj2.hasOwnProperty(name))\r
13987                                                         return FALSE;\r
13988                                         }\r
13989 \r
13990                                         return TRUE;\r
13991                                 };\r
13992 \r
13993                                 // Attribs are not the same\r
13994                                 if (!compareObjects(getAttribs(node1), getAttribs(node2)))\r
13995                                         return FALSE;\r
13996 \r
13997                                 // Styles are not the same\r
13998                                 if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style'))))\r
13999                                         return FALSE;\r
14000 \r
14001                                 return TRUE;\r
14002                         };\r
14003 \r
14004                         // Check if next/prev exists and that they are elements\r
14005                         if (prev && next) {\r
14006                                 function findElementSibling(node, sibling_name) {\r
14007                                         for (sibling = node; sibling; sibling = sibling[sibling_name]) {\r
14008                                                 if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling))\r
14009                                                         return node;\r
14010 \r
14011                                                 if (sibling.nodeType == 1 && !isBookmarkNode(sibling))\r
14012                                                         return sibling;\r
14013                                         }\r
14014 \r
14015                                         return node;\r
14016                                 };\r
14017 \r
14018                                 // If previous sibling is empty then jump over it\r
14019                                 prev = findElementSibling(prev, 'previousSibling');\r
14020                                 next = findElementSibling(next, 'nextSibling');\r
14021 \r
14022                                 // Compare next and previous nodes\r
14023                                 if (compareElements(prev, next)) {\r
14024                                         // Append nodes between\r
14025                                         for (sibling = prev.nextSibling; sibling && sibling != next;) {\r
14026                                                 tmpSibling = sibling;\r
14027                                                 sibling = sibling.nextSibling;\r
14028                                                 prev.appendChild(tmpSibling);\r
14029                                         }\r
14030 \r
14031                                         // Remove next node\r
14032                                         dom.remove(next);\r
14033 \r
14034                                         // Move children into prev node\r
14035                                         each(tinymce.grep(next.childNodes), function(node) {\r
14036                                                 prev.appendChild(node);\r
14037                                         });\r
14038 \r
14039                                         return prev;\r
14040                                 }\r
14041                         }\r
14042 \r
14043                         return next;\r
14044                 };\r
14045 \r
14046                 function isTextBlock(name) {\r
14047                         return /^(h[1-6]|p|div|pre|address|dl|dt|dd)$/.test(name);\r
14048                 };\r
14049 \r
14050                 function getContainer(rng, start) {\r
14051                         var container, offset, lastIdx;\r
14052 \r
14053                         container = rng[start ? 'startContainer' : 'endContainer'];\r
14054                         offset = rng[start ? 'startOffset' : 'endOffset'];\r
14055 \r
14056                         if (container.nodeType == 1) {\r
14057                                 lastIdx = container.childNodes.length - 1;\r
14058 \r
14059                                 if (!start && offset)\r
14060                                         offset--;\r
14061 \r
14062                                 container = container.childNodes[offset > lastIdx ? lastIdx : offset];\r
14063                         }\r
14064 \r
14065                         return container;\r
14066                 };\r
14067 \r
14068                 function performCaretAction(type, name, vars) {\r
14069                         var i, currentPendingFormats = pendingFormats[type],\r
14070                                 otherPendingFormats = pendingFormats[type == 'apply' ? 'remove' : 'apply'];\r
14071 \r
14072                         function hasPending() {\r
14073                                 return pendingFormats.apply.length || pendingFormats.remove.length;\r
14074                         };\r
14075 \r
14076                         function resetPending() {\r
14077                                 pendingFormats.apply = [];\r
14078                                 pendingFormats.remove = [];\r
14079                         };\r
14080 \r
14081                         function perform(caret_node) {\r
14082                                 // Apply pending formats\r
14083                                 each(pendingFormats.apply.reverse(), function(item) {\r
14084                                         apply(item.name, item.vars, caret_node);\r
14085                                 });\r
14086 \r
14087                                 // Remove pending formats\r
14088                                 each(pendingFormats.remove.reverse(), function(item) {\r
14089                                         remove(item.name, item.vars, caret_node);\r
14090                                 });\r
14091 \r
14092                                 dom.remove(caret_node, 1);\r
14093                                 resetPending();\r
14094                         };\r
14095 \r
14096                         // Check if it already exists then ignore it\r
14097                         for (i = currentPendingFormats.length - 1; i >= 0; i--) {\r
14098                                 if (currentPendingFormats[i].name == name)\r
14099                                         return;\r
14100                         }\r
14101 \r
14102                         currentPendingFormats.push({name : name, vars : vars});\r
14103 \r
14104                         // Check if it's in the other type, then remove it\r
14105                         for (i = otherPendingFormats.length - 1; i >= 0; i--) {\r
14106                                 if (otherPendingFormats[i].name == name)\r
14107                                         otherPendingFormats.splice(i, 1);\r
14108                         }\r
14109 \r
14110                         // Pending apply or remove formats\r
14111                         if (hasPending()) {\r
14112                                 ed.getDoc().execCommand('FontName', false, 'mceinline');\r
14113                                 pendingFormats.lastRng = selection.getRng();\r
14114 \r
14115                                 // IE will convert the current word\r
14116                                 each(dom.select('font,span'), function(node) {\r
14117                                         var bookmark;\r
14118 \r
14119                                         if (isCaretNode(node)) {\r
14120                                                 bookmark = selection.getBookmark();\r
14121                                                 perform(node);\r
14122                                                 selection.moveToBookmark(bookmark);\r
14123                                                 ed.nodeChanged();\r
14124                                         }\r
14125                                 });\r
14126 \r
14127                                 // Only register listeners once if we need to\r
14128                                 if (!pendingFormats.isListening && hasPending()) {\r
14129                                         pendingFormats.isListening = true;\r
14130 \r
14131                                         each('onKeyDown,onKeyUp,onKeyPress,onMouseUp'.split(','), function(event) {\r
14132                                                 ed[event].addToTop(function(ed, e) {\r
14133                                                         // Do we have pending formats and is the selection moved has moved\r
14134                                                         if (hasPending() && !tinymce.dom.RangeUtils.compareRanges(pendingFormats.lastRng, selection.getRng())) {\r
14135                                                                 each(dom.select('font,span'), function(node) {\r
14136                                                                         var textNode, rng;\r
14137 \r
14138                                                                         // Look for marker\r
14139                                                                         if (isCaretNode(node)) {\r
14140                                                                                 textNode = node.firstChild;\r
14141 \r
14142                                                                                 if (textNode) {\r
14143                                                                                         perform(node);\r
14144 \r
14145                                                                                         rng = dom.createRng();\r
14146                                                                                         rng.setStart(textNode, textNode.nodeValue.length);\r
14147                                                                                         rng.setEnd(textNode, textNode.nodeValue.length);\r
14148                                                                                         selection.setRng(rng);\r
14149                                                                                         ed.nodeChanged();\r
14150                                                                                 } else\r
14151                                                                                         dom.remove(node);\r
14152                                                                         }\r
14153                                                                 });\r
14154 \r
14155                                                                 // Always unbind and clear pending styles on keyup\r
14156                                                                 if (e.type == 'keyup' || e.type == 'mouseup')\r
14157                                                                         resetPending();\r
14158                                                         }\r
14159                                                 });\r
14160                                         });\r
14161                                 }\r
14162                         }\r
14163                 };\r
14164         };\r
14165 })(tinymce);\r
14166 \r
14167 tinymce.onAddEditor.add(function(tinymce, ed) {\r
14168         var filters, fontSizes, dom, settings = ed.settings;\r
14169 \r
14170         if (settings.inline_styles) {\r
14171                 fontSizes = tinymce.explode(settings.font_size_style_values);\r
14172 \r
14173                 function replaceWithSpan(node, styles) {\r
14174                         tinymce.each(styles, function(value, name) {\r
14175                                 if (value)\r
14176                                         dom.setStyle(node, name, value);\r
14177                         });\r
14178 \r
14179                         dom.rename(node, 'span');\r
14180                 };\r
14181 \r
14182                 filters = {\r
14183                         font : function(dom, node) {\r
14184                                 replaceWithSpan(node, {\r
14185                                         backgroundColor : node.style.backgroundColor,\r
14186                                         color : node.color,\r
14187                                         fontFamily : node.face,\r
14188                                         fontSize : fontSizes[parseInt(node.size) - 1]\r
14189                                 });\r
14190                         },\r
14191 \r
14192                         u : function(dom, node) {\r
14193                                 replaceWithSpan(node, {\r
14194                                         textDecoration : 'underline'\r
14195                                 });\r
14196                         },\r
14197 \r
14198                         strike : function(dom, node) {\r
14199                                 replaceWithSpan(node, {\r
14200                                         textDecoration : 'line-through'\r
14201                                 });\r
14202                         }\r
14203                 };\r
14204 \r
14205                 function convert(editor, params) {\r
14206                         dom = editor.dom;\r
14207 \r
14208                         if (settings.convert_fonts_to_spans) {\r
14209                                 tinymce.each(dom.select('font,u,strike', params.node), function(node) {\r
14210                                         filters[node.nodeName.toLowerCase()](ed.dom, node);\r
14211                                 });\r
14212                         }\r
14213                 };\r
14214 \r
14215                 ed.onPreProcess.add(convert);\r
14216 \r
14217                 ed.onInit.add(function() {\r
14218                         ed.selection.onSetContent.add(convert);\r
14219                 });\r
14220         }\r
14221 });\r
14222 \r