upgrade 0.11.2 pour XML, MySql et SQLite
[mtweb] / mw / libs / tiny_mce / tiny_mce_src.js
1 (function(win) {\r
2         var whiteSpaceRe = /^\s*|\s*$/g,\r
3                 undefined;\r
4 \r
5         var tinymce = {\r
6                 majorVersion : '3',\r
7 \r
8                 minorVersion : '3.8',\r
9 \r
10                 releaseDate : '2010-06-30',\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)(_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                 };\r
434 \r
435         // Initialize the API\r
436         tinymce._init();\r
437 \r
438         // Expose tinymce namespace to the global namespace (window)\r
439         win.tinymce = win.tinyMCE = tinymce;\r
440 })(window);\r
441 \r
442 \r
443 tinymce.create('tinymce.util.Dispatcher', {\r
444         scope : null,\r
445         listeners : null,\r
446 \r
447         Dispatcher : function(s) {\r
448                 this.scope = s || this;\r
449                 this.listeners = [];\r
450         },\r
451 \r
452         add : function(cb, s) {\r
453                 this.listeners.push({cb : cb, scope : s || this.scope});\r
454 \r
455                 return cb;\r
456         },\r
457 \r
458         addToTop : function(cb, s) {\r
459                 this.listeners.unshift({cb : cb, scope : s || this.scope});\r
460 \r
461                 return cb;\r
462         },\r
463 \r
464         remove : function(cb) {\r
465                 var l = this.listeners, o = null;\r
466 \r
467                 tinymce.each(l, function(c, i) {\r
468                         if (cb == c.cb) {\r
469                                 o = cb;\r
470                                 l.splice(i, 1);\r
471                                 return false;\r
472                         }\r
473                 });\r
474 \r
475                 return o;\r
476         },\r
477 \r
478         dispatch : function() {\r
479                 var s, a = arguments, i, li = this.listeners, c;\r
480 \r
481                 // Needs to be a real loop since the listener count might change while looping\r
482                 // And this is also more efficient\r
483                 for (i = 0; i<li.length; i++) {\r
484                         c = li[i];\r
485                         s = c.cb.apply(c.scope, a);\r
486 \r
487                         if (s === false)\r
488                                 break;\r
489                 }\r
490 \r
491                 return s;\r
492         }\r
493 \r
494         });\r
495 \r
496 (function() {\r
497         var each = tinymce.each;\r
498 \r
499         tinymce.create('tinymce.util.URI', {\r
500                 URI : function(u, s) {\r
501                         var t = this, o, a, b;\r
502 \r
503                         // Trim whitespace\r
504                         u = tinymce.trim(u);\r
505 \r
506                         // Default settings\r
507                         s = t.settings = s || {};\r
508 \r
509                         // Strange app protocol or local anchor\r
510                         if (/^(mailto|tel|news|javascript|about|data):/i.test(u) || /^\s*#/.test(u)) {\r
511                                 t.source = u;\r
512                                 return;\r
513                         }\r
514 \r
515                         // Absolute path with no host, fake host and protocol\r
516                         if (u.indexOf('/') === 0 && u.indexOf('//') !== 0)\r
517                                 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;\r
518 \r
519                         // Relative path http:// or protocol relative //path\r
520                         if (!/^\w*:?\/\//.test(u))\r
521                                 u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u);\r
522 \r
523                         // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)\r
524                         u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something\r
525                         u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);\r
526                         each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {\r
527                                 var s = u[i];\r
528 \r
529                                 // Zope 3 workaround, they use @@something\r
530                                 if (s)\r
531                                         s = s.replace(/\(mce_at\)/g, '@@');\r
532 \r
533                                 t[v] = s;\r
534                         });\r
535 \r
536                         if (b = s.base_uri) {\r
537                                 if (!t.protocol)\r
538                                         t.protocol = b.protocol;\r
539 \r
540                                 if (!t.userInfo)\r
541                                         t.userInfo = b.userInfo;\r
542 \r
543                                 if (!t.port && t.host == 'mce_host')\r
544                                         t.port = b.port;\r
545 \r
546                                 if (!t.host || t.host == 'mce_host')\r
547                                         t.host = b.host;\r
548 \r
549                                 t.source = '';\r
550                         }\r
551 \r
552                         //t.path = t.path || '/';\r
553                 },\r
554 \r
555                 setPath : function(p) {\r
556                         var t = this;\r
557 \r
558                         p = /^(.*?)\/?(\w+)?$/.exec(p);\r
559 \r
560                         // Update path parts\r
561                         t.path = p[0];\r
562                         t.directory = p[1];\r
563                         t.file = p[2];\r
564 \r
565                         // Rebuild source\r
566                         t.source = '';\r
567                         t.getURI();\r
568                 },\r
569 \r
570                 toRelative : function(u) {\r
571                         var t = this, o;\r
572 \r
573                         if (u === "./")\r
574                                 return u;\r
575 \r
576                         u = new tinymce.util.URI(u, {base_uri : t});\r
577 \r
578                         // Not on same domain/port or protocol\r
579                         if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)\r
580                                 return u.getURI();\r
581 \r
582                         o = t.toRelPath(t.path, u.path);\r
583 \r
584                         // Add query\r
585                         if (u.query)\r
586                                 o += '?' + u.query;\r
587 \r
588                         // Add anchor\r
589                         if (u.anchor)\r
590                                 o += '#' + u.anchor;\r
591 \r
592                         return o;\r
593                 },\r
594         \r
595                 toAbsolute : function(u, nh) {\r
596                         var u = new tinymce.util.URI(u, {base_uri : this});\r
597 \r
598                         return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0);\r
599                 },\r
600 \r
601                 toRelPath : function(base, path) {\r
602                         var items, bp = 0, out = '', i, l;\r
603 \r
604                         // Split the paths\r
605                         base = base.substring(0, base.lastIndexOf('/'));\r
606                         base = base.split('/');\r
607                         items = path.split('/');\r
608 \r
609                         if (base.length >= items.length) {\r
610                                 for (i = 0, l = base.length; i < l; i++) {\r
611                                         if (i >= items.length || base[i] != items[i]) {\r
612                                                 bp = i + 1;\r
613                                                 break;\r
614                                         }\r
615                                 }\r
616                         }\r
617 \r
618                         if (base.length < items.length) {\r
619                                 for (i = 0, l = items.length; i < l; i++) {\r
620                                         if (i >= base.length || base[i] != items[i]) {\r
621                                                 bp = i + 1;\r
622                                                 break;\r
623                                         }\r
624                                 }\r
625                         }\r
626 \r
627                         if (bp == 1)\r
628                                 return path;\r
629 \r
630                         for (i = 0, l = base.length - (bp - 1); i < l; i++)\r
631                                 out += "../";\r
632 \r
633                         for (i = bp - 1, l = items.length; i < l; i++) {\r
634                                 if (i != bp - 1)\r
635                                         out += "/" + items[i];\r
636                                 else\r
637                                         out += items[i];\r
638                         }\r
639 \r
640                         return out;\r
641                 },\r
642 \r
643                 toAbsPath : function(base, path) {\r
644                         var i, nb = 0, o = [], tr, outPath;\r
645 \r
646                         // Split paths\r
647                         tr = /\/$/.test(path) ? '/' : '';\r
648                         base = base.split('/');\r
649                         path = path.split('/');\r
650 \r
651                         // Remove empty chunks\r
652                         each(base, function(k) {\r
653                                 if (k)\r
654                                         o.push(k);\r
655                         });\r
656 \r
657                         base = o;\r
658 \r
659                         // Merge relURLParts chunks\r
660                         for (i = path.length - 1, o = []; i >= 0; i--) {\r
661                                 // Ignore empty or .\r
662                                 if (path[i].length == 0 || path[i] == ".")\r
663                                         continue;\r
664 \r
665                                 // Is parent\r
666                                 if (path[i] == '..') {\r
667                                         nb++;\r
668                                         continue;\r
669                                 }\r
670 \r
671                                 // Move up\r
672                                 if (nb > 0) {\r
673                                         nb--;\r
674                                         continue;\r
675                                 }\r
676 \r
677                                 o.push(path[i]);\r
678                         }\r
679 \r
680                         i = base.length - nb;\r
681 \r
682                         // If /a/b/c or /\r
683                         if (i <= 0)\r
684                                 outPath = o.reverse().join('/');\r
685                         else\r
686                                 outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');\r
687 \r
688                         // Add front / if it's needed\r
689                         if (outPath.indexOf('/') !== 0)\r
690                                 outPath = '/' + outPath;\r
691 \r
692                         // Add traling / if it's needed\r
693                         if (tr && outPath.lastIndexOf('/') !== outPath.length - 1)\r
694                                 outPath += tr;\r
695 \r
696                         return outPath;\r
697                 },\r
698 \r
699                 getURI : function(nh) {\r
700                         var s, t = this;\r
701 \r
702                         // Rebuild source\r
703                         if (!t.source || nh) {\r
704                                 s = '';\r
705 \r
706                                 if (!nh) {\r
707                                         if (t.protocol)\r
708                                                 s += t.protocol + '://';\r
709 \r
710                                         if (t.userInfo)\r
711                                                 s += t.userInfo + '@';\r
712 \r
713                                         if (t.host)\r
714                                                 s += t.host;\r
715 \r
716                                         if (t.port)\r
717                                                 s += ':' + t.port;\r
718                                 }\r
719 \r
720                                 if (t.path)\r
721                                         s += t.path;\r
722 \r
723                                 if (t.query)\r
724                                         s += '?' + t.query;\r
725 \r
726                                 if (t.anchor)\r
727                                         s += '#' + t.anchor;\r
728 \r
729                                 t.source = s;\r
730                         }\r
731 \r
732                         return t.source;\r
733                 }\r
734         });\r
735 })();\r
736 \r
737 (function() {\r
738         var each = tinymce.each;\r
739 \r
740         tinymce.create('static tinymce.util.Cookie', {\r
741                 getHash : function(n) {\r
742                         var v = this.get(n), h;\r
743 \r
744                         if (v) {\r
745                                 each(v.split('&'), function(v) {\r
746                                         v = v.split('=');\r
747                                         h = h || {};\r
748                                         h[unescape(v[0])] = unescape(v[1]);\r
749                                 });\r
750                         }\r
751 \r
752                         return h;\r
753                 },\r
754 \r
755                 setHash : function(n, v, e, p, d, s) {\r
756                         var o = '';\r
757 \r
758                         each(v, function(v, k) {\r
759                                 o += (!o ? '' : '&') + escape(k) + '=' + escape(v);\r
760                         });\r
761 \r
762                         this.set(n, o, e, p, d, s);\r
763                 },\r
764 \r
765                 get : function(n) {\r
766                         var c = document.cookie, e, p = n + "=", b;\r
767 \r
768                         // Strict mode\r
769                         if (!c)\r
770                                 return;\r
771 \r
772                         b = c.indexOf("; " + p);\r
773 \r
774                         if (b == -1) {\r
775                                 b = c.indexOf(p);\r
776 \r
777                                 if (b != 0)\r
778                                         return null;\r
779                         } else\r
780                                 b += 2;\r
781 \r
782                         e = c.indexOf(";", b);\r
783 \r
784                         if (e == -1)\r
785                                 e = c.length;\r
786 \r
787                         return unescape(c.substring(b + p.length, e));\r
788                 },\r
789 \r
790                 set : function(n, v, e, p, d, s) {\r
791                         document.cookie = n + "=" + escape(v) +\r
792                                 ((e) ? "; expires=" + e.toGMTString() : "") +\r
793                                 ((p) ? "; path=" + escape(p) : "") +\r
794                                 ((d) ? "; domain=" + d : "") +\r
795                                 ((s) ? "; secure" : "");\r
796                 },\r
797 \r
798                 remove : function(n, p) {\r
799                         var d = new Date();\r
800 \r
801                         d.setTime(d.getTime() - 1000);\r
802 \r
803                         this.set(n, '', d, p, d);\r
804                 }\r
805         });\r
806 })();\r
807 \r
808 tinymce.create('static tinymce.util.JSON', {\r
809         serialize : function(o) {\r
810                 var i, v, s = tinymce.util.JSON.serialize, t;\r
811 \r
812                 if (o == null)\r
813                         return 'null';\r
814 \r
815                 t = typeof o;\r
816 \r
817                 if (t == 'string') {\r
818                         v = '\bb\tt\nn\ff\rr\""\'\'\\\\';\r
819 \r
820                         return '"' + o.replace(/([\u0080-\uFFFF\x00-\x1f\"])/g, function(a, b) {\r
821                                 i = v.indexOf(b);\r
822 \r
823                                 if (i + 1)\r
824                                         return '\\' + v.charAt(i + 1);\r
825 \r
826                                 a = b.charCodeAt().toString(16);\r
827 \r
828                                 return '\\u' + '0000'.substring(a.length) + a;\r
829                         }) + '"';\r
830                 }\r
831 \r
832                 if (t == 'object') {\r
833                         if (o.hasOwnProperty && o instanceof Array) {\r
834                                         for (i=0, v = '['; i<o.length; i++)\r
835                                                 v += (i > 0 ? ',' : '') + s(o[i]);\r
836 \r
837                                         return v + ']';\r
838                                 }\r
839 \r
840                                 v = '{';\r
841 \r
842                                 for (i in o)\r
843                                         v += typeof o[i] != 'function' ? (v.length > 1 ? ',"' : '"') + i + '":' + s(o[i]) : '';\r
844 \r
845                                 return v + '}';\r
846                 }\r
847 \r
848                 return '' + o;\r
849         },\r
850 \r
851         parse : function(s) {\r
852                 try {\r
853                         return eval('(' + s + ')');\r
854                 } catch (ex) {\r
855                         // Ignore\r
856                 }\r
857         }\r
858 \r
859         });\r
860 \r
861 tinymce.create('static tinymce.util.XHR', {\r
862         send : function(o) {\r
863                 var x, t, w = window, c = 0;\r
864 \r
865                 // Default settings\r
866                 o.scope = o.scope || this;\r
867                 o.success_scope = o.success_scope || o.scope;\r
868                 o.error_scope = o.error_scope || o.scope;\r
869                 o.async = o.async === false ? false : true;\r
870                 o.data = o.data || '';\r
871 \r
872                 function get(s) {\r
873                         x = 0;\r
874 \r
875                         try {\r
876                                 x = new ActiveXObject(s);\r
877                         } catch (ex) {\r
878                         }\r
879 \r
880                         return x;\r
881                 };\r
882 \r
883                 x = w.XMLHttpRequest ? new XMLHttpRequest() : get('Microsoft.XMLHTTP') || get('Msxml2.XMLHTTP');\r
884 \r
885                 if (x) {\r
886                         if (x.overrideMimeType)\r
887                                 x.overrideMimeType(o.content_type);\r
888 \r
889                         x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async);\r
890 \r
891                         if (o.content_type)\r
892                                 x.setRequestHeader('Content-Type', o.content_type);\r
893 \r
894                         x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\r
895 \r
896                         x.send(o.data);\r
897 \r
898                         function ready() {\r
899                                 if (!o.async || x.readyState == 4 || c++ > 10000) {\r
900                                         if (o.success && c < 10000 && x.status == 200)\r
901                                                 o.success.call(o.success_scope, '' + x.responseText, x, o);\r
902                                         else if (o.error)\r
903                                                 o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);\r
904 \r
905                                         x = null;\r
906                                 } else\r
907                                         w.setTimeout(ready, 10);\r
908                         };\r
909 \r
910                         // Syncronous request\r
911                         if (!o.async)\r
912                                 return ready();\r
913 \r
914                         // Wait for response, onReadyStateChange can not be used since it leaks memory in IE\r
915                         t = w.setTimeout(ready, 10);\r
916                 }\r
917         }\r
918 });\r
919 \r
920 (function() {\r
921         var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;\r
922 \r
923         tinymce.create('tinymce.util.JSONRequest', {\r
924                 JSONRequest : function(s) {\r
925                         this.settings = extend({\r
926                         }, s);\r
927                         this.count = 0;\r
928                 },\r
929 \r
930                 send : function(o) {\r
931                         var ecb = o.error, scb = o.success;\r
932 \r
933                         o = extend(this.settings, o);\r
934 \r
935                         o.success = function(c, x) {\r
936                                 c = JSON.parse(c);\r
937 \r
938                                 if (typeof(c) == 'undefined') {\r
939                                         c = {\r
940                                                 error : 'JSON Parse error.'\r
941                                         };\r
942                                 }\r
943 \r
944                                 if (c.error)\r
945                                         ecb.call(o.error_scope || o.scope, c.error, x);\r
946                                 else\r
947                                         scb.call(o.success_scope || o.scope, c.result);\r
948                         };\r
949 \r
950                         o.error = function(ty, x) {\r
951                                 ecb.call(o.error_scope || o.scope, ty, x);\r
952                         };\r
953 \r
954                         o.data = JSON.serialize({\r
955                                 id : o.id || 'c' + (this.count++),\r
956                                 method : o.method,\r
957                                 params : o.params\r
958                         });\r
959 \r
960                         // JSON content type for Ruby on rails. Bug: #1883287\r
961                         o.content_type = 'application/json';\r
962 \r
963                         XHR.send(o);\r
964                 },\r
965 \r
966                 'static' : {\r
967                         sendRPC : function(o) {\r
968                                 return new tinymce.util.JSONRequest().send(o);\r
969                         }\r
970                 }\r
971         });\r
972 }());\r
973 (function(tinymce) {\r
974         // Shorten names\r
975         var each = tinymce.each,\r
976                 is = tinymce.is,\r
977                 isWebKit = tinymce.isWebKit,\r
978                 isIE = tinymce.isIE,\r
979                 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
980                 boolAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'),\r
981                 mceAttribs = makeMap('src,href,style,coords,shape'),\r
982                 encodedChars = {'&' : '&amp;', '"' : '&quot;', '<' : '&lt;', '>' : '&gt;'},\r
983                 encodeCharsRe = /[<>&\"]/g,\r
984                 simpleSelectorRe = /^([a-z0-9],?)+$/i,\r
985                 tagRegExp = /<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)(\s*\/?)>/g,\r
986                 attrRegExp = /(\w+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;\r
987 \r
988         function makeMap(str) {\r
989                 var map = {}, i;\r
990 \r
991                 str = str.split(',');\r
992                 for (i = str.length; i >= 0; i--)\r
993                         map[str[i]] = 1;\r
994 \r
995                 return map;\r
996         };\r
997 \r
998         tinymce.create('tinymce.dom.DOMUtils', {\r
999                 doc : null,\r
1000                 root : null,\r
1001                 files : null,\r
1002                 pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/,\r
1003                 props : {\r
1004                         "for" : "htmlFor",\r
1005                         "class" : "className",\r
1006                         className : "className",\r
1007                         checked : "checked",\r
1008                         disabled : "disabled",\r
1009                         maxlength : "maxLength",\r
1010                         readonly : "readOnly",\r
1011                         selected : "selected",\r
1012                         value : "value",\r
1013                         id : "id",\r
1014                         name : "name",\r
1015                         type : "type"\r
1016                 },\r
1017 \r
1018                 DOMUtils : function(d, s) {\r
1019                         var t = this, globalStyle;\r
1020 \r
1021                         t.doc = d;\r
1022                         t.win = window;\r
1023                         t.files = {};\r
1024                         t.cssFlicker = false;\r
1025                         t.counter = 0;\r
1026                         t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat"; \r
1027                         t.stdMode = d.documentMode === 8;\r
1028 \r
1029                         t.settings = s = tinymce.extend({\r
1030                                 keep_values : false,\r
1031                                 hex_colors : 1,\r
1032                                 process_html : 1\r
1033                         }, s);\r
1034 \r
1035                         // Fix IE6SP2 flicker and check it failed for pre SP2\r
1036                         if (tinymce.isIE6) {\r
1037                                 try {\r
1038                                         d.execCommand('BackgroundImageCache', false, true);\r
1039                                 } catch (e) {\r
1040                                         t.cssFlicker = true;\r
1041                                 }\r
1042                         }\r
1043 \r
1044                         // Build styles list\r
1045                         if (s.valid_styles) {\r
1046                                 t._styles = {};\r
1047 \r
1048                                 // Convert styles into a rule list\r
1049                                 each(s.valid_styles, function(value, key) {\r
1050                                         t._styles[key] = tinymce.explode(value);\r
1051                                 });\r
1052                         }\r
1053 \r
1054                         tinymce.addUnload(t.destroy, t);\r
1055                 },\r
1056 \r
1057                 getRoot : function() {\r
1058                         var t = this, s = t.settings;\r
1059 \r
1060                         return (s && t.get(s.root_element)) || t.doc.body;\r
1061                 },\r
1062 \r
1063                 getViewPort : function(w) {\r
1064                         var d, b;\r
1065 \r
1066                         w = !w ? this.win : w;\r
1067                         d = w.document;\r
1068                         b = this.boxModel ? d.documentElement : d.body;\r
1069 \r
1070                         // Returns viewport size excluding scrollbars\r
1071                         return {\r
1072                                 x : w.pageXOffset || b.scrollLeft,\r
1073                                 y : w.pageYOffset || b.scrollTop,\r
1074                                 w : w.innerWidth || b.clientWidth,\r
1075                                 h : w.innerHeight || b.clientHeight\r
1076                         };\r
1077                 },\r
1078 \r
1079                 getRect : function(e) {\r
1080                         var p, t = this, sr;\r
1081 \r
1082                         e = t.get(e);\r
1083                         p = t.getPos(e);\r
1084                         sr = t.getSize(e);\r
1085 \r
1086                         return {\r
1087                                 x : p.x,\r
1088                                 y : p.y,\r
1089                                 w : sr.w,\r
1090                                 h : sr.h\r
1091                         };\r
1092                 },\r
1093 \r
1094                 getSize : function(e) {\r
1095                         var t = this, w, h;\r
1096 \r
1097                         e = t.get(e);\r
1098                         w = t.getStyle(e, 'width');\r
1099                         h = t.getStyle(e, 'height');\r
1100 \r
1101                         // Non pixel value, then force offset/clientWidth\r
1102                         if (w.indexOf('px') === -1)\r
1103                                 w = 0;\r
1104 \r
1105                         // Non pixel value, then force offset/clientWidth\r
1106                         if (h.indexOf('px') === -1)\r
1107                                 h = 0;\r
1108 \r
1109                         return {\r
1110                                 w : parseInt(w) || e.offsetWidth || e.clientWidth,\r
1111                                 h : parseInt(h) || e.offsetHeight || e.clientHeight\r
1112                         };\r
1113                 },\r
1114 \r
1115                 getParent : function(n, f, r) {\r
1116                         return this.getParents(n, f, r, false);\r
1117                 },\r
1118 \r
1119                 getParents : function(n, f, r, c) {\r
1120                         var t = this, na, se = t.settings, o = [];\r
1121 \r
1122                         n = t.get(n);\r
1123                         c = c === undefined;\r
1124 \r
1125                         if (se.strict_root)\r
1126                                 r = r || t.getRoot();\r
1127 \r
1128                         // Wrap node name as func\r
1129                         if (is(f, 'string')) {\r
1130                                 na = f;\r
1131 \r
1132                                 if (f === '*') {\r
1133                                         f = function(n) {return n.nodeType == 1;};\r
1134                                 } else {\r
1135                                         f = function(n) {\r
1136                                                 return t.is(n, na);\r
1137                                         };\r
1138                                 }\r
1139                         }\r
1140 \r
1141                         while (n) {\r
1142                                 if (n == r || !n.nodeType || n.nodeType === 9)\r
1143                                         break;\r
1144 \r
1145                                 if (!f || f(n)) {\r
1146                                         if (c)\r
1147                                                 o.push(n);\r
1148                                         else\r
1149                                                 return n;\r
1150                                 }\r
1151 \r
1152                                 n = n.parentNode;\r
1153                         }\r
1154 \r
1155                         return c ? o : null;\r
1156                 },\r
1157 \r
1158                 get : function(e) {\r
1159                         var n;\r
1160 \r
1161                         if (e && this.doc && typeof(e) == 'string') {\r
1162                                 n = e;\r
1163                                 e = this.doc.getElementById(e);\r
1164 \r
1165                                 // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick\r
1166                                 if (e && e.id !== n)\r
1167                                         return this.doc.getElementsByName(n)[1];\r
1168                         }\r
1169 \r
1170                         return e;\r
1171                 },\r
1172 \r
1173                 getNext : function(node, selector) {\r
1174                         return this._findSib(node, selector, 'nextSibling');\r
1175                 },\r
1176 \r
1177                 getPrev : function(node, selector) {\r
1178                         return this._findSib(node, selector, 'previousSibling');\r
1179                 },\r
1180 \r
1181 \r
1182                 select : function(pa, s) {\r
1183                         var t = this;\r
1184 \r
1185                         return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []);\r
1186                 },\r
1187 \r
1188                 is : function(n, selector) {\r
1189                         var i;\r
1190 \r
1191                         // If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance\r
1192                         if (n.length === undefined) {\r
1193                                 // Simple all selector\r
1194                                 if (selector === '*')\r
1195                                         return n.nodeType == 1;\r
1196 \r
1197                                 // Simple selector just elements\r
1198                                 if (simpleSelectorRe.test(selector)) {\r
1199                                         selector = selector.toLowerCase().split(/,/);\r
1200                                         n = n.nodeName.toLowerCase();\r
1201 \r
1202                                         for (i = selector.length - 1; i >= 0; i--) {\r
1203                                                 if (selector[i] == n)\r
1204                                                         return true;\r
1205                                         }\r
1206 \r
1207                                         return false;\r
1208                                 }\r
1209                         }\r
1210 \r
1211                         return tinymce.dom.Sizzle.matches(selector, n.nodeType ? [n] : n).length > 0;\r
1212                 },\r
1213 \r
1214 \r
1215                 add : function(p, n, a, h, c) {\r
1216                         var t = this;\r
1217 \r
1218                         return this.run(p, function(p) {\r
1219                                 var e, k;\r
1220 \r
1221                                 e = is(n, 'string') ? t.doc.createElement(n) : n;\r
1222                                 t.setAttribs(e, a);\r
1223 \r
1224                                 if (h) {\r
1225                                         if (h.nodeType)\r
1226                                                 e.appendChild(h);\r
1227                                         else\r
1228                                                 t.setHTML(e, h);\r
1229                                 }\r
1230 \r
1231                                 return !c ? p.appendChild(e) : e;\r
1232                         });\r
1233                 },\r
1234 \r
1235                 create : function(n, a, h) {\r
1236                         return this.add(this.doc.createElement(n), n, a, h, 1);\r
1237                 },\r
1238 \r
1239                 createHTML : function(n, a, h) {\r
1240                         var o = '', t = this, k;\r
1241 \r
1242                         o += '<' + n;\r
1243 \r
1244                         for (k in a) {\r
1245                                 if (a.hasOwnProperty(k))\r
1246                                         o += ' ' + k + '="' + t.encode(a[k]) + '"';\r
1247                         }\r
1248 \r
1249                         if (tinymce.is(h))\r
1250                                 return o + '>' + h + '</' + n + '>';\r
1251 \r
1252                         return o + ' />';\r
1253                 },\r
1254 \r
1255                 remove : function(node, keep_children) {\r
1256                         return this.run(node, function(node) {\r
1257                                 var parent, child;\r
1258 \r
1259                                 parent = node.parentNode;\r
1260 \r
1261                                 if (!parent)\r
1262                                         return null;\r
1263 \r
1264                                 if (keep_children) {\r
1265                                         while (child = node.firstChild) {\r
1266                                                 // IE 8 will crash if you don't remove completely empty text nodes\r
1267                                                 if (!tinymce.isIE || child.nodeType !== 3 || child.nodeValue)\r
1268                                                         parent.insertBefore(child, node);\r
1269                                                 else\r
1270                                                         node.removeChild(child);\r
1271                                         }\r
1272                                 }\r
1273 \r
1274                                 return parent.removeChild(node);\r
1275                         });\r
1276                 },\r
1277 \r
1278                 setStyle : function(n, na, v) {\r
1279                         var t = this;\r
1280 \r
1281                         return t.run(n, function(e) {\r
1282                                 var s, i;\r
1283 \r
1284                                 s = e.style;\r
1285 \r
1286                                 // Camelcase it, if needed\r
1287                                 na = na.replace(/-(\D)/g, function(a, b){\r
1288                                         return b.toUpperCase();\r
1289                                 });\r
1290 \r
1291                                 // Default px suffix on these\r
1292                                 if (t.pixelStyles.test(na) && (tinymce.is(v, 'number') || /^[\-0-9\.]+$/.test(v)))\r
1293                                         v += 'px';\r
1294 \r
1295                                 switch (na) {\r
1296                                         case 'opacity':\r
1297                                                 // IE specific opacity\r
1298                                                 if (isIE) {\r
1299                                                         s.filter = v === '' ? '' : "alpha(opacity=" + (v * 100) + ")";\r
1300 \r
1301                                                         if (!n.currentStyle || !n.currentStyle.hasLayout)\r
1302                                                                 s.display = 'inline-block';\r
1303                                                 }\r
1304 \r
1305                                                 // Fix for older browsers\r
1306                                                 s[na] = s['-moz-opacity'] = s['-khtml-opacity'] = v || '';\r
1307                                                 break;\r
1308 \r
1309                                         case 'float':\r
1310                                                 isIE ? s.styleFloat = v : s.cssFloat = v;\r
1311                                                 break;\r
1312                                         \r
1313                                         default:\r
1314                                                 s[na] = v || '';\r
1315                                 }\r
1316 \r
1317                                 // Force update of the style data\r
1318                                 if (t.settings.update_styles)\r
1319                                         t.setAttrib(e, '_mce_style');\r
1320                         });\r
1321                 },\r
1322 \r
1323                 getStyle : function(n, na, c) {\r
1324                         n = this.get(n);\r
1325 \r
1326                         if (!n)\r
1327                                 return false;\r
1328 \r
1329                         // Gecko\r
1330                         if (this.doc.defaultView && c) {\r
1331                                 // Remove camelcase\r
1332                                 na = na.replace(/[A-Z]/g, function(a){\r
1333                                         return '-' + a;\r
1334                                 });\r
1335 \r
1336                                 try {\r
1337                                         return this.doc.defaultView.getComputedStyle(n, null).getPropertyValue(na);\r
1338                                 } catch (ex) {\r
1339                                         // Old safari might fail\r
1340                                         return null;\r
1341                                 }\r
1342                         }\r
1343 \r
1344                         // Camelcase it, if needed\r
1345                         na = na.replace(/-(\D)/g, function(a, b){\r
1346                                 return b.toUpperCase();\r
1347                         });\r
1348 \r
1349                         if (na == 'float')\r
1350                                 na = isIE ? 'styleFloat' : 'cssFloat';\r
1351 \r
1352                         // IE & Opera\r
1353                         if (n.currentStyle && c)\r
1354                                 return n.currentStyle[na];\r
1355 \r
1356                         return n.style[na];\r
1357                 },\r
1358 \r
1359                 setStyles : function(e, o) {\r
1360                         var t = this, s = t.settings, ol;\r
1361 \r
1362                         ol = s.update_styles;\r
1363                         s.update_styles = 0;\r
1364 \r
1365                         each(o, function(v, n) {\r
1366                                 t.setStyle(e, n, v);\r
1367                         });\r
1368 \r
1369                         // Update style info\r
1370                         s.update_styles = ol;\r
1371                         if (s.update_styles)\r
1372                                 t.setAttrib(e, s.cssText);\r
1373                 },\r
1374 \r
1375                 setAttrib : function(e, n, v) {\r
1376                         var t = this;\r
1377 \r
1378                         // Whats the point\r
1379                         if (!e || !n)\r
1380                                 return;\r
1381 \r
1382                         // Strict XML mode\r
1383                         if (t.settings.strict)\r
1384                                 n = n.toLowerCase();\r
1385 \r
1386                         return this.run(e, function(e) {\r
1387                                 var s = t.settings;\r
1388 \r
1389                                 switch (n) {\r
1390                                         case "style":\r
1391                                                 if (!is(v, 'string')) {\r
1392                                                         each(v, function(v, n) {\r
1393                                                                 t.setStyle(e, n, v);\r
1394                                                         });\r
1395 \r
1396                                                         return;\r
1397                                                 }\r
1398 \r
1399                                                 // No mce_style for elements with these since they might get resized by the user\r
1400                                                 if (s.keep_values) {\r
1401                                                         if (v && !t._isRes(v))\r
1402                                                                 e.setAttribute('_mce_style', v, 2);\r
1403                                                         else\r
1404                                                                 e.removeAttribute('_mce_style', 2);\r
1405                                                 }\r
1406 \r
1407                                                 e.style.cssText = v;\r
1408                                                 break;\r
1409 \r
1410                                         case "class":\r
1411                                                 e.className = v || ''; // Fix IE null bug\r
1412                                                 break;\r
1413 \r
1414                                         case "src":\r
1415                                         case "href":\r
1416                                                 if (s.keep_values) {\r
1417                                                         if (s.url_converter)\r
1418                                                                 v = s.url_converter.call(s.url_converter_scope || t, v, n, e);\r
1419 \r
1420                                                         t.setAttrib(e, '_mce_' + n, v, 2);\r
1421                                                 }\r
1422 \r
1423                                                 break;\r
1424                                         \r
1425                                         case "shape":\r
1426                                                 e.setAttribute('_mce_style', v);\r
1427                                                 break;\r
1428                                 }\r
1429 \r
1430                                 if (is(v) && v !== null && v.length !== 0)\r
1431                                         e.setAttribute(n, '' + v, 2);\r
1432                                 else\r
1433                                         e.removeAttribute(n, 2);\r
1434                         });\r
1435                 },\r
1436 \r
1437                 setAttribs : function(e, o) {\r
1438                         var t = this;\r
1439 \r
1440                         return this.run(e, function(e) {\r
1441                                 each(o, function(v, n) {\r
1442                                         t.setAttrib(e, n, v);\r
1443                                 });\r
1444                         });\r
1445                 },\r
1446 \r
1447                 getAttrib : function(e, n, dv) {\r
1448                         var v, t = this;\r
1449 \r
1450                         e = t.get(e);\r
1451 \r
1452                         if (!e || e.nodeType !== 1)\r
1453                                 return false;\r
1454 \r
1455                         if (!is(dv))\r
1456                                 dv = '';\r
1457 \r
1458                         // Try the mce variant for these\r
1459                         if (/^(src|href|style|coords|shape)$/.test(n)) {\r
1460                                 v = e.getAttribute("_mce_" + n);\r
1461 \r
1462                                 if (v)\r
1463                                         return v;\r
1464                         }\r
1465 \r
1466                         if (isIE && t.props[n]) {\r
1467                                 v = e[t.props[n]];\r
1468                                 v = v && v.nodeValue ? v.nodeValue : v;\r
1469                         }\r
1470 \r
1471                         if (!v)\r
1472                                 v = e.getAttribute(n, 2);\r
1473 \r
1474                         // Check boolean attribs\r
1475                         if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(n)) {\r
1476                                 if (e[t.props[n]] === true && v === '')\r
1477                                         return n;\r
1478 \r
1479                                 return v ? n : '';\r
1480                         }\r
1481 \r
1482                         // Inner input elements will override attributes on form elements\r
1483                         if (e.nodeName === "FORM" && e.getAttributeNode(n))\r
1484                                 return e.getAttributeNode(n).nodeValue;\r
1485 \r
1486                         if (n === 'style') {\r
1487                                 v = v || e.style.cssText;\r
1488 \r
1489                                 if (v) {\r
1490                                         v = t.serializeStyle(t.parseStyle(v), e.nodeName);\r
1491 \r
1492                                         if (t.settings.keep_values && !t._isRes(v))\r
1493                                                 e.setAttribute('_mce_style', v);\r
1494                                 }\r
1495                         }\r
1496 \r
1497                         // Remove Apple and WebKit stuff\r
1498                         if (isWebKit && n === "class" && v)\r
1499                                 v = v.replace(/(apple|webkit)\-[a-z\-]+/gi, '');\r
1500 \r
1501                         // Handle IE issues\r
1502                         if (isIE) {\r
1503                                 switch (n) {\r
1504                                         case 'rowspan':\r
1505                                         case 'colspan':\r
1506                                                 // IE returns 1 as default value\r
1507                                                 if (v === 1)\r
1508                                                         v = '';\r
1509 \r
1510                                                 break;\r
1511 \r
1512                                         case 'size':\r
1513                                                 // IE returns +0 as default value for size\r
1514                                                 if (v === '+0' || v === 20 || v === 0)\r
1515                                                         v = '';\r
1516 \r
1517                                                 break;\r
1518 \r
1519                                         case 'width':\r
1520                                         case 'height':\r
1521                                         case 'vspace':\r
1522                                         case 'checked':\r
1523                                         case 'disabled':\r
1524                                         case 'readonly':\r
1525                                                 if (v === 0)\r
1526                                                         v = '';\r
1527 \r
1528                                                 break;\r
1529 \r
1530                                         case 'hspace':\r
1531                                                 // IE returns -1 as default value\r
1532                                                 if (v === -1)\r
1533                                                         v = '';\r
1534 \r
1535                                                 break;\r
1536 \r
1537                                         case 'maxlength':\r
1538                                         case 'tabindex':\r
1539                                                 // IE returns default value\r
1540                                                 if (v === 32768 || v === 2147483647 || v === '32768')\r
1541                                                         v = '';\r
1542 \r
1543                                                 break;\r
1544 \r
1545                                         case 'multiple':\r
1546                                         case 'compact':\r
1547                                         case 'noshade':\r
1548                                         case 'nowrap':\r
1549                                                 if (v === 65535)\r
1550                                                         return n;\r
1551 \r
1552                                                 return dv;\r
1553 \r
1554                                         case 'shape':\r
1555                                                 v = v.toLowerCase();\r
1556                                                 break;\r
1557 \r
1558                                         default:\r
1559                                                 // IE has odd anonymous function for event attributes\r
1560                                                 if (n.indexOf('on') === 0 && v)\r
1561                                                         v = ('' + v).replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1');\r
1562                                 }\r
1563                         }\r
1564 \r
1565                         return (v !== undefined && v !== null && v !== '') ? '' + v : dv;\r
1566                 },\r
1567 \r
1568                 getPos : function(n, ro) {\r
1569                         var t = this, x = 0, y = 0, e, d = t.doc, r;\r
1570 \r
1571                         n = t.get(n);\r
1572                         ro = ro || d.body;\r
1573 \r
1574                         if (n) {\r
1575                                 // Use getBoundingClientRect on IE, Opera has it but it's not perfect\r
1576                                 if (isIE && !t.stdMode) {\r
1577                                         n = n.getBoundingClientRect();\r
1578                                         e = t.boxModel ? d.documentElement : d.body;\r
1579                                         x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border\r
1580                                         x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x;\r
1581 \r
1582                                         return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x};\r
1583                                 }\r
1584 \r
1585                                 r = n;\r
1586                                 while (r && r != ro && r.nodeType) {\r
1587                                         x += r.offsetLeft || 0;\r
1588                                         y += r.offsetTop || 0;\r
1589                                         r = r.offsetParent;\r
1590                                 }\r
1591 \r
1592                                 r = n.parentNode;\r
1593                                 while (r && r != ro && r.nodeType) {\r
1594                                         x -= r.scrollLeft || 0;\r
1595                                         y -= r.scrollTop || 0;\r
1596                                         r = r.parentNode;\r
1597                                 }\r
1598                         }\r
1599 \r
1600                         return {x : x, y : y};\r
1601                 },\r
1602 \r
1603                 parseStyle : function(st) {\r
1604                         var t = this, s = t.settings, o = {};\r
1605 \r
1606                         if (!st)\r
1607                                 return o;\r
1608 \r
1609                         function compress(p, s, ot) {\r
1610                                 var t, r, b, l;\r
1611 \r
1612                                 // Get values and check it it needs compressing\r
1613                                 t = o[p + '-top' + s];\r
1614                                 if (!t)\r
1615                                         return;\r
1616 \r
1617                                 r = o[p + '-right' + s];\r
1618                                 if (t != r)\r
1619                                         return;\r
1620 \r
1621                                 b = o[p + '-bottom' + s];\r
1622                                 if (r != b)\r
1623                                         return;\r
1624 \r
1625                                 l = o[p + '-left' + s];\r
1626                                 if (b != l)\r
1627                                         return;\r
1628 \r
1629                                 // Compress\r
1630                                 o[ot] = l;\r
1631                                 delete o[p + '-top' + s];\r
1632                                 delete o[p + '-right' + s];\r
1633                                 delete o[p + '-bottom' + s];\r
1634                                 delete o[p + '-left' + s];\r
1635                         };\r
1636 \r
1637                         function compress2(ta, a, b, c) {\r
1638                                 var t;\r
1639 \r
1640                                 t = o[a];\r
1641                                 if (!t)\r
1642                                         return;\r
1643 \r
1644                                 t = o[b];\r
1645                                 if (!t)\r
1646                                         return;\r
1647 \r
1648                                 t = o[c];\r
1649                                 if (!t)\r
1650                                         return;\r
1651 \r
1652                                 // Compress\r
1653                                 o[ta] = o[a] + ' ' + o[b] + ' ' + o[c];\r
1654                                 delete o[a];\r
1655                                 delete o[b];\r
1656                                 delete o[c];\r
1657                         };\r
1658 \r
1659                         st = st.replace(/&(#?[a-z0-9]+);/g, '&$1_MCE_SEMI_'); // Protect entities\r
1660 \r
1661                         each(st.split(';'), function(v) {\r
1662                                 var sv, ur = [];\r
1663 \r
1664                                 if (v) {\r
1665                                         v = v.replace(/_MCE_SEMI_/g, ';'); // Restore entities\r
1666                                         v = v.replace(/url\([^\)]+\)/g, function(v) {ur.push(v);return 'url(' + ur.length + ')';});\r
1667                                         v = v.split(':');\r
1668                                         sv = tinymce.trim(v[1]);\r
1669                                         sv = sv.replace(/url\(([^\)]+)\)/g, function(a, b) {return ur[parseInt(b) - 1];});\r
1670 \r
1671                                         sv = sv.replace(/rgb\([^\)]+\)/g, function(v) {\r
1672                                                 return t.toHex(v);\r
1673                                         });\r
1674 \r
1675                                         if (s.url_converter) {\r
1676                                                 sv = sv.replace(/url\([\'\"]?([^\)\'\"]+)[\'\"]?\)/g, function(x, c) {\r
1677                                                         return 'url(' + s.url_converter.call(s.url_converter_scope || t, t.decode(c), 'style', null) + ')';\r
1678                                                 });\r
1679                                         }\r
1680 \r
1681                                         o[tinymce.trim(v[0]).toLowerCase()] = sv;\r
1682                                 }\r
1683                         });\r
1684 \r
1685                         compress("border", "", "border");\r
1686                         compress("border", "-width", "border-width");\r
1687                         compress("border", "-color", "border-color");\r
1688                         compress("border", "-style", "border-style");\r
1689                         compress("padding", "", "padding");\r
1690                         compress("margin", "", "margin");\r
1691                         compress2('border', 'border-width', 'border-style', 'border-color');\r
1692 \r
1693                         if (isIE) {\r
1694                                 // Remove pointless border\r
1695                                 if (o.border == 'medium none')\r
1696                                         o.border = '';\r
1697                         }\r
1698 \r
1699                         return o;\r
1700                 },\r
1701 \r
1702                 serializeStyle : function(o, name) {\r
1703                         var t = this, s = '';\r
1704 \r
1705                         function add(v, k) {\r
1706                                 if (k && v) {\r
1707                                         // Remove browser specific styles like -moz- or -webkit-\r
1708                                         if (k.indexOf('-') === 0)\r
1709                                                 return;\r
1710 \r
1711                                         switch (k) {\r
1712                                                 case 'font-weight':\r
1713                                                         // Opera will output bold as 700\r
1714                                                         if (v == 700)\r
1715                                                                 v = 'bold';\r
1716 \r
1717                                                         break;\r
1718 \r
1719                                                 case 'color':\r
1720                                                 case 'background-color':\r
1721                                                         v = v.toLowerCase();\r
1722                                                         break;\r
1723                                         }\r
1724 \r
1725                                         s += (s ? ' ' : '') + k + ': ' + v + ';';\r
1726                                 }\r
1727                         };\r
1728 \r
1729                         // Validate style output\r
1730                         if (name && t._styles) {\r
1731                                 each(t._styles['*'], function(name) {\r
1732                                         add(o[name], name);\r
1733                                 });\r
1734 \r
1735                                 each(t._styles[name.toLowerCase()], function(name) {\r
1736                                         add(o[name], name);\r
1737                                 });\r
1738                         } else\r
1739                                 each(o, add);\r
1740 \r
1741                         return s;\r
1742                 },\r
1743 \r
1744                 loadCSS : function(u) {\r
1745                         var t = this, d = t.doc, head;\r
1746 \r
1747                         if (!u)\r
1748                                 u = '';\r
1749 \r
1750                         head = t.select('head')[0];\r
1751 \r
1752                         each(u.split(','), function(u) {\r
1753                                 var link;\r
1754 \r
1755                                 if (t.files[u])\r
1756                                         return;\r
1757 \r
1758                                 t.files[u] = true;\r
1759                                 link = t.create('link', {rel : 'stylesheet', href : tinymce._addVer(u)});\r
1760 \r
1761                                 // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug\r
1762                                 // This fix seems to resolve that issue by realcing the document ones a stylesheet finishes loading\r
1763                                 // It's ugly but it seems to work fine.\r
1764                                 if (isIE && d.documentMode) {\r
1765                                         link.onload = function() {\r
1766                                                 d.recalc();\r
1767                                                 link.onload = null;\r
1768                                         };\r
1769                                 }\r
1770 \r
1771                                 head.appendChild(link);\r
1772                         });\r
1773                 },\r
1774 \r
1775                 addClass : function(e, c) {\r
1776                         return this.run(e, function(e) {\r
1777                                 var o;\r
1778 \r
1779                                 if (!c)\r
1780                                         return 0;\r
1781 \r
1782                                 if (this.hasClass(e, c))\r
1783                                         return e.className;\r
1784 \r
1785                                 o = this.removeClass(e, c);\r
1786 \r
1787                                 return e.className = (o != '' ? (o + ' ') : '') + c;\r
1788                         });\r
1789                 },\r
1790 \r
1791                 removeClass : function(e, c) {\r
1792                         var t = this, re;\r
1793 \r
1794                         return t.run(e, function(e) {\r
1795                                 var v;\r
1796 \r
1797                                 if (t.hasClass(e, c)) {\r
1798                                         if (!re)\r
1799                                                 re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g");\r
1800 \r
1801                                         v = e.className.replace(re, ' ');\r
1802                                         v = tinymce.trim(v != ' ' ? v : '');\r
1803 \r
1804                                         e.className = v;\r
1805 \r
1806                                         // Empty class attr\r
1807                                         if (!v) {\r
1808                                                 e.removeAttribute('class');\r
1809                                                 e.removeAttribute('className');\r
1810                                         }\r
1811 \r
1812                                         return v;\r
1813                                 }\r
1814 \r
1815                                 return e.className;\r
1816                         });\r
1817                 },\r
1818 \r
1819                 hasClass : function(n, c) {\r
1820                         n = this.get(n);\r
1821 \r
1822                         if (!n || !c)\r
1823                                 return false;\r
1824 \r
1825                         return (' ' + n.className + ' ').indexOf(' ' + c + ' ') !== -1;\r
1826                 },\r
1827 \r
1828                 show : function(e) {\r
1829                         return this.setStyle(e, 'display', 'block');\r
1830                 },\r
1831 \r
1832                 hide : function(e) {\r
1833                         return this.setStyle(e, 'display', 'none');\r
1834                 },\r
1835 \r
1836                 isHidden : function(e) {\r
1837                         e = this.get(e);\r
1838 \r
1839                         return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none';\r
1840                 },\r
1841 \r
1842                 uniqueId : function(p) {\r
1843                         return (!p ? 'mce_' : p) + (this.counter++);\r
1844                 },\r
1845 \r
1846                 setHTML : function(e, h) {\r
1847                         var t = this;\r
1848 \r
1849                         return this.run(e, function(e) {\r
1850                                 var x, i, nl, n, p, x;\r
1851 \r
1852                                 h = t.processHTML(h);\r
1853 \r
1854                                 if (isIE) {\r
1855                                         function set() {\r
1856                                                 // Remove all child nodes\r
1857                                                 while (e.firstChild)\r
1858                                                         e.firstChild.removeNode();\r
1859 \r
1860                                                 try {\r
1861                                                         // IE will remove comments from the beginning\r
1862                                                         // unless you padd the contents with something\r
1863                                                         e.innerHTML = '<br />' + h;\r
1864                                                         e.removeChild(e.firstChild);\r
1865                                                 } catch (ex) {\r
1866                                                         // 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
1867                                                         // This seems to fix this problem\r
1868 \r
1869                                                         // Create new div with HTML contents and a BR infront to keep comments\r
1870                                                         x = t.create('div');\r
1871                                                         x.innerHTML = '<br />' + h;\r
1872 \r
1873                                                         // Add all children from div to target\r
1874                                                         each (x.childNodes, function(n, i) {\r
1875                                                                 // Skip br element\r
1876                                                                 if (i)\r
1877                                                                         e.appendChild(n);\r
1878                                                         });\r
1879                                                 }\r
1880                                         };\r
1881 \r
1882                                         // IE has a serious bug when it comes to paragraphs it can produce an invalid\r
1883                                         // DOM tree if contents like this <p><ul><li>Item 1</li></ul></p> is inserted\r
1884                                         // It seems to be that IE doesn't like a root block element placed inside another root block element\r
1885                                         if (t.settings.fix_ie_paragraphs)\r
1886                                                 h = h.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi, '<p$1 _mce_keep="true">&nbsp;</p>');\r
1887 \r
1888                                         set();\r
1889 \r
1890                                         if (t.settings.fix_ie_paragraphs) {\r
1891                                                 // Check for odd paragraphs this is a sign of a broken DOM\r
1892                                                 nl = e.getElementsByTagName("p");\r
1893                                                 for (i = nl.length - 1, x = 0; i >= 0; i--) {\r
1894                                                         n = nl[i];\r
1895 \r
1896                                                         if (!n.hasChildNodes()) {\r
1897                                                                 if (!n._mce_keep) {\r
1898                                                                         x = 1; // Is broken\r
1899                                                                         break;\r
1900                                                                 }\r
1901 \r
1902                                                                 n.removeAttribute('_mce_keep');\r
1903                                                         }\r
1904                                                 }\r
1905                                         }\r
1906 \r
1907                                         // Time to fix the madness IE left us\r
1908                                         if (x) {\r
1909                                                 // So if we replace the p elements with divs and mark them and then replace them back to paragraphs\r
1910                                                 // after we use innerHTML we can fix the DOM tree\r
1911                                                 h = h.replace(/<p ([^>]+)>|<p>/ig, '<div $1 _mce_tmp="1">');\r
1912                                                 h = h.replace(/<\/p>/gi, '</div>');\r
1913 \r
1914                                                 // Set the new HTML with DIVs\r
1915                                                 set();\r
1916 \r
1917                                                 // Replace all DIV elements with the _mce_tmp attibute back to paragraphs\r
1918                                                 // This is needed since IE has a annoying bug see above for details\r
1919                                                 // This is a slow process but it has to be done. :(\r
1920                                                 if (t.settings.fix_ie_paragraphs) {\r
1921                                                         nl = e.getElementsByTagName("DIV");\r
1922                                                         for (i = nl.length - 1; i >= 0; i--) {\r
1923                                                                 n = nl[i];\r
1924 \r
1925                                                                 // Is it a temp div\r
1926                                                                 if (n._mce_tmp) {\r
1927                                                                         // Create new paragraph\r
1928                                                                         p = t.doc.createElement('p');\r
1929 \r
1930                                                                         // Copy all attributes\r
1931                                                                         n.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi, function(a, b) {\r
1932                                                                                 var v;\r
1933 \r
1934                                                                                 if (b !== '_mce_tmp') {\r
1935                                                                                         v = n.getAttribute(b);\r
1936 \r
1937                                                                                         if (!v && b === 'class')\r
1938                                                                                                 v = n.className;\r
1939 \r
1940                                                                                         p.setAttribute(b, v);\r
1941                                                                                 }\r
1942                                                                         });\r
1943 \r
1944                                                                         // Append all children to new paragraph\r
1945                                                                         for (x = 0; x<n.childNodes.length; x++)\r
1946                                                                                 p.appendChild(n.childNodes[x].cloneNode(true));\r
1947 \r
1948                                                                         // Replace div with new paragraph\r
1949                                                                         n.swapNode(p);\r
1950                                                                 }\r
1951                                                         }\r
1952                                                 }\r
1953                                         }\r
1954                                 } else\r
1955                                         e.innerHTML = h;\r
1956 \r
1957                                 return h;\r
1958                         });\r
1959                 },\r
1960 \r
1961                 processHTML : function(h) {\r
1962                         var t = this, s = t.settings, codeBlocks = [];\r
1963 \r
1964                         if (!s.process_html)\r
1965                                 return h;\r
1966 \r
1967                         if (isIE) {\r
1968                                 h = h.replace(/&apos;/g, '&#39;'); // IE can't handle apos\r
1969                                 h = h.replace(/\s+(disabled|checked|readonly|selected)\s*=\s*[\"\']?(false|0)[\"\']?/gi, ''); // IE doesn't handle default values correct\r
1970                         }\r
1971 \r
1972                         // Fix some issues\r
1973                         h = h.replace(/<a( )([^>]+)\/>|<a\/>/gi, '<a$1$2></a>'); // Force open\r
1974 \r
1975                         // Store away src and href in _mce_src and mce_href since browsers mess them up\r
1976                         if (s.keep_values) {\r
1977                                 // Wrap scripts and styles in comments for serialization purposes\r
1978                                 if (/<script|noscript|style/i.test(h)) {\r
1979                                         function trim(s) {\r
1980                                                 // Remove prefix and suffix code for element\r
1981                                                 s = s.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n');\r
1982                                                 s = s.replace(/^[\r\n]*|[\r\n]*$/g, '');\r
1983                                                 s = s.replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, '');\r
1984                                                 s = s.replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, '');\r
1985 \r
1986                                                 return s;\r
1987                                         };\r
1988 \r
1989                                         // Wrap the script contents in CDATA and keep them from executing\r
1990                                         h = h.replace(/<script([^>]+|)>([\s\S]*?)<\/script>/gi, function(v, attribs, text) {\r
1991                                                 // Force type attribute\r
1992                                                 if (!attribs)\r
1993                                                         attribs = ' type="text/javascript"';\r
1994 \r
1995                                                 // Convert the src attribute of the scripts\r
1996                                                 attribs = attribs.replace(/src=\"([^\"]+)\"?/i, function(a, url) {\r
1997                                                         if (s.url_converter)\r
1998                                                                 url = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(url), 'src', 'script'));\r
1999 \r
2000                                                         return '_mce_src="' + url + '"';\r
2001                                                 });\r
2002 \r
2003                                                 // Wrap text contents\r
2004                                                 if (tinymce.trim(text)) {\r
2005                                                         codeBlocks.push(trim(text));\r
2006                                                         text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n// -->';\r
2007                                                 }\r
2008 \r
2009                                                 return '<mce:script' + attribs + '>' + text + '</mce:script>';\r
2010                                         });\r
2011 \r
2012                                         // Wrap style elements\r
2013                                         h = h.replace(/<style([^>]+|)>([\s\S]*?)<\/style>/gi, function(v, attribs, text) {\r
2014                                                 // Wrap text contents\r
2015                                                 if (text) {\r
2016                                                         codeBlocks.push(trim(text));\r
2017                                                         text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n-->';\r
2018                                                 }\r
2019 \r
2020                                                 return '<mce:style' + attribs + '>' + text + '</mce:style><style ' + attribs + ' _mce_bogus="1">' + text + '</style>';\r
2021                                         });\r
2022 \r
2023                                         // Wrap noscript elements\r
2024                                         h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {\r
2025                                                 return '<mce:noscript' + attribs + '><!--' + t.encode(text).replace(/--/g, '&#45;&#45;') + '--></mce:noscript>';\r
2026                                         });\r
2027                                 }\r
2028 \r
2029                                 h = h.replace(/<!\[CDATA\[([\s\S]+)\]\]>/g, '<!--[CDATA[$1]]-->');\r
2030 \r
2031                                 // This function processes the attributes in the HTML string to force boolean\r
2032                                 // attributes to the attr="attr" format and convert style, src and href to _mce_ versions\r
2033                                 function processTags(html) {\r
2034                                         return html.replace(tagRegExp, function(match, elm_name, attrs, end) {\r
2035                                                 return '<' + elm_name + attrs.replace(attrRegExp, function(match, name, value, val2, val3) {\r
2036                                                         var mceValue;\r
2037 \r
2038                                                         name = name.toLowerCase();\r
2039                                                         value = value || val2 || val3 || "";\r
2040 \r
2041                                                         // Treat boolean attributes\r
2042                                                         if (boolAttrs[name]) {\r
2043                                                                 // false or 0 is treated as a missing attribute\r
2044                                                                 if (value === 'false' || value === '0')\r
2045                                                                         return;\r
2046 \r
2047                                                                 return name + '="' + name + '"';\r
2048                                                         }\r
2049 \r
2050                                                         // Is attribute one that needs special treatment\r
2051                                                         if (mceAttribs[name] && attrs.indexOf('_mce_' + name) == -1) {\r
2052                                                                 mceValue = t.decode(value);\r
2053 \r
2054                                                                 // Convert URLs to relative/absolute ones\r
2055                                                                 if (s.url_converter && (name == "src" || name == "href"))\r
2056                                                                         mceValue = s.url_converter.call(s.url_converter_scope || t, mceValue, name, elm_name);\r
2057 \r
2058                                                                 // Process styles lowercases them and compresses them\r
2059                                                                 if (name == 'style')\r
2060                                                                         mceValue = t.serializeStyle(t.parseStyle(mceValue), name);\r
2061 \r
2062                                                                 return name + '="' + value + '"' + ' _mce_' + name + '="' + t.encode(mceValue) + '"';\r
2063                                                         }\r
2064 \r
2065                                                         return match;\r
2066                                                 }) + end + '>';\r
2067                                         });\r
2068                                 };\r
2069 \r
2070                                 h = processTags(h);\r
2071 \r
2072                                 // Restore script blocks\r
2073                                 h = h.replace(/MCE_SCRIPT:([0-9]+)/g, function(val, idx) {\r
2074                                         return codeBlocks[idx];\r
2075                                 });\r
2076                         }\r
2077 \r
2078                         return h;\r
2079                 },\r
2080 \r
2081                 getOuterHTML : function(e) {\r
2082                         var d;\r
2083 \r
2084                         e = this.get(e);\r
2085 \r
2086                         if (!e)\r
2087                                 return null;\r
2088 \r
2089                         if (e.outerHTML !== undefined)\r
2090                                 return e.outerHTML;\r
2091 \r
2092                         d = (e.ownerDocument || this.doc).createElement("body");\r
2093                         d.appendChild(e.cloneNode(true));\r
2094 \r
2095                         return d.innerHTML;\r
2096                 },\r
2097 \r
2098                 setOuterHTML : function(e, h, d) {\r
2099                         var t = this;\r
2100 \r
2101                         function setHTML(e, h, d) {\r
2102                                 var n, tp;\r
2103 \r
2104                                 tp = d.createElement("body");\r
2105                                 tp.innerHTML = h;\r
2106 \r
2107                                 n = tp.lastChild;\r
2108                                 while (n) {\r
2109                                         t.insertAfter(n.cloneNode(true), e);\r
2110                                         n = n.previousSibling;\r
2111                                 }\r
2112 \r
2113                                 t.remove(e);\r
2114                         };\r
2115 \r
2116                         return this.run(e, function(e) {\r
2117                                 e = t.get(e);\r
2118 \r
2119                                 // Only set HTML on elements\r
2120                                 if (e.nodeType == 1) {\r
2121                                         d = d || e.ownerDocument || t.doc;\r
2122 \r
2123                                         if (isIE) {\r
2124                                                 try {\r
2125                                                         // Try outerHTML for IE it sometimes produces an unknown runtime error\r
2126                                                         if (isIE && e.nodeType == 1)\r
2127                                                                 e.outerHTML = h;\r
2128                                                         else\r
2129                                                                 setHTML(e, h, d);\r
2130                                                 } catch (ex) {\r
2131                                                         // Fix for unknown runtime error\r
2132                                                         setHTML(e, h, d);\r
2133                                                 }\r
2134                                         } else\r
2135                                                 setHTML(e, h, d);\r
2136                                 }\r
2137                         });\r
2138                 },\r
2139 \r
2140                 decode : function(s) {\r
2141                         var e, n, v;\r
2142 \r
2143                         // Look for entities to decode\r
2144                         if (/&[\w#]+;/.test(s)) {\r
2145                                 // Decode the entities using a div element not super efficient but less code\r
2146                                 e = this.doc.createElement("div");\r
2147                                 e.innerHTML = s;\r
2148                                 n = e.firstChild;\r
2149                                 v = '';\r
2150 \r
2151                                 if (n) {\r
2152                                         do {\r
2153                                                 v += n.nodeValue;\r
2154                                         } while (n = n.nextSibling);\r
2155                                 }\r
2156 \r
2157                                 return v || s;\r
2158                         }\r
2159 \r
2160                         return s;\r
2161                 },\r
2162 \r
2163                 encode : function(str) {\r
2164                         return ('' + str).replace(encodeCharsRe, function(chr) {\r
2165                                 return encodedChars[chr];\r
2166                         });\r
2167                 },\r
2168 \r
2169                 insertAfter : function(node, reference_node) {\r
2170                         reference_node = this.get(reference_node);\r
2171 \r
2172                         return this.run(node, function(node) {\r
2173                                 var parent, nextSibling;\r
2174 \r
2175                                 parent = reference_node.parentNode;\r
2176                                 nextSibling = reference_node.nextSibling;\r
2177 \r
2178                                 if (nextSibling)\r
2179                                         parent.insertBefore(node, nextSibling);\r
2180                                 else\r
2181                                         parent.appendChild(node);\r
2182 \r
2183                                 return node;\r
2184                         });\r
2185                 },\r
2186 \r
2187                 isBlock : function(n) {\r
2188                         if (n.nodeType && n.nodeType !== 1)\r
2189                                 return false;\r
2190 \r
2191                         n = n.nodeName || n;\r
2192 \r
2193                         return blockRe.test(n);\r
2194                 },\r
2195 \r
2196                 replace : function(n, o, k) {\r
2197                         var t = this;\r
2198 \r
2199                         if (is(o, 'array'))\r
2200                                 n = n.cloneNode(true);\r
2201 \r
2202                         return t.run(o, function(o) {\r
2203                                 if (k) {\r
2204                                         each(tinymce.grep(o.childNodes), function(c) {\r
2205                                                 n.appendChild(c);\r
2206                                         });\r
2207                                 }\r
2208 \r
2209                                 return o.parentNode.replaceChild(n, o);\r
2210                         });\r
2211                 },\r
2212 \r
2213                 rename : function(elm, name) {\r
2214                         var t = this, newElm;\r
2215 \r
2216                         if (elm.nodeName != name.toUpperCase()) {\r
2217                                 // Rename block element\r
2218                                 newElm = t.create(name);\r
2219 \r
2220                                 // Copy attribs to new block\r
2221                                 each(t.getAttribs(elm), function(attr_node) {\r
2222                                         t.setAttrib(newElm, attr_node.nodeName, t.getAttrib(elm, attr_node.nodeName));\r
2223                                 });\r
2224 \r
2225                                 // Replace block\r
2226                                 t.replace(newElm, elm, 1);\r
2227                         }\r
2228 \r
2229                         return newElm || elm;\r
2230                 },\r
2231 \r
2232                 findCommonAncestor : function(a, b) {\r
2233                         var ps = a, pe;\r
2234 \r
2235                         while (ps) {\r
2236                                 pe = b;\r
2237 \r
2238                                 while (pe && ps != pe)\r
2239                                         pe = pe.parentNode;\r
2240 \r
2241                                 if (ps == pe)\r
2242                                         break;\r
2243 \r
2244                                 ps = ps.parentNode;\r
2245                         }\r
2246 \r
2247                         if (!ps && a.ownerDocument)\r
2248                                 return a.ownerDocument.documentElement;\r
2249 \r
2250                         return ps;\r
2251                 },\r
2252 \r
2253                 toHex : function(s) {\r
2254                         var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);\r
2255 \r
2256                         function hex(s) {\r
2257                                 s = parseInt(s).toString(16);\r
2258 \r
2259                                 return s.length > 1 ? s : '0' + s; // 0 -> 00\r
2260                         };\r
2261 \r
2262                         if (c) {\r
2263                                 s = '#' + hex(c[1]) + hex(c[2]) + hex(c[3]);\r
2264 \r
2265                                 return s;\r
2266                         }\r
2267 \r
2268                         return s;\r
2269                 },\r
2270 \r
2271                 getClasses : function() {\r
2272                         var t = this, cl = [], i, lo = {}, f = t.settings.class_filter, ov;\r
2273 \r
2274                         if (t.classes)\r
2275                                 return t.classes;\r
2276 \r
2277                         function addClasses(s) {\r
2278                                 // IE style imports\r
2279                                 each(s.imports, function(r) {\r
2280                                         addClasses(r);\r
2281                                 });\r
2282 \r
2283                                 each(s.cssRules || s.rules, function(r) {\r
2284                                         // Real type or fake it on IE\r
2285                                         switch (r.type || 1) {\r
2286                                                 // Rule\r
2287                                                 case 1:\r
2288                                                         if (r.selectorText) {\r
2289                                                                 each(r.selectorText.split(','), function(v) {\r
2290                                                                         v = v.replace(/^\s*|\s*$|^\s\./g, "");\r
2291 \r
2292                                                                         // Is internal or it doesn't contain a class\r
2293                                                                         if (/\.mce/.test(v) || !/\.[\w\-]+$/.test(v))\r
2294                                                                                 return;\r
2295 \r
2296                                                                         // Remove everything but class name\r
2297                                                                         ov = v;\r
2298                                                                         v = v.replace(/.*\.([a-z0-9_\-]+).*/i, '$1');\r
2299 \r
2300                                                                         // Filter classes\r
2301                                                                         if (f && !(v = f(v, ov)))\r
2302                                                                                 return;\r
2303 \r
2304                                                                         if (!lo[v]) {\r
2305                                                                                 cl.push({'class' : v});\r
2306                                                                                 lo[v] = 1;\r
2307                                                                         }\r
2308                                                                 });\r
2309                                                         }\r
2310                                                         break;\r
2311 \r
2312                                                 // Import\r
2313                                                 case 3:\r
2314                                                         addClasses(r.styleSheet);\r
2315                                                         break;\r
2316                                         }\r
2317                                 });\r
2318                         };\r
2319 \r
2320                         try {\r
2321                                 each(t.doc.styleSheets, addClasses);\r
2322                         } catch (ex) {\r
2323                                 // Ignore\r
2324                         }\r
2325 \r
2326                         if (cl.length > 0)\r
2327                                 t.classes = cl;\r
2328 \r
2329                         return cl;\r
2330                 },\r
2331 \r
2332                 run : function(e, f, s) {\r
2333                         var t = this, o;\r
2334 \r
2335                         if (t.doc && typeof(e) === 'string')\r
2336                                 e = t.get(e);\r
2337 \r
2338                         if (!e)\r
2339                                 return false;\r
2340 \r
2341                         s = s || this;\r
2342                         if (!e.nodeType && (e.length || e.length === 0)) {\r
2343                                 o = [];\r
2344 \r
2345                                 each(e, function(e, i) {\r
2346                                         if (e) {\r
2347                                                 if (typeof(e) == 'string')\r
2348                                                         e = t.doc.getElementById(e);\r
2349 \r
2350                                                 o.push(f.call(s, e, i));\r
2351                                         }\r
2352                                 });\r
2353 \r
2354                                 return o;\r
2355                         }\r
2356 \r
2357                         return f.call(s, e);\r
2358                 },\r
2359 \r
2360                 getAttribs : function(n) {\r
2361                         var o;\r
2362 \r
2363                         n = this.get(n);\r
2364 \r
2365                         if (!n)\r
2366                                 return [];\r
2367 \r
2368                         if (isIE) {\r
2369                                 o = [];\r
2370 \r
2371                                 // Object will throw exception in IE\r
2372                                 if (n.nodeName == 'OBJECT')\r
2373                                         return n.attributes;\r
2374 \r
2375                                 // IE doesn't keep the selected attribute if you clone option elements\r
2376                                 if (n.nodeName === 'OPTION' && this.getAttrib(n, 'selected'))\r
2377                                         o.push({specified : 1, nodeName : 'selected'});\r
2378 \r
2379                                 // It's crazy that this is faster in IE but it's because it returns all attributes all the time\r
2380                                 n.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi, '').replace(/[\w:\-]+/gi, function(a) {\r
2381                                         o.push({specified : 1, nodeName : a});\r
2382                                 });\r
2383 \r
2384                                 return o;\r
2385                         }\r
2386 \r
2387                         return n.attributes;\r
2388                 },\r
2389 \r
2390                 destroy : function(s) {\r
2391                         var t = this;\r
2392 \r
2393                         if (t.events)\r
2394                                 t.events.destroy();\r
2395 \r
2396                         t.win = t.doc = t.root = t.events = null;\r
2397 \r
2398                         // Manual destroy then remove unload handler\r
2399                         if (!s)\r
2400                                 tinymce.removeUnload(t.destroy);\r
2401                 },\r
2402 \r
2403                 createRng : function() {\r
2404                         var d = this.doc;\r
2405 \r
2406                         return d.createRange ? d.createRange() : new tinymce.dom.Range(this);\r
2407                 },\r
2408 \r
2409                 nodeIndex : function(node, normalized) {\r
2410                         var idx = 0, lastNodeType, lastNode, nodeType;\r
2411 \r
2412                         if (node) {\r
2413                                 for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {\r
2414                                         nodeType = node.nodeType;\r
2415 \r
2416                                         // Normalize text nodes\r
2417                                         if (normalized && nodeType == 3) {\r
2418                                                 if (nodeType == lastNodeType || !node.nodeValue.length)\r
2419                                                         continue;\r
2420                                         }\r
2421 \r
2422                                         idx++;\r
2423                                         lastNodeType = nodeType;\r
2424                                 }\r
2425                         }\r
2426 \r
2427                         return idx;\r
2428                 },\r
2429 \r
2430                 split : function(pe, e, re) {\r
2431                         var t = this, r = t.createRng(), bef, aft, pa;\r
2432 \r
2433                         // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sense\r
2434                         // but we don't want that in our code since it serves no purpose for the end user\r
2435                         // For example if this is chopped:\r
2436                         //   <p>text 1<span><b>CHOP</b></span>text 2</p>\r
2437                         // would produce:\r
2438                         //   <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>\r
2439                         // this function will then trim of empty edges and produce:\r
2440                         //   <p>text 1</p><b>CHOP</b><p>text 2</p>\r
2441                         function trim(node) {\r
2442                                 var i, children = node.childNodes;\r
2443 \r
2444                                 if (node.nodeType == 1 && node.getAttribute('_mce_type') == 'bookmark')\r
2445                                         return;\r
2446 \r
2447                                 for (i = children.length - 1; i >= 0; i--)\r
2448                                         trim(children[i]);\r
2449 \r
2450                                 if (node.nodeType != 9) {\r
2451                                         // Keep non whitespace text nodes\r
2452                                         if (node.nodeType == 3 && node.nodeValue.length > 0)\r
2453                                                 return;\r
2454 \r
2455                                         if (node.nodeType == 1) {\r
2456                                                 // If the only child is a bookmark then move it up\r
2457                                                 children = node.childNodes;\r
2458                                                 if (children.length == 1 && children[0] && children[0].nodeType == 1 && children[0].getAttribute('_mce_type') == 'bookmark')\r
2459                                                         node.parentNode.insertBefore(children[0], node);\r
2460 \r
2461                                                 // Keep non empty elements or img, hr etc\r
2462                                                 if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName))\r
2463                                                         return;\r
2464                                         }\r
2465 \r
2466                                         t.remove(node);\r
2467                                 }\r
2468 \r
2469                                 return node;\r
2470                         };\r
2471 \r
2472                         if (pe && e) {\r
2473                                 // Get before chunk\r
2474                                 r.setStart(pe.parentNode, t.nodeIndex(pe));\r
2475                                 r.setEnd(e.parentNode, t.nodeIndex(e));\r
2476                                 bef = r.extractContents();\r
2477 \r
2478                                 // Get after chunk\r
2479                                 r = t.createRng();\r
2480                                 r.setStart(e.parentNode, t.nodeIndex(e) + 1);\r
2481                                 r.setEnd(pe.parentNode, t.nodeIndex(pe) + 1);\r
2482                                 aft = r.extractContents();\r
2483 \r
2484                                 // Insert before chunk\r
2485                                 pa = pe.parentNode;\r
2486                                 pa.insertBefore(trim(bef), pe);\r
2487 \r
2488                                 // Insert middle chunk\r
2489                                 if (re)\r
2490                                         pa.replaceChild(re, e);\r
2491                                 else\r
2492                                         pa.insertBefore(e, pe);\r
2493 \r
2494                                 // Insert after chunk\r
2495                                 pa.insertBefore(trim(aft), pe);\r
2496                                 t.remove(pe);\r
2497 \r
2498                                 return re || e;\r
2499                         }\r
2500                 },\r
2501 \r
2502                 bind : function(target, name, func, scope) {\r
2503                         var t = this;\r
2504 \r
2505                         if (!t.events)\r
2506                                 t.events = new tinymce.dom.EventUtils();\r
2507 \r
2508                         return t.events.add(target, name, func, scope || this);\r
2509                 },\r
2510 \r
2511                 unbind : function(target, name, func) {\r
2512                         var t = this;\r
2513 \r
2514                         if (!t.events)\r
2515                                 t.events = new tinymce.dom.EventUtils();\r
2516 \r
2517                         return t.events.remove(target, name, func);\r
2518                 },\r
2519 \r
2520 \r
2521                 _findSib : function(node, selector, name) {\r
2522                         var t = this, f = selector;\r
2523 \r
2524                         if (node) {\r
2525                                 // If expression make a function of it using is\r
2526                                 if (is(f, 'string')) {\r
2527                                         f = function(node) {\r
2528                                                 return t.is(node, selector);\r
2529                                         };\r
2530                                 }\r
2531 \r
2532                                 // Loop all siblings\r
2533                                 for (node = node[name]; node; node = node[name]) {\r
2534                                         if (f(node))\r
2535                                                 return node;\r
2536                                 }\r
2537                         }\r
2538 \r
2539                         return null;\r
2540                 },\r
2541 \r
2542                 _isRes : function(c) {\r
2543                         // Is live resizble element\r
2544                         return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c);\r
2545                 }\r
2546 \r
2547                 /*\r
2548                 walk : function(n, f, s) {\r
2549                         var d = this.doc, w;\r
2550 \r
2551                         if (d.createTreeWalker) {\r
2552                                 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);\r
2553 \r
2554                                 while ((n = w.nextNode()) != null)\r
2555                                         f.call(s || this, n);\r
2556                         } else\r
2557                                 tinymce.walk(n, f, 'childNodes', s);\r
2558                 }\r
2559                 */\r
2560 \r
2561                 /*\r
2562                 toRGB : function(s) {\r
2563                         var c = /^\s*?#([0-9A-F]{2})([0-9A-F]{1,2})([0-9A-F]{2})?\s*?$/.exec(s);\r
2564 \r
2565                         if (c) {\r
2566                                 // #FFF -> #FFFFFF\r
2567                                 if (!is(c[3]))\r
2568                                         c[3] = c[2] = c[1];\r
2569 \r
2570                                 return "rgb(" + parseInt(c[1], 16) + "," + parseInt(c[2], 16) + "," + parseInt(c[3], 16) + ")";\r
2571                         }\r
2572 \r
2573                         return s;\r
2574                 }\r
2575                 */\r
2576         });\r
2577 \r
2578         tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});\r
2579 })(tinymce);\r
2580 \r
2581 (function(ns) {\r
2582         // Range constructor\r
2583         function Range(dom) {\r
2584                 var t = this,\r
2585                         doc = dom.doc,\r
2586                         EXTRACT = 0,\r
2587                         CLONE = 1,\r
2588                         DELETE = 2,\r
2589                         TRUE = true,\r
2590                         FALSE = false,\r
2591                         START_OFFSET = 'startOffset',\r
2592                         START_CONTAINER = 'startContainer',\r
2593                         END_CONTAINER = 'endContainer',\r
2594                         END_OFFSET = 'endOffset',\r
2595                         extend = tinymce.extend,\r
2596                         nodeIndex = dom.nodeIndex;\r
2597 \r
2598                 extend(t, {\r
2599                         // Inital states\r
2600                         startContainer : doc,\r
2601                         startOffset : 0,\r
2602                         endContainer : doc,\r
2603                         endOffset : 0,\r
2604                         collapsed : TRUE,\r
2605                         commonAncestorContainer : doc,\r
2606 \r
2607                         // Range constants\r
2608                         START_TO_START : 0,\r
2609                         START_TO_END : 1,\r
2610                         END_TO_END : 2,\r
2611                         END_TO_START : 3,\r
2612 \r
2613                         // Public methods\r
2614                         setStart : setStart,\r
2615                         setEnd : setEnd,\r
2616                         setStartBefore : setStartBefore,\r
2617                         setStartAfter : setStartAfter,\r
2618                         setEndBefore : setEndBefore,\r
2619                         setEndAfter : setEndAfter,\r
2620                         collapse : collapse,\r
2621                         selectNode : selectNode,\r
2622                         selectNodeContents : selectNodeContents,\r
2623                         compareBoundaryPoints : compareBoundaryPoints,\r
2624                         deleteContents : deleteContents,\r
2625                         extractContents : extractContents,\r
2626                         cloneContents : cloneContents,\r
2627                         insertNode : insertNode,\r
2628                         surroundContents : surroundContents,\r
2629                         cloneRange : cloneRange\r
2630                 });\r
2631 \r
2632                 function setStart(n, o) {\r
2633                         _setEndPoint(TRUE, n, o);\r
2634                 };\r
2635 \r
2636                 function setEnd(n, o) {\r
2637                         _setEndPoint(FALSE, n, o);\r
2638                 };\r
2639 \r
2640                 function setStartBefore(n) {\r
2641                         setStart(n.parentNode, nodeIndex(n));\r
2642                 };\r
2643 \r
2644                 function setStartAfter(n) {\r
2645                         setStart(n.parentNode, nodeIndex(n) + 1);\r
2646                 };\r
2647 \r
2648                 function setEndBefore(n) {\r
2649                         setEnd(n.parentNode, nodeIndex(n));\r
2650                 };\r
2651 \r
2652                 function setEndAfter(n) {\r
2653                         setEnd(n.parentNode, nodeIndex(n) + 1);\r
2654                 };\r
2655 \r
2656                 function collapse(ts) {\r
2657                         if (ts) {\r
2658                                 t[END_CONTAINER] = t[START_CONTAINER];\r
2659                                 t[END_OFFSET] = t[START_OFFSET];\r
2660                         } else {\r
2661                                 t[START_CONTAINER] = t[END_CONTAINER];\r
2662                                 t[START_OFFSET] = t[END_OFFSET];\r
2663                         }\r
2664 \r
2665                         t.collapsed = TRUE;\r
2666                 };\r
2667 \r
2668                 function selectNode(n) {\r
2669                         setStartBefore(n);\r
2670                         setEndAfter(n);\r
2671                 };\r
2672 \r
2673                 function selectNodeContents(n) {\r
2674                         setStart(n, 0);\r
2675                         setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);\r
2676                 };\r
2677 \r
2678                 function compareBoundaryPoints(h, r) {\r
2679                         var sc = t[START_CONTAINER], so = t[START_OFFSET], ec = t[END_CONTAINER], eo = t[END_OFFSET];\r
2680 \r
2681                         // Check START_TO_START\r
2682                         if (h === 0)\r
2683                                 return _compareBoundaryPoints(sc, so, sc, so);\r
2684 \r
2685                         // Check START_TO_END\r
2686                         if (h === 1)\r
2687                                 return _compareBoundaryPoints(sc, so, ec, eo);\r
2688 \r
2689                         // Check END_TO_END\r
2690                         if (h === 2)\r
2691                                 return _compareBoundaryPoints(ec, eo, ec, eo);\r
2692 \r
2693                         // Check END_TO_START\r
2694                         if (h === 3)\r
2695                                 return _compareBoundaryPoints(ec, eo, sc, so);\r
2696                 };\r
2697 \r
2698                 function deleteContents() {\r
2699                         _traverse(DELETE);\r
2700                 };\r
2701 \r
2702                 function extractContents() {\r
2703                         return _traverse(EXTRACT);\r
2704                 };\r
2705 \r
2706                 function cloneContents() {\r
2707                         return _traverse(CLONE);\r
2708                 };\r
2709 \r
2710                 function insertNode(n) {\r
2711                         var startContainer = this[START_CONTAINER],\r
2712                                 startOffset = this[START_OFFSET], nn, o;\r
2713 \r
2714                         // Node is TEXT_NODE or CDATA\r
2715                         if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {\r
2716                                 if (!startOffset) {\r
2717                                         // At the start of text\r
2718                                         startContainer.parentNode.insertBefore(n, startContainer);\r
2719                                 } else if (startOffset >= startContainer.nodeValue.length) {\r
2720                                         // At the end of text\r
2721                                         dom.insertAfter(n, startContainer);\r
2722                                 } else {\r
2723                                         // Middle, need to split\r
2724                                         nn = startContainer.splitText(startOffset);\r
2725                                         startContainer.parentNode.insertBefore(n, nn);\r
2726                                 }\r
2727                         } else {\r
2728                                 // Insert element node\r
2729                                 if (startContainer.childNodes.length > 0)\r
2730                                         o = startContainer.childNodes[startOffset];\r
2731 \r
2732                                 if (o)\r
2733                                         startContainer.insertBefore(n, o);\r
2734                                 else\r
2735                                         startContainer.appendChild(n);\r
2736                         }\r
2737                 };\r
2738 \r
2739                 function surroundContents(n) {\r
2740                         var f = t.extractContents();\r
2741 \r
2742                         t.insertNode(n);\r
2743                         n.appendChild(f);\r
2744                         t.selectNode(n);\r
2745                 };\r
2746 \r
2747                 function cloneRange() {\r
2748                         return extend(new Range(dom), {\r
2749                                 startContainer : t[START_CONTAINER],\r
2750                                 startOffset : t[START_OFFSET],\r
2751                                 endContainer : t[END_CONTAINER],\r
2752                                 endOffset : t[END_OFFSET],\r
2753                                 collapsed : t.collapsed,\r
2754                                 commonAncestorContainer : t.commonAncestorContainer\r
2755                         });\r
2756                 };\r
2757 \r
2758                 // Private methods\r
2759 \r
2760                 function _getSelectedNode(container, offset) {\r
2761                         var child;\r
2762 \r
2763                         if (container.nodeType == 3 /* TEXT_NODE */)\r
2764                                 return container;\r
2765 \r
2766                         if (offset < 0)\r
2767                                 return container;\r
2768 \r
2769                         child = container.firstChild;\r
2770                         while (child && offset > 0) {\r
2771                                 --offset;\r
2772                                 child = child.nextSibling;\r
2773                         }\r
2774 \r
2775                         if (child)\r
2776                                 return child;\r
2777 \r
2778                         return container;\r
2779                 };\r
2780 \r
2781                 function _isCollapsed() {\r
2782                         return (t[START_CONTAINER] == t[END_CONTAINER] && t[START_OFFSET] == t[END_OFFSET]);\r
2783                 };\r
2784 \r
2785                 function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {\r
2786                         var c, offsetC, n, cmnRoot, childA, childB;\r
2787 \r
2788                         // In the first case the boundary-points have the same container. A is before B\r
2789                         // if its offset is less than the offset of B, A is equal to B if its offset is\r
2790                         // equal to the offset of B, and A is after B if its offset is greater than the\r
2791                         // offset of B.\r
2792                         if (containerA == containerB) {\r
2793                                 if (offsetA == offsetB)\r
2794                                         return 0; // equal\r
2795 \r
2796                                 if (offsetA < offsetB)\r
2797                                         return -1; // before\r
2798 \r
2799                                 return 1; // after\r
2800                         }\r
2801 \r
2802                         // In the second case a child node C of the container of A is an ancestor\r
2803                         // container of B. In this case, A is before B if the offset of A is less than or\r
2804                         // equal to the index of the child node C and A is after B otherwise.\r
2805                         c = containerB;\r
2806                         while (c && c.parentNode != containerA)\r
2807                                 c = c.parentNode;\r
2808 \r
2809                         if (c) {\r
2810                                 offsetC = 0;\r
2811                                 n = containerA.firstChild;\r
2812 \r
2813                                 while (n != c && offsetC < offsetA) {\r
2814                                         offsetC++;\r
2815                                         n = n.nextSibling;\r
2816                                 }\r
2817 \r
2818                                 if (offsetA <= offsetC)\r
2819                                         return -1; // before\r
2820 \r
2821                                 return 1; // after\r
2822                         }\r
2823 \r
2824                         // In the third case a child node C of the container of B is an ancestor container\r
2825                         // of A. In this case, A is before B if the index of the child node C is less than\r
2826                         // the offset of B and A is after B otherwise.\r
2827                         c = containerA;\r
2828                         while (c && c.parentNode != containerB) {\r
2829                                 c = c.parentNode;\r
2830                         }\r
2831 \r
2832                         if (c) {\r
2833                                 offsetC = 0;\r
2834                                 n = containerB.firstChild;\r
2835 \r
2836                                 while (n != c && offsetC < offsetB) {\r
2837                                         offsetC++;\r
2838                                         n = n.nextSibling;\r
2839                                 }\r
2840 \r
2841                                 if (offsetC < offsetB)\r
2842                                         return -1; // before\r
2843 \r
2844                                 return 1; // after\r
2845                         }\r
2846 \r
2847                         // In the fourth case, none of three other cases hold: the containers of A and B\r
2848                         // are siblings or descendants of sibling nodes. In this case, A is before B if\r
2849                         // the container of A is before the container of B in a pre-order traversal of the\r
2850                         // Ranges' context tree and A is after B otherwise.\r
2851                         cmnRoot = dom.findCommonAncestor(containerA, containerB);\r
2852                         childA = containerA;\r
2853 \r
2854                         while (childA && childA.parentNode != cmnRoot)\r
2855                                 childA = childA.parentNode;\r
2856 \r
2857                         if (!childA)\r
2858                                 childA = cmnRoot;\r
2859 \r
2860                         childB = containerB;\r
2861                         while (childB && childB.parentNode != cmnRoot)\r
2862                                 childB = childB.parentNode;\r
2863 \r
2864                         if (!childB)\r
2865                                 childB = cmnRoot;\r
2866 \r
2867                         if (childA == childB)\r
2868                                 return 0; // equal\r
2869 \r
2870                         n = cmnRoot.firstChild;\r
2871                         while (n) {\r
2872                                 if (n == childA)\r
2873                                         return -1; // before\r
2874 \r
2875                                 if (n == childB)\r
2876                                         return 1; // after\r
2877 \r
2878                                 n = n.nextSibling;\r
2879                         }\r
2880                 };\r
2881 \r
2882                 function _setEndPoint(st, n, o) {\r
2883                         var ec, sc;\r
2884 \r
2885                         if (st) {\r
2886                                 t[START_CONTAINER] = n;\r
2887                                 t[START_OFFSET] = o;\r
2888                         } else {\r
2889                                 t[END_CONTAINER] = n;\r
2890                                 t[END_OFFSET] = o;\r
2891                         }\r
2892 \r
2893                         // If one boundary-point of a Range is set to have a root container\r
2894                         // other than the current one for the Range, the Range is collapsed to\r
2895                         // the new position. This enforces the restriction that both boundary-\r
2896                         // points of a Range must have the same root container.\r
2897                         ec = t[END_CONTAINER];\r
2898                         while (ec.parentNode)\r
2899                                 ec = ec.parentNode;\r
2900 \r
2901                         sc = t[START_CONTAINER];\r
2902                         while (sc.parentNode)\r
2903                                 sc = sc.parentNode;\r
2904 \r
2905                         if (sc == ec) {\r
2906                                 // The start position of a Range is guaranteed to never be after the\r
2907                                 // end position. To enforce this restriction, if the start is set to\r
2908                                 // be at a position after the end, the Range is collapsed to that\r
2909                                 // position.\r
2910                                 if (_compareBoundaryPoints(t[START_CONTAINER], t[START_OFFSET], t[END_CONTAINER], t[END_OFFSET]) > 0)\r
2911                                         t.collapse(st);\r
2912                         } else\r
2913                                 t.collapse(st);\r
2914 \r
2915                         t.collapsed = _isCollapsed();\r
2916                         t.commonAncestorContainer = dom.findCommonAncestor(t[START_CONTAINER], t[END_CONTAINER]);\r
2917                 };\r
2918 \r
2919                 function _traverse(how) {\r
2920                         var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;\r
2921 \r
2922                         if (t[START_CONTAINER] == t[END_CONTAINER])\r
2923                                 return _traverseSameContainer(how);\r
2924 \r
2925                         for (c = t[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
2926                                 if (p == t[START_CONTAINER])\r
2927                                         return _traverseCommonStartContainer(c, how);\r
2928 \r
2929                                 ++endContainerDepth;\r
2930                         }\r
2931 \r
2932                         for (c = t[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {\r
2933                                 if (p == t[END_CONTAINER])\r
2934                                         return _traverseCommonEndContainer(c, how);\r
2935 \r
2936                                 ++startContainerDepth;\r
2937                         }\r
2938 \r
2939                         depthDiff = startContainerDepth - endContainerDepth;\r
2940 \r
2941                         startNode = t[START_CONTAINER];\r
2942                         while (depthDiff > 0) {\r
2943                                 startNode = startNode.parentNode;\r
2944                                 depthDiff--;\r
2945                         }\r
2946 \r
2947                         endNode = t[END_CONTAINER];\r
2948                         while (depthDiff < 0) {\r
2949                                 endNode = endNode.parentNode;\r
2950                                 depthDiff++;\r
2951                         }\r
2952 \r
2953                         // ascend the ancestor hierarchy until we have a common parent.\r
2954                         for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {\r
2955                                 startNode = sp;\r
2956                                 endNode = ep;\r
2957                         }\r
2958 \r
2959                         return _traverseCommonAncestors(startNode, endNode, how);\r
2960                 };\r
2961 \r
2962                  function _traverseSameContainer(how) {\r
2963                         var frag, s, sub, n, cnt, sibling, xferNode;\r
2964 \r
2965                         if (how != DELETE)\r
2966                                 frag = doc.createDocumentFragment();\r
2967 \r
2968                         // If selection is empty, just return the fragment\r
2969                         if (t[START_OFFSET] == t[END_OFFSET])\r
2970                                 return frag;\r
2971 \r
2972                         // Text node needs special case handling\r
2973                         if (t[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {\r
2974                                 // get the substring\r
2975                                 s = t[START_CONTAINER].nodeValue;\r
2976                                 sub = s.substring(t[START_OFFSET], t[END_OFFSET]);\r
2977 \r
2978                                 // set the original text node to its new value\r
2979                                 if (how != CLONE) {\r
2980                                         t[START_CONTAINER].deleteData(t[START_OFFSET], t[END_OFFSET] - t[START_OFFSET]);\r
2981 \r
2982                                         // Nothing is partially selected, so collapse to start point\r
2983                                         t.collapse(TRUE);\r
2984                                 }\r
2985 \r
2986                                 if (how == DELETE)\r
2987                                         return;\r
2988 \r
2989                                 frag.appendChild(doc.createTextNode(sub));\r
2990                                 return frag;\r
2991                         }\r
2992 \r
2993                         // Copy nodes between the start/end offsets.\r
2994                         n = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]);\r
2995                         cnt = t[END_OFFSET] - t[START_OFFSET];\r
2996 \r
2997                         while (cnt > 0) {\r
2998                                 sibling = n.nextSibling;\r
2999                                 xferNode = _traverseFullySelected(n, how);\r
3000 \r
3001                                 if (frag)\r
3002                                         frag.appendChild( xferNode );\r
3003 \r
3004                                 --cnt;\r
3005                                 n = sibling;\r
3006                         }\r
3007 \r
3008                         // Nothing is partially selected, so collapse to start point\r
3009                         if (how != CLONE)\r
3010                                 t.collapse(TRUE);\r
3011 \r
3012                         return frag;\r
3013                 };\r
3014 \r
3015                 function _traverseCommonStartContainer(endAncestor, how) {\r
3016                         var frag, n, endIdx, cnt, sibling, xferNode;\r
3017 \r
3018                         if (how != DELETE)\r
3019                                 frag = doc.createDocumentFragment();\r
3020 \r
3021                         n = _traverseRightBoundary(endAncestor, how);\r
3022 \r
3023                         if (frag)\r
3024                                 frag.appendChild(n);\r
3025 \r
3026                         endIdx = nodeIndex(endAncestor);\r
3027                         cnt = endIdx - t[START_OFFSET];\r
3028 \r
3029                         if (cnt <= 0) {\r
3030                                 // Collapse to just before the endAncestor, which\r
3031                                 // is partially selected.\r
3032                                 if (how != CLONE) {\r
3033                                         t.setEndBefore(endAncestor);\r
3034                                         t.collapse(FALSE);\r
3035                                 }\r
3036 \r
3037                                 return frag;\r
3038                         }\r
3039 \r
3040                         n = endAncestor.previousSibling;\r
3041                         while (cnt > 0) {\r
3042                                 sibling = n.previousSibling;\r
3043                                 xferNode = _traverseFullySelected(n, how);\r
3044 \r
3045                                 if (frag)\r
3046                                         frag.insertBefore(xferNode, frag.firstChild);\r
3047 \r
3048                                 --cnt;\r
3049                                 n = sibling;\r
3050                         }\r
3051 \r
3052                         // Collapse to just before the endAncestor, which\r
3053                         // is partially selected.\r
3054                         if (how != CLONE) {\r
3055                                 t.setEndBefore(endAncestor);\r
3056                                 t.collapse(FALSE);\r
3057                         }\r
3058 \r
3059                         return frag;\r
3060                 };\r
3061 \r
3062                 function _traverseCommonEndContainer(startAncestor, how) {\r
3063                         var frag, startIdx, n, cnt, sibling, xferNode;\r
3064 \r
3065                         if (how != DELETE)\r
3066                                 frag = doc.createDocumentFragment();\r
3067 \r
3068                         n = _traverseLeftBoundary(startAncestor, how);\r
3069                         if (frag)\r
3070                                 frag.appendChild(n);\r
3071 \r
3072                         startIdx = nodeIndex(startAncestor);\r
3073                         ++startIdx;  // Because we already traversed it....\r
3074 \r
3075                         cnt = t[END_OFFSET] - startIdx;\r
3076                         n = startAncestor.nextSibling;\r
3077                         while (cnt > 0) {\r
3078                                 sibling = n.nextSibling;\r
3079                                 xferNode = _traverseFullySelected(n, how);\r
3080 \r
3081                                 if (frag)\r
3082                                         frag.appendChild(xferNode);\r
3083 \r
3084                                 --cnt;\r
3085                                 n = sibling;\r
3086                         }\r
3087 \r
3088                         if (how != CLONE) {\r
3089                                 t.setStartAfter(startAncestor);\r
3090                                 t.collapse(TRUE);\r
3091                         }\r
3092 \r
3093                         return frag;\r
3094                 };\r
3095 \r
3096                 function _traverseCommonAncestors(startAncestor, endAncestor, how) {\r
3097                         var n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;\r
3098 \r
3099                         if (how != DELETE)\r
3100                                 frag = doc.createDocumentFragment();\r
3101 \r
3102                         n = _traverseLeftBoundary(startAncestor, how);\r
3103                         if (frag)\r
3104                                 frag.appendChild(n);\r
3105 \r
3106                         commonParent = startAncestor.parentNode;\r
3107                         startOffset = nodeIndex(startAncestor);\r
3108                         endOffset = nodeIndex(endAncestor);\r
3109                         ++startOffset;\r
3110 \r
3111                         cnt = endOffset - startOffset;\r
3112                         sibling = startAncestor.nextSibling;\r
3113 \r
3114                         while (cnt > 0) {\r
3115                                 nextSibling = sibling.nextSibling;\r
3116                                 n = _traverseFullySelected(sibling, how);\r
3117 \r
3118                                 if (frag)\r
3119                                         frag.appendChild(n);\r
3120 \r
3121                                 sibling = nextSibling;\r
3122                                 --cnt;\r
3123                         }\r
3124 \r
3125                         n = _traverseRightBoundary(endAncestor, how);\r
3126 \r
3127                         if (frag)\r
3128                                 frag.appendChild(n);\r
3129 \r
3130                         if (how != CLONE) {\r
3131                                 t.setStartAfter(startAncestor);\r
3132                                 t.collapse(TRUE);\r
3133                         }\r
3134 \r
3135                         return frag;\r
3136                 };\r
3137 \r
3138                 function _traverseRightBoundary(root, how) {\r
3139                         var next = _getSelectedNode(t[END_CONTAINER], t[END_OFFSET] - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != t[END_CONTAINER];\r
3140 \r
3141                         if (next == root)\r
3142                                 return _traverseNode(next, isFullySelected, FALSE, how);\r
3143 \r
3144                         parent = next.parentNode;\r
3145                         clonedParent = _traverseNode(parent, FALSE, FALSE, how);\r
3146 \r
3147                         while (parent) {\r
3148                                 while (next) {\r
3149                                         prevSibling = next.previousSibling;\r
3150                                         clonedChild = _traverseNode(next, isFullySelected, FALSE, how);\r
3151 \r
3152                                         if (how != DELETE)\r
3153                                                 clonedParent.insertBefore(clonedChild, clonedParent.firstChild);\r
3154 \r
3155                                         isFullySelected = TRUE;\r
3156                                         next = prevSibling;\r
3157                                 }\r
3158 \r
3159                                 if (parent == root)\r
3160                                         return clonedParent;\r
3161 \r
3162                                 next = parent.previousSibling;\r
3163                                 parent = parent.parentNode;\r
3164 \r
3165                                 clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);\r
3166 \r
3167                                 if (how != DELETE)\r
3168                                         clonedGrandParent.appendChild(clonedParent);\r
3169 \r
3170                                 clonedParent = clonedGrandParent;\r
3171                         }\r
3172                 };\r
3173 \r
3174                 function _traverseLeftBoundary(root, how) {\r
3175                         var next = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]), isFullySelected = next != t[START_CONTAINER], parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;\r
3176 \r
3177                         if (next == root)\r
3178                                 return _traverseNode(next, isFullySelected, TRUE, how);\r
3179 \r
3180                         parent = next.parentNode;\r
3181                         clonedParent = _traverseNode(parent, FALSE, TRUE, how);\r
3182 \r
3183                         while (parent) {\r
3184                                 while (next) {\r
3185                                         nextSibling = next.nextSibling;\r
3186                                         clonedChild = _traverseNode(next, isFullySelected, TRUE, how);\r
3187 \r
3188                                         if (how != DELETE)\r
3189                                                 clonedParent.appendChild(clonedChild);\r
3190 \r
3191                                         isFullySelected = TRUE;\r
3192                                         next = nextSibling;\r
3193                                 }\r
3194 \r
3195                                 if (parent == root)\r
3196                                         return clonedParent;\r
3197 \r
3198                                 next = parent.nextSibling;\r
3199                                 parent = parent.parentNode;\r
3200 \r
3201                                 clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);\r
3202 \r
3203                                 if (how != DELETE)\r
3204                                         clonedGrandParent.appendChild(clonedParent);\r
3205 \r
3206                                 clonedParent = clonedGrandParent;\r
3207                         }\r
3208                 };\r
3209 \r
3210                 function _traverseNode(n, isFullySelected, isLeft, how) {\r
3211                         var txtValue, newNodeValue, oldNodeValue, offset, newNode;\r
3212 \r
3213                         if (isFullySelected)\r
3214                                 return _traverseFullySelected(n, how);\r
3215 \r
3216                         if (n.nodeType == 3 /* TEXT_NODE */) {\r
3217                                 txtValue = n.nodeValue;\r
3218 \r
3219                                 if (isLeft) {\r
3220                                         offset = t[START_OFFSET];\r
3221                                         newNodeValue = txtValue.substring(offset);\r
3222                                         oldNodeValue = txtValue.substring(0, offset);\r
3223                                 } else {\r
3224                                         offset = t[END_OFFSET];\r
3225                                         newNodeValue = txtValue.substring(0, offset);\r
3226                                         oldNodeValue = txtValue.substring(offset);\r
3227                                 }\r
3228 \r
3229                                 if (how != CLONE)\r
3230                                         n.nodeValue = oldNodeValue;\r
3231 \r
3232                                 if (how == DELETE)\r
3233                                         return;\r
3234 \r
3235                                 newNode = n.cloneNode(FALSE);\r
3236                                 newNode.nodeValue = newNodeValue;\r
3237 \r
3238                                 return newNode;\r
3239                         }\r
3240 \r
3241                         if (how == DELETE)\r
3242                                 return;\r
3243 \r
3244                         return n.cloneNode(FALSE);\r
3245                 };\r
3246 \r
3247                 function _traverseFullySelected(n, how) {\r
3248                         if (how != DELETE)\r
3249                                 return how == CLONE ? n.cloneNode(TRUE) : n;\r
3250 \r
3251                         n.parentNode.removeChild(n);\r
3252                 };\r
3253         };\r
3254 \r
3255         ns.Range = Range;\r
3256 })(tinymce.dom);\r
3257 \r
3258 (function() {\r
3259         function Selection(selection) {\r
3260                 var t = this, invisibleChar = '\uFEFF', range, lastIERng, dom = selection.dom, TRUE = true, FALSE = false;\r
3261 \r
3262                 // Returns a W3C DOM compatible range object by using the IE Range API\r
3263                 function getRange() {\r
3264                         var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed;\r
3265 \r
3266                         // If selection is outside the current document just return an empty range\r
3267                         element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();\r
3268                         if (element.ownerDocument != dom.doc)\r
3269                                 return domRange;\r
3270 \r
3271                         // Handle control selection or text selection of a image\r
3272                         if (ieRange.item || !element.hasChildNodes()) {\r
3273                                 domRange.setStart(element.parentNode, dom.nodeIndex(element));\r
3274                                 domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);\r
3275 \r
3276                                 return domRange;\r
3277                         }\r
3278 \r
3279                         collapsed = selection.isCollapsed();\r
3280 \r
3281                         function findEndPoint(start) {\r
3282                                 var marker, container, offset, nodes, startIndex = 0, endIndex, index, parent, checkRng, position;\r
3283 \r
3284                                 // Setup temp range and collapse it\r
3285                                 checkRng = ieRange.duplicate();\r
3286                                 checkRng.collapse(start);\r
3287 \r
3288                                 // Create marker and insert it at the end of the endpoints parent\r
3289                                 marker = dom.create('a');\r
3290                                 parent = checkRng.parentElement();\r
3291 \r
3292                                 // If parent doesn't have any children then set the container to that parent and the index to 0\r
3293                                 if (!parent.hasChildNodes()) {\r
3294                                         domRange[start ? 'setStart' : 'setEnd'](parent, 0);\r
3295                                         return;\r
3296                                 }\r
3297 \r
3298                                 parent.appendChild(marker);\r
3299                                 checkRng.moveToElementText(marker);\r
3300                                 position = ieRange.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', checkRng);\r
3301                                 if (position > 0) {\r
3302                                         // The position is after the end of the parent element.\r
3303                                         // This is the case where IE puts the caret to the left edge of a table.\r
3304                                         domRange[start ? 'setStartAfter' : 'setEndAfter'](parent);\r
3305                                         dom.remove(marker);\r
3306                                         return;\r
3307                                 }\r
3308 \r
3309                                 // Setup node list and endIndex\r
3310                                 nodes = tinymce.grep(parent.childNodes);\r
3311                                 endIndex = nodes.length - 1;\r
3312                                 // Perform a binary search for the position\r
3313                                 while (startIndex <= endIndex) {\r
3314                                         index = Math.floor((startIndex + endIndex) / 2);\r
3315 \r
3316                                         // Insert marker and check it's position relative to the selection\r
3317                                         parent.insertBefore(marker, nodes[index]);\r
3318                                         checkRng.moveToElementText(marker);\r
3319                                         position = ieRange.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', checkRng);\r
3320                                         if (position > 0) {\r
3321                                                 // Marker is to the right\r
3322                                                 startIndex = index + 1;\r
3323                                         } else if (position < 0) {\r
3324                                                 // Marker is to the left\r
3325                                                 endIndex = index - 1;\r
3326                                         } else {\r
3327                                                 // Maker is where we are\r
3328                                                 found = true;\r
3329                                                 break;\r
3330                                         }\r
3331                                 }\r
3332 \r
3333                                 // Setup container\r
3334                                 container = position > 0 || index == 0 ? marker.nextSibling : marker.previousSibling;\r
3335 \r
3336                                 // Handle element selection\r
3337                                 if (container.nodeType == 1) {\r
3338                                         dom.remove(marker);\r
3339 \r
3340                                         // Find offset and container\r
3341                                         offset = dom.nodeIndex(container);\r
3342                                         container = container.parentNode;\r
3343 \r
3344                                         // Move the offset if we are setting the end or the position is after an element\r
3345                                         if (!start || index > 0)\r
3346                                                 offset++;\r
3347                                 } else {\r
3348                                         // Calculate offset within text node\r
3349                                         if (position > 0 || index == 0) {\r
3350                                                 checkRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', ieRange);\r
3351                                                 offset = checkRng.text.length;\r
3352                                         } else {\r
3353                                                 checkRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', ieRange);\r
3354                                                 offset = container.nodeValue.length - checkRng.text.length;\r
3355                                         }\r
3356 \r
3357                                         dom.remove(marker);\r
3358                                 }\r
3359 \r
3360                                 domRange[start ? 'setStart' : 'setEnd'](container, offset);\r
3361                         };\r
3362 \r
3363                         // Find start point\r
3364                         findEndPoint(true);\r
3365 \r
3366                         // Find end point if needed\r
3367                         if (!collapsed)\r
3368                                 findEndPoint();\r
3369 \r
3370                         return domRange;\r
3371                 };\r
3372 \r
3373                 this.addRange = function(rng) {\r
3374                         var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, doc = selection.dom.doc, body = doc.body;\r
3375 \r
3376                         function setEndPoint(start) {\r
3377                                 var container, offset, marker, tmpRng, nodes;\r
3378 \r
3379                                 marker = dom.create('a');\r
3380                                 container = start ? startContainer : endContainer;\r
3381                                 offset = start ? startOffset : endOffset;\r
3382                                 tmpRng = ieRng.duplicate();\r
3383 \r
3384                                 if (container == doc) {\r
3385                                         container = body;\r
3386                                         offset = 0;\r
3387                                 }\r
3388 \r
3389                                 if (container.nodeType == 3) {\r
3390                                         container.parentNode.insertBefore(marker, container);\r
3391                                         tmpRng.moveToElementText(marker);\r
3392                                         tmpRng.moveStart('character', offset);\r
3393                                         dom.remove(marker);\r
3394                                         ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);\r
3395                                 } else {\r
3396                                         nodes = container.childNodes;\r
3397 \r
3398                                         if (nodes.length) {\r
3399                                                 if (offset >= nodes.length) {\r
3400                                                         dom.insertAfter(marker, nodes[nodes.length - 1]);\r
3401                                                 } else {\r
3402                                                         container.insertBefore(marker, nodes[offset]);\r
3403                                                 }\r
3404 \r
3405                                                 tmpRng.moveToElementText(marker);\r
3406                                         } else {\r
3407                                                 // Empty node selection for example <div>|</div>\r
3408                                                 marker = doc.createTextNode(invisibleChar);\r
3409                                                 container.appendChild(marker);\r
3410                                                 tmpRng.moveToElementText(marker.parentNode);\r
3411                                                 tmpRng.collapse(TRUE);\r
3412                                         }\r
3413 \r
3414                                         ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);\r
3415                                         dom.remove(marker);\r
3416                                 }\r
3417                         }\r
3418 \r
3419                         // Destroy cached range\r
3420                         this.destroy();\r
3421 \r
3422                         // Setup some shorter versions\r
3423                         startContainer = rng.startContainer;\r
3424                         startOffset = rng.startOffset;\r
3425                         endContainer = rng.endContainer;\r
3426                         endOffset = rng.endOffset;\r
3427                         ieRng = body.createTextRange();\r
3428 \r
3429                         // If single element selection then try making a control selection out of it\r
3430                         if (startContainer == endContainer && startContainer.nodeType == 1 && startOffset == endOffset - 1) {\r
3431                                 if (startOffset == endOffset - 1) {\r
3432                                         try {\r
3433                                                 ctrlRng = body.createControlRange();\r
3434                                                 ctrlRng.addElement(startContainer.childNodes[startOffset]);\r
3435                                                 ctrlRng.select();\r
3436                                                 ctrlRng.scrollIntoView();\r
3437                                                 return;\r
3438                                         } catch (ex) {\r
3439                                                 // Ignore\r
3440                                         }\r
3441                                 }\r
3442                         }\r
3443 \r
3444                         // Set start/end point of selection\r
3445                         setEndPoint(true);\r
3446                         setEndPoint();\r
3447 \r
3448                         // Select the new range and scroll it into view\r
3449                         ieRng.select();\r
3450                         ieRng.scrollIntoView();\r
3451                 };\r
3452 \r
3453                 this.getRangeAt = function() {\r
3454                         // Setup new range if the cache is empty\r
3455                         if (!range || !tinymce.dom.RangeUtils.compareRanges(lastIERng, selection.getRng())) {\r
3456                                 range = getRange();\r
3457 \r
3458                                 // Store away text range for next call\r
3459                                 lastIERng = selection.getRng();\r
3460                         }\r
3461 \r
3462                         // IE will say that the range is equal then produce an invalid argument exception\r
3463                         // if you perform specific operations in a keyup event. For example Ctrl+Del.\r
3464                         // This hack will invalidate the range cache if the exception occurs\r
3465                         try {\r
3466                                 range.startContainer.nextSibling;\r
3467                         } catch (ex) {\r
3468                                 range = getRange();\r
3469                                 lastIERng = null;\r
3470                         }\r
3471 \r
3472                         // Return cached range\r
3473                         return range;\r
3474                 };\r
3475 \r
3476                 this.destroy = function() {\r
3477                         // Destroy cached range and last IE range to avoid memory leaks\r
3478                         lastIERng = range = null;\r
3479                 };\r
3480 \r
3481                 // 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
3482                 if (selection.dom.boxModel) {\r
3483                         (function() {\r
3484                                 var doc = dom.doc, body = doc.body, started, startRng;\r
3485 \r
3486                                 // Make HTML element unselectable since we are going to handle selection by hand\r
3487                                 doc.documentElement.unselectable = TRUE;\r
3488 \r
3489                                 // Return range from point or null if it failed\r
3490                                 function rngFromPoint(x, y) {\r
3491                                         var rng = body.createTextRange();\r
3492 \r
3493                                         try {\r
3494                                                 rng.moveToPoint(x, y);\r
3495                                         } catch (ex) {\r
3496                                                 // IE sometimes throws and exception, so lets just ignore it\r
3497                                                 rng = null;\r
3498                                         }\r
3499 \r
3500                                         return rng;\r
3501                                 };\r
3502 \r
3503                                 // Fires while the selection is changing\r
3504                                 function selectionChange(e) {\r
3505                                         var pointRng;\r
3506 \r
3507                                         // Check if the button is down or not\r
3508                                         if (e.button) {\r
3509                                                 // Create range from mouse position\r
3510                                                 pointRng = rngFromPoint(e.x, e.y);\r
3511 \r
3512                                                 if (pointRng) {\r
3513                                                         // Check if pointRange is before/after selection then change the endPoint\r
3514                                                         if (pointRng.compareEndPoints('StartToStart', startRng) > 0)\r
3515                                                                 pointRng.setEndPoint('StartToStart', startRng);\r
3516                                                         else\r
3517                                                                 pointRng.setEndPoint('EndToEnd', startRng);\r
3518 \r
3519                                                         pointRng.select();\r
3520                                                 }\r
3521                                         } else\r
3522                                                 endSelection();\r
3523                                 }\r
3524 \r
3525                                 // Removes listeners\r
3526                                 function endSelection() {\r
3527                                         dom.unbind(doc, 'mouseup', endSelection);\r
3528                                         dom.unbind(doc, 'mousemove', selectionChange);\r
3529                                         started = 0;\r
3530                                 };\r
3531 \r
3532                                 // Detect when user selects outside BODY\r
3533                                 dom.bind(doc, 'mousedown', function(e) {\r
3534                                         if (e.target.nodeName === 'HTML') {\r
3535                                                 if (started)\r
3536                                                         endSelection();\r
3537 \r
3538                                                 started = 1;\r
3539 \r
3540                                                 // Setup start position\r
3541                                                 startRng = rngFromPoint(e.x, e.y);\r
3542                                                 if (startRng) {\r
3543                                                         // Listen for selection change events\r
3544                                                         dom.bind(doc, 'mouseup', endSelection);\r
3545                                                         dom.bind(doc, 'mousemove', selectionChange);\r
3546 \r
3547                                                         startRng.select();\r
3548                                                 }\r
3549                                         }\r
3550                                 });\r
3551                         })();\r
3552                 }\r
3553         };\r
3554 \r
3555         // Expose the selection object\r
3556         tinymce.dom.TridentSelection = Selection;\r
3557 })();\r
3558 \r
3559 \r
3560 /*\r
3561  * Sizzle CSS Selector Engine - v1.0\r
3562  *  Copyright 2009, The Dojo Foundation\r
3563  *  Released under the MIT, BSD, and GPL Licenses.\r
3564  *  More information: http://sizzlejs.com/\r
3565  */\r
3566 (function(){\r
3567 \r
3568 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,\r
3569         done = 0,\r
3570         toString = Object.prototype.toString,\r
3571         hasDuplicate = false,\r
3572         baseHasDuplicate = true;\r
3573 \r
3574 // Here we check if the JavaScript engine is using some sort of\r
3575 // optimization where it does not always call our comparision\r
3576 // function. If that is the case, discard the hasDuplicate value.\r
3577 //   Thus far that includes Google Chrome.\r
3578 [0, 0].sort(function(){\r
3579         baseHasDuplicate = false;\r
3580         return 0;\r
3581 });\r
3582 \r
3583 var Sizzle = function(selector, context, results, seed) {\r
3584         results = results || [];\r
3585         context = context || document;\r
3586 \r
3587         var origContext = context;\r
3588 \r
3589         if ( context.nodeType !== 1 && context.nodeType !== 9 ) {\r
3590                 return [];\r
3591         }\r
3592         \r
3593         if ( !selector || typeof selector !== "string" ) {\r
3594                 return results;\r
3595         }\r
3596 \r
3597         var parts = [], m, set, checkSet, extra, prune = true, contextXML = Sizzle.isXML(context),\r
3598                 soFar = selector, ret, cur, pop, i;\r
3599         \r
3600         // Reset the position of the chunker regexp (start from head)\r
3601         do {\r
3602                 chunker.exec("");\r
3603                 m = chunker.exec(soFar);\r
3604 \r
3605                 if ( m ) {\r
3606                         soFar = m[3];\r
3607                 \r
3608                         parts.push( m[1] );\r
3609                 \r
3610                         if ( m[2] ) {\r
3611                                 extra = m[3];\r
3612                                 break;\r
3613                         }\r
3614                 }\r
3615         } while ( m );\r
3616 \r
3617         if ( parts.length > 1 && origPOS.exec( selector ) ) {\r
3618                 if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {\r
3619                         set = posProcess( parts[0] + parts[1], context );\r
3620                 } else {\r
3621                         set = Expr.relative[ parts[0] ] ?\r
3622                                 [ context ] :\r
3623                                 Sizzle( parts.shift(), context );\r
3624 \r
3625                         while ( parts.length ) {\r
3626                                 selector = parts.shift();\r
3627 \r
3628                                 if ( Expr.relative[ selector ] ) {\r
3629                                         selector += parts.shift();\r
3630                                 }\r
3631                                 \r
3632                                 set = posProcess( selector, set );\r
3633                         }\r
3634                 }\r
3635         } else {\r
3636                 // Take a shortcut and set the context if the root selector is an ID\r
3637                 // (but not if it'll be faster if the inner selector is an ID)\r
3638                 if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&\r
3639                                 Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {\r
3640                         ret = Sizzle.find( parts.shift(), context, contextXML );\r
3641                         context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];\r
3642                 }\r
3643 \r
3644                 if ( context ) {\r
3645                         ret = seed ?\r
3646                                 { expr: parts.pop(), set: makeArray(seed) } :\r
3647                                 Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );\r
3648                         set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;\r
3649 \r
3650                         if ( parts.length > 0 ) {\r
3651                                 checkSet = makeArray(set);\r
3652                         } else {\r
3653                                 prune = false;\r
3654                         }\r
3655 \r
3656                         while ( parts.length ) {\r
3657                                 cur = parts.pop();\r
3658                                 pop = cur;\r
3659 \r
3660                                 if ( !Expr.relative[ cur ] ) {\r
3661                                         cur = "";\r
3662                                 } else {\r
3663                                         pop = parts.pop();\r
3664                                 }\r
3665 \r
3666                                 if ( pop == null ) {\r
3667                                         pop = context;\r
3668                                 }\r
3669 \r
3670                                 Expr.relative[ cur ]( checkSet, pop, contextXML );\r
3671                         }\r
3672                 } else {\r
3673                         checkSet = parts = [];\r
3674                 }\r
3675         }\r
3676 \r
3677         if ( !checkSet ) {\r
3678                 checkSet = set;\r
3679         }\r
3680 \r
3681         if ( !checkSet ) {\r
3682                 Sizzle.error( cur || selector );\r
3683         }\r
3684 \r
3685         if ( toString.call(checkSet) === "[object Array]" ) {\r
3686                 if ( !prune ) {\r
3687                         results.push.apply( results, checkSet );\r
3688                 } else if ( context && context.nodeType === 1 ) {\r
3689                         for ( i = 0; checkSet[i] != null; i++ ) {\r
3690                                 if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {\r
3691                                         results.push( set[i] );\r
3692                                 }\r
3693                         }\r
3694                 } else {\r
3695                         for ( i = 0; checkSet[i] != null; i++ ) {\r
3696                                 if ( checkSet[i] && checkSet[i].nodeType === 1 ) {\r
3697                                         results.push( set[i] );\r
3698                                 }\r
3699                         }\r
3700                 }\r
3701         } else {\r
3702                 makeArray( checkSet, results );\r
3703         }\r
3704 \r
3705         if ( extra ) {\r
3706                 Sizzle( extra, origContext, results, seed );\r
3707                 Sizzle.uniqueSort( results );\r
3708         }\r
3709 \r
3710         return results;\r
3711 };\r
3712 \r
3713 Sizzle.uniqueSort = function(results){\r
3714         if ( sortOrder ) {\r
3715                 hasDuplicate = baseHasDuplicate;\r
3716                 results.sort(sortOrder);\r
3717 \r
3718                 if ( hasDuplicate ) {\r
3719                         for ( var i = 1; i < results.length; i++ ) {\r
3720                                 if ( results[i] === results[i-1] ) {\r
3721                                         results.splice(i--, 1);\r
3722                                 }\r
3723                         }\r
3724                 }\r
3725         }\r
3726 \r
3727         return results;\r
3728 };\r
3729 \r
3730 Sizzle.matches = function(expr, set){\r
3731         return Sizzle(expr, null, null, set);\r
3732 };\r
3733 \r
3734 Sizzle.find = function(expr, context, isXML){\r
3735         var set;\r
3736 \r
3737         if ( !expr ) {\r
3738                 return [];\r
3739         }\r
3740 \r
3741         for ( var i = 0, l = Expr.order.length; i < l; i++ ) {\r
3742                 var type = Expr.order[i], match;\r
3743                 \r
3744                 if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {\r
3745                         var left = match[1];\r
3746                         match.splice(1,1);\r
3747 \r
3748                         if ( left.substr( left.length - 1 ) !== "\\" ) {\r
3749                                 match[1] = (match[1] || "").replace(/\\/g, "");\r
3750                                 set = Expr.find[ type ]( match, context, isXML );\r
3751                                 if ( set != null ) {\r
3752                                         expr = expr.replace( Expr.match[ type ], "" );\r
3753                                         break;\r
3754                                 }\r
3755                         }\r
3756                 }\r
3757         }\r
3758 \r
3759         if ( !set ) {\r
3760                 set = context.getElementsByTagName("*");\r
3761         }\r
3762 \r
3763         return {set: set, expr: expr};\r
3764 };\r
3765 \r
3766 Sizzle.filter = function(expr, set, inplace, not){\r
3767         var old = expr, result = [], curLoop = set, match, anyFound,\r
3768                 isXMLFilter = set && set[0] && Sizzle.isXML(set[0]);\r
3769 \r
3770         while ( expr && set.length ) {\r
3771                 for ( var type in Expr.filter ) {\r
3772                         if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {\r
3773                                 var filter = Expr.filter[ type ], found, item, left = match[1];\r
3774                                 anyFound = false;\r
3775 \r
3776                                 match.splice(1,1);\r
3777 \r
3778                                 if ( left.substr( left.length - 1 ) === "\\" ) {\r
3779                                         continue;\r
3780                                 }\r
3781 \r
3782                                 if ( curLoop === result ) {\r
3783                                         result = [];\r
3784                                 }\r
3785 \r
3786                                 if ( Expr.preFilter[ type ] ) {\r
3787                                         match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );\r
3788 \r
3789                                         if ( !match ) {\r
3790                                                 anyFound = found = true;\r
3791                                         } else if ( match === true ) {\r
3792                                                 continue;\r
3793                                         }\r
3794                                 }\r
3795 \r
3796                                 if ( match ) {\r
3797                                         for ( var i = 0; (item = curLoop[i]) != null; i++ ) {\r
3798                                                 if ( item ) {\r
3799                                                         found = filter( item, match, i, curLoop );\r
3800                                                         var pass = not ^ !!found;\r
3801 \r
3802                                                         if ( inplace && found != null ) {\r
3803                                                                 if ( pass ) {\r
3804                                                                         anyFound = true;\r
3805                                                                 } else {\r
3806                                                                         curLoop[i] = false;\r
3807                                                                 }\r
3808                                                         } else if ( pass ) {\r
3809                                                                 result.push( item );\r
3810                                                                 anyFound = true;\r
3811                                                         }\r
3812                                                 }\r
3813                                         }\r
3814                                 }\r
3815 \r
3816                                 if ( found !== undefined ) {\r
3817                                         if ( !inplace ) {\r
3818                                                 curLoop = result;\r
3819                                         }\r
3820 \r
3821                                         expr = expr.replace( Expr.match[ type ], "" );\r
3822 \r
3823                                         if ( !anyFound ) {\r
3824                                                 return [];\r
3825                                         }\r
3826 \r
3827                                         break;\r
3828                                 }\r
3829                         }\r
3830                 }\r
3831 \r
3832                 // Improper expression\r
3833                 if ( expr === old ) {\r
3834                         if ( anyFound == null ) {\r
3835                                 Sizzle.error( expr );\r
3836                         } else {\r
3837                                 break;\r
3838                         }\r
3839                 }\r
3840 \r
3841                 old = expr;\r
3842         }\r
3843 \r
3844         return curLoop;\r
3845 };\r
3846 \r
3847 Sizzle.error = function( msg ) {\r
3848         throw "Syntax error, unrecognized expression: " + msg;\r
3849 };\r
3850 \r
3851 var Expr = Sizzle.selectors = {\r
3852         order: [ "ID", "NAME", "TAG" ],\r
3853         match: {\r
3854                 ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,\r
3855                 CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,\r
3856                 NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,\r
3857                 ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,\r
3858                 TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,\r
3859                 CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,\r
3860                 POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,\r
3861                 PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/\r
3862         },\r
3863         leftMatch: {},\r
3864         attrMap: {\r
3865                 "class": "className",\r
3866                 "for": "htmlFor"\r
3867         },\r
3868         attrHandle: {\r
3869                 href: function(elem){\r
3870                         return elem.getAttribute("href");\r
3871                 }\r
3872         },\r
3873         relative: {\r
3874                 "+": function(checkSet, part){\r
3875                         var isPartStr = typeof part === "string",\r
3876                                 isTag = isPartStr && !/\W/.test(part),\r
3877                                 isPartStrNotTag = isPartStr && !isTag;\r
3878 \r
3879                         if ( isTag ) {\r
3880                                 part = part.toLowerCase();\r
3881                         }\r
3882 \r
3883                         for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {\r
3884                                 if ( (elem = checkSet[i]) ) {\r
3885                                         while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}\r
3886 \r
3887                                         checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?\r
3888                                                 elem || false :\r
3889                                                 elem === part;\r
3890                                 }\r
3891                         }\r
3892 \r
3893                         if ( isPartStrNotTag ) {\r
3894                                 Sizzle.filter( part, checkSet, true );\r
3895                         }\r
3896                 },\r
3897                 ">": function(checkSet, part){\r
3898                         var isPartStr = typeof part === "string",\r
3899                                 elem, i = 0, l = checkSet.length;\r
3900 \r
3901                         if ( isPartStr && !/\W/.test(part) ) {\r
3902                                 part = part.toLowerCase();\r
3903 \r
3904                                 for ( ; i < l; i++ ) {\r
3905                                         elem = checkSet[i];\r
3906                                         if ( elem ) {\r
3907                                                 var parent = elem.parentNode;\r
3908                                                 checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;\r
3909                                         }\r
3910                                 }\r
3911                         } else {\r
3912                                 for ( ; i < l; i++ ) {\r
3913                                         elem = checkSet[i];\r
3914                                         if ( elem ) {\r
3915                                                 checkSet[i] = isPartStr ?\r
3916                                                         elem.parentNode :\r
3917                                                         elem.parentNode === part;\r
3918                                         }\r
3919                                 }\r
3920 \r
3921                                 if ( isPartStr ) {\r
3922                                         Sizzle.filter( part, checkSet, true );\r
3923                                 }\r
3924                         }\r
3925                 },\r
3926                 "": function(checkSet, part, isXML){\r
3927                         var doneName = done++, checkFn = dirCheck, nodeCheck;\r
3928 \r
3929                         if ( typeof part === "string" && !/\W/.test(part) ) {\r
3930                                 part = part.toLowerCase();\r
3931                                 nodeCheck = part;\r
3932                                 checkFn = dirNodeCheck;\r
3933                         }\r
3934 \r
3935                         checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);\r
3936                 },\r
3937                 "~": function(checkSet, part, isXML){\r
3938                         var doneName = done++, checkFn = dirCheck, nodeCheck;\r
3939 \r
3940                         if ( typeof part === "string" && !/\W/.test(part) ) {\r
3941                                 part = part.toLowerCase();\r
3942                                 nodeCheck = part;\r
3943                                 checkFn = dirNodeCheck;\r
3944                         }\r
3945 \r
3946                         checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);\r
3947                 }\r
3948         },\r
3949         find: {\r
3950                 ID: function(match, context, isXML){\r
3951                         if ( typeof context.getElementById !== "undefined" && !isXML ) {\r
3952                                 var m = context.getElementById(match[1]);\r
3953                                 return m ? [m] : [];\r
3954                         }\r
3955                 },\r
3956                 NAME: function(match, context){\r
3957                         if ( typeof context.getElementsByName !== "undefined" ) {\r
3958                                 var ret = [], results = context.getElementsByName(match[1]);\r
3959 \r
3960                                 for ( var i = 0, l = results.length; i < l; i++ ) {\r
3961                                         if ( results[i].getAttribute("name") === match[1] ) {\r
3962                                                 ret.push( results[i] );\r
3963                                         }\r
3964                                 }\r
3965 \r
3966                                 return ret.length === 0 ? null : ret;\r
3967                         }\r
3968                 },\r
3969                 TAG: function(match, context){\r
3970                         return context.getElementsByTagName(match[1]);\r
3971                 }\r
3972         },\r
3973         preFilter: {\r
3974                 CLASS: function(match, curLoop, inplace, result, not, isXML){\r
3975                         match = " " + match[1].replace(/\\/g, "") + " ";\r
3976 \r
3977                         if ( isXML ) {\r
3978                                 return match;\r
3979                         }\r
3980 \r
3981                         for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {\r
3982                                 if ( elem ) {\r
3983                                         if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {\r
3984                                                 if ( !inplace ) {\r
3985                                                         result.push( elem );\r
3986                                                 }\r
3987                                         } else if ( inplace ) {\r
3988                                                 curLoop[i] = false;\r
3989                                         }\r
3990                                 }\r
3991                         }\r
3992 \r
3993                         return false;\r
3994                 },\r
3995                 ID: function(match){\r
3996                         return match[1].replace(/\\/g, "");\r
3997                 },\r
3998                 TAG: function(match, curLoop){\r
3999                         return match[1].toLowerCase();\r
4000                 },\r
4001                 CHILD: function(match){\r
4002                         if ( match[1] === "nth" ) {\r
4003                                 // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'\r
4004                                 var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(\r
4005                                         match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||\r
4006                                         !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);\r
4007 \r
4008                                 // calculate the numbers (first)n+(last) including if they are negative\r
4009                                 match[2] = (test[1] + (test[2] || 1)) - 0;\r
4010                                 match[3] = test[3] - 0;\r
4011                         }\r
4012 \r
4013                         // TODO: Move to normal caching system\r
4014                         match[0] = done++;\r
4015 \r
4016                         return match;\r
4017                 },\r
4018                 ATTR: function(match, curLoop, inplace, result, not, isXML){\r
4019                         var name = match[1].replace(/\\/g, "");\r
4020                         \r
4021                         if ( !isXML && Expr.attrMap[name] ) {\r
4022                                 match[1] = Expr.attrMap[name];\r
4023                         }\r
4024 \r
4025                         if ( match[2] === "~=" ) {\r
4026                                 match[4] = " " + match[4] + " ";\r
4027                         }\r
4028 \r
4029                         return match;\r
4030                 },\r
4031                 PSEUDO: function(match, curLoop, inplace, result, not){\r
4032                         if ( match[1] === "not" ) {\r
4033                                 // If we're dealing with a complex expression, or a simple one\r
4034                                 if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {\r
4035                                         match[3] = Sizzle(match[3], null, null, curLoop);\r
4036                                 } else {\r
4037                                         var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);\r
4038                                         if ( !inplace ) {\r
4039                                                 result.push.apply( result, ret );\r
4040                                         }\r
4041                                         return false;\r
4042                                 }\r
4043                         } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {\r
4044                                 return true;\r
4045                         }\r
4046                         \r
4047                         return match;\r
4048                 },\r
4049                 POS: function(match){\r
4050                         match.unshift( true );\r
4051                         return match;\r
4052                 }\r
4053         },\r
4054         filters: {\r
4055                 enabled: function(elem){\r
4056                         return elem.disabled === false && elem.type !== "hidden";\r
4057                 },\r
4058                 disabled: function(elem){\r
4059                         return elem.disabled === true;\r
4060                 },\r
4061                 checked: function(elem){\r
4062                         return elem.checked === true;\r
4063                 },\r
4064                 selected: function(elem){\r
4065                         // Accessing this property makes selected-by-default\r
4066                         // options in Safari work properly\r
4067                         elem.parentNode.selectedIndex;\r
4068                         return elem.selected === true;\r
4069                 },\r
4070                 parent: function(elem){\r
4071                         return !!elem.firstChild;\r
4072                 },\r
4073                 empty: function(elem){\r
4074                         return !elem.firstChild;\r
4075                 },\r
4076                 has: function(elem, i, match){\r
4077                         return !!Sizzle( match[3], elem ).length;\r
4078                 },\r
4079                 header: function(elem){\r
4080                         return (/h\d/i).test( elem.nodeName );\r
4081                 },\r
4082                 text: function(elem){\r
4083                         return "text" === elem.type;\r
4084                 },\r
4085                 radio: function(elem){\r
4086                         return "radio" === elem.type;\r
4087                 },\r
4088                 checkbox: function(elem){\r
4089                         return "checkbox" === elem.type;\r
4090                 },\r
4091                 file: function(elem){\r
4092                         return "file" === elem.type;\r
4093                 },\r
4094                 password: function(elem){\r
4095                         return "password" === elem.type;\r
4096                 },\r
4097                 submit: function(elem){\r
4098                         return "submit" === elem.type;\r
4099                 },\r
4100                 image: function(elem){\r
4101                         return "image" === elem.type;\r
4102                 },\r
4103                 reset: function(elem){\r
4104                         return "reset" === elem.type;\r
4105                 },\r
4106                 button: function(elem){\r
4107                         return "button" === elem.type || elem.nodeName.toLowerCase() === "button";\r
4108                 },\r
4109                 input: function(elem){\r
4110                         return (/input|select|textarea|button/i).test(elem.nodeName);\r
4111                 }\r
4112         },\r
4113         setFilters: {\r
4114                 first: function(elem, i){\r
4115                         return i === 0;\r
4116                 },\r
4117                 last: function(elem, i, match, array){\r
4118                         return i === array.length - 1;\r
4119                 },\r
4120                 even: function(elem, i){\r
4121                         return i % 2 === 0;\r
4122                 },\r
4123                 odd: function(elem, i){\r
4124                         return i % 2 === 1;\r
4125                 },\r
4126                 lt: function(elem, i, match){\r
4127                         return i < match[3] - 0;\r
4128                 },\r
4129                 gt: function(elem, i, match){\r
4130                         return i > match[3] - 0;\r
4131                 },\r
4132                 nth: function(elem, i, match){\r
4133                         return match[3] - 0 === i;\r
4134                 },\r
4135                 eq: function(elem, i, match){\r
4136                         return match[3] - 0 === i;\r
4137                 }\r
4138         },\r
4139         filter: {\r
4140                 PSEUDO: function(elem, match, i, array){\r
4141                         var name = match[1], filter = Expr.filters[ name ];\r
4142 \r
4143                         if ( filter ) {\r
4144                                 return filter( elem, i, match, array );\r
4145                         } else if ( name === "contains" ) {\r
4146                                 return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;\r
4147                         } else if ( name === "not" ) {\r
4148                                 var not = match[3];\r
4149 \r
4150                                 for ( var j = 0, l = not.length; j < l; j++ ) {\r
4151                                         if ( not[j] === elem ) {\r
4152                                                 return false;\r
4153                                         }\r
4154                                 }\r
4155 \r
4156                                 return true;\r
4157                         } else {\r
4158                                 Sizzle.error( "Syntax error, unrecognized expression: " + name );\r
4159                         }\r
4160                 },\r
4161                 CHILD: function(elem, match){\r
4162                         var type = match[1], node = elem;\r
4163                         switch (type) {\r
4164                                 case 'only':\r
4165                                 case 'first':\r
4166                                         while ( (node = node.previousSibling) )  {\r
4167                                                 if ( node.nodeType === 1 ) { \r
4168                                                         return false; \r
4169                                                 }\r
4170                                         }\r
4171                                         if ( type === "first" ) { \r
4172                                                 return true; \r
4173                                         }\r
4174                                         node = elem;\r
4175                                 case 'last':\r
4176                                         while ( (node = node.nextSibling) )      {\r
4177                                                 if ( node.nodeType === 1 ) { \r
4178                                                         return false; \r
4179                                                 }\r
4180                                         }\r
4181                                         return true;\r
4182                                 case 'nth':\r
4183                                         var first = match[2], last = match[3];\r
4184 \r
4185                                         if ( first === 1 && last === 0 ) {\r
4186                                                 return true;\r
4187                                         }\r
4188                                         \r
4189                                         var doneName = match[0],\r
4190                                                 parent = elem.parentNode;\r
4191         \r
4192                                         if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {\r
4193                                                 var count = 0;\r
4194                                                 for ( node = parent.firstChild; node; node = node.nextSibling ) {\r
4195                                                         if ( node.nodeType === 1 ) {\r
4196                                                                 node.nodeIndex = ++count;\r
4197                                                         }\r
4198                                                 } \r
4199                                                 parent.sizcache = doneName;\r
4200                                         }\r
4201                                         \r
4202                                         var diff = elem.nodeIndex - last;\r
4203                                         if ( first === 0 ) {\r
4204                                                 return diff === 0;\r
4205                                         } else {\r
4206                                                 return ( diff % first === 0 && diff / first >= 0 );\r
4207                                         }\r
4208                         }\r
4209                 },\r
4210                 ID: function(elem, match){\r
4211                         return elem.nodeType === 1 && elem.getAttribute("id") === match;\r
4212                 },\r
4213                 TAG: function(elem, match){\r
4214                         return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;\r
4215                 },\r
4216                 CLASS: function(elem, match){\r
4217                         return (" " + (elem.className || elem.getAttribute("class")) + " ")\r
4218                                 .indexOf( match ) > -1;\r
4219                 },\r
4220                 ATTR: function(elem, match){\r
4221                         var name = match[1],\r
4222                                 result = Expr.attrHandle[ name ] ?\r
4223                                         Expr.attrHandle[ name ]( elem ) :\r
4224                                         elem[ name ] != null ?\r
4225                                                 elem[ name ] :\r
4226                                                 elem.getAttribute( name ),\r
4227                                 value = result + "",\r
4228                                 type = match[2],\r
4229                                 check = match[4];\r
4230 \r
4231                         return result == null ?\r
4232                                 type === "!=" :\r
4233                                 type === "=" ?\r
4234                                 value === check :\r
4235                                 type === "*=" ?\r
4236                                 value.indexOf(check) >= 0 :\r
4237                                 type === "~=" ?\r
4238                                 (" " + value + " ").indexOf(check) >= 0 :\r
4239                                 !check ?\r
4240                                 value && result !== false :\r
4241                                 type === "!=" ?\r
4242                                 value !== check :\r
4243                                 type === "^=" ?\r
4244                                 value.indexOf(check) === 0 :\r
4245                                 type === "$=" ?\r
4246                                 value.substr(value.length - check.length) === check :\r
4247                                 type === "|=" ?\r
4248                                 value === check || value.substr(0, check.length + 1) === check + "-" :\r
4249                                 false;\r
4250                 },\r
4251                 POS: function(elem, match, i, array){\r
4252                         var name = match[2], filter = Expr.setFilters[ name ];\r
4253 \r
4254                         if ( filter ) {\r
4255                                 return filter( elem, i, match, array );\r
4256                         }\r
4257                 }\r
4258         }\r
4259 };\r
4260 \r
4261 var origPOS = Expr.match.POS,\r
4262         fescape = function(all, num){\r
4263                 return "\\" + (num - 0 + 1);\r
4264         };\r
4265 \r
4266 for ( var type in Expr.match ) {\r
4267         Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );\r
4268         Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );\r
4269 }\r
4270 \r
4271 var makeArray = function(array, results) {\r
4272         array = Array.prototype.slice.call( array, 0 );\r
4273 \r
4274         if ( results ) {\r
4275                 results.push.apply( results, array );\r
4276                 return results;\r
4277         }\r
4278         \r
4279         return array;\r
4280 };\r
4281 \r
4282 // Perform a simple check to determine if the browser is capable of\r
4283 // converting a NodeList to an array using builtin methods.\r
4284 // Also verifies that the returned array holds DOM nodes\r
4285 // (which is not the case in the Blackberry browser)\r
4286 try {\r
4287         Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;\r
4288 \r
4289 // Provide a fallback method if it does not work\r
4290 } catch(e){\r
4291         makeArray = function(array, results) {\r
4292                 var ret = results || [], i = 0;\r
4293 \r
4294                 if ( toString.call(array) === "[object Array]" ) {\r
4295                         Array.prototype.push.apply( ret, array );\r
4296                 } else {\r
4297                         if ( typeof array.length === "number" ) {\r
4298                                 for ( var l = array.length; i < l; i++ ) {\r
4299                                         ret.push( array[i] );\r
4300                                 }\r
4301                         } else {\r
4302                                 for ( ; array[i]; i++ ) {\r
4303                                         ret.push( array[i] );\r
4304                                 }\r
4305                         }\r
4306                 }\r
4307 \r
4308                 return ret;\r
4309         };\r
4310 }\r
4311 \r
4312 var sortOrder;\r
4313 \r
4314 if ( document.documentElement.compareDocumentPosition ) {\r
4315         sortOrder = function( a, b ) {\r
4316                 if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {\r
4317                         if ( a == b ) {\r
4318                                 hasDuplicate = true;\r
4319                         }\r
4320                         return a.compareDocumentPosition ? -1 : 1;\r
4321                 }\r
4322 \r
4323                 var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;\r
4324                 if ( ret === 0 ) {\r
4325                         hasDuplicate = true;\r
4326                 }\r
4327                 return ret;\r
4328         };\r
4329 } else if ( "sourceIndex" in document.documentElement ) {\r
4330         sortOrder = function( a, b ) {\r
4331                 if ( !a.sourceIndex || !b.sourceIndex ) {\r
4332                         if ( a == b ) {\r
4333                                 hasDuplicate = true;\r
4334                         }\r
4335                         return a.sourceIndex ? -1 : 1;\r
4336                 }\r
4337 \r
4338                 var ret = a.sourceIndex - b.sourceIndex;\r
4339                 if ( ret === 0 ) {\r
4340                         hasDuplicate = true;\r
4341                 }\r
4342                 return ret;\r
4343         };\r
4344 } else if ( document.createRange ) {\r
4345         sortOrder = function( a, b ) {\r
4346                 if ( !a.ownerDocument || !b.ownerDocument ) {\r
4347                         if ( a == b ) {\r
4348                                 hasDuplicate = true;\r
4349                         }\r
4350                         return a.ownerDocument ? -1 : 1;\r
4351                 }\r
4352 \r
4353                 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();\r
4354                 aRange.setStart(a, 0);\r
4355                 aRange.setEnd(a, 0);\r
4356                 bRange.setStart(b, 0);\r
4357                 bRange.setEnd(b, 0);\r
4358                 var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);\r
4359                 if ( ret === 0 ) {\r
4360                         hasDuplicate = true;\r
4361                 }\r
4362                 return ret;\r
4363         };\r
4364 }\r
4365 \r
4366 // Utility function for retreiving the text value of an array of DOM nodes\r
4367 Sizzle.getText = function( elems ) {\r
4368         var ret = "", elem;\r
4369 \r
4370         for ( var i = 0; elems[i]; i++ ) {\r
4371                 elem = elems[i];\r
4372 \r
4373                 // Get the text from text nodes and CDATA nodes\r
4374                 if ( elem.nodeType === 3 || elem.nodeType === 4 ) {\r
4375                         ret += elem.nodeValue;\r
4376 \r
4377                 // Traverse everything else, except comment nodes\r
4378                 } else if ( elem.nodeType !== 8 ) {\r
4379                         ret += Sizzle.getText( elem.childNodes );\r
4380                 }\r
4381         }\r
4382 \r
4383         return ret;\r
4384 };\r
4385 \r
4386 // Check to see if the browser returns elements by name when\r
4387 // querying by getElementById (and provide a workaround)\r
4388 (function(){\r
4389         // We're going to inject a fake input element with a specified name\r
4390         var form = document.createElement("div"),\r
4391                 id = "script" + (new Date()).getTime();\r
4392         form.innerHTML = "<a name='" + id + "'/>";\r
4393 \r
4394         // Inject it into the root element, check its status, and remove it quickly\r
4395         var root = document.documentElement;\r
4396         root.insertBefore( form, root.firstChild );\r
4397 \r
4398         // The workaround has to do additional checks after a getElementById\r
4399         // Which slows things down for other browsers (hence the branching)\r
4400         if ( document.getElementById( id ) ) {\r
4401                 Expr.find.ID = function(match, context, isXML){\r
4402                         if ( typeof context.getElementById !== "undefined" && !isXML ) {\r
4403                                 var m = context.getElementById(match[1]);\r
4404                                 return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];\r
4405                         }\r
4406                 };\r
4407 \r
4408                 Expr.filter.ID = function(elem, match){\r
4409                         var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");\r
4410                         return elem.nodeType === 1 && node && node.nodeValue === match;\r
4411                 };\r
4412         }\r
4413 \r
4414         root.removeChild( form );\r
4415         root = form = null; // release memory in IE\r
4416 })();\r
4417 \r
4418 (function(){\r
4419         // Check to see if the browser returns only elements\r
4420         // when doing getElementsByTagName("*")\r
4421 \r
4422         // Create a fake element\r
4423         var div = document.createElement("div");\r
4424         div.appendChild( document.createComment("") );\r
4425 \r
4426         // Make sure no comments are found\r
4427         if ( div.getElementsByTagName("*").length > 0 ) {\r
4428                 Expr.find.TAG = function(match, context){\r
4429                         var results = context.getElementsByTagName(match[1]);\r
4430 \r
4431                         // Filter out possible comments\r
4432                         if ( match[1] === "*" ) {\r
4433                                 var tmp = [];\r
4434 \r
4435                                 for ( var i = 0; results[i]; i++ ) {\r
4436                                         if ( results[i].nodeType === 1 ) {\r
4437                                                 tmp.push( results[i] );\r
4438                                         }\r
4439                                 }\r
4440 \r
4441                                 results = tmp;\r
4442                         }\r
4443 \r
4444                         return results;\r
4445                 };\r
4446         }\r
4447 \r
4448         // Check to see if an attribute returns normalized href attributes\r
4449         div.innerHTML = "<a href='#'></a>";\r
4450         if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&\r
4451                         div.firstChild.getAttribute("href") !== "#" ) {\r
4452                 Expr.attrHandle.href = function(elem){\r
4453                         return elem.getAttribute("href", 2);\r
4454                 };\r
4455         }\r
4456 \r
4457         div = null; // release memory in IE\r
4458 })();\r
4459 \r
4460 if ( document.querySelectorAll ) {\r
4461         (function(){\r
4462                 var oldSizzle = Sizzle, div = document.createElement("div");\r
4463                 div.innerHTML = "<p class='TEST'></p>";\r
4464 \r
4465                 // Safari can't handle uppercase or unicode characters when\r
4466                 // in quirks mode.\r
4467                 if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {\r
4468                         return;\r
4469                 }\r
4470         \r
4471                 Sizzle = function(query, context, extra, seed){\r
4472                         context = context || document;\r
4473 \r
4474                         // Only use querySelectorAll on non-XML documents\r
4475                         // (ID selectors don't work in non-HTML documents)\r
4476                         if ( !seed && context.nodeType === 9 && !Sizzle.isXML(context) ) {\r
4477                                 try {\r
4478                                         return makeArray( context.querySelectorAll(query), extra );\r
4479                                 } catch(e){}\r
4480                         }\r
4481                 \r
4482                         return oldSizzle(query, context, extra, seed);\r
4483                 };\r
4484 \r
4485                 for ( var prop in oldSizzle ) {\r
4486                         Sizzle[ prop ] = oldSizzle[ prop ];\r
4487                 }\r
4488 \r
4489                 div = null; // release memory in IE\r
4490         })();\r
4491 }\r
4492 \r
4493 (function(){\r
4494         var div = document.createElement("div");\r
4495 \r
4496         div.innerHTML = "<div class='test e'></div><div class='test'></div>";\r
4497 \r
4498         // Opera can't find a second classname (in 9.6)\r
4499         // Also, make sure that getElementsByClassName actually exists\r
4500         if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {\r
4501                 return;\r
4502         }\r
4503 \r
4504         // Safari caches class attributes, doesn't catch changes (in 3.2)\r
4505         div.lastChild.className = "e";\r
4506 \r
4507         if ( div.getElementsByClassName("e").length === 1 ) {\r
4508                 return;\r
4509         }\r
4510         \r
4511         Expr.order.splice(1, 0, "CLASS");\r
4512         Expr.find.CLASS = function(match, context, isXML) {\r
4513                 if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {\r
4514                         return context.getElementsByClassName(match[1]);\r
4515                 }\r
4516         };\r
4517 \r
4518         div = null; // release memory in IE\r
4519 })();\r
4520 \r
4521 function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\r
4522         for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
4523                 var elem = checkSet[i];\r
4524                 if ( elem ) {\r
4525                         elem = elem[dir];\r
4526                         var match = false;\r
4527 \r
4528                         while ( elem ) {\r
4529                                 if ( elem.sizcache === doneName ) {\r
4530                                         match = checkSet[elem.sizset];\r
4531                                         break;\r
4532                                 }\r
4533 \r
4534                                 if ( elem.nodeType === 1 && !isXML ){\r
4535                                         elem.sizcache = doneName;\r
4536                                         elem.sizset = i;\r
4537                                 }\r
4538 \r
4539                                 if ( elem.nodeName.toLowerCase() === cur ) {\r
4540                                         match = elem;\r
4541                                         break;\r
4542                                 }\r
4543 \r
4544                                 elem = elem[dir];\r
4545                         }\r
4546 \r
4547                         checkSet[i] = match;\r
4548                 }\r
4549         }\r
4550 }\r
4551 \r
4552 function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\r
4553         for ( var i = 0, l = checkSet.length; i < l; i++ ) {\r
4554                 var elem = checkSet[i];\r
4555                 if ( elem ) {\r
4556                         elem = elem[dir];\r
4557                         var match = false;\r
4558 \r
4559                         while ( elem ) {\r
4560                                 if ( elem.sizcache === doneName ) {\r
4561                                         match = checkSet[elem.sizset];\r
4562                                         break;\r
4563                                 }\r
4564 \r
4565                                 if ( elem.nodeType === 1 ) {\r
4566                                         if ( !isXML ) {\r
4567                                                 elem.sizcache = doneName;\r
4568                                                 elem.sizset = i;\r
4569                                         }\r
4570                                         if ( typeof cur !== "string" ) {\r
4571                                                 if ( elem === cur ) {\r
4572                                                         match = true;\r
4573                                                         break;\r
4574                                                 }\r
4575 \r
4576                                         } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {\r
4577                                                 match = elem;\r
4578                                                 break;\r
4579                                         }\r
4580                                 }\r
4581 \r
4582                                 elem = elem[dir];\r
4583                         }\r
4584 \r
4585                         checkSet[i] = match;\r
4586                 }\r
4587         }\r
4588 }\r
4589 \r
4590 Sizzle.contains = document.compareDocumentPosition ? function(a, b){\r
4591         return !!(a.compareDocumentPosition(b) & 16);\r
4592 } : function(a, b){\r
4593         return a !== b && (a.contains ? a.contains(b) : true);\r
4594 };\r
4595 \r
4596 Sizzle.isXML = function(elem){\r
4597         // documentElement is verified for cases where it doesn't yet exist\r
4598         // (such as loading iframes in IE - #4833) \r
4599         var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;\r
4600         return documentElement ? documentElement.nodeName !== "HTML" : false;\r
4601 };\r
4602 \r
4603 var posProcess = function(selector, context){\r
4604         var tmpSet = [], later = "", match,\r
4605                 root = context.nodeType ? [context] : context;\r
4606 \r
4607         // Position selectors must be done after the filter\r
4608         // And so must :not(positional) so we move all PSEUDOs to the end\r
4609         while ( (match = Expr.match.PSEUDO.exec( selector )) ) {\r
4610                 later += match[0];\r
4611                 selector = selector.replace( Expr.match.PSEUDO, "" );\r
4612         }\r
4613 \r
4614         selector = Expr.relative[selector] ? selector + "*" : selector;\r
4615 \r
4616         for ( var i = 0, l = root.length; i < l; i++ ) {\r
4617                 Sizzle( selector, root[i], tmpSet );\r
4618         }\r
4619 \r
4620         return Sizzle.filter( later, tmpSet );\r
4621 };\r
4622 \r
4623 // EXPOSE\r
4624 \r
4625 window.tinymce.dom.Sizzle = Sizzle;\r
4626 \r
4627 })();\r
4628 \r
4629 \r
4630 (function(tinymce) {\r
4631         // Shorten names\r
4632         var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event;\r
4633 \r
4634         tinymce.create('tinymce.dom.EventUtils', {\r
4635                 EventUtils : function() {\r
4636                         this.inits = [];\r
4637                         this.events = [];\r
4638                 },\r
4639 \r
4640                 add : function(o, n, f, s) {\r
4641                         var cb, t = this, el = t.events, r;\r
4642 \r
4643                         if (n instanceof Array) {\r
4644                                 r = [];\r
4645 \r
4646                                 each(n, function(n) {\r
4647                                         r.push(t.add(o, n, f, s));\r
4648                                 });\r
4649 \r
4650                                 return r;\r
4651                         }\r
4652 \r
4653                         // Handle array\r
4654                         if (o && o.hasOwnProperty && o instanceof Array) {\r
4655                                 r = [];\r
4656 \r
4657                                 each(o, function(o) {\r
4658                                         o = DOM.get(o);\r
4659                                         r.push(t.add(o, n, f, s));\r
4660                                 });\r
4661 \r
4662                                 return r;\r
4663                         }\r
4664 \r
4665                         o = DOM.get(o);\r
4666 \r
4667                         if (!o)\r
4668                                 return;\r
4669 \r
4670                         // Setup event callback\r
4671                         cb = function(e) {\r
4672                                 // Is all events disabled\r
4673                                 if (t.disabled)\r
4674                                         return;\r
4675 \r
4676                                 e = e || window.event;\r
4677 \r
4678                                 // Patch in target, preventDefault and stopPropagation in IE it's W3C valid\r
4679                                 if (e && isIE) {\r
4680                                         if (!e.target)\r
4681                                                 e.target = e.srcElement;\r
4682 \r
4683                                         // Patch in preventDefault, stopPropagation methods for W3C compatibility\r
4684                                         tinymce.extend(e, t._stoppers);\r
4685                                 }\r
4686 \r
4687                                 if (!s)\r
4688                                         return f(e);\r
4689 \r
4690                                 return f.call(s, e);\r
4691                         };\r
4692 \r
4693                         if (n == 'unload') {\r
4694                                 tinymce.unloads.unshift({func : cb});\r
4695                                 return cb;\r
4696                         }\r
4697 \r
4698                         if (n == 'init') {\r
4699                                 if (t.domLoaded)\r
4700                                         cb();\r
4701                                 else\r
4702                                         t.inits.push(cb);\r
4703 \r
4704                                 return cb;\r
4705                         }\r
4706 \r
4707                         // Store away listener reference\r
4708                         el.push({\r
4709                                 obj : o,\r
4710                                 name : n,\r
4711                                 func : f,\r
4712                                 cfunc : cb,\r
4713                                 scope : s\r
4714                         });\r
4715 \r
4716                         t._add(o, n, cb);\r
4717 \r
4718                         return f;\r
4719                 },\r
4720 \r
4721                 remove : function(o, n, f) {\r
4722                         var t = this, a = t.events, s = false, r;\r
4723 \r
4724                         // Handle array\r
4725                         if (o && o.hasOwnProperty && o instanceof Array) {\r
4726                                 r = [];\r
4727 \r
4728                                 each(o, function(o) {\r
4729                                         o = DOM.get(o);\r
4730                                         r.push(t.remove(o, n, f));\r
4731                                 });\r
4732 \r
4733                                 return r;\r
4734                         }\r
4735 \r
4736                         o = DOM.get(o);\r
4737 \r
4738                         each(a, function(e, i) {\r
4739                                 if (e.obj == o && e.name == n && (!f || (e.func == f || e.cfunc == f))) {\r
4740                                         a.splice(i, 1);\r
4741                                         t._remove(o, n, e.cfunc);\r
4742                                         s = true;\r
4743                                         return false;\r
4744                                 }\r
4745                         });\r
4746 \r
4747                         return s;\r
4748                 },\r
4749 \r
4750                 clear : function(o) {\r
4751                         var t = this, a = t.events, i, e;\r
4752 \r
4753                         if (o) {\r
4754                                 o = DOM.get(o);\r
4755 \r
4756                                 for (i = a.length - 1; i >= 0; i--) {\r
4757                                         e = a[i];\r
4758 \r
4759                                         if (e.obj === o) {\r
4760                                                 t._remove(e.obj, e.name, e.cfunc);\r
4761                                                 e.obj = e.cfunc = null;\r
4762                                                 a.splice(i, 1);\r
4763                                         }\r
4764                                 }\r
4765                         }\r
4766                 },\r
4767 \r
4768                 cancel : function(e) {\r
4769                         if (!e)\r
4770                                 return false;\r
4771 \r
4772                         this.stop(e);\r
4773 \r
4774                         return this.prevent(e);\r
4775                 },\r
4776 \r
4777                 stop : function(e) {\r
4778                         if (e.stopPropagation)\r
4779                                 e.stopPropagation();\r
4780                         else\r
4781                                 e.cancelBubble = true;\r
4782 \r
4783                         return false;\r
4784                 },\r
4785 \r
4786                 prevent : function(e) {\r
4787                         if (e.preventDefault)\r
4788                                 e.preventDefault();\r
4789                         else\r
4790                                 e.returnValue = false;\r
4791 \r
4792                         return false;\r
4793                 },\r
4794 \r
4795                 destroy : function() {\r
4796                         var t = this;\r
4797 \r
4798                         each(t.events, function(e, i) {\r
4799                                 t._remove(e.obj, e.name, e.cfunc);\r
4800                                 e.obj = e.cfunc = null;\r
4801                         });\r
4802 \r
4803                         t.events = [];\r
4804                         t = null;\r
4805                 },\r
4806 \r
4807                 _add : function(o, n, f) {\r
4808                         if (o.attachEvent)\r
4809                                 o.attachEvent('on' + n, f);\r
4810                         else if (o.addEventListener)\r
4811                                 o.addEventListener(n, f, false);\r
4812                         else\r
4813                                 o['on' + n] = f;\r
4814                 },\r
4815 \r
4816                 _remove : function(o, n, f) {\r
4817                         if (o) {\r
4818                                 try {\r
4819                                         if (o.detachEvent)\r
4820                                                 o.detachEvent('on' + n, f);\r
4821                                         else if (o.removeEventListener)\r
4822                                                 o.removeEventListener(n, f, false);\r
4823                                         else\r
4824                                                 o['on' + n] = null;\r
4825                                 } catch (ex) {\r
4826                                         // Might fail with permission denined on IE so we just ignore that\r
4827                                 }\r
4828                         }\r
4829                 },\r
4830 \r
4831                 _pageInit : function(win) {\r
4832                         var t = this;\r
4833 \r
4834                         // Keep it from running more than once\r
4835                         if (t.domLoaded)\r
4836                                 return;\r
4837 \r
4838                         t.domLoaded = true;\r
4839 \r
4840                         each(t.inits, function(c) {\r
4841                                 c();\r
4842                         });\r
4843 \r
4844                         t.inits = [];\r
4845                 },\r
4846 \r
4847                 _wait : function(win) {\r
4848                         var t = this, doc = win.document;\r
4849 \r
4850                         // No need since the document is already loaded\r
4851                         if (win.tinyMCE_GZ && tinyMCE_GZ.loaded) {\r
4852                                 t.domLoaded = 1;\r
4853                                 return;\r
4854                         }\r
4855 \r
4856                         // Use IE method\r
4857                         if (doc.attachEvent) {\r
4858                                 doc.attachEvent("onreadystatechange", function() {\r
4859                                         if (doc.readyState === "complete") {\r
4860                                                 doc.detachEvent("onreadystatechange", arguments.callee);\r
4861                                                 t._pageInit(win);\r
4862                                         }\r
4863                                 });\r
4864 \r
4865                                 if (doc.documentElement.doScroll && win == win.top) {\r
4866                                         (function() {\r
4867                                                 if (t.domLoaded)\r
4868                                                         return;\r
4869 \r
4870                                                 try {\r
4871                                                         // If IE is used, use the trick by Diego Perini\r
4872                                                         // http://javascript.nwbox.com/IEContentLoaded/\r
4873                                                         doc.documentElement.doScroll("left");\r
4874                                                 } catch (ex) {\r
4875                                                         setTimeout(arguments.callee, 0);\r
4876                                                         return;\r
4877                                                 }\r
4878 \r
4879                                                 t._pageInit(win);\r
4880                                         })();\r
4881                                 }\r
4882                         } else if (doc.addEventListener) {\r
4883                                 t._add(win, 'DOMContentLoaded', function() {\r
4884                                         t._pageInit(win);\r
4885                                 });\r
4886                         }\r
4887 \r
4888                         t._add(win, 'load', function() {\r
4889                                 t._pageInit(win);\r
4890                         });\r
4891                 },\r
4892 \r
4893                 _stoppers : {\r
4894                         preventDefault :  function() {\r
4895                                 this.returnValue = false;\r
4896                         },\r
4897 \r
4898                         stopPropagation : function() {\r
4899                                 this.cancelBubble = true;\r
4900                         }\r
4901                 }\r
4902         });\r
4903 \r
4904         Event = tinymce.dom.Event = new tinymce.dom.EventUtils();\r
4905 \r
4906         // Dispatch DOM content loaded event for IE and Safari\r
4907         Event._wait(window);\r
4908 \r
4909         tinymce.addUnload(function() {\r
4910                 Event.destroy();\r
4911         });\r
4912 })(tinymce);\r
4913 \r
4914 (function(tinymce) {\r
4915         tinymce.dom.Element = function(id, settings) {\r
4916                 var t = this, dom, el;\r
4917 \r
4918                 t.settings = settings = settings || {};\r
4919                 t.id = id;\r
4920                 t.dom = dom = settings.dom || tinymce.DOM;\r
4921 \r
4922                 // Only IE leaks DOM references, this is a lot faster\r
4923                 if (!tinymce.isIE)\r
4924                         el = dom.get(t.id);\r
4925 \r
4926                 tinymce.each(\r
4927                                 ('getPos,getRect,getParent,add,setStyle,getStyle,setStyles,' + \r
4928                                 'setAttrib,setAttribs,getAttrib,addClass,removeClass,' + \r
4929                                 'hasClass,getOuterHTML,setOuterHTML,remove,show,hide,' + \r
4930                                 'isHidden,setHTML,get').split(/,/)\r
4931                         , function(k) {\r
4932                                 t[k] = function() {\r
4933                                         var a = [id], i;\r
4934 \r
4935                                         for (i = 0; i < arguments.length; i++)\r
4936                                                 a.push(arguments[i]);\r
4937 \r
4938                                         a = dom[k].apply(dom, a);\r
4939                                         t.update(k);\r
4940 \r
4941                                         return a;\r
4942                                 };\r
4943                 });\r
4944 \r
4945                 tinymce.extend(t, {\r
4946                         on : function(n, f, s) {\r
4947                                 return tinymce.dom.Event.add(t.id, n, f, s);\r
4948                         },\r
4949 \r
4950                         getXY : function() {\r
4951                                 return {\r
4952                                         x : parseInt(t.getStyle('left')),\r
4953                                         y : parseInt(t.getStyle('top'))\r
4954                                 };\r
4955                         },\r
4956 \r
4957                         getSize : function() {\r
4958                                 var n = dom.get(t.id);\r
4959 \r
4960                                 return {\r
4961                                         w : parseInt(t.getStyle('width') || n.clientWidth),\r
4962                                         h : parseInt(t.getStyle('height') || n.clientHeight)\r
4963                                 };\r
4964                         },\r
4965 \r
4966                         moveTo : function(x, y) {\r
4967                                 t.setStyles({left : x, top : y});\r
4968                         },\r
4969 \r
4970                         moveBy : function(x, y) {\r
4971                                 var p = t.getXY();\r
4972 \r
4973                                 t.moveTo(p.x + x, p.y + y);\r
4974                         },\r
4975 \r
4976                         resizeTo : function(w, h) {\r
4977                                 t.setStyles({width : w, height : h});\r
4978                         },\r
4979 \r
4980                         resizeBy : function(w, h) {\r
4981                                 var s = t.getSize();\r
4982 \r
4983                                 t.resizeTo(s.w + w, s.h + h);\r
4984                         },\r
4985 \r
4986                         update : function(k) {\r
4987                                 var b;\r
4988 \r
4989                                 if (tinymce.isIE6 && settings.blocker) {\r
4990                                         k = k || '';\r
4991 \r
4992                                         // Ignore getters\r
4993                                         if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0)\r
4994                                                 return;\r
4995 \r
4996                                         // Remove blocker on remove\r
4997                                         if (k == 'remove') {\r
4998                                                 dom.remove(t.blocker);\r
4999                                                 return;\r
5000                                         }\r
5001 \r
5002                                         if (!t.blocker) {\r
5003                                                 t.blocker = dom.uniqueId();\r
5004                                                 b = dom.add(settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'});\r
5005                                                 dom.setStyle(b, 'opacity', 0);\r
5006                                         } else\r
5007                                                 b = dom.get(t.blocker);\r
5008 \r
5009                                         dom.setStyles(b, {\r
5010                                                 left : t.getStyle('left', 1),\r
5011                                                 top : t.getStyle('top', 1),\r
5012                                                 width : t.getStyle('width', 1),\r
5013                                                 height : t.getStyle('height', 1),\r
5014                                                 display : t.getStyle('display', 1),\r
5015                                                 zIndex : parseInt(t.getStyle('zIndex', 1) || 0) - 1\r
5016                                         });\r
5017                                 }\r
5018                         }\r
5019                 });\r
5020         };\r
5021 })(tinymce);\r
5022 \r
5023 (function(tinymce) {\r
5024         function trimNl(s) {\r
5025                 return s.replace(/[\n\r]+/g, '');\r
5026         };\r
5027 \r
5028         // Shorten names\r
5029         var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each;\r
5030 \r
5031         tinymce.create('tinymce.dom.Selection', {\r
5032                 Selection : function(dom, win, serializer) {\r
5033                         var t = this;\r
5034 \r
5035                         t.dom = dom;\r
5036                         t.win = win;\r
5037                         t.serializer = serializer;\r
5038 \r
5039                         // Add events\r
5040                         each([\r
5041                                 'onBeforeSetContent',\r
5042                                 'onBeforeGetContent',\r
5043                                 'onSetContent',\r
5044                                 'onGetContent'\r
5045                         ], function(e) {\r
5046                                 t[e] = new tinymce.util.Dispatcher(t);\r
5047                         });\r
5048 \r
5049                         // No W3C Range support\r
5050                         if (!t.win.getSelection)\r
5051                                 t.tridentSel = new tinymce.dom.TridentSelection(t);\r
5052 \r
5053                         // Prevent leaks\r
5054                         tinymce.addUnload(t.destroy, t);\r
5055                 },\r
5056 \r
5057                 getContent : function(s) {\r
5058                         var t = this, r = t.getRng(), e = t.dom.create("body"), se = t.getSel(), wb, wa, n;\r
5059 \r
5060                         s = s || {};\r
5061                         wb = wa = '';\r
5062                         s.get = true;\r
5063                         s.format = s.format || 'html';\r
5064                         t.onBeforeGetContent.dispatch(t, s);\r
5065 \r
5066                         if (s.format == 'text')\r
5067                                 return t.isCollapsed() ? '' : (r.text || (se.toString ? se.toString() : ''));\r
5068 \r
5069                         if (r.cloneContents) {\r
5070                                 n = r.cloneContents();\r
5071 \r
5072                                 if (n)\r
5073                                         e.appendChild(n);\r
5074                         } else if (is(r.item) || is(r.htmlText))\r
5075                                 e.innerHTML = r.item ? r.item(0).outerHTML : r.htmlText;\r
5076                         else\r
5077                                 e.innerHTML = r.toString();\r
5078 \r
5079                         // Keep whitespace before and after\r
5080                         if (/^\s/.test(e.innerHTML))\r
5081                                 wb = ' ';\r
5082 \r
5083                         if (/\s+$/.test(e.innerHTML))\r
5084                                 wa = ' ';\r
5085 \r
5086                         s.getInner = true;\r
5087 \r
5088                         s.content = t.isCollapsed() ? '' : wb + t.serializer.serialize(e, s) + wa;\r
5089                         t.onGetContent.dispatch(t, s);\r
5090 \r
5091                         return s.content;\r
5092                 },\r
5093 \r
5094                 setContent : function(h, s) {\r
5095                         var t = this, r = t.getRng(), c, d = t.win.document;\r
5096 \r
5097                         s = s || {format : 'html'};\r
5098                         s.set = true;\r
5099                         h = s.content = t.dom.processHTML(h);\r
5100 \r
5101                         // Dispatch before set content event\r
5102                         t.onBeforeSetContent.dispatch(t, s);\r
5103                         h = s.content;\r
5104 \r
5105                         if (r.insertNode) {\r
5106                                 // Make caret marker since insertNode places the caret in the beginning of text after insert\r
5107                                 h += '<span id="__caret">_</span>';\r
5108 \r
5109                                 // Delete and insert new node\r
5110                                 \r
5111                                 if (r.startContainer == d && r.endContainer ==  d) {\r
5112                                         // WebKit will fail if the body is empty since the range is then invalid and it can't insert contents\r
5113                                         d.body.innerHTML = h;\r
5114                                 } else {\r
5115                                         r.deleteContents();\r
5116                                         if (d.body.childNodes.length == 0) {\r
5117                                                 d.body.innerHTML = h;\r
5118                                         } else {\r
5119                                                 r.insertNode(r.createContextualFragment(h));\r
5120                                         }\r
5121                                 }\r
5122 \r
5123                                 // Move to caret marker\r
5124                                 c = t.dom.get('__caret');\r
5125                                 // Make sure we wrap it compleatly, Opera fails with a simple select call\r
5126                                 r = d.createRange();\r
5127                                 r.setStartBefore(c);\r
5128                                 r.setEndBefore(c);\r
5129                                 t.setRng(r);\r
5130 \r
5131                                 // Remove the caret position\r
5132                                 t.dom.remove('__caret');\r
5133                         } else {\r
5134                                 if (r.item) {\r
5135                                         // Delete content and get caret text selection\r
5136                                         d.execCommand('Delete', false, null);\r
5137                                         r = t.getRng();\r
5138                                 }\r
5139 \r
5140                                 r.pasteHTML(h);\r
5141                         }\r
5142 \r
5143                         // Dispatch set content event\r
5144                         t.onSetContent.dispatch(t, s);\r
5145                 },\r
5146 \r
5147                 getStart : function() {\r
5148                         var rng = this.getRng(), startElement, parentElement, checkRng, node;\r
5149 \r
5150                         if (rng.duplicate || rng.item) {\r
5151                                 // Control selection, return first item\r
5152                                 if (rng.item)\r
5153                                         return rng.item(0);\r
5154 \r
5155                                 // Get start element\r
5156                                 checkRng = rng.duplicate();\r
5157                                 checkRng.collapse(1);\r
5158                                 startElement = checkRng.parentElement();\r
5159 \r
5160                                 // Check if range parent is inside the start element, then return the inner parent element\r
5161                                 // This will fix issues when a single element is selected, IE would otherwise return the wrong start element\r
5162                                 parentElement = node = rng.parentElement();\r
5163                                 while (node = node.parentNode) {\r
5164                                         if (node == startElement) {\r
5165                                                 startElement = parentElement;\r
5166                                                 break;\r
5167                                         }\r
5168                                 }\r
5169 \r
5170                                 // If start element is body element try to move to the first child if it exists\r
5171                                 if (startElement && startElement.nodeName == 'BODY')\r
5172                                         return startElement.firstChild || startElement;\r
5173 \r
5174                                 return startElement;\r
5175                         } else {\r
5176                                 startElement = rng.startContainer;\r
5177 \r
5178                                 if (startElement.nodeType == 1 && startElement.hasChildNodes())\r
5179                                         startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];\r
5180 \r
5181                                 if (startElement && startElement.nodeType == 3)\r
5182                                         return startElement.parentNode;\r
5183 \r
5184                                 return startElement;\r
5185                         }\r
5186                 },\r
5187 \r
5188                 getEnd : function() {\r
5189                         var t = this, r = t.getRng(), e, eo;\r
5190 \r
5191                         if (r.duplicate || r.item) {\r
5192                                 if (r.item)\r
5193                                         return r.item(0);\r
5194 \r
5195                                 r = r.duplicate();\r
5196                                 r.collapse(0);\r
5197                                 e = r.parentElement();\r
5198 \r
5199                                 if (e && e.nodeName == 'BODY')\r
5200                                         return e.lastChild || e;\r
5201 \r
5202                                 return e;\r
5203                         } else {\r
5204                                 e = r.endContainer;\r
5205                                 eo = r.endOffset;\r
5206 \r
5207                                 if (e.nodeType == 1 && e.hasChildNodes())\r
5208                                         e = e.childNodes[eo > 0 ? eo - 1 : eo];\r
5209 \r
5210                                 if (e && e.nodeType == 3)\r
5211                                         return e.parentNode;\r
5212 \r
5213                                 return e;\r
5214                         }\r
5215                 },\r
5216 \r
5217                 getBookmark : function(type, normalized) {\r
5218                         var t = this, dom = t.dom, rng, rng2, id, collapsed, name, element, index, chr = '\uFEFF', styles;\r
5219 \r
5220                         function findIndex(name, element) {\r
5221                                 var index = 0;\r
5222 \r
5223                                 each(dom.select(name), function(node, i) {\r
5224                                         if (node == element)\r
5225                                                 index = i;\r
5226                                 });\r
5227 \r
5228                                 return index;\r
5229                         };\r
5230 \r
5231                         if (type == 2) {\r
5232                                 function getLocation() {\r
5233                                         var rng = t.getRng(true), root = dom.getRoot(), bookmark = {};\r
5234 \r
5235                                         function getPoint(rng, start) {\r
5236                                                 var container = rng[start ? 'startContainer' : 'endContainer'],\r
5237                                                         offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;\r
5238 \r
5239                                                 if (container.nodeType == 3) {\r
5240                                                         if (normalized) {\r
5241                                                                 for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling)\r
5242                                                                         offset += node.nodeValue.length;\r
5243                                                         }\r
5244 \r
5245                                                         point.push(offset);\r
5246                                                 } else {\r
5247                                                         childNodes = container.childNodes;\r
5248 \r
5249                                                         if (offset >= childNodes.length && childNodes.length) {\r
5250                                                                 after = 1;\r
5251                                                                 offset = Math.max(0, childNodes.length - 1);\r
5252                                                         }\r
5253 \r
5254                                                         point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after);\r
5255                                                 }\r
5256 \r
5257                                                 for (; container && container != root; container = container.parentNode)\r
5258                                                         point.push(t.dom.nodeIndex(container, normalized));\r
5259 \r
5260                                                 return point;\r
5261                                         };\r
5262 \r
5263                                         bookmark.start = getPoint(rng, true);\r
5264 \r
5265                                         if (!t.isCollapsed())\r
5266                                                 bookmark.end = getPoint(rng);\r
5267 \r
5268                                         return bookmark;\r
5269                                 };\r
5270 \r
5271                                 return getLocation();\r
5272                         }\r
5273 \r
5274                         // Handle simple range\r
5275                         if (type)\r
5276                                 return {rng : t.getRng()};\r
5277 \r
5278                         rng = t.getRng();\r
5279                         id = dom.uniqueId();\r
5280                         collapsed = tinyMCE.activeEditor.selection.isCollapsed();\r
5281                         styles = 'overflow:hidden;line-height:0px';\r
5282 \r
5283                         // Explorer method\r
5284                         if (rng.duplicate || rng.item) {\r
5285                                 // Text selection\r
5286                                 if (!rng.item) {\r
5287                                         rng2 = rng.duplicate();\r
5288 \r
5289                                         // Insert start marker\r
5290                                         rng.collapse();\r
5291                                         rng.pasteHTML('<span _mce_type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');\r
5292 \r
5293                                         // Insert end marker\r
5294                                         if (!collapsed) {\r
5295                                                 rng2.collapse(false);\r
5296                                                 rng2.pasteHTML('<span _mce_type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');\r
5297                                         }\r
5298                                 } else {\r
5299                                         // Control selection\r
5300                                         element = rng.item(0);\r
5301                                         name = element.nodeName;\r
5302 \r
5303                                         return {name : name, index : findIndex(name, element)};\r
5304                                 }\r
5305                         } else {\r
5306                                 element = t.getNode();\r
5307                                 name = element.nodeName;\r
5308                                 if (name == 'IMG')\r
5309                                         return {name : name, index : findIndex(name, element)};\r
5310 \r
5311                                 // W3C method\r
5312                                 rng2 = rng.cloneRange();\r
5313 \r
5314                                 // Insert end marker\r
5315                                 if (!collapsed) {\r
5316                                         rng2.collapse(false);\r
5317                                         rng2.insertNode(dom.create('span', {_mce_type : "bookmark", id : id + '_end', style : styles}, chr));\r
5318                                 }\r
5319 \r
5320                                 rng.collapse(true);\r
5321                                 rng.insertNode(dom.create('span', {_mce_type : "bookmark", id : id + '_start', style : styles}, chr));\r
5322                         }\r
5323 \r
5324                         t.moveToBookmark({id : id, keep : 1});\r
5325 \r
5326                         return {id : id};\r
5327                 },\r
5328 \r
5329                 moveToBookmark : function(bookmark) {\r
5330                         var t = this, dom = t.dom, marker1, marker2, rng, root, startContainer, endContainer, startOffset, endOffset;\r
5331 \r
5332                         // Clear selection cache\r
5333                         if (t.tridentSel)\r
5334                                 t.tridentSel.destroy();\r
5335 \r
5336                         if (bookmark) {\r
5337                                 if (bookmark.start) {\r
5338                                         rng = dom.createRng();\r
5339                                         root = dom.getRoot();\r
5340 \r
5341                                         function setEndPoint(start) {\r
5342                                                 var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;\r
5343 \r
5344                                                 if (point) {\r
5345                                                         // Find container node\r
5346                                                         for (node = root, i = point.length - 1; i >= 1; i--) {\r
5347                                                                 children = node.childNodes;\r
5348 \r
5349                                                                 if (children.length)\r
5350                                                                         node = children[point[i]];\r
5351                                                         }\r
5352 \r
5353                                                         // Set offset within container node\r
5354                                                         if (start)\r
5355                                                                 rng.setStart(node, point[0]);\r
5356                                                         else\r
5357                                                                 rng.setEnd(node, point[0]);\r
5358                                                 }\r
5359                                         };\r
5360 \r
5361                                         setEndPoint(true);\r
5362                                         setEndPoint();\r
5363 \r
5364                                         t.setRng(rng);\r
5365                                 } else if (bookmark.id) {\r
5366                                         function restoreEndPoint(suffix) {\r
5367                                                 var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;\r
5368 \r
5369                                                 if (marker) {\r
5370                                                         node = marker.parentNode;\r
5371 \r
5372                                                         if (suffix == 'start') {\r
5373                                                                 if (!keep) {\r
5374                                                                         idx = dom.nodeIndex(marker);\r
5375                                                                 } else {\r
5376                                                                         node = marker.firstChild;\r
5377                                                                         idx = 1;\r
5378                                                                 }\r
5379 \r
5380                                                                 startContainer = endContainer = node;\r
5381                                                                 startOffset = endOffset = idx;\r
5382                                                         } else {\r
5383                                                                 if (!keep) {\r
5384                                                                         idx = dom.nodeIndex(marker);\r
5385                                                                 } else {\r
5386                                                                         node = marker.firstChild;\r
5387                                                                         idx = 1;\r
5388                                                                 }\r
5389 \r
5390                                                                 endContainer = node;\r
5391                                                                 endOffset = idx;\r
5392                                                         }\r
5393 \r
5394                                                         if (!keep) {\r
5395                                                                 prev = marker.previousSibling;\r
5396                                                                 next = marker.nextSibling;\r
5397 \r
5398                                                                 // Remove all marker text nodes\r
5399                                                                 each(tinymce.grep(marker.childNodes), function(node) {\r
5400                                                                         if (node.nodeType == 3)\r
5401                                                                                 node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');\r
5402                                                                 });\r
5403 \r
5404                                                                 // Remove marker but keep children if for example contents where inserted into the marker\r
5405                                                                 // Also remove duplicated instances of the marker for example by a split operation or by WebKit auto split on paste feature\r
5406                                                                 while (marker = dom.get(bookmark.id + '_' + suffix))\r
5407                                                                         dom.remove(marker, 1);\r
5408 \r
5409                                                                 // If siblings are text nodes then merge them\r
5410                                                                 if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3) {\r
5411                                                                         idx = prev.nodeValue.length;\r
5412                                                                         prev.appendData(next.nodeValue);\r
5413                                                                         dom.remove(next);\r
5414 \r
5415                                                                         if (suffix == 'start') {\r
5416                                                                                 startContainer = endContainer = prev;\r
5417                                                                                 startOffset = endOffset = idx;\r
5418                                                                         } else {\r
5419                                                                                 endContainer = prev;\r
5420                                                                                 endOffset = idx;\r
5421                                                                         }\r
5422                                                                 }\r
5423                                                         }\r
5424                                                 }\r
5425                                         };\r
5426 \r
5427                                         function addBogus(node) {\r
5428                                                 // Adds a bogus BR element for empty block elements\r
5429                                                 // on non IE browsers just to have a place to put the caret\r
5430                                                 if (!isIE && dom.isBlock(node) && !node.innerHTML)\r
5431                                                         node.innerHTML = '<br _mce_bogus="1" />';\r
5432 \r
5433                                                 return node;\r
5434                                         };\r
5435 \r
5436                                         // Restore start/end points\r
5437                                         restoreEndPoint('start');\r
5438                                         restoreEndPoint('end');\r
5439 \r
5440                                         rng = dom.createRng();\r
5441                                         rng.setStart(addBogus(startContainer), startOffset);\r
5442                                         rng.setEnd(addBogus(endContainer), endOffset);\r
5443                                         t.setRng(rng);\r
5444                                 } else if (bookmark.name) {\r
5445                                         t.select(dom.select(bookmark.name)[bookmark.index]);\r
5446                                 } else if (bookmark.rng)\r
5447                                         t.setRng(bookmark.rng);\r
5448                         }\r
5449                 },\r
5450 \r
5451                 select : function(node, content) {\r
5452                         var t = this, dom = t.dom, rng = dom.createRng(), idx;\r
5453 \r
5454                         idx = dom.nodeIndex(node);\r
5455                         rng.setStart(node.parentNode, idx);\r
5456                         rng.setEnd(node.parentNode, idx + 1);\r
5457 \r
5458                         // Find first/last text node or BR element\r
5459                         if (content) {\r
5460                                 function setPoint(node, start) {\r
5461                                         var walker = new tinymce.dom.TreeWalker(node, node);\r
5462 \r
5463                                         do {\r
5464                                                 // Text node\r
5465                                                 if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length != 0) {\r
5466                                                         if (start)\r
5467                                                                 rng.setStart(node, 0);\r
5468                                                         else\r
5469                                                                 rng.setEnd(node, node.nodeValue.length);\r
5470 \r
5471                                                         return;\r
5472                                                 }\r
5473 \r
5474                                                 // BR element\r
5475                                                 if (node.nodeName == 'BR') {\r
5476                                                         if (start)\r
5477                                                                 rng.setStartBefore(node);\r
5478                                                         else\r
5479                                                                 rng.setEndBefore(node);\r
5480 \r
5481                                                         return;\r
5482                                                 }\r
5483                                         } while (node = (start ? walker.next() : walker.prev()));\r
5484                                 };\r
5485 \r
5486                                 setPoint(node, 1);\r
5487                                 setPoint(node);\r
5488                         }\r
5489 \r
5490                         t.setRng(rng);\r
5491 \r
5492                         return node;\r
5493                 },\r
5494 \r
5495                 isCollapsed : function() {\r
5496                         var t = this, r = t.getRng(), s = t.getSel();\r
5497 \r
5498                         if (!r || r.item)\r
5499                                 return false;\r
5500 \r
5501                         if (r.compareEndPoints)\r
5502                                 return r.compareEndPoints('StartToEnd', r) === 0;\r
5503 \r
5504                         return !s || r.collapsed;\r
5505                 },\r
5506 \r
5507                 collapse : function(b) {\r
5508                         var t = this, r = t.getRng(), n;\r
5509 \r
5510                         // Control range on IE\r
5511                         if (r.item) {\r
5512                                 n = r.item(0);\r
5513                                 r = this.win.document.body.createTextRange();\r
5514                                 r.moveToElementText(n);\r
5515                         }\r
5516 \r
5517                         r.collapse(!!b);\r
5518                         t.setRng(r);\r
5519                 },\r
5520 \r
5521                 getSel : function() {\r
5522                         var t = this, w = this.win;\r
5523 \r
5524                         return w.getSelection ? w.getSelection() : w.document.selection;\r
5525                 },\r
5526 \r
5527                 getRng : function(w3c) {\r
5528                         var t = this, s, r;\r
5529 \r
5530                         // Found tridentSel object then we need to use that one\r
5531                         if (w3c && t.tridentSel)\r
5532                                 return t.tridentSel.getRangeAt(0);\r
5533 \r
5534                         try {\r
5535                                 if (s = t.getSel())\r
5536                                         r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : t.win.document.createRange());\r
5537                         } catch (ex) {\r
5538                                 // IE throws unspecified error here if TinyMCE is placed in a frame/iframe\r
5539                         }\r
5540 \r
5541                         // No range found then create an empty one\r
5542                         // This can occur when the editor is placed in a hidden container element on Gecko\r
5543                         // Or on IE when there was an exception\r
5544                         if (!r)\r
5545                                 r = t.win.document.createRange ? t.win.document.createRange() : t.win.document.body.createTextRange();\r
5546 \r
5547                         if (t.selectedRange && t.explicitRange) {\r
5548                                 if (r.compareBoundaryPoints(r.START_TO_START, t.selectedRange) === 0 && r.compareBoundaryPoints(r.END_TO_END, t.selectedRange) === 0) {\r
5549                                         // Safari, Opera and Chrome only ever select text which causes the range to change.\r
5550                                         // This lets us use the originally set range if the selection hasn't been changed by the user.\r
5551                                         r = t.explicitRange;\r
5552                                 } else {\r
5553                                         t.selectedRange = null;\r
5554                                         t.explicitRange = null;\r
5555                                 }\r
5556                         }\r
5557                         return r;\r
5558                 },\r
5559 \r
5560                 setRng : function(r) {\r
5561                         var s, t = this;\r
5562                         \r
5563                         if (!t.tridentSel) {\r
5564                                 s = t.getSel();\r
5565 \r
5566                                 if (s) {\r
5567                                         t.explicitRange = r;\r
5568                                         s.removeAllRanges();\r
5569                                         s.addRange(r);\r
5570                                         t.selectedRange = s.getRangeAt(0);\r
5571                                 }\r
5572                         } else {\r
5573                                 // Is W3C Range\r
5574                                 if (r.cloneRange) {\r
5575                                         t.tridentSel.addRange(r);\r
5576                                         return;\r
5577                                 }\r
5578 \r
5579                                 // Is IE specific range\r
5580                                 try {\r
5581                                         r.select();\r
5582                                 } catch (ex) {\r
5583                                         // Needed for some odd IE bug #1843306\r
5584                                 }\r
5585                         }\r
5586                 },\r
5587 \r
5588                 setNode : function(n) {\r
5589                         var t = this;\r
5590 \r
5591                         t.setContent(t.dom.getOuterHTML(n));\r
5592 \r
5593                         return n;\r
5594                 },\r
5595 \r
5596                 getNode : function() {\r
5597                         var t = this, rng = t.getRng(), sel = t.getSel(), elm;\r
5598 \r
5599                         if (rng.setStart) {\r
5600                                 // Range maybe lost after the editor is made visible again\r
5601                                 if (!rng)\r
5602                                         return t.dom.getRoot();\r
5603 \r
5604                                 elm = rng.commonAncestorContainer;\r
5605 \r
5606                                 // Handle selection a image or other control like element such as anchors\r
5607                                 if (!rng.collapsed) {\r
5608                                         if (rng.startContainer == rng.endContainer) {\r
5609                                                 if (rng.startOffset - rng.endOffset < 2) {\r
5610                                                         if (rng.startContainer.hasChildNodes())\r
5611                                                                 elm = rng.startContainer.childNodes[rng.startOffset];\r
5612                                                 }\r
5613                                         }\r
5614 \r
5615                                         // If the anchor node is a element instead of a text node then return this element\r
5616                                         if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1) \r
5617                                                 return sel.anchorNode.childNodes[sel.anchorOffset]; \r
5618                                 }\r
5619 \r
5620                                 if (elm && elm.nodeType == 3)\r
5621                                         return elm.parentNode;\r
5622 \r
5623                                 return elm;\r
5624                         }\r
5625 \r
5626                         return rng.item ? rng.item(0) : rng.parentElement();\r
5627                 },\r
5628 \r
5629                 getSelectedBlocks : function(st, en) {\r
5630                         var t = this, dom = t.dom, sb, eb, n, bl = [];\r
5631 \r
5632                         sb = dom.getParent(st || t.getStart(), dom.isBlock);\r
5633                         eb = dom.getParent(en || t.getEnd(), dom.isBlock);\r
5634 \r
5635                         if (sb)\r
5636                                 bl.push(sb);\r
5637 \r
5638                         if (sb && eb && sb != eb) {\r
5639                                 n = sb;\r
5640 \r
5641                                 while ((n = n.nextSibling) && n != eb) {\r
5642                                         if (dom.isBlock(n))\r
5643                                                 bl.push(n);\r
5644                                 }\r
5645                         }\r
5646 \r
5647                         if (eb && sb != eb)\r
5648                                 bl.push(eb);\r
5649 \r
5650                         return bl;\r
5651                 },\r
5652 \r
5653                 destroy : function(s) {\r
5654                         var t = this;\r
5655 \r
5656                         t.win = null;\r
5657 \r
5658                         if (t.tridentSel)\r
5659                                 t.tridentSel.destroy();\r
5660 \r
5661                         // Manual destroy then remove unload handler\r
5662                         if (!s)\r
5663                                 tinymce.removeUnload(t.destroy);\r
5664                 }\r
5665         });\r
5666 })(tinymce);\r
5667 \r
5668 (function(tinymce) {\r
5669         tinymce.create('tinymce.dom.XMLWriter', {\r
5670                 node : null,\r
5671 \r
5672                 XMLWriter : function(s) {\r
5673                         // Get XML document\r
5674                         function getXML() {\r
5675                                 var i = document.implementation;\r
5676 \r
5677                                 if (!i || !i.createDocument) {\r
5678                                         // Try IE objects\r
5679                                         try {return new ActiveXObject('MSXML2.DOMDocument');} catch (ex) {}\r
5680                                         try {return new ActiveXObject('Microsoft.XmlDom');} catch (ex) {}\r
5681                                 } else\r
5682                                         return i.createDocument('', '', null);\r
5683                         };\r
5684 \r
5685                         this.doc = getXML();\r
5686                         \r
5687                         // Since Opera and WebKit doesn't escape > into &gt; we need to do it our self to normalize the output for all browsers\r
5688                         this.valid = tinymce.isOpera || tinymce.isWebKit;\r
5689 \r
5690                         this.reset();\r
5691                 },\r
5692 \r
5693                 reset : function() {\r
5694                         var t = this, d = t.doc;\r
5695 \r
5696                         if (d.firstChild)\r
5697                                 d.removeChild(d.firstChild);\r
5698 \r
5699                         t.node = d.appendChild(d.createElement("html"));\r
5700                 },\r
5701 \r
5702                 writeStartElement : function(n) {\r
5703                         var t = this;\r
5704 \r
5705                         t.node = t.node.appendChild(t.doc.createElement(n));\r
5706                 },\r
5707 \r
5708                 writeAttribute : function(n, v) {\r
5709                         if (this.valid)\r
5710                                 v = v.replace(/>/g, '%MCGT%');\r
5711 \r
5712                         this.node.setAttribute(n, v);\r
5713                 },\r
5714 \r
5715                 writeEndElement : function() {\r
5716                         this.node = this.node.parentNode;\r
5717                 },\r
5718 \r
5719                 writeFullEndElement : function() {\r
5720                         var t = this, n = t.node;\r
5721 \r
5722                         n.appendChild(t.doc.createTextNode(""));\r
5723                         t.node = n.parentNode;\r
5724                 },\r
5725 \r
5726                 writeText : function(v) {\r
5727                         if (this.valid)\r
5728                                 v = v.replace(/>/g, '%MCGT%');\r
5729 \r
5730                         this.node.appendChild(this.doc.createTextNode(v));\r
5731                 },\r
5732 \r
5733                 writeCDATA : function(v) {\r
5734                         this.node.appendChild(this.doc.createCDATASection(v));\r
5735                 },\r
5736 \r
5737                 writeComment : function(v) {\r
5738                         // Fix for bug #2035694\r
5739                         if (tinymce.isIE)\r
5740                                 v = v.replace(/^\-|\-$/g, ' ');\r
5741 \r
5742                         this.node.appendChild(this.doc.createComment(v.replace(/\-\-/g, ' ')));\r
5743                 },\r
5744 \r
5745                 getContent : function() {\r
5746                         var h;\r
5747 \r
5748                         h = this.doc.xml || new XMLSerializer().serializeToString(this.doc);\r
5749                         h = h.replace(/<\?[^?]+\?>|<html>|<\/html>|<html\/>|<!DOCTYPE[^>]+>/g, '');\r
5750                         h = h.replace(/ ?\/>/g, ' />');\r
5751 \r
5752                         if (this.valid)\r
5753                                 h = h.replace(/\%MCGT%/g, '&gt;');\r
5754 \r
5755                         return h;\r
5756                 }\r
5757         });\r
5758 })(tinymce);\r
5759 \r
5760 (function(tinymce) {\r
5761         tinymce.create('tinymce.dom.StringWriter', {\r
5762                 str : null,\r
5763                 tags : null,\r
5764                 count : 0,\r
5765                 settings : null,\r
5766                 indent : null,\r
5767 \r
5768                 StringWriter : function(s) {\r
5769                         this.settings = tinymce.extend({\r
5770                                 indent_char : ' ',\r
5771                                 indentation : 0\r
5772                         }, s);\r
5773 \r
5774                         this.reset();\r
5775                 },\r
5776 \r
5777                 reset : function() {\r
5778                         this.indent = '';\r
5779                         this.str = "";\r
5780                         this.tags = [];\r
5781                         this.count = 0;\r
5782                 },\r
5783 \r
5784                 writeStartElement : function(n) {\r
5785                         this._writeAttributesEnd();\r
5786                         this.writeRaw('<' + n);\r
5787                         this.tags.push(n);\r
5788                         this.inAttr = true;\r
5789                         this.count++;\r
5790                         this.elementCount = this.count;\r
5791                 },\r
5792 \r
5793                 writeAttribute : function(n, v) {\r
5794                         var t = this;\r
5795 \r
5796                         t.writeRaw(" " + t.encode(n) + '="' + t.encode(v) + '"');\r
5797                 },\r
5798 \r
5799                 writeEndElement : function() {\r
5800                         var n;\r
5801 \r
5802                         if (this.tags.length > 0) {\r
5803                                 n = this.tags.pop();\r
5804 \r
5805                                 if (this._writeAttributesEnd(1))\r
5806                                         this.writeRaw('</' + n + '>');\r
5807 \r
5808                                 if (this.settings.indentation > 0)\r
5809                                         this.writeRaw('\n');\r
5810                         }\r
5811                 },\r
5812 \r
5813                 writeFullEndElement : function() {\r
5814                         if (this.tags.length > 0) {\r
5815                                 this._writeAttributesEnd();\r
5816                                 this.writeRaw('</' + this.tags.pop() + '>');\r
5817 \r
5818                                 if (this.settings.indentation > 0)\r
5819                                         this.writeRaw('\n');\r
5820                         }\r
5821                 },\r
5822 \r
5823                 writeText : function(v) {\r
5824                         this._writeAttributesEnd();\r
5825                         this.writeRaw(this.encode(v));\r
5826                         this.count++;\r
5827                 },\r
5828 \r
5829                 writeCDATA : function(v) {\r
5830                         this._writeAttributesEnd();\r
5831                         this.writeRaw('<![CDATA[' + v + ']]>');\r
5832                         this.count++;\r
5833                 },\r
5834 \r
5835                 writeComment : function(v) {\r
5836                         this._writeAttributesEnd();\r
5837                         this.writeRaw('<!-- ' + v + '-->');\r
5838                         this.count++;\r
5839                 },\r
5840 \r
5841                 writeRaw : function(v) {\r
5842                         this.str += v;\r
5843                 },\r
5844 \r
5845                 encode : function(s) {\r
5846                         return s.replace(/[<>&"]/g, function(v) {\r
5847                                 switch (v) {\r
5848                                         case '<':\r
5849                                                 return '&lt;';\r
5850 \r
5851                                         case '>':\r
5852                                                 return '&gt;';\r
5853 \r
5854                                         case '&':\r
5855                                                 return '&amp;';\r
5856 \r
5857                                         case '"':\r
5858                                                 return '&quot;';\r
5859                                 }\r
5860 \r
5861                                 return v;\r
5862                         });\r
5863                 },\r
5864 \r
5865                 getContent : function() {\r
5866                         return this.str;\r
5867                 },\r
5868 \r
5869                 _writeAttributesEnd : function(s) {\r
5870                         if (!this.inAttr)\r
5871                                 return;\r
5872 \r
5873                         this.inAttr = false;\r
5874 \r
5875                         if (s && this.elementCount == this.count) {\r
5876                                 this.writeRaw(' />');\r
5877                                 return false;\r
5878                         }\r
5879 \r
5880                         this.writeRaw('>');\r
5881 \r
5882                         return true;\r
5883                 }\r
5884         });\r
5885 })(tinymce);\r
5886 \r
5887 (function(tinymce) {\r
5888         // Shorten names\r
5889         var extend = tinymce.extend, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, isIE = tinymce.isIE, isGecko = tinymce.isGecko;\r
5890 \r
5891         function wildcardToRE(s) {\r
5892                 return s.replace(/([?+*])/g, '.$1');\r
5893         };\r
5894 \r
5895         tinymce.create('tinymce.dom.Serializer', {\r
5896                 Serializer : function(s) {\r
5897                         var t = this;\r
5898 \r
5899                         t.key = 0;\r
5900                         t.onPreProcess = new Dispatcher(t);\r
5901                         t.onPostProcess = new Dispatcher(t);\r
5902 \r
5903                         try {\r
5904                                 t.writer = new tinymce.dom.XMLWriter();\r
5905                         } catch (ex) {\r
5906                                 // IE might throw exception if ActiveX is disabled so we then switch to the slightly slower StringWriter\r
5907                                 t.writer = new tinymce.dom.StringWriter();\r
5908                         }\r
5909 \r
5910                         // Default settings\r
5911                         t.settings = s = extend({\r
5912                                 dom : tinymce.DOM,\r
5913                                 valid_nodes : 0,\r
5914                                 node_filter : 0,\r
5915                                 attr_filter : 0,\r
5916                                 invalid_attrs : /^(_mce_|_moz_|sizset|sizcache)/,\r
5917                                 closed : /^(br|hr|input|meta|img|link|param|area)$/,\r
5918                                 entity_encoding : 'named',\r
5919                                 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
5920                                 valid_elements : '*[*]',\r
5921                                 extended_valid_elements : 0,\r
5922                                 invalid_elements : 0,\r
5923                                 fix_table_elements : 1,\r
5924                                 fix_list_elements : true,\r
5925                                 fix_content_duplication : true,\r
5926                                 convert_fonts_to_spans : false,\r
5927                                 font_size_classes : 0,\r
5928                                 apply_source_formatting : 0,\r
5929                                 indent_mode : 'simple',\r
5930                                 indent_char : '\t',\r
5931                                 indent_levels : 1,\r
5932                                 remove_linebreaks : 1,\r
5933                                 remove_redundant_brs : 1,\r
5934                                 element_format : 'xhtml'\r
5935                         }, s);\r
5936 \r
5937                         t.dom = s.dom;\r
5938                         t.schema = s.schema;\r
5939 \r
5940                         // Use raw entities if no entities are defined\r
5941                         if (s.entity_encoding == 'named' && !s.entities)\r
5942                                 s.entity_encoding = 'raw';\r
5943 \r
5944                         if (s.remove_redundant_brs) {\r
5945                                 t.onPostProcess.add(function(se, o) {\r
5946                                         // Remove single BR at end of block elements since they get rendered\r
5947                                         o.content = o.content.replace(/(<br \/>\s*)+<\/(p|h[1-6]|div|li)>/gi, function(a, b, c) {\r
5948                                                 // Check if it's a single element\r
5949                                                 if (/^<br \/>\s*<\//.test(a))\r
5950                                                         return '</' + c + '>';\r
5951 \r
5952                                                 return a;\r
5953                                         });\r
5954                                 });\r
5955                         }\r
5956 \r
5957                         // Remove XHTML element endings i.e. produce crap :) XHTML is better\r
5958                         if (s.element_format == 'html') {\r
5959                                 t.onPostProcess.add(function(se, o) {\r
5960                                         o.content = o.content.replace(/<([^>]+) \/>/g, '<$1>');\r
5961                                 });\r
5962                         }\r
5963 \r
5964                         if (s.fix_list_elements) {\r
5965                                 t.onPreProcess.add(function(se, o) {\r
5966                                         var nl, x, a = ['ol', 'ul'], i, n, p, r = /^(OL|UL)$/, np;\r
5967 \r
5968                                         function prevNode(e, n) {\r
5969                                                 var a = n.split(','), i;\r
5970 \r
5971                                                 while ((e = e.previousSibling) != null) {\r
5972                                                         for (i=0; i<a.length; i++) {\r
5973                                                                 if (e.nodeName == a[i])\r
5974                                                                         return e;\r
5975                                                         }\r
5976                                                 }\r
5977 \r
5978                                                 return null;\r
5979                                         };\r
5980 \r
5981                                         for (x=0; x<a.length; x++) {\r
5982                                                 nl = t.dom.select(a[x], o.node);\r
5983 \r
5984                                                 for (i=0; i<nl.length; i++) {\r
5985                                                         n = nl[i];\r
5986                                                         p = n.parentNode;\r
5987 \r
5988                                                         if (r.test(p.nodeName)) {\r
5989                                                                 np = prevNode(n, 'LI');\r
5990 \r
5991                                                                 if (!np) {\r
5992                                                                         np = t.dom.create('li');\r
5993                                                                         np.innerHTML = '&nbsp;';\r
5994                                                                         np.appendChild(n);\r
5995                                                                         p.insertBefore(np, p.firstChild);\r
5996                                                                 } else\r
5997                                                                         np.appendChild(n);\r
5998                                                         }\r
5999                                                 }\r
6000                                         }\r
6001                                 });\r
6002                         }\r
6003 \r
6004                         if (s.fix_table_elements) {\r
6005                                 t.onPreProcess.add(function(se, o) {\r
6006                                         // Since Opera will crash if you attach the node to a dynamic document we need to brrowser sniff a specific build\r
6007                                         // so Opera users with an older version will have to live with less compaible output not much we can do here\r
6008                                         if (!tinymce.isOpera || opera.buildNumber() >= 1767) {\r
6009                                                 each(t.dom.select('p table', o.node).reverse(), function(n) {\r
6010                                                         var parent = t.dom.getParent(n.parentNode, 'table,p');\r
6011 \r
6012                                                         if (parent.nodeName != 'TABLE') {\r
6013                                                                 try {\r
6014                                                                         t.dom.split(parent, n);\r
6015                                                                 } catch (ex) {\r
6016                                                                         // IE can sometimes fire an unknown runtime error so we just ignore it\r
6017                                                                 }\r
6018                                                         }\r
6019                                                 });\r
6020                                         }\r
6021                                 });\r
6022                         }\r
6023                 },\r
6024 \r
6025                 setEntities : function(s) {\r
6026                         var t = this, a, i, l = {}, v;\r
6027 \r
6028                         // No need to setup more than once\r
6029                         if (t.entityLookup)\r
6030                                 return;\r
6031 \r
6032                         // Build regex and lookup array\r
6033                         a = s.split(',');\r
6034                         for (i = 0; i < a.length; i += 2) {\r
6035                                 v = a[i];\r
6036 \r
6037                                 // Don't add default &amp; &quot; etc.\r
6038                                 if (v == 34 || v == 38 || v == 60 || v == 62)\r
6039                                         continue;\r
6040 \r
6041                                 l[String.fromCharCode(a[i])] = a[i + 1];\r
6042 \r
6043                                 v = parseInt(a[i]).toString(16);\r
6044                         }\r
6045 \r
6046                         t.entityLookup = l;\r
6047                 },\r
6048 \r
6049                 setRules : function(s) {\r
6050                         var t = this;\r
6051 \r
6052                         t._setup();\r
6053                         t.rules = {};\r
6054                         t.wildRules = [];\r
6055                         t.validElements = {};\r
6056 \r
6057                         return t.addRules(s);\r
6058                 },\r
6059 \r
6060                 addRules : function(s) {\r
6061                         var t = this, dr;\r
6062 \r
6063                         if (!s)\r
6064                                 return;\r
6065 \r
6066                         t._setup();\r
6067 \r
6068                         each(s.split(','), function(s) {\r
6069                                 var p = s.split(/\[|\]/), tn = p[0].split('/'), ra, at, wat, va = [];\r
6070 \r
6071                                 // Extend with default rules\r
6072                                 if (dr)\r
6073                                         at = tinymce.extend([], dr.attribs);\r
6074 \r
6075                                 // Parse attributes\r
6076                                 if (p.length > 1) {\r
6077                                         each(p[1].split('|'), function(s) {\r
6078                                                 var ar = {}, i;\r
6079 \r
6080                                                 at = at || [];\r
6081 \r
6082                                                 // Parse attribute rule\r
6083                                                 s = s.replace(/::/g, '~');\r
6084                                                 s = /^([!\-])?([\w*.?~_\-]+|)([=:<])?(.+)?$/.exec(s);\r
6085                                                 s[2] = s[2].replace(/~/g, ':');\r
6086 \r
6087                                                 // Add required attributes\r
6088                                                 if (s[1] == '!') {\r
6089                                                         ra = ra || [];\r
6090                                                         ra.push(s[2]);\r
6091                                                 }\r
6092 \r
6093                                                 // Remove inherited attributes\r
6094                                                 if (s[1] == '-') {\r
6095                                                         for (i = 0; i <at.length; i++) {\r
6096                                                                 if (at[i].name == s[2]) {\r
6097                                                                         at.splice(i, 1);\r
6098                                                                         return;\r
6099                                                                 }\r
6100                                                         }\r
6101                                                 }\r
6102 \r
6103                                                 switch (s[3]) {\r
6104                                                         // Add default attrib values\r
6105                                                         case '=':\r
6106                                                                 ar.defaultVal = s[4] || '';\r
6107                                                                 break;\r
6108 \r
6109                                                         // Add forced attrib values\r
6110                                                         case ':':\r
6111                                                                 ar.forcedVal = s[4];\r
6112                                                                 break;\r
6113 \r
6114                                                         // Add validation values\r
6115                                                         case '<':\r
6116                                                                 ar.validVals = s[4].split('?');\r
6117                                                                 break;\r
6118                                                 }\r
6119 \r
6120                                                 if (/[*.?]/.test(s[2])) {\r
6121                                                         wat = wat || [];\r
6122                                                         ar.nameRE = new RegExp('^' + wildcardToRE(s[2]) + '$');\r
6123                                                         wat.push(ar);\r
6124                                                 } else {\r
6125                                                         ar.name = s[2];\r
6126                                                         at.push(ar);\r
6127                                                 }\r
6128 \r
6129                                                 va.push(s[2]);\r
6130                                         });\r
6131                                 }\r
6132 \r
6133                                 // Handle element names\r
6134                                 each(tn, function(s, i) {\r
6135                                         var pr = s.charAt(0), x = 1, ru = {};\r
6136 \r
6137                                         // Extend with default rule data\r
6138                                         if (dr) {\r
6139                                                 if (dr.noEmpty)\r
6140                                                         ru.noEmpty = dr.noEmpty;\r
6141 \r
6142                                                 if (dr.fullEnd)\r
6143                                                         ru.fullEnd = dr.fullEnd;\r
6144 \r
6145                                                 if (dr.padd)\r
6146                                                         ru.padd = dr.padd;\r
6147                                         }\r
6148 \r
6149                                         // Handle prefixes\r
6150                                         switch (pr) {\r
6151                                                 case '-':\r
6152                                                         ru.noEmpty = true;\r
6153                                                         break;\r
6154 \r
6155                                                 case '+':\r
6156                                                         ru.fullEnd = true;\r
6157                                                         break;\r
6158 \r
6159                                                 case '#':\r
6160                                                         ru.padd = true;\r
6161                                                         break;\r
6162 \r
6163                                                 default:\r
6164                                                         x = 0;\r
6165                                         }\r
6166 \r
6167                                         tn[i] = s = s.substring(x);\r
6168                                         t.validElements[s] = 1;\r
6169 \r
6170                                         // Add element name or element regex\r
6171                                         if (/[*.?]/.test(tn[0])) {\r
6172                                                 ru.nameRE = new RegExp('^' + wildcardToRE(tn[0]) + '$');\r
6173                                                 t.wildRules = t.wildRules || {};\r
6174                                                 t.wildRules.push(ru);\r
6175                                         } else {\r
6176                                                 ru.name = tn[0];\r
6177 \r
6178                                                 // Store away default rule\r
6179                                                 if (tn[0] == '@')\r
6180                                                         dr = ru;\r
6181 \r
6182                                                 t.rules[s] = ru;\r
6183                                         }\r
6184 \r
6185                                         ru.attribs = at;\r
6186 \r
6187                                         if (ra)\r
6188                                                 ru.requiredAttribs = ra;\r
6189 \r
6190                                         if (wat) {\r
6191                                                 // Build valid attributes regexp\r
6192                                                 s = '';\r
6193                                                 each(va, function(v) {\r
6194                                                         if (s)\r
6195                                                                 s += '|';\r
6196 \r
6197                                                         s += '(' + wildcardToRE(v) + ')';\r
6198                                                 });\r
6199                                                 ru.validAttribsRE = new RegExp('^' + s.toLowerCase() + '$');\r
6200                                                 ru.wildAttribs = wat;\r
6201                                         }\r
6202                                 });\r
6203                         });\r
6204 \r
6205                         // Build valid elements regexp\r
6206                         s = '';\r
6207                         each(t.validElements, function(v, k) {\r
6208                                 if (s)\r
6209                                         s += '|';\r
6210 \r
6211                                 if (k != '@')\r
6212                                         s += k;\r
6213                         });\r
6214                         t.validElementsRE = new RegExp('^(' + wildcardToRE(s.toLowerCase()) + ')$');\r
6215 \r
6216                         //console.debug(t.validElementsRE.toString());\r
6217                         //console.dir(t.rules);\r
6218                         //console.dir(t.wildRules);\r
6219                 },\r
6220 \r
6221                 findRule : function(n) {\r
6222                         var t = this, rl = t.rules, i, r;\r
6223 \r
6224                         t._setup();\r
6225 \r
6226                         // Exact match\r
6227                         r = rl[n];\r
6228                         if (r)\r
6229                                 return r;\r
6230 \r
6231                         // Try wildcards\r
6232                         rl = t.wildRules;\r
6233                         for (i = 0; i < rl.length; i++) {\r
6234                                 if (rl[i].nameRE.test(n))\r
6235                                         return rl[i];\r
6236                         }\r
6237 \r
6238                         return null;\r
6239                 },\r
6240 \r
6241                 findAttribRule : function(ru, n) {\r
6242                         var i, wa = ru.wildAttribs;\r
6243 \r
6244                         for (i = 0; i < wa.length; i++) {\r
6245                                 if (wa[i].nameRE.test(n))\r
6246                                         return wa[i];\r
6247                         }\r
6248 \r
6249                         return null;\r
6250                 },\r
6251 \r
6252                 serialize : function(n, o) {\r
6253                         var h, t = this, doc, oldDoc, impl, selected;\r
6254 \r
6255                         t._setup();\r
6256                         o = o || {};\r
6257                         o.format = o.format || 'html';\r
6258                         t.processObj = o;\r
6259 \r
6260                         // IE looses the selected attribute on option elements so we need to store it\r
6261                         // See: http://support.microsoft.com/kb/829907\r
6262                         if (isIE) {\r
6263                                 selected = [];\r
6264                                 each(n.getElementsByTagName('option'), function(n) {\r
6265                                         var v = t.dom.getAttrib(n, 'selected');\r
6266 \r
6267                                         selected.push(v ? v : null);\r
6268                                 });\r
6269                         }\r
6270 \r
6271                         n = n.cloneNode(true);\r
6272 \r
6273                         // IE looses the selected attribute on option elements so we need to restore it\r
6274                         if (isIE) {\r
6275                                 each(n.getElementsByTagName('option'), function(n, i) {\r
6276                                         t.dom.setAttrib(n, 'selected', selected[i]);\r
6277                                 });\r
6278                         }\r
6279 \r
6280                         // Nodes needs to be attached to something in WebKit/Opera\r
6281                         // Older builds of Opera crashes if you attach the node to an document created dynamically\r
6282                         // and since we can't feature detect a crash we need to sniff the acutal build number\r
6283                         // This fix will make DOM ranges and make Sizzle happy!\r
6284                         impl = n.ownerDocument.implementation;\r
6285                         if (impl.createHTMLDocument && (tinymce.isOpera && opera.buildNumber() >= 1767)) {\r
6286                                 // Create an empty HTML document\r
6287                                 doc = impl.createHTMLDocument("");\r
6288 \r
6289                                 // Add the element or it's children if it's a body element to the new document\r
6290                                 each(n.nodeName == 'BODY' ? n.childNodes : [n], function(node) {\r
6291                                         doc.body.appendChild(doc.importNode(node, true));\r
6292                                 });\r
6293 \r
6294                                 // Grab first child or body element for serialization\r
6295                                 if (n.nodeName != 'BODY')\r
6296                                         n = doc.body.firstChild;\r
6297                                 else\r
6298                                         n = doc.body;\r
6299 \r
6300                                 // set the new document in DOMUtils so createElement etc works\r
6301                                 oldDoc = t.dom.doc;\r
6302                                 t.dom.doc = doc;\r
6303                         }\r
6304 \r
6305                         t.key = '' + (parseInt(t.key) + 1);\r
6306 \r
6307                         // Pre process\r
6308                         if (!o.no_events) {\r
6309                                 o.node = n;\r
6310                                 t.onPreProcess.dispatch(t, o);\r
6311                         }\r
6312 \r
6313                         // Serialize HTML DOM into a string\r
6314                         t.writer.reset();\r
6315                         t._info = o;\r
6316                         t._serializeNode(n, o.getInner);\r
6317 \r
6318                         // Post process\r
6319                         o.content = t.writer.getContent();\r
6320 \r
6321                         // Restore the old document if it was changed\r
6322                         if (oldDoc)\r
6323                                 t.dom.doc = oldDoc;\r
6324 \r
6325                         if (!o.no_events)\r
6326                                 t.onPostProcess.dispatch(t, o);\r
6327 \r
6328                         t._postProcess(o);\r
6329                         o.node = null;\r
6330 \r
6331                         return tinymce.trim(o.content);\r
6332                 },\r
6333 \r
6334                 // Internal functions\r
6335 \r
6336                 _postProcess : function(o) {\r
6337                         var t = this, s = t.settings, h = o.content, sc = [], p;\r
6338 \r
6339                         if (o.format == 'html') {\r
6340                                 // Protect some elements\r
6341                                 p = t._protect({\r
6342                                         content : h,\r
6343                                         patterns : [\r
6344                                                 {pattern : /(<script[^>]*>)(.*?)(<\/script>)/g},\r
6345                                                 {pattern : /(<noscript[^>]*>)(.*?)(<\/noscript>)/g},\r
6346                                                 {pattern : /(<style[^>]*>)(.*?)(<\/style>)/g},\r
6347                                                 {pattern : /(<pre[^>]*>)(.*?)(<\/pre>)/g, encode : 1},\r
6348                                                 {pattern : /(<!--\[CDATA\[)(.*?)(\]\]-->)/g}\r
6349                                         ]\r
6350                                 });\r
6351 \r
6352                                 h = p.content;\r
6353 \r
6354                                 // Entity encode\r
6355                                 if (s.entity_encoding !== 'raw')\r
6356                                         h = t._encode(h);\r
6357 \r
6358                                 // Use BR instead of &nbsp; padded P elements inside editor and use <p>&nbsp;</p> outside editor\r
6359 /*                              if (o.set)\r
6360                                         h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p><br /></p>');\r
6361                                 else\r
6362                                         h = h.replace(/<p>\s+(&nbsp;|&#160;|\u00a0|<br \/>)\s+<\/p>/g, '<p>$1</p>');*/\r
6363 \r
6364                                 // Since Gecko and Safari keeps whitespace in the DOM we need to\r
6365                                 // remove it inorder to match other browsers. But I think Gecko and Safari is right.\r
6366                                 // This process is only done when getting contents out from the editor.\r
6367                                 if (!o.set) {\r
6368                                         // We need to replace paragraph whitespace with an nbsp before indentation to keep the \u00a0 char\r
6369                                         h = h.replace(/<p>\s+<\/p>|<p([^>]+)>\s+<\/p>/g, s.entity_encoding == 'numeric' ? '<p$1>&#160;</p>' : '<p$1>&nbsp;</p>');\r
6370 \r
6371                                         if (s.remove_linebreaks) {\r
6372                                                 h = h.replace(/\r?\n|\r/g, ' ');\r
6373                                                 h = h.replace(/(<[^>]+>)\s+/g, '$1 ');\r
6374                                                 h = h.replace(/\s+(<\/[^>]+>)/g, ' $1');\r
6375                                                 h = h.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>'); // Trim block start\r
6376                                                 h = h.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>'); // Trim block start\r
6377                                                 h = h.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>'); // Trim block end\r
6378                                         }\r
6379 \r
6380                                         // Simple indentation\r
6381                                         if (s.apply_source_formatting && s.indent_mode == 'simple') {\r
6382                                                 // Add line breaks before and after block elements\r
6383                                                 h = h.replace(/<(\/?)(ul|hr|table|meta|link|tbody|tr|object|body|head|html|map)(|[^>]+)>\s*/g, '\n<$1$2$3>\n');\r
6384                                                 h = h.replace(/\s*<(p|h[1-6]|blockquote|div|title|style|pre|script|td|li|area)(|[^>]+)>/g, '\n<$1$2>');\r
6385                                                 h = h.replace(/<\/(p|h[1-6]|blockquote|div|title|style|pre|script|td|li)>\s*/g, '</$1>\n');\r
6386                                                 h = h.replace(/\n\n/g, '\n');\r
6387                                         }\r
6388                                 }\r
6389 \r
6390                                 h = t._unprotect(h, p);\r
6391 \r
6392                                 // Restore CDATA sections\r
6393                                 h = h.replace(/<!--\[CDATA\[([\s\S]+)\]\]-->/g, '<![CDATA[$1]]>');\r
6394 \r
6395                                 // Restore the \u00a0 character if raw mode is enabled\r
6396                                 if (s.entity_encoding == 'raw')\r
6397                                         h = h.replace(/<p>&nbsp;<\/p>|<p([^>]+)>&nbsp;<\/p>/g, '<p$1>\u00a0</p>');\r
6398 \r
6399                                 // Restore noscript elements\r
6400                                 h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) {\r
6401                                         return '<noscript' + attribs + '>' + t.dom.decode(text.replace(/<!--|-->/g, '')) + '</noscript>';\r
6402                                 });\r
6403                         }\r
6404 \r
6405                         o.content = h;\r
6406                 },\r
6407 \r
6408                 _serializeNode : function(n, inner) {\r
6409                         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
6410 \r
6411                         if (!s.node_filter || s.node_filter(n)) {\r
6412                                 switch (n.nodeType) {\r
6413                                         case 1: // Element\r
6414                                                 if (n.hasAttribute ? n.hasAttribute('_mce_bogus') : n.getAttribute('_mce_bogus'))\r
6415                                                         return;\r
6416 \r
6417                                                 iv = keep = false;\r
6418                                                 hc = n.hasChildNodes();\r
6419                                                 nn = n.getAttribute('_mce_name') || n.nodeName.toLowerCase();\r
6420 \r
6421                                                 // Get internal type\r
6422                                                 type = n.getAttribute('_mce_type');\r
6423                                                 if (type) {\r
6424                                                         if (!t._info.cleanup) {\r
6425                                                                 iv = true;\r
6426                                                                 return;\r
6427                                                         } else\r
6428                                                                 keep = 1;\r
6429                                                 }\r
6430 \r
6431                                                 // Add correct prefix on IE\r
6432                                                 if (isIE) {\r
6433                                                         scopeName = n.scopeName;\r
6434                                                         if (scopeName && scopeName !== 'HTML' && scopeName !== 'html')\r
6435                                                                 nn = scopeName + ':' + nn;\r
6436                                                 }\r
6437 \r
6438                                                 // Remove mce prefix on IE needed for the abbr element\r
6439                                                 if (nn.indexOf('mce:') === 0)\r
6440                                                         nn = nn.substring(4);\r
6441 \r
6442                                                 // Check if valid\r
6443                                                 if (!keep) {\r
6444                                                         if (!t.validElementsRE || !t.validElementsRE.test(nn) || (t.invalidElementsRE && t.invalidElementsRE.test(nn)) || inner) {\r
6445                                                                 iv = true;\r
6446                                                                 break;\r
6447                                                         }\r
6448                                                 }\r
6449 \r
6450                                                 if (isIE) {\r
6451                                                         // Fix IE content duplication (DOM can have multiple copies of the same node)\r
6452                                                         if (s.fix_content_duplication) {\r
6453                                                                 if (n._mce_serialized == t.key)\r
6454                                                                         return;\r
6455 \r
6456                                                                 n._mce_serialized = t.key;\r
6457                                                         }\r
6458 \r
6459                                                         // IE sometimes adds a / infront of the node name\r
6460                                                         if (nn.charAt(0) == '/')\r
6461                                                                 nn = nn.substring(1);\r
6462                                                 } else if (isGecko) {\r
6463                                                         // Ignore br elements\r
6464                                                         if (n.nodeName === 'BR' && n.getAttribute('type') == '_moz')\r
6465                                                                 return;\r
6466                                                 }\r
6467 \r
6468                                                 // Check if valid child\r
6469                                                 if (s.validate_children) {\r
6470                                                         if (t.elementName && !t.schema.isValid(t.elementName, nn)) {\r
6471                                                                 iv = true;\r
6472                                                                 break;\r
6473                                                         }\r
6474 \r
6475                                                         t.elementName = nn;\r
6476                                                 }\r
6477 \r
6478                                                 ru = t.findRule(nn);\r
6479                                                 \r
6480                                                 // No valid rule for this element could be found then skip it\r
6481                                                 if (!ru) {\r
6482                                                         iv = true;\r
6483                                                         break;\r
6484                                                 }\r
6485 \r
6486                                                 nn = ru.name || nn;\r
6487                                                 closed = s.closed.test(nn);\r
6488 \r
6489                                                 // Skip empty nodes or empty node name in IE\r
6490                                                 if ((!hc && ru.noEmpty) || (isIE && !nn)) {\r
6491                                                         iv = true;\r
6492                                                         break;\r
6493                                                 }\r
6494 \r
6495                                                 // Check required\r
6496                                                 if (ru.requiredAttribs) {\r
6497                                                         a = ru.requiredAttribs;\r
6498 \r
6499                                                         for (i = a.length - 1; i >= 0; i--) {\r
6500                                                                 if (this.dom.getAttrib(n, a[i]) !== '')\r
6501                                                                         break;\r
6502                                                         }\r
6503 \r
6504                                                         // None of the required was there\r
6505                                                         if (i == -1) {\r
6506                                                                 iv = true;\r
6507                                                                 break;\r
6508                                                         }\r
6509                                                 }\r
6510 \r
6511                                                 w.writeStartElement(nn);\r
6512 \r
6513                                                 // Add ordered attributes\r
6514                                                 if (ru.attribs) {\r
6515                                                         for (i=0, at = ru.attribs, l = at.length; i<l; i++) {\r
6516                                                                 a = at[i];\r
6517                                                                 v = t._getAttrib(n, a);\r
6518 \r
6519                                                                 if (v !== null)\r
6520                                                                         w.writeAttribute(a.name, v);\r
6521                                                         }\r
6522                                                 }\r
6523 \r
6524                                                 // Add wild attributes\r
6525                                                 if (ru.validAttribsRE) {\r
6526                                                         at = t.dom.getAttribs(n);\r
6527                                                         for (i=at.length-1; i>-1; i--) {\r
6528                                                                 no = at[i];\r
6529 \r
6530                                                                 if (no.specified) {\r
6531                                                                         a = no.nodeName.toLowerCase();\r
6532 \r
6533                                                                         if (s.invalid_attrs.test(a) || !ru.validAttribsRE.test(a))\r
6534                                                                                 continue;\r
6535 \r
6536                                                                         ar = t.findAttribRule(ru, a);\r
6537                                                                         v = t._getAttrib(n, ar, a);\r
6538 \r
6539                                                                         if (v !== null)\r
6540                                                                                 w.writeAttribute(a, v);\r
6541                                                                 }\r
6542                                                         }\r
6543                                                 }\r
6544 \r
6545                                                 // Keep type attribute\r
6546                                                 if (type && keep)\r
6547                                                         w.writeAttribute('_mce_type', type);\r
6548 \r
6549                                                 // Write text from script\r
6550                                                 if (nn === 'script' && tinymce.trim(n.innerHTML)) {\r
6551                                                         w.writeText('// '); // Padd it with a comment so it will parse on older browsers\r
6552                                                         w.writeCDATA(n.innerHTML.replace(/<!--|-->|<\[CDATA\[|\]\]>/g, '')); // Remove comments and cdata stuctures\r
6553                                                         hc = false;\r
6554                                                         break;\r
6555                                                 }\r
6556 \r
6557                                                 // Padd empty nodes with a &nbsp;\r
6558                                                 if (ru.padd) {\r
6559                                                         // If it has only one bogus child, padd it anyway workaround for <td><br /></td> bug\r
6560                                                         if (hc && (cn = n.firstChild) && cn.nodeType === 1 && n.childNodes.length === 1) {\r
6561                                                                 if (cn.hasAttribute ? cn.hasAttribute('_mce_bogus') : cn.getAttribute('_mce_bogus'))\r
6562                                                                         w.writeText('\u00a0');\r
6563                                                         } else if (!hc)\r
6564                                                                 w.writeText('\u00a0'); // No children then padd it\r
6565                                                 }\r
6566 \r
6567                                                 break;\r
6568 \r
6569                                         case 3: // Text\r
6570                                                 // Check if valid child\r
6571                                                 if (s.validate_children && t.elementName && !t.schema.isValid(t.elementName, '#text'))\r
6572                                                         return;\r
6573 \r
6574                                                 return w.writeText(n.nodeValue);\r
6575 \r
6576                                         case 4: // CDATA\r
6577                                                 return w.writeCDATA(n.nodeValue);\r
6578 \r
6579                                         case 8: // Comment\r
6580                                                 return w.writeComment(n.nodeValue);\r
6581                                 }\r
6582                         } else if (n.nodeType == 1)\r
6583                                 hc = n.hasChildNodes();\r
6584 \r
6585                         if (hc && !closed) {\r
6586                                 cn = n.firstChild;\r
6587 \r
6588                                 while (cn) {\r
6589                                         t._serializeNode(cn);\r
6590                                         t.elementName = nn;\r
6591                                         cn = cn.nextSibling;\r
6592                                 }\r
6593                         }\r
6594 \r
6595                         // Write element end\r
6596                         if (!iv) {\r
6597                                 if (!closed)\r
6598                                         w.writeFullEndElement();\r
6599                                 else\r
6600                                         w.writeEndElement();\r
6601                         }\r
6602                 },\r
6603 \r
6604                 _protect : function(o) {\r
6605                         var t = this;\r
6606 \r
6607                         o.items = o.items || [];\r
6608 \r
6609                         function enc(s) {\r
6610                                 return s.replace(/[\r\n\\]/g, function(c) {\r
6611                                         if (c === '\n')\r
6612                                                 return '\\n';\r
6613                                         else if (c === '\\')\r
6614                                                 return '\\\\';\r
6615 \r
6616                                         return '\\r';\r
6617                                 });\r
6618                         };\r
6619 \r
6620                         function dec(s) {\r
6621                                 return s.replace(/\\[\\rn]/g, function(c) {\r
6622                                         if (c === '\\n')\r
6623                                                 return '\n';\r
6624                                         else if (c === '\\\\')\r
6625                                                 return '\\';\r
6626 \r
6627                                         return '\r';\r
6628                                 });\r
6629                         };\r
6630 \r
6631                         each(o.patterns, function(p) {\r
6632                                 o.content = dec(enc(o.content).replace(p.pattern, function(x, a, b, c) {\r
6633                                         b = dec(b);\r
6634 \r
6635                                         if (p.encode)\r
6636                                                 b = t._encode(b);\r
6637 \r
6638                                         o.items.push(b);\r
6639                                         return a + '<!--mce:' + (o.items.length - 1) + '-->' + c;\r
6640                                 }));\r
6641                         });\r
6642 \r
6643                         return o;\r
6644                 },\r
6645 \r
6646                 _unprotect : function(h, o) {\r
6647                         h = h.replace(/\<!--mce:([0-9]+)--\>/g, function(a, b) {\r
6648                                 return o.items[parseInt(b)];\r
6649                         });\r
6650 \r
6651                         o.items = [];\r
6652 \r
6653                         return h;\r
6654                 },\r
6655 \r
6656                 _encode : function(h) {\r
6657                         var t = this, s = t.settings, l;\r
6658 \r
6659                         // Entity encode\r
6660                         if (s.entity_encoding !== 'raw') {\r
6661                                 if (s.entity_encoding.indexOf('named') != -1) {\r
6662                                         t.setEntities(s.entities);\r
6663                                         l = t.entityLookup;\r
6664 \r
6665                                         h = h.replace(/[\u007E-\uFFFF]/g, function(a) {\r
6666                                                 var v;\r
6667 \r
6668                                                 if (v = l[a])\r
6669                                                         a = '&' + v + ';';\r
6670 \r
6671                                                 return a;\r
6672                                         });\r
6673                                 }\r
6674 \r
6675                                 if (s.entity_encoding.indexOf('numeric') != -1) {\r
6676                                         h = h.replace(/[\u007E-\uFFFF]/g, function(a) {\r
6677                                                 return '&#' + a.charCodeAt(0) + ';';\r
6678                                         });\r
6679                                 }\r
6680                         }\r
6681 \r
6682                         return h;\r
6683                 },\r
6684 \r
6685                 _setup : function() {\r
6686                         var t = this, s = this.settings;\r
6687 \r
6688                         if (t.done)\r
6689                                 return;\r
6690 \r
6691                         t.done = 1;\r
6692 \r
6693                         t.setRules(s.valid_elements);\r
6694                         t.addRules(s.extended_valid_elements);\r
6695 \r
6696                         if (s.invalid_elements)\r
6697                                 t.invalidElementsRE = new RegExp('^(' + wildcardToRE(s.invalid_elements.replace(/,/g, '|').toLowerCase()) + ')$');\r
6698 \r
6699                         if (s.attrib_value_filter)\r
6700                                 t.attribValueFilter = s.attribValueFilter;\r
6701                 },\r
6702 \r
6703                 _getAttrib : function(n, a, na) {\r
6704                         var i, v;\r
6705 \r
6706                         na = na || a.name;\r
6707 \r
6708                         if (a.forcedVal && (v = a.forcedVal)) {\r
6709                                 if (v === '{$uid}')\r
6710                                         return this.dom.uniqueId();\r
6711 \r
6712                                 return v;\r
6713                         }\r
6714 \r
6715                         v = this.dom.getAttrib(n, na);\r
6716 \r
6717                         switch (na) {\r
6718                                 case 'rowspan':\r
6719                                 case 'colspan':\r
6720                                         // Whats the point? Remove usless attribute value\r
6721                                         if (v == '1')\r
6722                                                 v = '';\r
6723 \r
6724                                         break;\r
6725                         }\r
6726 \r
6727                         if (this.attribValueFilter)\r
6728                                 v = this.attribValueFilter(na, v, n);\r
6729 \r
6730                         if (a.validVals) {\r
6731                                 for (i = a.validVals.length - 1; i >= 0; i--) {\r
6732                                         if (v == a.validVals[i])\r
6733                                                 break;\r
6734                                 }\r
6735 \r
6736                                 if (i == -1)\r
6737                                         return null;\r
6738                         }\r
6739 \r
6740                         if (v === '' && typeof(a.defaultVal) != 'undefined') {\r
6741                                 v = a.defaultVal;\r
6742 \r
6743                                 if (v === '{$uid}')\r
6744                                         return this.dom.uniqueId();\r
6745 \r
6746                                 return v;\r
6747                         } else {\r
6748                                 // Remove internal mceItemXX classes when content is extracted from editor\r
6749                                 if (na == 'class' && this.processObj.get)\r
6750                                         v = v.replace(/\s?mceItem\w+\s?/g, '');\r
6751                         }\r
6752 \r
6753                         if (v === '')\r
6754                                 return null;\r
6755 \r
6756 \r
6757                         return v;\r
6758                 }\r
6759         });\r
6760 })(tinymce);\r
6761 \r
6762 (function(tinymce) {\r
6763         tinymce.dom.ScriptLoader = function(settings) {\r
6764                 var QUEUED = 0,\r
6765                         LOADING = 1,\r
6766                         LOADED = 2,\r
6767                         states = {},\r
6768                         queue = [],\r
6769                         scriptLoadedCallbacks = {},\r
6770                         queueLoadedCallbacks = [],\r
6771                         loading = 0,\r
6772                         undefined;\r
6773 \r
6774                 function loadScript(url, callback) {\r
6775                         var t = this, dom = tinymce.DOM, elm, uri, loc, id;\r
6776 \r
6777                         // Execute callback when script is loaded\r
6778                         function done() {\r
6779                                 dom.remove(id);\r
6780 \r
6781                                 if (elm)\r
6782                                         elm.onreadystatechange = elm.onload = elm = null;\r
6783 \r
6784                                 callback();\r
6785                         };\r
6786 \r
6787                         id = dom.uniqueId();\r
6788 \r
6789                         if (tinymce.isIE6) {\r
6790                                 uri = new tinymce.util.URI(url);\r
6791                                 loc = location;\r
6792 \r
6793                                 // If script is from same domain and we\r
6794                                 // use IE 6 then use XHR since it's more reliable\r
6795                                 if (uri.host == loc.hostname && uri.port == loc.port && (uri.protocol + ':') == loc.protocol) {\r
6796                                         tinymce.util.XHR.send({\r
6797                                                 url : tinymce._addVer(uri.getURI()),\r
6798                                                 success : function(content) {\r
6799                                                         // Create new temp script element\r
6800                                                         var script = dom.create('script', {\r
6801                                                                 type : 'text/javascript'\r
6802                                                         });\r
6803 \r
6804                                                         // Evaluate script in global scope\r
6805                                                         script.text = content;\r
6806                                                         document.getElementsByTagName('head')[0].appendChild(script);\r
6807                                                         dom.remove(script);\r
6808 \r
6809                                                         done();\r
6810                                                 }\r
6811                                         });\r
6812 \r
6813                                         return;\r
6814                                 }\r
6815                         }\r
6816 \r
6817                         // Create new script element\r
6818                         elm = dom.create('script', {\r
6819                                 id : id,\r
6820                                 type : 'text/javascript',\r
6821                                 src : tinymce._addVer(url)\r
6822                         });\r
6823 \r
6824                         // Add onload and readystate listeners\r
6825                         elm.onload = done;\r
6826                         elm.onreadystatechange = function() {\r
6827                                 var state = elm.readyState;\r
6828 \r
6829                                 // Loaded state is passed on IE 6 however there\r
6830                                 // are known issues with this method but we can't use\r
6831                                 // XHR in a cross domain loading\r
6832                                 if (state == 'complete' || state == 'loaded')\r
6833                                         done();\r
6834                         };\r
6835 \r
6836                         // Most browsers support this feature so we report errors\r
6837                         // for those at least to help users track their missing plugins etc\r
6838                         // todo: Removed since it produced error if the document is unloaded by navigating away, re-add it as an option\r
6839                         /*elm.onerror = function() {\r
6840                                 alert('Failed to load: ' + url);\r
6841                         };*/\r
6842 \r
6843                         // Add script to document\r
6844                         (document.getElementsByTagName('head')[0] || document.body).appendChild(elm);\r
6845                 };\r
6846 \r
6847                 this.isDone = function(url) {\r
6848                         return states[url] == LOADED;\r
6849                 };\r
6850 \r
6851                 this.markDone = function(url) {\r
6852                         states[url] = LOADED;\r
6853                 };\r
6854 \r
6855                 this.add = this.load = function(url, callback, scope) {\r
6856                         var item, state = states[url];\r
6857 \r
6858                         // Add url to load queue\r
6859                         if (state == undefined) {\r
6860                                 queue.push(url);\r
6861                                 states[url] = QUEUED;\r
6862                         }\r
6863 \r
6864                         if (callback) {\r
6865                                 // Store away callback for later execution\r
6866                                 if (!scriptLoadedCallbacks[url])\r
6867                                         scriptLoadedCallbacks[url] = [];\r
6868 \r
6869                                 scriptLoadedCallbacks[url].push({\r
6870                                         func : callback,\r
6871                                         scope : scope || this\r
6872                                 });\r
6873                         }\r
6874                 };\r
6875 \r
6876                 this.loadQueue = function(callback, scope) {\r
6877                         this.loadScripts(queue, callback, scope);\r
6878                 };\r
6879 \r
6880                 this.loadScripts = function(scripts, callback, scope) {\r
6881                         var loadScripts;\r
6882 \r
6883                         function execScriptLoadedCallbacks(url) {\r
6884                                 // Execute URL callback functions\r
6885                                 tinymce.each(scriptLoadedCallbacks[url], function(callback) {\r
6886                                         callback.func.call(callback.scope);\r
6887                                 });\r
6888 \r
6889                                 scriptLoadedCallbacks[url] = undefined;\r
6890                         };\r
6891 \r
6892                         queueLoadedCallbacks.push({\r
6893                                 func : callback,\r
6894                                 scope : scope || this\r
6895                         });\r
6896 \r
6897                         loadScripts = function() {\r
6898                                 var loadingScripts = tinymce.grep(scripts);\r
6899 \r
6900                                 // Current scripts has been handled\r
6901                                 scripts.length = 0;\r
6902 \r
6903                                 // Load scripts that needs to be loaded\r
6904                                 tinymce.each(loadingScripts, function(url) {\r
6905                                         // Script is already loaded then execute script callbacks directly\r
6906                                         if (states[url] == LOADED) {\r
6907                                                 execScriptLoadedCallbacks(url);\r
6908                                                 return;\r
6909                                         }\r
6910 \r
6911                                         // Is script not loading then start loading it\r
6912                                         if (states[url] != LOADING) {\r
6913                                                 states[url] = LOADING;\r
6914                                                 loading++;\r
6915 \r
6916                                                 loadScript(url, function() {\r
6917                                                         states[url] = LOADED;\r
6918                                                         loading--;\r
6919 \r
6920                                                         execScriptLoadedCallbacks(url);\r
6921 \r
6922                                                         // Load more scripts if they where added by the recently loaded script\r
6923                                                         loadScripts();\r
6924                                                 });\r
6925                                         }\r
6926                                 });\r
6927 \r
6928                                 // No scripts are currently loading then execute all pending queue loaded callbacks\r
6929                                 if (!loading) {\r
6930                                         tinymce.each(queueLoadedCallbacks, function(callback) {\r
6931                                                 callback.func.call(callback.scope);\r
6932                                         });\r
6933 \r
6934                                         queueLoadedCallbacks.length = 0;\r
6935                                 }\r
6936                         };\r
6937 \r
6938                         loadScripts();\r
6939                 };\r
6940         };\r
6941 \r
6942         // Global script loader\r
6943         tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();\r
6944 })(tinymce);\r
6945 \r
6946 tinymce.dom.TreeWalker = function(start_node, root_node) {\r
6947         var node = start_node;\r
6948 \r
6949         function findSibling(node, start_name, sibling_name, shallow) {\r
6950                 var sibling, parent;\r
6951 \r
6952                 if (node) {\r
6953                         // Walk into nodes if it has a start\r
6954                         if (!shallow && node[start_name])\r
6955                                 return node[start_name];\r
6956 \r
6957                         // Return the sibling if it has one\r
6958                         if (node != root_node) {\r
6959                                 sibling = node[sibling_name];\r
6960                                 if (sibling)\r
6961                                         return sibling;\r
6962 \r
6963                                 // Walk up the parents to look for siblings\r
6964                                 for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) {\r
6965                                         sibling = parent[sibling_name];\r
6966                                         if (sibling)\r
6967                                                 return sibling;\r
6968                                 }\r
6969                         }\r
6970                 }\r
6971         };\r
6972 \r
6973         this.current = function() {\r
6974                 return node;\r
6975         };\r
6976 \r
6977         this.next = function(shallow) {\r
6978                 return (node = findSibling(node, 'firstChild', 'nextSibling', shallow));\r
6979         };\r
6980 \r
6981         this.prev = function(shallow) {\r
6982                 return (node = findSibling(node, 'lastChild', 'lastSibling', shallow));\r
6983         };\r
6984 };\r
6985 \r
6986 (function() {\r
6987         var transitional = {};\r
6988 \r
6989         function unpack(lookup, data) {\r
6990                 var key;\r
6991 \r
6992                 function replace(value) {\r
6993                         return value.replace(/[A-Z]+/g, function(key) {\r
6994                                 return replace(lookup[key]);\r
6995                         });\r
6996                 };\r
6997 \r
6998                 // Unpack lookup\r
6999                 for (key in lookup) {\r
7000                         if (lookup.hasOwnProperty(key))\r
7001                                 lookup[key] = replace(lookup[key]);\r
7002                 }\r
7003 \r
7004                 // Unpack and parse data into object map\r
7005                 replace(data).replace(/#/g, '#text').replace(/(\w+)\[([^\]]+)\]/g, function(str, name, children) {\r
7006                         var i, map = {};\r
7007 \r
7008                         children = children.split(/\|/);\r
7009 \r
7010                         for (i = children.length - 1; i >= 0; i--)\r
7011                                 map[children[i]] = 1;\r
7012 \r
7013                         transitional[name] = map;\r
7014                 });\r
7015         };\r
7016 \r
7017         // This is the XHTML 1.0 transitional elements with it's children packed to reduce it's size\r
7018         // we will later include the attributes here and use it as a default for valid elements but it\r
7019         // requires us to rewrite the serializer engine\r
7020         unpack({\r
7021                 Z : '#|H|K|N|O|P',\r
7022                 Y : '#|X|form|R|Q',\r
7023                 X : 'p|T|div|U|W|isindex|fieldset|table',\r
7024                 W : 'pre|hr|blockquote|address|center|noframes',\r
7025                 U : 'ul|ol|dl|menu|dir',\r
7026                 ZC : '#|p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q',\r
7027                 T : 'h1|h2|h3|h4|h5|h6',\r
7028                 ZB : '#|X|S|Q',\r
7029                 S : 'R|P',\r
7030                 ZA : '#|a|G|J|M|O|P',\r
7031                 R : '#|a|H|K|N|O',\r
7032                 Q : 'noscript|P',\r
7033                 P : 'ins|del|script',\r
7034                 O : 'input|select|textarea|label|button',\r
7035                 N : 'M|L',\r
7036                 M : 'em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym',\r
7037                 L : 'sub|sup',\r
7038                 K : 'J|I',\r
7039                 J : 'tt|i|b|u|s|strike',\r
7040                 I : 'big|small|font|basefont',\r
7041                 H : 'G|F',\r
7042                 G : 'br|span|bdo',\r
7043                 F : 'object|applet|img|map|iframe'\r
7044         }, 'script[]' + \r
7045                 'style[]' + \r
7046                 'object[#|param|X|form|a|H|K|N|O|Q]' + \r
7047                 'param[]' + \r
7048                 'p[S]' + \r
7049                 'a[Z]' + \r
7050                 'br[]' + \r
7051                 'span[S]' + \r
7052                 'bdo[S]' + \r
7053                 'applet[#|param|X|form|a|H|K|N|O|Q]' + \r
7054                 'h1[S]' + \r
7055                 'img[]' + \r
7056                 'map[X|form|Q|area]' + \r
7057                 'h2[S]' + \r
7058                 'iframe[#|X|form|a|H|K|N|O|Q]' + \r
7059                 'h3[S]' + \r
7060                 'tt[S]' + \r
7061                 'i[S]' + \r
7062                 'b[S]' + \r
7063                 'u[S]' + \r
7064                 's[S]' + \r
7065                 'strike[S]' + \r
7066                 'big[S]' + \r
7067                 'small[S]' + \r
7068                 'font[S]' + \r
7069                 'basefont[]' + \r
7070                 'em[S]' + \r
7071                 'strong[S]' + \r
7072                 'dfn[S]' + \r
7073                 'code[S]' + \r
7074                 'q[S]' + \r
7075                 'samp[S]' + \r
7076                 'kbd[S]' + \r
7077                 'var[S]' + \r
7078                 'cite[S]' + \r
7079                 'abbr[S]' + \r
7080                 'acronym[S]' + \r
7081                 'sub[S]' + \r
7082                 'sup[S]' + \r
7083                 'input[]' + \r
7084                 'select[optgroup|option]' + \r
7085                 'optgroup[option]' + \r
7086                 'option[]' + \r
7087                 'textarea[]' + \r
7088                 'label[S]' + \r
7089                 'button[#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]' + \r
7090                 'h4[S]' + \r
7091                 'ins[#|X|form|a|H|K|N|O|Q]' + \r
7092                 'h5[S]' + \r
7093                 'del[#|X|form|a|H|K|N|O|Q]' + \r
7094                 'h6[S]' + \r
7095                 'div[#|X|form|a|H|K|N|O|Q]' + \r
7096                 'ul[li]' + \r
7097                 'li[#|X|form|a|H|K|N|O|Q]' + \r
7098                 'ol[li]' + \r
7099                 'dl[dt|dd]' + \r
7100                 'dt[S]' + \r
7101                 'dd[#|X|form|a|H|K|N|O|Q]' + \r
7102                 'menu[li]' + \r
7103                 'dir[li]' + \r
7104                 'pre[ZA]' + \r
7105                 'hr[]' + \r
7106                 'blockquote[#|X|form|a|H|K|N|O|Q]' + \r
7107                 'address[S|p]' + \r
7108                 'center[#|X|form|a|H|K|N|O|Q]' + \r
7109                 'noframes[#|X|form|a|H|K|N|O|Q]' + \r
7110                 'isindex[]' + \r
7111                 'fieldset[#|legend|X|form|a|H|K|N|O|Q]' + \r
7112                 'legend[S]' + \r
7113                 'table[caption|col|colgroup|thead|tfoot|tbody|tr]' + \r
7114                 'caption[S]' + \r
7115                 'col[]' + \r
7116                 'colgroup[col]' + \r
7117                 'thead[tr]' + \r
7118                 'tr[th|td]' + \r
7119                 'th[#|X|form|a|H|K|N|O|Q]' + \r
7120                 'form[#|X|a|H|K|N|O|Q]' + \r
7121                 'noscript[#|X|form|a|H|K|N|O|Q]' + \r
7122                 'td[#|X|form|a|H|K|N|O|Q]' + \r
7123                 'tfoot[tr]' + \r
7124                 'tbody[tr]' + \r
7125                 'area[]' + \r
7126                 'base[]' + \r
7127                 'body[#|X|form|a|H|K|N|O|Q]'\r
7128         );\r
7129 \r
7130         tinymce.dom.Schema = function() {\r
7131                 var t = this, elements = transitional;\r
7132 \r
7133                 t.isValid = function(name, child_name) {\r
7134                         var element = elements[name];\r
7135 \r
7136                         return !!(element && (!child_name || element[child_name]));\r
7137                 };\r
7138         };\r
7139 })();\r
7140 (function(tinymce) {\r
7141         tinymce.dom.RangeUtils = function(dom) {\r
7142                 var INVISIBLE_CHAR = '\uFEFF';\r
7143 \r
7144                 this.walk = function(rng, callback) {\r
7145                         var startContainer = rng.startContainer,\r
7146                                 startOffset = rng.startOffset,\r
7147                                 endContainer = rng.endContainer,\r
7148                                 endOffset = rng.endOffset,\r
7149                                 ancestor, startPoint,\r
7150                                 endPoint, node, parent, siblings, nodes;\r
7151 \r
7152                         // Handle table cell selection the table plugin enables\r
7153                         // you to fake select table cells and perform formatting actions on them\r
7154                         nodes = dom.select('td.mceSelected,th.mceSelected');\r
7155                         if (nodes.length > 0) {\r
7156                                 tinymce.each(nodes, function(node) {\r
7157                                         callback([node]);\r
7158                                 });\r
7159 \r
7160                                 return;\r
7161                         }\r
7162 \r
7163                         function collectSiblings(node, name, end_node) {\r
7164                                 var siblings = [];\r
7165 \r
7166                                 for (; node && node != end_node; node = node[name])\r
7167                                         siblings.push(node);\r
7168 \r
7169                                 return siblings;\r
7170                         };\r
7171 \r
7172                         function findEndPoint(node, root) {\r
7173                                 do {\r
7174                                         if (node.parentNode == root)\r
7175                                                 return node;\r
7176 \r
7177                                         node = node.parentNode;\r
7178                                 } while(node);\r
7179                         };\r
7180 \r
7181                         function walkBoundary(start_node, end_node, next) {\r
7182                                 var siblingName = next ? 'nextSibling' : 'previousSibling';\r
7183 \r
7184                                 for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) {\r
7185                                         parent = node.parentNode;\r
7186                                         siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName);\r
7187 \r
7188                                         if (siblings.length) {\r
7189                                                 if (!next)\r
7190                                                         siblings.reverse();\r
7191 \r
7192                                                 callback(siblings);\r
7193                                         }\r
7194                                 }\r
7195                         };\r
7196 \r
7197                         // If index based start position then resolve it\r
7198                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes())\r
7199                                 startContainer = startContainer.childNodes[startOffset];\r
7200 \r
7201                         // If index based end position then resolve it\r
7202                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes())\r
7203                                 endContainer = endContainer.childNodes[Math.min(startOffset == endOffset ? endOffset : endOffset - 1, endContainer.childNodes.length - 1)];\r
7204 \r
7205                         // Find common ancestor and end points\r
7206                         ancestor = dom.findCommonAncestor(startContainer, endContainer);\r
7207 \r
7208                         // Same container\r
7209                         if (startContainer == endContainer)\r
7210                                 return callback([startContainer]);\r
7211 \r
7212                         // Process left side\r
7213                         for (node = startContainer; node; node = node.parentNode) {\r
7214                                 if (node == endContainer)\r
7215                                         return walkBoundary(startContainer, ancestor, true);\r
7216 \r
7217                                 if (node == ancestor)\r
7218                                         break;\r
7219                         }\r
7220 \r
7221                         // Process right side\r
7222                         for (node = endContainer; node; node = node.parentNode) {\r
7223                                 if (node == startContainer)\r
7224                                         return walkBoundary(endContainer, ancestor);\r
7225 \r
7226                                 if (node == ancestor)\r
7227                                         break;\r
7228                         }\r
7229 \r
7230                         // Find start/end point\r
7231                         startPoint = findEndPoint(startContainer, ancestor) || startContainer;\r
7232                         endPoint = findEndPoint(endContainer, ancestor) || endContainer;\r
7233 \r
7234                         // Walk left leaf\r
7235                         walkBoundary(startContainer, startPoint, true);\r
7236 \r
7237                         // Walk the middle from start to end point\r
7238                         siblings = collectSiblings(\r
7239                                 startPoint == startContainer ? startPoint : startPoint.nextSibling,\r
7240                                 'nextSibling',\r
7241                                 endPoint == endContainer ? endPoint.nextSibling : endPoint\r
7242                         );\r
7243 \r
7244                         if (siblings.length)\r
7245                                 callback(siblings);\r
7246 \r
7247                         // Walk right leaf\r
7248                         walkBoundary(endContainer, endPoint);\r
7249                 };\r
7250 \r
7251                 /*              this.split = function(rng) {\r
7252                         var startContainer = rng.startContainer,\r
7253                                 startOffset = rng.startOffset,\r
7254                                 endContainer = rng.endContainer,\r
7255                                 endOffset = rng.endOffset;\r
7256 \r
7257                         function splitText(node, offset) {\r
7258                                 if (offset == node.nodeValue.length)\r
7259                                         node.appendData(INVISIBLE_CHAR);\r
7260 \r
7261                                 node = node.splitText(offset);\r
7262 \r
7263                                 if (node.nodeValue === INVISIBLE_CHAR)\r
7264                                         node.nodeValue = '';\r
7265 \r
7266                                 return node;\r
7267                         };\r
7268 \r
7269                         // Handle single text node\r
7270                         if (startContainer == endContainer) {\r
7271                                 if (startContainer.nodeType == 3) {\r
7272                                         if (startOffset != 0)\r
7273                                                 startContainer = endContainer = splitText(startContainer, startOffset);\r
7274 \r
7275                                         if (endOffset - startOffset != startContainer.nodeValue.length)\r
7276                                                 splitText(startContainer, endOffset - startOffset);\r
7277                                 }\r
7278                         } else {\r
7279                                 // Split startContainer text node if needed\r
7280                                 if (startContainer.nodeType == 3 && startOffset != 0) {\r
7281                                         startContainer = splitText(startContainer, startOffset);\r
7282                                         startOffset = 0;\r
7283                                 }\r
7284 \r
7285                                 // Split endContainer text node if needed\r
7286                                 if (endContainer.nodeType == 3 && endOffset != endContainer.nodeValue.length) {\r
7287                                         endContainer = splitText(endContainer, endOffset).previousSibling;\r
7288                                         endOffset = endContainer.nodeValue.length;\r
7289                                 }\r
7290                         }\r
7291 \r
7292                         return {\r
7293                                 startContainer : startContainer,\r
7294                                 startOffset : startOffset,\r
7295                                 endContainer : endContainer,\r
7296                                 endOffset : endOffset\r
7297                         };\r
7298                 };\r
7299 */\r
7300         };\r
7301 \r
7302         tinymce.dom.RangeUtils.compareRanges = function(rng1, rng2) {\r
7303                 if (rng1 && rng2) {\r
7304                         // Compare native IE ranges\r
7305                         if (rng1.item || rng1.duplicate) {\r
7306                                 // Both are control ranges and the selected element matches\r
7307                                 if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))\r
7308                                         return true;\r
7309 \r
7310                                 // Both are text ranges and the range matches\r
7311                                 if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1))\r
7312                                         return true;\r
7313                         } else {\r
7314                                 // Compare w3c ranges\r
7315                                 return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;\r
7316                         }\r
7317                 }\r
7318 \r
7319                 return false;\r
7320         };\r
7321 })(tinymce);\r
7322 \r
7323 (function(tinymce) {\r
7324         // Shorten class names\r
7325         var DOM = tinymce.DOM, is = tinymce.is;\r
7326 \r
7327         tinymce.create('tinymce.ui.Control', {\r
7328                 Control : function(id, s) {\r
7329                         this.id = id;\r
7330                         this.settings = s = s || {};\r
7331                         this.rendered = false;\r
7332                         this.onRender = new tinymce.util.Dispatcher(this);\r
7333                         this.classPrefix = '';\r
7334                         this.scope = s.scope || this;\r
7335                         this.disabled = 0;\r
7336                         this.active = 0;\r
7337                 },\r
7338 \r
7339                 setDisabled : function(s) {\r
7340                         var e;\r
7341 \r
7342                         if (s != this.disabled) {\r
7343                                 e = DOM.get(this.id);\r
7344 \r
7345                                 // Add accessibility title for unavailable actions\r
7346                                 if (e && this.settings.unavailable_prefix) {\r
7347                                         if (s) {\r
7348                                                 this.prevTitle = e.title;\r
7349                                                 e.title = this.settings.unavailable_prefix + ": " + e.title;\r
7350                                         } else\r
7351                                                 e.title = this.prevTitle;\r
7352                                 }\r
7353 \r
7354                                 this.setState('Disabled', s);\r
7355                                 this.setState('Enabled', !s);\r
7356                                 this.disabled = s;\r
7357                         }\r
7358                 },\r
7359 \r
7360                 isDisabled : function() {\r
7361                         return this.disabled;\r
7362                 },\r
7363 \r
7364                 setActive : function(s) {\r
7365                         if (s != this.active) {\r
7366                                 this.setState('Active', s);\r
7367                                 this.active = s;\r
7368                         }\r
7369                 },\r
7370 \r
7371                 isActive : function() {\r
7372                         return this.active;\r
7373                 },\r
7374 \r
7375                 setState : function(c, s) {\r
7376                         var n = DOM.get(this.id);\r
7377 \r
7378                         c = this.classPrefix + c;\r
7379 \r
7380                         if (s)\r
7381                                 DOM.addClass(n, c);\r
7382                         else\r
7383                                 DOM.removeClass(n, c);\r
7384                 },\r
7385 \r
7386                 isRendered : function() {\r
7387                         return this.rendered;\r
7388                 },\r
7389 \r
7390                 renderHTML : function() {\r
7391                 },\r
7392 \r
7393                 renderTo : function(n) {\r
7394                         DOM.setHTML(n, this.renderHTML());\r
7395                 },\r
7396 \r
7397                 postRender : function() {\r
7398                         var t = this, b;\r
7399 \r
7400                         // Set pending states\r
7401                         if (is(t.disabled)) {\r
7402                                 b = t.disabled;\r
7403                                 t.disabled = -1;\r
7404                                 t.setDisabled(b);\r
7405                         }\r
7406 \r
7407                         if (is(t.active)) {\r
7408                                 b = t.active;\r
7409                                 t.active = -1;\r
7410                                 t.setActive(b);\r
7411                         }\r
7412                 },\r
7413 \r
7414                 remove : function() {\r
7415                         DOM.remove(this.id);\r
7416                         this.destroy();\r
7417                 },\r
7418 \r
7419                 destroy : function() {\r
7420                         tinymce.dom.Event.clear(this.id);\r
7421                 }\r
7422         });\r
7423 })(tinymce);\r
7424 tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {\r
7425         Container : function(id, s) {\r
7426                 this.parent(id, s);\r
7427 \r
7428                 this.controls = [];\r
7429 \r
7430                 this.lookup = {};\r
7431         },\r
7432 \r
7433         add : function(c) {\r
7434                 this.lookup[c.id] = c;\r
7435                 this.controls.push(c);\r
7436 \r
7437                 return c;\r
7438         },\r
7439 \r
7440         get : function(n) {\r
7441                 return this.lookup[n];\r
7442         }\r
7443 });\r
7444 \r
7445 \r
7446 tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {\r
7447         Separator : function(id, s) {\r
7448                 this.parent(id, s);\r
7449                 this.classPrefix = 'mceSeparator';\r
7450         },\r
7451 \r
7452         renderHTML : function() {\r
7453                 return tinymce.DOM.createHTML('span', {'class' : this.classPrefix});\r
7454         }\r
7455 });\r
7456 \r
7457 (function(tinymce) {\r
7458         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
7459 \r
7460         tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', {\r
7461                 MenuItem : function(id, s) {\r
7462                         this.parent(id, s);\r
7463                         this.classPrefix = 'mceMenuItem';\r
7464                 },\r
7465 \r
7466                 setSelected : function(s) {\r
7467                         this.setState('Selected', s);\r
7468                         this.selected = s;\r
7469                 },\r
7470 \r
7471                 isSelected : function() {\r
7472                         return this.selected;\r
7473                 },\r
7474 \r
7475                 postRender : function() {\r
7476                         var t = this;\r
7477                         \r
7478                         t.parent();\r
7479 \r
7480                         // Set pending state\r
7481                         if (is(t.selected))\r
7482                                 t.setSelected(t.selected);\r
7483                 }\r
7484         });\r
7485 })(tinymce);\r
7486 \r
7487 (function(tinymce) {\r
7488         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
7489 \r
7490         tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', {\r
7491                 Menu : function(id, s) {\r
7492                         var t = this;\r
7493 \r
7494                         t.parent(id, s);\r
7495                         t.items = {};\r
7496                         t.collapsed = false;\r
7497                         t.menuCount = 0;\r
7498                         t.onAddItem = new tinymce.util.Dispatcher(this);\r
7499                 },\r
7500 \r
7501                 expand : function(d) {\r
7502                         var t = this;\r
7503 \r
7504                         if (d) {\r
7505                                 walk(t, function(o) {\r
7506                                         if (o.expand)\r
7507                                                 o.expand();\r
7508                                 }, 'items', t);\r
7509                         }\r
7510 \r
7511                         t.collapsed = false;\r
7512                 },\r
7513 \r
7514                 collapse : function(d) {\r
7515                         var t = this;\r
7516 \r
7517                         if (d) {\r
7518                                 walk(t, function(o) {\r
7519                                         if (o.collapse)\r
7520                                                 o.collapse();\r
7521                                 }, 'items', t);\r
7522                         }\r
7523 \r
7524                         t.collapsed = true;\r
7525                 },\r
7526 \r
7527                 isCollapsed : function() {\r
7528                         return this.collapsed;\r
7529                 },\r
7530 \r
7531                 add : function(o) {\r
7532                         if (!o.settings)\r
7533                                 o = new tinymce.ui.MenuItem(o.id || DOM.uniqueId(), o);\r
7534 \r
7535                         this.onAddItem.dispatch(this, o);\r
7536 \r
7537                         return this.items[o.id] = o;\r
7538                 },\r
7539 \r
7540                 addSeparator : function() {\r
7541                         return this.add({separator : true});\r
7542                 },\r
7543 \r
7544                 addMenu : function(o) {\r
7545                         if (!o.collapse)\r
7546                                 o = this.createMenu(o);\r
7547 \r
7548                         this.menuCount++;\r
7549 \r
7550                         return this.add(o);\r
7551                 },\r
7552 \r
7553                 hasMenus : function() {\r
7554                         return this.menuCount !== 0;\r
7555                 },\r
7556 \r
7557                 remove : function(o) {\r
7558                         delete this.items[o.id];\r
7559                 },\r
7560 \r
7561                 removeAll : function() {\r
7562                         var t = this;\r
7563 \r
7564                         walk(t, function(o) {\r
7565                                 if (o.removeAll)\r
7566                                         o.removeAll();\r
7567                                 else\r
7568                                         o.remove();\r
7569 \r
7570                                 o.destroy();\r
7571                         }, 'items', t);\r
7572 \r
7573                         t.items = {};\r
7574                 },\r
7575 \r
7576                 createMenu : function(o) {\r
7577                         var m = new tinymce.ui.Menu(o.id || DOM.uniqueId(), o);\r
7578 \r
7579                         m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem);\r
7580 \r
7581                         return m;\r
7582                 }\r
7583         });\r
7584 })(tinymce);\r
7585 (function(tinymce) {\r
7586         var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element;\r
7587 \r
7588         tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', {\r
7589                 DropMenu : function(id, s) {\r
7590                         s = s || {};\r
7591                         s.container = s.container || DOM.doc.body;\r
7592                         s.offset_x = s.offset_x || 0;\r
7593                         s.offset_y = s.offset_y || 0;\r
7594                         s.vp_offset_x = s.vp_offset_x || 0;\r
7595                         s.vp_offset_y = s.vp_offset_y || 0;\r
7596 \r
7597                         if (is(s.icons) && !s.icons)\r
7598                                 s['class'] += ' mceNoIcons';\r
7599 \r
7600                         this.parent(id, s);\r
7601                         this.onShowMenu = new tinymce.util.Dispatcher(this);\r
7602                         this.onHideMenu = new tinymce.util.Dispatcher(this);\r
7603                         this.classPrefix = 'mceMenu';\r
7604                 },\r
7605 \r
7606                 createMenu : function(s) {\r
7607                         var t = this, cs = t.settings, m;\r
7608 \r
7609                         s.container = s.container || cs.container;\r
7610                         s.parent = t;\r
7611                         s.constrain = s.constrain || cs.constrain;\r
7612                         s['class'] = s['class'] || cs['class'];\r
7613                         s.vp_offset_x = s.vp_offset_x || cs.vp_offset_x;\r
7614                         s.vp_offset_y = s.vp_offset_y || cs.vp_offset_y;\r
7615                         m = new tinymce.ui.DropMenu(s.id || DOM.uniqueId(), s);\r
7616 \r
7617                         m.onAddItem.add(t.onAddItem.dispatch, t.onAddItem);\r
7618 \r
7619                         return m;\r
7620                 },\r
7621 \r
7622                 update : function() {\r
7623                         var t = this, s = t.settings, tb = DOM.get('menu_' + t.id + '_tbl'), co = DOM.get('menu_' + t.id + '_co'), tw, th;\r
7624 \r
7625                         tw = s.max_width ? Math.min(tb.clientWidth, s.max_width) : tb.clientWidth;\r
7626                         th = s.max_height ? Math.min(tb.clientHeight, s.max_height) : tb.clientHeight;\r
7627 \r
7628                         if (!DOM.boxModel)\r
7629                                 t.element.setStyles({width : tw + 2, height : th + 2});\r
7630                         else\r
7631                                 t.element.setStyles({width : tw, height : th});\r
7632 \r
7633                         if (s.max_width)\r
7634                                 DOM.setStyle(co, 'width', tw);\r
7635 \r
7636                         if (s.max_height) {\r
7637                                 DOM.setStyle(co, 'height', th);\r
7638 \r
7639                                 if (tb.clientHeight < s.max_height)\r
7640                                         DOM.setStyle(co, 'overflow', 'hidden');\r
7641                         }\r
7642                 },\r
7643 \r
7644                 showMenu : function(x, y, px) {\r
7645                         var t = this, s = t.settings, co, vp = DOM.getViewPort(), w, h, mx, my, ot = 2, dm, tb, cp = t.classPrefix;\r
7646 \r
7647                         t.collapse(1);\r
7648 \r
7649                         if (t.isMenuVisible)\r
7650                                 return;\r
7651 \r
7652                         if (!t.rendered) {\r
7653                                 co = DOM.add(t.settings.container, t.renderNode());\r
7654 \r
7655                                 each(t.items, function(o) {\r
7656                                         o.postRender();\r
7657                                 });\r
7658 \r
7659                                 t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
7660                         } else\r
7661                                 co = DOM.get('menu_' + t.id);\r
7662 \r
7663                         // Move layer out of sight unless it's Opera since it scrolls to top of page due to an bug\r
7664                         if (!tinymce.isOpera)\r
7665                                 DOM.setStyles(co, {left : -0xFFFF , top : -0xFFFF});\r
7666 \r
7667                         DOM.show(co);\r
7668                         t.update();\r
7669 \r
7670                         x += s.offset_x || 0;\r
7671                         y += s.offset_y || 0;\r
7672                         vp.w -= 4;\r
7673                         vp.h -= 4;\r
7674 \r
7675                         // Move inside viewport if not submenu\r
7676                         if (s.constrain) {\r
7677                                 w = co.clientWidth - ot;\r
7678                                 h = co.clientHeight - ot;\r
7679                                 mx = vp.x + vp.w;\r
7680                                 my = vp.y + vp.h;\r
7681 \r
7682                                 if ((x + s.vp_offset_x + w) > mx)\r
7683                                         x = px ? px - w : Math.max(0, (mx - s.vp_offset_x) - w);\r
7684 \r
7685                                 if ((y + s.vp_offset_y + h) > my)\r
7686                                         y = Math.max(0, (my - s.vp_offset_y) - h);\r
7687                         }\r
7688 \r
7689                         DOM.setStyles(co, {left : x , top : y});\r
7690                         t.element.update();\r
7691 \r
7692                         t.isMenuVisible = 1;\r
7693                         t.mouseClickFunc = Event.add(co, 'click', function(e) {\r
7694                                 var m;\r
7695 \r
7696                                 e = e.target;\r
7697 \r
7698                                 if (e && (e = DOM.getParent(e, 'tr')) && !DOM.hasClass(e, cp + 'ItemSub')) {\r
7699                                         m = t.items[e.id];\r
7700 \r
7701                                         if (m.isDisabled())\r
7702                                                 return;\r
7703 \r
7704                                         dm = t;\r
7705 \r
7706                                         while (dm) {\r
7707                                                 if (dm.hideMenu)\r
7708                                                         dm.hideMenu();\r
7709 \r
7710                                                 dm = dm.settings.parent;\r
7711                                         }\r
7712 \r
7713                                         if (m.settings.onclick)\r
7714                                                 m.settings.onclick(e);\r
7715 \r
7716                                         return Event.cancel(e); // Cancel to fix onbeforeunload problem\r
7717                                 }\r
7718                         });\r
7719 \r
7720                         if (t.hasMenus()) {\r
7721                                 t.mouseOverFunc = Event.add(co, 'mouseover', function(e) {\r
7722                                         var m, r, mi;\r
7723 \r
7724                                         e = e.target;\r
7725                                         if (e && (e = DOM.getParent(e, 'tr'))) {\r
7726                                                 m = t.items[e.id];\r
7727 \r
7728                                                 if (t.lastMenu)\r
7729                                                         t.lastMenu.collapse(1);\r
7730 \r
7731                                                 if (m.isDisabled())\r
7732                                                         return;\r
7733 \r
7734                                                 if (e && DOM.hasClass(e, cp + 'ItemSub')) {\r
7735                                                         //p = DOM.getPos(s.container);\r
7736                                                         r = DOM.getRect(e);\r
7737                                                         m.showMenu((r.x + r.w - ot), r.y - ot, r.x);\r
7738                                                         t.lastMenu = m;\r
7739                                                         DOM.addClass(DOM.get(m.id).firstChild, cp + 'ItemActive');\r
7740                                                 }\r
7741                                         }\r
7742                                 });\r
7743                         }\r
7744 \r
7745                         t.onShowMenu.dispatch(t);\r
7746 \r
7747                         if (s.keyboard_focus) {\r
7748                                 Event.add(co, 'keydown', t._keyHandler, t);\r
7749                                 DOM.select('a', 'menu_' + t.id)[0].focus(); // Select first link\r
7750                                 t._focusIdx = 0;\r
7751                         }\r
7752                 },\r
7753 \r
7754                 hideMenu : function(c) {\r
7755                         var t = this, co = DOM.get('menu_' + t.id), e;\r
7756 \r
7757                         if (!t.isMenuVisible)\r
7758                                 return;\r
7759 \r
7760                         Event.remove(co, 'mouseover', t.mouseOverFunc);\r
7761                         Event.remove(co, 'click', t.mouseClickFunc);\r
7762                         Event.remove(co, 'keydown', t._keyHandler);\r
7763                         DOM.hide(co);\r
7764                         t.isMenuVisible = 0;\r
7765 \r
7766                         if (!c)\r
7767                                 t.collapse(1);\r
7768 \r
7769                         if (t.element)\r
7770                                 t.element.hide();\r
7771 \r
7772                         if (e = DOM.get(t.id))\r
7773                                 DOM.removeClass(e.firstChild, t.classPrefix + 'ItemActive');\r
7774 \r
7775                         t.onHideMenu.dispatch(t);\r
7776                 },\r
7777 \r
7778                 add : function(o) {\r
7779                         var t = this, co;\r
7780 \r
7781                         o = t.parent(o);\r
7782 \r
7783                         if (t.isRendered && (co = DOM.get('menu_' + t.id)))\r
7784                                 t._add(DOM.select('tbody', co)[0], o);\r
7785 \r
7786                         return o;\r
7787                 },\r
7788 \r
7789                 collapse : function(d) {\r
7790                         this.parent(d);\r
7791                         this.hideMenu(1);\r
7792                 },\r
7793 \r
7794                 remove : function(o) {\r
7795                         DOM.remove(o.id);\r
7796                         this.destroy();\r
7797 \r
7798                         return this.parent(o);\r
7799                 },\r
7800 \r
7801                 destroy : function() {\r
7802                         var t = this, co = DOM.get('menu_' + t.id);\r
7803 \r
7804                         Event.remove(co, 'mouseover', t.mouseOverFunc);\r
7805                         Event.remove(co, 'click', t.mouseClickFunc);\r
7806 \r
7807                         if (t.element)\r
7808                                 t.element.remove();\r
7809 \r
7810                         DOM.remove(co);\r
7811                 },\r
7812 \r
7813                 renderNode : function() {\r
7814                         var t = this, s = t.settings, n, tb, co, w;\r
7815 \r
7816                         w = DOM.create('div', {id : 'menu_' + t.id, 'class' : s['class'], 'style' : 'position:absolute;left:0;top:0;z-index:200000'});\r
7817                         co = DOM.add(w, 'div', {id : 'menu_' + t.id + '_co', 'class' : t.classPrefix + (s['class'] ? ' ' + s['class'] : '')});\r
7818                         t.element = new Element('menu_' + t.id, {blocker : 1, container : s.container});\r
7819 \r
7820                         if (s.menu_line)\r
7821                                 DOM.add(co, 'span', {'class' : t.classPrefix + 'Line'});\r
7822 \r
7823 //                      n = DOM.add(co, 'div', {id : 'menu_' + t.id + '_co', 'class' : 'mceMenuContainer'});\r
7824                         n = DOM.add(co, 'table', {id : 'menu_' + t.id + '_tbl', border : 0, cellPadding : 0, cellSpacing : 0});\r
7825                         tb = DOM.add(n, 'tbody');\r
7826 \r
7827                         each(t.items, function(o) {\r
7828                                 t._add(tb, o);\r
7829                         });\r
7830 \r
7831                         t.rendered = true;\r
7832 \r
7833                         return w;\r
7834                 },\r
7835 \r
7836                 // Internal functions\r
7837 \r
7838                 _keyHandler : function(e) {\r
7839                         var t = this, kc = e.keyCode;\r
7840 \r
7841                         function focus(d) {\r
7842                                 var i = t._focusIdx + d, e = DOM.select('a', 'menu_' + t.id)[i];\r
7843 \r
7844                                 if (e) {\r
7845                                         t._focusIdx = i;\r
7846                                         e.focus();\r
7847                                 }\r
7848                         };\r
7849 \r
7850                         switch (kc) {\r
7851                                 case 38:\r
7852                                         focus(-1); // Select first link\r
7853                                         return;\r
7854 \r
7855                                 case 40:\r
7856                                         focus(1);\r
7857                                         return;\r
7858 \r
7859                                 case 13:\r
7860                                         return;\r
7861 \r
7862                                 case 27:\r
7863                                         return this.hideMenu();\r
7864                         }\r
7865                 },\r
7866 \r
7867                 _add : function(tb, o) {\r
7868                         var n, s = o.settings, a, ro, it, cp = this.classPrefix, ic;\r
7869 \r
7870                         if (s.separator) {\r
7871                                 ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'ItemSeparator'});\r
7872                                 DOM.add(ro, 'td', {'class' : cp + 'ItemSeparator'});\r
7873 \r
7874                                 if (n = ro.previousSibling)\r
7875                                         DOM.addClass(n, 'mceLast');\r
7876 \r
7877                                 return;\r
7878                         }\r
7879 \r
7880                         n = ro = DOM.add(tb, 'tr', {id : o.id, 'class' : cp + 'Item ' + cp + 'ItemEnabled'});\r
7881                         n = it = DOM.add(n, 'td');\r
7882                         n = a = DOM.add(n, 'a', {href : 'javascript:;', onclick : "return false;", onmousedown : 'return false;'});\r
7883 \r
7884                         DOM.addClass(it, s['class']);\r
7885 //                      n = DOM.add(n, 'span', {'class' : 'item'});\r
7886 \r
7887                         ic = DOM.add(n, 'span', {'class' : 'mceIcon' + (s.icon ? ' mce_' + s.icon : '')});\r
7888 \r
7889                         if (s.icon_src)\r
7890                                 DOM.add(ic, 'img', {src : s.icon_src});\r
7891 \r
7892                         n = DOM.add(n, s.element || 'span', {'class' : 'mceText', title : o.settings.title}, o.settings.title);\r
7893 \r
7894                         if (o.settings.style)\r
7895                                 DOM.setAttrib(n, 'style', o.settings.style);\r
7896 \r
7897                         if (tb.childNodes.length == 1)\r
7898                                 DOM.addClass(ro, 'mceFirst');\r
7899 \r
7900                         if ((n = ro.previousSibling) && DOM.hasClass(n, cp + 'ItemSeparator'))\r
7901                                 DOM.addClass(ro, 'mceFirst');\r
7902 \r
7903                         if (o.collapse)\r
7904                                 DOM.addClass(ro, cp + 'ItemSub');\r
7905 \r
7906                         if (n = ro.previousSibling)\r
7907                                 DOM.removeClass(n, 'mceLast');\r
7908 \r
7909                         DOM.addClass(ro, 'mceLast');\r
7910                 }\r
7911         });\r
7912 })(tinymce);\r
7913 (function(tinymce) {\r
7914         var DOM = tinymce.DOM;\r
7915 \r
7916         tinymce.create('tinymce.ui.Button:tinymce.ui.Control', {\r
7917                 Button : function(id, s) {\r
7918                         this.parent(id, s);\r
7919                         this.classPrefix = 'mceButton';\r
7920                 },\r
7921 \r
7922                 renderHTML : function() {\r
7923                         var cp = this.classPrefix, s = this.settings, h, l;\r
7924 \r
7925                         l = DOM.encode(s.label || '');\r
7926                         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
7927 \r
7928                         if (s.image)\r
7929                                 h += '<img class="mceIcon" src="' + s.image + '" />' + l + '</a>';\r
7930                         else\r
7931                                 h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '') + '</a>';\r
7932 \r
7933                         return h;\r
7934                 },\r
7935 \r
7936                 postRender : function() {\r
7937                         var t = this, s = t.settings;\r
7938 \r
7939                         tinymce.dom.Event.add(t.id, 'click', function(e) {\r
7940                                 if (!t.isDisabled())\r
7941                                         return s.onclick.call(s.scope, e);\r
7942                         });\r
7943                 }\r
7944         });\r
7945 })(tinymce);\r
7946 \r
7947 (function(tinymce) {\r
7948         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
7949 \r
7950         tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {\r
7951                 ListBox : function(id, s) {\r
7952                         var t = this;\r
7953 \r
7954                         t.parent(id, s);\r
7955 \r
7956                         t.items = [];\r
7957 \r
7958                         t.onChange = new Dispatcher(t);\r
7959 \r
7960                         t.onPostRender = new Dispatcher(t);\r
7961 \r
7962                         t.onAdd = new Dispatcher(t);\r
7963 \r
7964                         t.onRenderMenu = new tinymce.util.Dispatcher(this);\r
7965 \r
7966                         t.classPrefix = 'mceListBox';\r
7967                 },\r
7968 \r
7969                 select : function(va) {\r
7970                         var t = this, fv, f;\r
7971 \r
7972                         if (va == undefined)\r
7973                                 return t.selectByIndex(-1);\r
7974 \r
7975                         // Is string or number make function selector\r
7976                         if (va && va.call)\r
7977                                 f = va;\r
7978                         else {\r
7979                                 f = function(v) {\r
7980                                         return v == va;\r
7981                                 };\r
7982                         }\r
7983 \r
7984                         // Do we need to do something?\r
7985                         if (va != t.selectedValue) {\r
7986                                 // Find item\r
7987                                 each(t.items, function(o, i) {\r
7988                                         if (f(o.value)) {\r
7989                                                 fv = 1;\r
7990                                                 t.selectByIndex(i);\r
7991                                                 return false;\r
7992                                         }\r
7993                                 });\r
7994 \r
7995                                 if (!fv)\r
7996                                         t.selectByIndex(-1);\r
7997                         }\r
7998                 },\r
7999 \r
8000                 selectByIndex : function(idx) {\r
8001                         var t = this, e, o;\r
8002 \r
8003                         if (idx != t.selectedIndex) {\r
8004                                 e = DOM.get(t.id + '_text');\r
8005                                 o = t.items[idx];\r
8006 \r
8007                                 if (o) {\r
8008                                         t.selectedValue = o.value;\r
8009                                         t.selectedIndex = idx;\r
8010                                         DOM.setHTML(e, DOM.encode(o.title));\r
8011                                         DOM.removeClass(e, 'mceTitle');\r
8012                                 } else {\r
8013                                         DOM.setHTML(e, DOM.encode(t.settings.title));\r
8014                                         DOM.addClass(e, 'mceTitle');\r
8015                                         t.selectedValue = t.selectedIndex = null;\r
8016                                 }\r
8017 \r
8018                                 e = 0;\r
8019                         }\r
8020                 },\r
8021 \r
8022                 add : function(n, v, o) {\r
8023                         var t = this;\r
8024 \r
8025                         o = o || {};\r
8026                         o = tinymce.extend(o, {\r
8027                                 title : n,\r
8028                                 value : v\r
8029                         });\r
8030 \r
8031                         t.items.push(o);\r
8032                         t.onAdd.dispatch(t, o);\r
8033                 },\r
8034 \r
8035                 getLength : function() {\r
8036                         return this.items.length;\r
8037                 },\r
8038 \r
8039                 renderHTML : function() {\r
8040                         var h = '', t = this, s = t.settings, cp = t.classPrefix;\r
8041 \r
8042                         h = '<table id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>';\r
8043                         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
8044                         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
8045                         h += '</tr></tbody></table>';\r
8046 \r
8047                         return h;\r
8048                 },\r
8049 \r
8050                 showMenu : function() {\r
8051                         var t = this, p1, p2, e = DOM.get(this.id), m;\r
8052 \r
8053                         if (t.isDisabled() || t.items.length == 0)\r
8054                                 return;\r
8055 \r
8056                         if (t.menu && t.menu.isMenuVisible)\r
8057                                 return t.hideMenu();\r
8058 \r
8059                         if (!t.isMenuRendered) {\r
8060                                 t.renderMenu();\r
8061                                 t.isMenuRendered = true;\r
8062                         }\r
8063 \r
8064                         p1 = DOM.getPos(this.settings.menu_container);\r
8065                         p2 = DOM.getPos(e);\r
8066 \r
8067                         m = t.menu;\r
8068                         m.settings.offset_x = p2.x;\r
8069                         m.settings.offset_y = p2.y;\r
8070                         m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus\r
8071 \r
8072                         // Select in menu\r
8073                         if (t.oldID)\r
8074                                 m.items[t.oldID].setSelected(0);\r
8075 \r
8076                         each(t.items, function(o) {\r
8077                                 if (o.value === t.selectedValue) {\r
8078                                         m.items[o.id].setSelected(1);\r
8079                                         t.oldID = o.id;\r
8080                                 }\r
8081                         });\r
8082 \r
8083                         m.showMenu(0, e.clientHeight);\r
8084 \r
8085                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
8086                         DOM.addClass(t.id, t.classPrefix + 'Selected');\r
8087 \r
8088                         //DOM.get(t.id + '_text').focus();\r
8089                 },\r
8090 \r
8091                 hideMenu : function(e) {\r
8092                         var t = this;\r
8093 \r
8094                         if (t.menu && t.menu.isMenuVisible) {\r
8095                                 // Prevent double toogles by canceling the mouse click event to the button\r
8096                                 if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open'))\r
8097                                         return;\r
8098 \r
8099                                 if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
8100                                         DOM.removeClass(t.id, t.classPrefix + 'Selected');\r
8101                                         Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
8102                                         t.menu.hideMenu();\r
8103                                 }\r
8104                         }\r
8105                 },\r
8106 \r
8107                 renderMenu : function() {\r
8108                         var t = this, m;\r
8109 \r
8110                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
8111                                 menu_line : 1,\r
8112                                 'class' : t.classPrefix + 'Menu mceNoIcons',\r
8113                                 max_width : 150,\r
8114                                 max_height : 150\r
8115                         });\r
8116 \r
8117                         m.onHideMenu.add(t.hideMenu, t);\r
8118 \r
8119                         m.add({\r
8120                                 title : t.settings.title,\r
8121                                 'class' : 'mceMenuItemTitle',\r
8122                                 onclick : function() {\r
8123                                         if (t.settings.onselect('') !== false)\r
8124                                                 t.select(''); // Must be runned after\r
8125                                 }\r
8126                         });\r
8127 \r
8128                         each(t.items, function(o) {\r
8129                                 // No value then treat it as a title\r
8130                                 if (o.value === undefined) {\r
8131                                         m.add({\r
8132                                                 title : o.title,\r
8133                                                 'class' : 'mceMenuItemTitle',\r
8134                                                 onclick : function() {\r
8135                                                         if (t.settings.onselect('') !== false)\r
8136                                                                 t.select(''); // Must be runned after\r
8137                                                 }\r
8138                                         });\r
8139                                 } else {\r
8140                                         o.id = DOM.uniqueId();\r
8141                                         o.onclick = function() {\r
8142                                                 if (t.settings.onselect(o.value) !== false)\r
8143                                                         t.select(o.value); // Must be runned after\r
8144                                         };\r
8145 \r
8146                                         m.add(o);\r
8147                                 }\r
8148                         });\r
8149 \r
8150                         t.onRenderMenu.dispatch(t, m);\r
8151                         t.menu = m;\r
8152                 },\r
8153 \r
8154                 postRender : function() {\r
8155                         var t = this, cp = t.classPrefix;\r
8156 \r
8157                         Event.add(t.id, 'click', t.showMenu, t);\r
8158                         Event.add(t.id + '_text', 'focus', function() {\r
8159                                 if (!t._focused) {\r
8160                                         t.keyDownHandler = Event.add(t.id + '_text', 'keydown', function(e) {\r
8161                                                 var idx = -1, v, kc = e.keyCode;\r
8162 \r
8163                                                 // Find current index\r
8164                                                 each(t.items, function(v, i) {\r
8165                                                         if (t.selectedValue == v.value)\r
8166                                                                 idx = i;\r
8167                                                 });\r
8168 \r
8169                                                 // Move up/down\r
8170                                                 if (kc == 38)\r
8171                                                         v = t.items[idx - 1];\r
8172                                                 else if (kc == 40)\r
8173                                                         v = t.items[idx + 1];\r
8174                                                 else if (kc == 13) {\r
8175                                                         // Fake select on enter\r
8176                                                         v = t.selectedValue;\r
8177                                                         t.selectedValue = null; // Needs to be null to fake change\r
8178                                                         t.settings.onselect(v);\r
8179                                                         return Event.cancel(e);\r
8180                                                 }\r
8181 \r
8182                                                 if (v) {\r
8183                                                         t.hideMenu();\r
8184                                                         t.select(v.value);\r
8185                                                 }\r
8186                                         });\r
8187                                 }\r
8188 \r
8189                                 t._focused = 1;\r
8190                         });\r
8191                         Event.add(t.id + '_text', 'blur', function() {Event.remove(t.id + '_text', 'keydown', t.keyDownHandler); t._focused = 0;});\r
8192 \r
8193                         // Old IE doesn't have hover on all elements\r
8194                         if (tinymce.isIE6 || !DOM.boxModel) {\r
8195                                 Event.add(t.id, 'mouseover', function() {\r
8196                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
8197                                                 DOM.addClass(t.id, cp + 'Hover');\r
8198                                 });\r
8199 \r
8200                                 Event.add(t.id, 'mouseout', function() {\r
8201                                         if (!DOM.hasClass(t.id, cp + 'Disabled'))\r
8202                                                 DOM.removeClass(t.id, cp + 'Hover');\r
8203                                 });\r
8204                         }\r
8205 \r
8206                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
8207                 },\r
8208 \r
8209                 destroy : function() {\r
8210                         this.parent();\r
8211 \r
8212                         Event.clear(this.id + '_text');\r
8213                         Event.clear(this.id + '_open');\r
8214                 }\r
8215         });\r
8216 })(tinymce);\r
8217 (function(tinymce) {\r
8218         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
8219 \r
8220         tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {\r
8221                 NativeListBox : function(id, s) {\r
8222                         this.parent(id, s);\r
8223                         this.classPrefix = 'mceNativeListBox';\r
8224                 },\r
8225 \r
8226                 setDisabled : function(s) {\r
8227                         DOM.get(this.id).disabled = s;\r
8228                 },\r
8229 \r
8230                 isDisabled : function() {\r
8231                         return DOM.get(this.id).disabled;\r
8232                 },\r
8233 \r
8234                 select : function(va) {\r
8235                         var t = this, fv, f;\r
8236 \r
8237                         if (va == undefined)\r
8238                                 return t.selectByIndex(-1);\r
8239 \r
8240                         // Is string or number make function selector\r
8241                         if (va && va.call)\r
8242                                 f = va;\r
8243                         else {\r
8244                                 f = function(v) {\r
8245                                         return v == va;\r
8246                                 };\r
8247                         }\r
8248 \r
8249                         // Do we need to do something?\r
8250                         if (va != t.selectedValue) {\r
8251                                 // Find item\r
8252                                 each(t.items, function(o, i) {\r
8253                                         if (f(o.value)) {\r
8254                                                 fv = 1;\r
8255                                                 t.selectByIndex(i);\r
8256                                                 return false;\r
8257                                         }\r
8258                                 });\r
8259 \r
8260                                 if (!fv)\r
8261                                         t.selectByIndex(-1);\r
8262                         }\r
8263                 },\r
8264 \r
8265                 selectByIndex : function(idx) {\r
8266                         DOM.get(this.id).selectedIndex = idx + 1;\r
8267                         this.selectedValue = this.items[idx] ? this.items[idx].value : null;\r
8268                 },\r
8269 \r
8270                 add : function(n, v, a) {\r
8271                         var o, t = this;\r
8272 \r
8273                         a = a || {};\r
8274                         a.value = v;\r
8275 \r
8276                         if (t.isRendered())\r
8277                                 DOM.add(DOM.get(this.id), 'option', a, n);\r
8278 \r
8279                         o = {\r
8280                                 title : n,\r
8281                                 value : v,\r
8282                                 attribs : a\r
8283                         };\r
8284 \r
8285                         t.items.push(o);\r
8286                         t.onAdd.dispatch(t, o);\r
8287                 },\r
8288 \r
8289                 getLength : function() {\r
8290                         return this.items.length;\r
8291                 },\r
8292 \r
8293                 renderHTML : function() {\r
8294                         var h, t = this;\r
8295 \r
8296                         h = DOM.createHTML('option', {value : ''}, '-- ' + t.settings.title + ' --');\r
8297 \r
8298                         each(t.items, function(it) {\r
8299                                 h += DOM.createHTML('option', {value : it.value}, it.title);\r
8300                         });\r
8301 \r
8302                         h = DOM.createHTML('select', {id : t.id, 'class' : 'mceNativeListBox'}, h);\r
8303 \r
8304                         return h;\r
8305                 },\r
8306 \r
8307                 postRender : function() {\r
8308                         var t = this, ch;\r
8309 \r
8310                         t.rendered = true;\r
8311 \r
8312                         function onChange(e) {\r
8313                                 var v = t.items[e.target.selectedIndex - 1];\r
8314 \r
8315                                 if (v && (v = v.value)) {\r
8316                                         t.onChange.dispatch(t, v);\r
8317 \r
8318                                         if (t.settings.onselect)\r
8319                                                 t.settings.onselect(v);\r
8320                                 }\r
8321                         };\r
8322 \r
8323                         Event.add(t.id, 'change', onChange);\r
8324 \r
8325                         // Accessibility keyhandler\r
8326                         Event.add(t.id, 'keydown', function(e) {\r
8327                                 var bf;\r
8328 \r
8329                                 Event.remove(t.id, 'change', ch);\r
8330 \r
8331                                 bf = Event.add(t.id, 'blur', function() {\r
8332                                         Event.add(t.id, 'change', onChange);\r
8333                                         Event.remove(t.id, 'blur', bf);\r
8334                                 });\r
8335 \r
8336                                 if (e.keyCode == 13 || e.keyCode == 32) {\r
8337                                         onChange(e);\r
8338                                         return Event.cancel(e);\r
8339                                 }\r
8340                         });\r
8341 \r
8342                         t.onPostRender.dispatch(t, DOM.get(t.id));\r
8343                 }\r
8344         });\r
8345 })(tinymce);\r
8346 (function(tinymce) {\r
8347         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
8348 \r
8349         tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', {\r
8350                 MenuButton : function(id, s) {\r
8351                         this.parent(id, s);\r
8352 \r
8353                         this.onRenderMenu = new tinymce.util.Dispatcher(this);\r
8354 \r
8355                         s.menu_container = s.menu_container || DOM.doc.body;\r
8356                 },\r
8357 \r
8358                 showMenu : function() {\r
8359                         var t = this, p1, p2, e = DOM.get(t.id), m;\r
8360 \r
8361                         if (t.isDisabled())\r
8362                                 return;\r
8363 \r
8364                         if (!t.isMenuRendered) {\r
8365                                 t.renderMenu();\r
8366                                 t.isMenuRendered = true;\r
8367                         }\r
8368 \r
8369                         if (t.isMenuVisible)\r
8370                                 return t.hideMenu();\r
8371 \r
8372                         p1 = DOM.getPos(t.settings.menu_container);\r
8373                         p2 = DOM.getPos(e);\r
8374 \r
8375                         m = t.menu;\r
8376                         m.settings.offset_x = p2.x;\r
8377                         m.settings.offset_y = p2.y;\r
8378                         m.settings.vp_offset_x = p2.x;\r
8379                         m.settings.vp_offset_y = p2.y;\r
8380                         m.settings.keyboard_focus = t._focused;\r
8381                         m.showMenu(0, e.clientHeight);\r
8382 \r
8383                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
8384                         t.setState('Selected', 1);\r
8385 \r
8386                         t.isMenuVisible = 1;\r
8387                 },\r
8388 \r
8389                 renderMenu : function() {\r
8390                         var t = this, m;\r
8391 \r
8392                         m = t.settings.control_manager.createDropMenu(t.id + '_menu', {\r
8393                                 menu_line : 1,\r
8394                                 'class' : this.classPrefix + 'Menu',\r
8395                                 icons : t.settings.icons\r
8396                         });\r
8397 \r
8398                         m.onHideMenu.add(t.hideMenu, t);\r
8399 \r
8400                         t.onRenderMenu.dispatch(t, m);\r
8401                         t.menu = m;\r
8402                 },\r
8403 \r
8404                 hideMenu : function(e) {\r
8405                         var t = this;\r
8406 \r
8407                         // Prevent double toogles by canceling the mouse click event to the button\r
8408                         if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id || e.id === t.id + '_open';}))\r
8409                                 return;\r
8410 \r
8411                         if (!e || !DOM.getParent(e.target, '.mceMenu')) {\r
8412                                 t.setState('Selected', 0);\r
8413                                 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
8414                                 if (t.menu)\r
8415                                         t.menu.hideMenu();\r
8416                         }\r
8417 \r
8418                         t.isMenuVisible = 0;\r
8419                 },\r
8420 \r
8421                 postRender : function() {\r
8422                         var t = this, s = t.settings;\r
8423 \r
8424                         Event.add(t.id, 'click', function() {\r
8425                                 if (!t.isDisabled()) {\r
8426                                         if (s.onclick)\r
8427                                                 s.onclick(t.value);\r
8428 \r
8429                                         t.showMenu();\r
8430                                 }\r
8431                         });\r
8432                 }\r
8433         });\r
8434 })(tinymce);\r
8435 \r
8436 (function(tinymce) {\r
8437         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
8438 \r
8439         tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', {\r
8440                 SplitButton : function(id, s) {\r
8441                         this.parent(id, s);\r
8442                         this.classPrefix = 'mceSplitButton';\r
8443                 },\r
8444 \r
8445                 renderHTML : function() {\r
8446                         var h, t = this, s = t.settings, h1;\r
8447 \r
8448                         h = '<tbody><tr>';\r
8449 \r
8450                         if (s.image)\r
8451                                 h1 = DOM.createHTML('img ', {src : s.image, 'class' : 'mceAction ' + s['class']});\r
8452                         else\r
8453                                 h1 = DOM.createHTML('span', {'class' : 'mceAction ' + s['class']}, '');\r
8454 \r
8455                         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
8456         \r
8457                         h1 = DOM.createHTML('span', {'class' : 'mceOpen ' + s['class']});\r
8458                         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
8459 \r
8460                         h += '</tr></tbody>';\r
8461 \r
8462                         return DOM.createHTML('table', {id : t.id, 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', onmousedown : 'return false;', title : s.title}, h);\r
8463                 },\r
8464 \r
8465                 postRender : function() {\r
8466                         var t = this, s = t.settings;\r
8467 \r
8468                         if (s.onclick) {\r
8469                                 Event.add(t.id + '_action', 'click', function() {\r
8470                                         if (!t.isDisabled())\r
8471                                                 s.onclick(t.value);\r
8472                                 });\r
8473                         }\r
8474 \r
8475                         Event.add(t.id + '_open', 'click', t.showMenu, t);\r
8476                         Event.add(t.id + '_open', 'focus', function() {t._focused = 1;});\r
8477                         Event.add(t.id + '_open', 'blur', function() {t._focused = 0;});\r
8478 \r
8479                         // Old IE doesn't have hover on all elements\r
8480                         if (tinymce.isIE6 || !DOM.boxModel) {\r
8481                                 Event.add(t.id, 'mouseover', function() {\r
8482                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
8483                                                 DOM.addClass(t.id, 'mceSplitButtonHover');\r
8484                                 });\r
8485 \r
8486                                 Event.add(t.id, 'mouseout', function() {\r
8487                                         if (!DOM.hasClass(t.id, 'mceSplitButtonDisabled'))\r
8488                                                 DOM.removeClass(t.id, 'mceSplitButtonHover');\r
8489                                 });\r
8490                         }\r
8491                 },\r
8492 \r
8493                 destroy : function() {\r
8494                         this.parent();\r
8495 \r
8496                         Event.clear(this.id + '_action');\r
8497                         Event.clear(this.id + '_open');\r
8498                 }\r
8499         });\r
8500 })(tinymce);\r
8501 \r
8502 (function(tinymce) {\r
8503         var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each;\r
8504 \r
8505         tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', {\r
8506                 ColorSplitButton : function(id, s) {\r
8507                         var t = this;\r
8508 \r
8509                         t.parent(id, s);\r
8510 \r
8511                         t.settings = s = tinymce.extend({\r
8512                                 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
8513                                 grid_width : 8,\r
8514                                 default_color : '#888888'\r
8515                         }, t.settings);\r
8516 \r
8517                         t.onShowMenu = new tinymce.util.Dispatcher(t);\r
8518 \r
8519                         t.onHideMenu = new tinymce.util.Dispatcher(t);\r
8520 \r
8521                         t.value = s.default_color;\r
8522                 },\r
8523 \r
8524                 showMenu : function() {\r
8525                         var t = this, r, p, e, p2;\r
8526 \r
8527                         if (t.isDisabled())\r
8528                                 return;\r
8529 \r
8530                         if (!t.isMenuRendered) {\r
8531                                 t.renderMenu();\r
8532                                 t.isMenuRendered = true;\r
8533                         }\r
8534 \r
8535                         if (t.isMenuVisible)\r
8536                                 return t.hideMenu();\r
8537 \r
8538                         e = DOM.get(t.id);\r
8539                         DOM.show(t.id + '_menu');\r
8540                         DOM.addClass(e, 'mceSplitButtonSelected');\r
8541                         p2 = DOM.getPos(e);\r
8542                         DOM.setStyles(t.id + '_menu', {\r
8543                                 left : p2.x,\r
8544                                 top : p2.y + e.clientHeight,\r
8545                                 zIndex : 200000\r
8546                         });\r
8547                         e = 0;\r
8548 \r
8549                         Event.add(DOM.doc, 'mousedown', t.hideMenu, t);\r
8550                         t.onShowMenu.dispatch(t);\r
8551 \r
8552                         if (t._focused) {\r
8553                                 t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) {\r
8554                                         if (e.keyCode == 27)\r
8555                                                 t.hideMenu();\r
8556                                 });\r
8557 \r
8558                                 DOM.select('a', t.id + '_menu')[0].focus(); // Select first link\r
8559                         }\r
8560 \r
8561                         t.isMenuVisible = 1;\r
8562                 },\r
8563 \r
8564                 hideMenu : function(e) {\r
8565                         var t = this;\r
8566 \r
8567                         // Prevent double toogles by canceling the mouse click event to the button\r
8568                         if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';}))\r
8569                                 return;\r
8570 \r
8571                         if (!e || !DOM.getParent(e.target, '.mceSplitButtonMenu')) {\r
8572                                 DOM.removeClass(t.id, 'mceSplitButtonSelected');\r
8573                                 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);\r
8574                                 Event.remove(t.id + '_menu', 'keydown', t._keyHandler);\r
8575                                 DOM.hide(t.id + '_menu');\r
8576                         }\r
8577 \r
8578                         t.onHideMenu.dispatch(t);\r
8579 \r
8580                         t.isMenuVisible = 0;\r
8581                 },\r
8582 \r
8583                 renderMenu : function() {\r
8584                         var t = this, m, i = 0, s = t.settings, n, tb, tr, w;\r
8585 \r
8586                         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
8587                         m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'});\r
8588                         DOM.add(m, 'span', {'class' : 'mceMenuLine'});\r
8589 \r
8590                         n = DOM.add(m, 'table', {'class' : 'mceColorSplitMenu'});\r
8591                         tb = DOM.add(n, 'tbody');\r
8592 \r
8593                         // Generate color grid\r
8594                         i = 0;\r
8595                         each(is(s.colors, 'array') ? s.colors : s.colors.split(','), function(c) {\r
8596                                 c = c.replace(/^#/, '');\r
8597 \r
8598                                 if (!i--) {\r
8599                                         tr = DOM.add(tb, 'tr');\r
8600                                         i = s.grid_width - 1;\r
8601                                 }\r
8602 \r
8603                                 n = DOM.add(tr, 'td');\r
8604 \r
8605                                 n = DOM.add(n, 'a', {\r
8606                                         href : 'javascript:;',\r
8607                                         style : {\r
8608                                                 backgroundColor : '#' + c\r
8609                                         },\r
8610                                         _mce_color : '#' + c\r
8611                                 });\r
8612                         });\r
8613 \r
8614                         if (s.more_colors_func) {\r
8615                                 n = DOM.add(tb, 'tr');\r
8616                                 n = DOM.add(n, 'td', {colspan : s.grid_width, 'class' : 'mceMoreColors'});\r
8617                                 n = DOM.add(n, 'a', {id : t.id + '_more', href : 'javascript:;', onclick : 'return false;', 'class' : 'mceMoreColors'}, s.more_colors_title);\r
8618 \r
8619                                 Event.add(n, 'click', function(e) {\r
8620                                         s.more_colors_func.call(s.more_colors_scope || this);\r
8621                                         return Event.cancel(e); // Cancel to fix onbeforeunload problem\r
8622                                 });\r
8623                         }\r
8624 \r
8625                         DOM.addClass(m, 'mceColorSplitMenu');\r
8626 \r
8627                         Event.add(t.id + '_menu', 'click', function(e) {\r
8628                                 var c;\r
8629 \r
8630                                 e = e.target;\r
8631 \r
8632                                 if (e.nodeName == 'A' && (c = e.getAttribute('_mce_color')))\r
8633                                         t.setColor(c);\r
8634 \r
8635                                 return Event.cancel(e); // Prevent IE auto save warning\r
8636                         });\r
8637 \r
8638                         return w;\r
8639                 },\r
8640 \r
8641                 setColor : function(c) {\r
8642                         var t = this;\r
8643 \r
8644                         DOM.setStyle(t.id + '_preview', 'backgroundColor', c);\r
8645 \r
8646                         t.value = c;\r
8647                         t.hideMenu();\r
8648                         t.settings.onselect(c);\r
8649                 },\r
8650 \r
8651                 postRender : function() {\r
8652                         var t = this, id = t.id;\r
8653 \r
8654                         t.parent();\r
8655                         DOM.add(id + '_action', 'div', {id : id + '_preview', 'class' : 'mceColorPreview'});\r
8656                         DOM.setStyle(t.id + '_preview', 'backgroundColor', t.value);\r
8657                 },\r
8658 \r
8659                 destroy : function() {\r
8660                         this.parent();\r
8661 \r
8662                         Event.clear(this.id + '_menu');\r
8663                         Event.clear(this.id + '_more');\r
8664                         DOM.remove(this.id + '_menu');\r
8665                 }\r
8666         });\r
8667 })(tinymce);\r
8668 \r
8669 tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {\r
8670         renderHTML : function() {\r
8671                 var t = this, h = '', c, co, dom = tinymce.DOM, s = t.settings, i, pr, nx, cl;\r
8672 \r
8673                 cl = t.controls;\r
8674                 for (i=0; i<cl.length; i++) {\r
8675                         // Get current control, prev control, next control and if the control is a list box or not\r
8676                         co = cl[i];\r
8677                         pr = cl[i - 1];\r
8678                         nx = cl[i + 1];\r
8679 \r
8680                         // Add toolbar start\r
8681                         if (i === 0) {\r
8682                                 c = 'mceToolbarStart';\r
8683 \r
8684                                 if (co.Button)\r
8685                                         c += ' mceToolbarStartButton';\r
8686                                 else if (co.SplitButton)\r
8687                                         c += ' mceToolbarStartSplitButton';\r
8688                                 else if (co.ListBox)\r
8689                                         c += ' mceToolbarStartListBox';\r
8690 \r
8691                                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
8692                         }\r
8693 \r
8694                         // Add toolbar end before list box and after the previous button\r
8695                         // This is to fix the o2k7 editor skins\r
8696                         if (pr && co.ListBox) {\r
8697                                 if (pr.Button || pr.SplitButton)\r
8698                                         h += dom.createHTML('td', {'class' : 'mceToolbarEnd'}, dom.createHTML('span', null, '<!-- IE -->'));\r
8699                         }\r
8700 \r
8701                         // Render control HTML\r
8702 \r
8703                         // IE 8 quick fix, needed to propertly generate a hit area for anchors\r
8704                         if (dom.stdMode)\r
8705                                 h += '<td style="position: relative">' + co.renderHTML() + '</td>';\r
8706                         else\r
8707                                 h += '<td>' + co.renderHTML() + '</td>';\r
8708 \r
8709                         // Add toolbar start after list box and before the next button\r
8710                         // This is to fix the o2k7 editor skins\r
8711                         if (nx && co.ListBox) {\r
8712                                 if (nx.Button || nx.SplitButton)\r
8713                                         h += dom.createHTML('td', {'class' : 'mceToolbarStart'}, dom.createHTML('span', null, '<!-- IE -->'));\r
8714                         }\r
8715                 }\r
8716 \r
8717                 c = 'mceToolbarEnd';\r
8718 \r
8719                 if (co.Button)\r
8720                         c += ' mceToolbarEndButton';\r
8721                 else if (co.SplitButton)\r
8722                         c += ' mceToolbarEndSplitButton';\r
8723                 else if (co.ListBox)\r
8724                         c += ' mceToolbarEndListBox';\r
8725 \r
8726                 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->'));\r
8727 \r
8728                 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
8729         }\r
8730 });\r
8731 \r
8732 (function(tinymce) {\r
8733         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;\r
8734 \r
8735         tinymce.create('tinymce.AddOnManager', {\r
8736                 items : [],\r
8737                 urls : {},\r
8738                 lookup : {},\r
8739 \r
8740                 onAdd : new Dispatcher(this),\r
8741 \r
8742                 get : function(n) {\r
8743                         return this.lookup[n];\r
8744                 },\r
8745 \r
8746                 requireLangPack : function(n) {\r
8747                         var s = tinymce.settings;\r
8748 \r
8749                         if (s && s.language)\r
8750                                 tinymce.ScriptLoader.add(this.urls[n] + '/langs/' + s.language + '.js');\r
8751                 },\r
8752 \r
8753                 add : function(id, o) {\r
8754                         this.items.push(o);\r
8755                         this.lookup[id] = o;\r
8756                         this.onAdd.dispatch(this, id, o);\r
8757 \r
8758                         return o;\r
8759                 },\r
8760 \r
8761                 load : function(n, u, cb, s) {\r
8762                         var t = this;\r
8763 \r
8764                         if (t.urls[n])\r
8765                                 return;\r
8766 \r
8767                         if (u.indexOf('/') != 0 && u.indexOf('://') == -1)\r
8768                                 u = tinymce.baseURL + '/' +  u;\r
8769 \r
8770                         t.urls[n] = u.substring(0, u.lastIndexOf('/'));\r
8771                         tinymce.ScriptLoader.add(u, cb, s);\r
8772                 }\r
8773         });\r
8774 \r
8775         // Create plugin and theme managers\r
8776         tinymce.PluginManager = new tinymce.AddOnManager();\r
8777         tinymce.ThemeManager = new tinymce.AddOnManager();\r
8778 }(tinymce));\r
8779 \r
8780 (function(tinymce) {\r
8781         // Shorten names\r
8782         var each = tinymce.each, extend = tinymce.extend,\r
8783                 DOM = tinymce.DOM, Event = tinymce.dom.Event,\r
8784                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
8785                 explode = tinymce.explode,\r
8786                 Dispatcher = tinymce.util.Dispatcher, undefined, instanceCounter = 0;\r
8787 \r
8788         // Setup some URLs where the editor API is located and where the document is\r
8789         tinymce.documentBaseURL = window.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');\r
8790         if (!/[\/\\]$/.test(tinymce.documentBaseURL))\r
8791                 tinymce.documentBaseURL += '/';\r
8792 \r
8793         tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL);\r
8794 \r
8795         tinymce.baseURI = new tinymce.util.URI(tinymce.baseURL);\r
8796 \r
8797         // Add before unload listener\r
8798         // This was required since IE was leaking memory if you added and removed beforeunload listeners\r
8799         // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event\r
8800         tinymce.onBeforeUnload = new Dispatcher(tinymce);\r
8801 \r
8802         // Must be on window or IE will leak if the editor is placed in frame or iframe\r
8803         Event.add(window, 'beforeunload', function(e) {\r
8804                 tinymce.onBeforeUnload.dispatch(tinymce, e);\r
8805         });\r
8806 \r
8807         tinymce.onAddEditor = new Dispatcher(tinymce);\r
8808 \r
8809         tinymce.onRemoveEditor = new Dispatcher(tinymce);\r
8810 \r
8811         tinymce.EditorManager = extend(tinymce, {\r
8812                 editors : [],\r
8813 \r
8814                 i18n : {},\r
8815 \r
8816                 activeEditor : null,\r
8817 \r
8818                 init : function(s) {\r
8819                         var t = this, pl, sl = tinymce.ScriptLoader, e, el = [], ed;\r
8820 \r
8821                         function execCallback(se, n, s) {\r
8822                                 var f = se[n];\r
8823 \r
8824                                 if (!f)\r
8825                                         return;\r
8826 \r
8827                                 if (tinymce.is(f, 'string')) {\r
8828                                         s = f.replace(/\.\w+$/, '');\r
8829                                         s = s ? tinymce.resolve(s) : 0;\r
8830                                         f = tinymce.resolve(f);\r
8831                                 }\r
8832 \r
8833                                 return f.apply(s || this, Array.prototype.slice.call(arguments, 2));\r
8834                         };\r
8835 \r
8836                         s = extend({\r
8837                                 theme : "simple",\r
8838                                 language : "en"\r
8839                         }, s);\r
8840 \r
8841                         t.settings = s;\r
8842 \r
8843                         // Legacy call\r
8844                         Event.add(document, 'init', function() {\r
8845                                 var l, co;\r
8846 \r
8847                                 execCallback(s, 'onpageload');\r
8848 \r
8849                                 switch (s.mode) {\r
8850                                         case "exact":\r
8851                                                 l = s.elements || '';\r
8852 \r
8853                                                 if(l.length > 0) {\r
8854                                                         each(explode(l), function(v) {\r
8855                                                                 if (DOM.get(v)) {\r
8856                                                                         ed = new tinymce.Editor(v, s);\r
8857                                                                         el.push(ed);\r
8858                                                                         ed.render(1);\r
8859                                                                 } else {\r
8860                                                                         each(document.forms, function(f) {\r
8861                                                                                 each(f.elements, function(e) {\r
8862                                                                                         if (e.name === v) {\r
8863                                                                                                 v = 'mce_editor_' + instanceCounter++;\r
8864                                                                                                 DOM.setAttrib(e, 'id', v);\r
8865 \r
8866                                                                                                 ed = new tinymce.Editor(v, s);\r
8867                                                                                                 el.push(ed);\r
8868                                                                                                 ed.render(1);\r
8869                                                                                         }\r
8870                                                                                 });\r
8871                                                                         });\r
8872                                                                 }\r
8873                                                         });\r
8874                                                 }\r
8875                                                 break;\r
8876 \r
8877                                         case "textareas":\r
8878                                         case "specific_textareas":\r
8879                                                 function hasClass(n, c) {\r
8880                                                         return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);\r
8881                                                 };\r
8882 \r
8883                                                 each(DOM.select('textarea'), function(v) {\r
8884                                                         if (s.editor_deselector && hasClass(v, s.editor_deselector))\r
8885                                                                 return;\r
8886 \r
8887                                                         if (!s.editor_selector || hasClass(v, s.editor_selector)) {\r
8888                                                                 // Can we use the name\r
8889                                                                 e = DOM.get(v.name);\r
8890                                                                 if (!v.id && !e)\r
8891                                                                         v.id = v.name;\r
8892 \r
8893                                                                 // Generate unique name if missing or already exists\r
8894                                                                 if (!v.id || t.get(v.id))\r
8895                                                                         v.id = DOM.uniqueId();\r
8896 \r
8897                                                                 ed = new tinymce.Editor(v.id, s);\r
8898                                                                 el.push(ed);\r
8899                                                                 ed.render(1);\r
8900                                                         }\r
8901                                                 });\r
8902                                                 break;\r
8903                                 }\r
8904 \r
8905                                 // Call onInit when all editors are initialized\r
8906                                 if (s.oninit) {\r
8907                                         l = co = 0;\r
8908 \r
8909                                         each(el, function(ed) {\r
8910                                                 co++;\r
8911 \r
8912                                                 if (!ed.initialized) {\r
8913                                                         // Wait for it\r
8914                                                         ed.onInit.add(function() {\r
8915                                                                 l++;\r
8916 \r
8917                                                                 // All done\r
8918                                                                 if (l == co)\r
8919                                                                         execCallback(s, 'oninit');\r
8920                                                         });\r
8921                                                 } else\r
8922                                                         l++;\r
8923 \r
8924                                                 // All done\r
8925                                                 if (l == co)\r
8926                                                         execCallback(s, 'oninit');                                      \r
8927                                         });\r
8928                                 }\r
8929                         });\r
8930                 },\r
8931 \r
8932                 get : function(id) {\r
8933                         if (id === undefined)\r
8934                                 return this.editors;\r
8935 \r
8936                         return this.editors[id];\r
8937                 },\r
8938 \r
8939                 getInstanceById : function(id) {\r
8940                         return this.get(id);\r
8941                 },\r
8942 \r
8943                 add : function(editor) {\r
8944                         var self = this, editors = self.editors;\r
8945 \r
8946                         // Add named and index editor instance\r
8947                         editors[editor.id] = editor;\r
8948                         editors.push(editor);\r
8949 \r
8950                         self._setActive(editor);\r
8951                         self.onAddEditor.dispatch(self, editor);\r
8952 \r
8953 \r
8954                         return editor;\r
8955                 },\r
8956 \r
8957                 remove : function(editor) {\r
8958                         var t = this, i, editors = t.editors;\r
8959 \r
8960                         // Not in the collection\r
8961                         if (!editors[editor.id])\r
8962                                 return null;\r
8963 \r
8964                         delete editors[editor.id];\r
8965 \r
8966                         for (i = 0; i < editors.length; i++) {\r
8967                                 if (editors[i] == editor) {\r
8968                                         editors.splice(i, 1);\r
8969                                         break;\r
8970                                 }\r
8971                         }\r
8972 \r
8973                         // Select another editor since the active one was removed\r
8974                         if (t.activeEditor == editor)\r
8975                                 t._setActive(editors[0]);\r
8976 \r
8977                         editor.destroy();\r
8978                         t.onRemoveEditor.dispatch(t, editor);\r
8979 \r
8980                         return editor;\r
8981                 },\r
8982 \r
8983                 execCommand : function(c, u, v) {\r
8984                         var t = this, ed = t.get(v), w;\r
8985 \r
8986                         // Manager commands\r
8987                         switch (c) {\r
8988                                 case "mceFocus":\r
8989                                         ed.focus();\r
8990                                         return true;\r
8991 \r
8992                                 case "mceAddEditor":\r
8993                                 case "mceAddControl":\r
8994                                         if (!t.get(v))\r
8995                                                 new tinymce.Editor(v, t.settings).render();\r
8996 \r
8997                                         return true;\r
8998 \r
8999                                 case "mceAddFrameControl":\r
9000                                         w = v.window;\r
9001 \r
9002                                         // Add tinyMCE global instance and tinymce namespace to specified window\r
9003                                         w.tinyMCE = tinyMCE;\r
9004                                         w.tinymce = tinymce;\r
9005 \r
9006                                         tinymce.DOM.doc = w.document;\r
9007                                         tinymce.DOM.win = w;\r
9008 \r
9009                                         ed = new tinymce.Editor(v.element_id, v);\r
9010                                         ed.render();\r
9011 \r
9012                                         // Fix IE memory leaks\r
9013                                         if (tinymce.isIE) {\r
9014                                                 function clr() {\r
9015                                                         ed.destroy();\r
9016                                                         w.detachEvent('onunload', clr);\r
9017                                                         w = w.tinyMCE = w.tinymce = null; // IE leak\r
9018                                                 };\r
9019 \r
9020                                                 w.attachEvent('onunload', clr);\r
9021                                         }\r
9022 \r
9023                                         v.page_window = null;\r
9024 \r
9025                                         return true;\r
9026 \r
9027                                 case "mceRemoveEditor":\r
9028                                 case "mceRemoveControl":\r
9029                                         if (ed)\r
9030                                                 ed.remove();\r
9031 \r
9032                                         return true;\r
9033 \r
9034                                 case 'mceToggleEditor':\r
9035                                         if (!ed) {\r
9036                                                 t.execCommand('mceAddControl', 0, v);\r
9037                                                 return true;\r
9038                                         }\r
9039 \r
9040                                         if (ed.isHidden())\r
9041                                                 ed.show();\r
9042                                         else\r
9043                                                 ed.hide();\r
9044 \r
9045                                         return true;\r
9046                         }\r
9047 \r
9048                         // Run command on active editor\r
9049                         if (t.activeEditor)\r
9050                                 return t.activeEditor.execCommand(c, u, v);\r
9051 \r
9052                         return false;\r
9053                 },\r
9054 \r
9055                 execInstanceCommand : function(id, c, u, v) {\r
9056                         var ed = this.get(id);\r
9057 \r
9058                         if (ed)\r
9059                                 return ed.execCommand(c, u, v);\r
9060 \r
9061                         return false;\r
9062                 },\r
9063 \r
9064                 triggerSave : function() {\r
9065                         each(this.editors, function(e) {\r
9066                                 e.save();\r
9067                         });\r
9068                 },\r
9069 \r
9070                 addI18n : function(p, o) {\r
9071                         var lo, i18n = this.i18n;\r
9072 \r
9073                         if (!tinymce.is(p, 'string')) {\r
9074                                 each(p, function(o, lc) {\r
9075                                         each(o, function(o, g) {\r
9076                                                 each(o, function(o, k) {\r
9077                                                         if (g === 'common')\r
9078                                                                 i18n[lc + '.' + k] = o;\r
9079                                                         else\r
9080                                                                 i18n[lc + '.' + g + '.' + k] = o;\r
9081                                                 });\r
9082                                         });\r
9083                                 });\r
9084                         } else {\r
9085                                 each(o, function(o, k) {\r
9086                                         i18n[p + '.' + k] = o;\r
9087                                 });\r
9088                         }\r
9089                 },\r
9090 \r
9091                 // Private methods\r
9092 \r
9093                 _setActive : function(editor) {\r
9094                         this.selectedInstance = this.activeEditor = editor;\r
9095                 }\r
9096         });\r
9097 })(tinymce);\r
9098 \r
9099 (function(tinymce) {\r
9100         // Shorten these names\r
9101         var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend,\r
9102                 Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isGecko = tinymce.isGecko,\r
9103                 isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, is = tinymce.is,\r
9104                 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
9105                 inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;\r
9106 \r
9107         tinymce.create('tinymce.Editor', {\r
9108                 Editor : function(id, s) {\r
9109                         var t = this;\r
9110 \r
9111                         t.id = t.editorId = id;\r
9112 \r
9113                         t.execCommands = {};\r
9114                         t.queryStateCommands = {};\r
9115                         t.queryValueCommands = {};\r
9116 \r
9117                         t.isNotDirty = false;\r
9118 \r
9119                         t.plugins = {};\r
9120 \r
9121                         // Add events to the editor\r
9122                         each([\r
9123                                 'onPreInit',\r
9124 \r
9125                                 'onBeforeRenderUI',\r
9126 \r
9127                                 'onPostRender',\r
9128 \r
9129                                 'onInit',\r
9130 \r
9131                                 'onRemove',\r
9132 \r
9133                                 'onActivate',\r
9134 \r
9135                                 'onDeactivate',\r
9136 \r
9137                                 'onClick',\r
9138 \r
9139                                 'onEvent',\r
9140 \r
9141                                 'onMouseUp',\r
9142 \r
9143                                 'onMouseDown',\r
9144 \r
9145                                 'onDblClick',\r
9146 \r
9147                                 'onKeyDown',\r
9148 \r
9149                                 'onKeyUp',\r
9150 \r
9151                                 'onKeyPress',\r
9152 \r
9153                                 'onContextMenu',\r
9154 \r
9155                                 'onSubmit',\r
9156 \r
9157                                 'onReset',\r
9158 \r
9159                                 'onPaste',\r
9160 \r
9161                                 'onPreProcess',\r
9162 \r
9163                                 'onPostProcess',\r
9164 \r
9165                                 'onBeforeSetContent',\r
9166 \r
9167                                 'onBeforeGetContent',\r
9168 \r
9169                                 'onSetContent',\r
9170 \r
9171                                 'onGetContent',\r
9172 \r
9173                                 'onLoadContent',\r
9174 \r
9175                                 'onSaveContent',\r
9176 \r
9177                                 'onNodeChange',\r
9178 \r
9179                                 'onChange',\r
9180 \r
9181                                 'onBeforeExecCommand',\r
9182 \r
9183                                 'onExecCommand',\r
9184 \r
9185                                 'onUndo',\r
9186 \r
9187                                 'onRedo',\r
9188 \r
9189                                 'onVisualAid',\r
9190 \r
9191                                 'onSetProgressState'\r
9192                         ], function(e) {\r
9193                                 t[e] = new Dispatcher(t);\r
9194                         });\r
9195 \r
9196                         t.settings = s = extend({\r
9197                                 id : id,\r
9198                                 language : 'en',\r
9199                                 docs_language : 'en',\r
9200                                 theme : 'simple',\r
9201                                 skin : 'default',\r
9202                                 delta_width : 0,\r
9203                                 delta_height : 0,\r
9204                                 popup_css : '',\r
9205                                 plugins : '',\r
9206                                 document_base_url : tinymce.documentBaseURL,\r
9207                                 add_form_submit_trigger : 1,\r
9208                                 submit_patch : 1,\r
9209                                 add_unload_trigger : 1,\r
9210                                 convert_urls : 1,\r
9211                                 relative_urls : 1,\r
9212                                 remove_script_host : 1,\r
9213                                 table_inline_editing : 0,\r
9214                                 object_resizing : 1,\r
9215                                 cleanup : 1,\r
9216                                 accessibility_focus : 1,\r
9217                                 custom_shortcuts : 1,\r
9218                                 custom_undo_redo_keyboard_shortcuts : 1,\r
9219                                 custom_undo_redo_restore_selection : 1,\r
9220                                 custom_undo_redo : 1,\r
9221                                 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
9222                                 visual_table_class : 'mceItemTable',\r
9223                                 visual : 1,\r
9224                                 font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',\r
9225                                 apply_source_formatting : 1,\r
9226                                 directionality : 'ltr',\r
9227                                 forced_root_block : 'p',\r
9228                                 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
9229                                 hidden_input : 1,\r
9230                                 padd_empty_editor : 1,\r
9231                                 render_ui : 1,\r
9232                                 init_theme : 1,\r
9233                                 force_p_newlines : 1,\r
9234                                 indentation : '30px',\r
9235                                 keep_styles : 1,\r
9236                                 fix_table_elements : 1,\r
9237                                 inline_styles : 1,\r
9238                                 convert_fonts_to_spans : true\r
9239                         }, s);\r
9240 \r
9241                         t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {\r
9242                                 base_uri : tinyMCE.baseURI\r
9243                         });\r
9244 \r
9245                         t.baseURI = tinymce.baseURI;\r
9246 \r
9247                         // Call setup\r
9248                         t.execCallback('setup', t);\r
9249                 },\r
9250 \r
9251                 render : function(nst) {\r
9252                         var t = this, s = t.settings, id = t.id, sl = tinymce.ScriptLoader;\r
9253 \r
9254                         // Page is not loaded yet, wait for it\r
9255                         if (!Event.domLoaded) {\r
9256                                 Event.add(document, 'init', function() {\r
9257                                         t.render();\r
9258                                 });\r
9259                                 return;\r
9260                         }\r
9261 \r
9262                         tinyMCE.settings = s;\r
9263 \r
9264                         // Element not found, then skip initialization\r
9265                         if (!t.getElement())\r
9266                                 return;\r
9267 \r
9268                         // Is a iPad/iPhone, then skip initialization. We need to sniff here since the\r
9269                         // browser says it has contentEditable support but there is no visible caret\r
9270                         // We will remove this check ones Apple implements full contentEditable support\r
9271                         if (tinymce.isIDevice)\r
9272                                 return;\r
9273 \r
9274                         // Add hidden input for non input elements inside form elements\r
9275                         if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))\r
9276                                 DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);\r
9277 \r
9278                         if (tinymce.WindowManager)\r
9279                                 t.windowManager = new tinymce.WindowManager(t);\r
9280 \r
9281                         if (s.encoding == 'xml') {\r
9282                                 t.onGetContent.add(function(ed, o) {\r
9283                                         if (o.save)\r
9284                                                 o.content = DOM.encode(o.content);\r
9285                                 });\r
9286                         }\r
9287 \r
9288                         if (s.add_form_submit_trigger) {\r
9289                                 t.onSubmit.addToTop(function() {\r
9290                                         if (t.initialized) {\r
9291                                                 t.save();\r
9292                                                 t.isNotDirty = 1;\r
9293                                         }\r
9294                                 });\r
9295                         }\r
9296 \r
9297                         if (s.add_unload_trigger) {\r
9298                                 t._beforeUnload = tinyMCE.onBeforeUnload.add(function() {\r
9299                                         if (t.initialized && !t.destroyed && !t.isHidden())\r
9300                                                 t.save({format : 'raw', no_events : true});\r
9301                                 });\r
9302                         }\r
9303 \r
9304                         tinymce.addUnload(t.destroy, t);\r
9305 \r
9306                         if (s.submit_patch) {\r
9307                                 t.onBeforeRenderUI.add(function() {\r
9308                                         var n = t.getElement().form;\r
9309 \r
9310                                         if (!n)\r
9311                                                 return;\r
9312 \r
9313                                         // Already patched\r
9314                                         if (n._mceOldSubmit)\r
9315                                                 return;\r
9316 \r
9317                                         // Check page uses id="submit" or name="submit" for it's submit button\r
9318                                         if (!n.submit.nodeType && !n.submit.length) {\r
9319                                                 t.formElement = n;\r
9320                                                 n._mceOldSubmit = n.submit;\r
9321                                                 n.submit = function() {\r
9322                                                         // Save all instances\r
9323                                                         tinymce.triggerSave();\r
9324                                                         t.isNotDirty = 1;\r
9325 \r
9326                                                         return t.formElement._mceOldSubmit(t.formElement);\r
9327                                                 };\r
9328                                         }\r
9329 \r
9330                                         n = null;\r
9331                                 });\r
9332                         }\r
9333 \r
9334                         // Load scripts\r
9335                         function loadScripts() {\r
9336                                 if (s.language)\r
9337                                         sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');\r
9338 \r
9339                                 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])\r
9340                                         ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');\r
9341 \r
9342                                 each(explode(s.plugins), function(p) {\r
9343                                         if (p && p.charAt(0) != '-' && !PluginManager.urls[p]) {\r
9344                                                 // Skip safari plugin, since it is removed as of 3.3b1\r
9345                                                 if (p == 'safari')\r
9346                                                         return;\r
9347 \r
9348                                                 PluginManager.load(p, 'plugins/' + p + '/editor_plugin' + tinymce.suffix + '.js');\r
9349                                         }\r
9350                                 });\r
9351 \r
9352                                 // Init when que is loaded\r
9353                                 sl.loadQueue(function() {\r
9354                                         if (!t.removed)\r
9355                                                 t.init();\r
9356                                 });\r
9357                         };\r
9358 \r
9359                         loadScripts();\r
9360                 },\r
9361 \r
9362                 init : function() {\r
9363                         var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re;\r
9364 \r
9365                         tinymce.add(t);\r
9366 \r
9367                         if (s.theme) {\r
9368                                 s.theme = s.theme.replace(/-/, '');\r
9369                                 o = ThemeManager.get(s.theme);\r
9370                                 t.theme = new o();\r
9371 \r
9372                                 if (t.theme.init && s.init_theme)\r
9373                                         t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));\r
9374                         }\r
9375 \r
9376                         // Create all plugins\r
9377                         each(explode(s.plugins.replace(/\-/g, '')), function(p) {\r
9378                                 var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;\r
9379 \r
9380                                 if (c) {\r
9381                                         po = new c(t, u);\r
9382 \r
9383                                         t.plugins[p] = po;\r
9384 \r
9385                                         if (po.init)\r
9386                                                 po.init(t, u);\r
9387                                 }\r
9388                         });\r
9389 \r
9390                         // Setup popup CSS path(s)\r
9391                         if (s.popup_css !== false) {\r
9392                                 if (s.popup_css)\r
9393                                         s.popup_css = t.documentBaseURI.toAbsolute(s.popup_css);\r
9394                                 else\r
9395                                         s.popup_css = t.baseURI.toAbsolute("themes/" + s.theme + "/skins/" + s.skin + "/dialog.css");\r
9396                         }\r
9397 \r
9398                         if (s.popup_css_add)\r
9399                                 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add);\r
9400 \r
9401                         t.controlManager = new tinymce.ControlManager(t);\r
9402 \r
9403                         if (s.custom_undo_redo) {\r
9404                                 // Add initial undo level\r
9405                                 t.onBeforeExecCommand.add(function(ed, cmd, ui, val, a) {\r
9406                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo)) {\r
9407                                                 if (!t.undoManager.hasUndo())\r
9408                                                         t.undoManager.add();\r
9409                                         }\r
9410                                 });\r
9411 \r
9412                                 t.onExecCommand.add(function(ed, cmd, ui, val, a) {\r
9413                                         if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))\r
9414                                                 t.undoManager.add();\r
9415                                 });\r
9416                         }\r
9417 \r
9418                         t.onExecCommand.add(function(ed, c) {\r
9419                                 // Don't refresh the select lists until caret move\r
9420                                 if (!/^(FontName|FontSize)$/.test(c))\r
9421                                         t.nodeChanged();\r
9422                         });\r
9423 \r
9424                         // Remove ghost selections on images and tables in Gecko\r
9425                         if (isGecko) {\r
9426                                 function repaint(a, o) {\r
9427                                         if (!o || !o.initial)\r
9428                                                 t.execCommand('mceRepaint');\r
9429                                 };\r
9430 \r
9431                                 t.onUndo.add(repaint);\r
9432                                 t.onRedo.add(repaint);\r
9433                                 t.onSetContent.add(repaint);\r
9434                         }\r
9435 \r
9436                         // Enables users to override the control factory\r
9437                         t.onBeforeRenderUI.dispatch(t, t.controlManager);\r
9438 \r
9439                         // Measure box\r
9440                         if (s.render_ui) {\r
9441                                 w = s.width || e.style.width || e.offsetWidth;\r
9442                                 h = s.height || e.style.height || e.offsetHeight;\r
9443                                 t.orgDisplay = e.style.display;\r
9444                                 re = /^[0-9\.]+(|px)$/i;\r
9445 \r
9446                                 if (re.test('' + w))\r
9447                                         w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);\r
9448 \r
9449                                 if (re.test('' + h))\r
9450                                         h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);\r
9451 \r
9452                                 // Render UI\r
9453                                 o = t.theme.renderUI({\r
9454                                         targetNode : e,\r
9455                                         width : w,\r
9456                                         height : h,\r
9457                                         deltaWidth : s.delta_width,\r
9458                                         deltaHeight : s.delta_height\r
9459                                 });\r
9460 \r
9461                                 t.editorContainer = o.editorContainer;\r
9462                         }\r
9463 \r
9464 \r
9465                         // User specified a document.domain value\r
9466                         if (document.domain && location.hostname != document.domain)\r
9467                                 tinymce.relaxedDomain = document.domain;\r
9468 \r
9469                         // Resize editor\r
9470                         DOM.setStyles(o.sizeContainer || o.editorContainer, {\r
9471                                 width : w,\r
9472                                 height : h\r
9473                         });\r
9474 \r
9475                         h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');\r
9476                         if (h < 100)\r
9477                                 h = 100;\r
9478 \r
9479                         t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">';\r
9480 \r
9481                         // We only need to override paths if we have to\r
9482                         // IE has a bug where it remove site absolute urls to relative ones if this is specified\r
9483                         if (s.document_base_url != tinymce.documentBaseURL)\r
9484                                 t.iframeHTML += '<base href="' + t.documentBaseURI.getURI() + '" />';\r
9485 \r
9486                         t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" /><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';\r
9487 \r
9488                         if (tinymce.relaxedDomain)\r
9489                                 t.iframeHTML += '<script type="text/javascript">document.domain = "' + tinymce.relaxedDomain + '";</script>';\r
9490 \r
9491                         bi = s.body_id || 'tinymce';\r
9492                         if (bi.indexOf('=') != -1) {\r
9493                                 bi = t.getParam('body_id', '', 'hash');\r
9494                                 bi = bi[t.id] || bi;\r
9495                         }\r
9496 \r
9497                         bc = s.body_class || '';\r
9498                         if (bc.indexOf('=') != -1) {\r
9499                                 bc = t.getParam('body_class', '', 'hash');\r
9500                                 bc = bc[t.id] || '';\r
9501                         }\r
9502 \r
9503                         t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"></body></html>';\r
9504 \r
9505                         // Domain relaxing enabled, then set document domain\r
9506                         if (tinymce.relaxedDomain) {\r
9507                                 // We need to write the contents here in IE since multiple writes messes up refresh button and back button\r
9508                                 if (isIE || (tinymce.isOpera && parseFloat(opera.version()) >= 9.5))\r
9509                                         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
9510                                 else if (tinymce.isOpera)\r
9511                                         u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";document.close();ed.setupIframe();})()';                                  \r
9512                         }\r
9513 \r
9514                         // Create iframe\r
9515                         n = DOM.add(o.iframeContainer, 'iframe', {\r
9516                                 id : t.id + "_ifr",\r
9517                                 src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7\r
9518                                 frameBorder : '0',\r
9519                                 style : {\r
9520                                         width : '100%',\r
9521                                         height : h\r
9522                                 }\r
9523                         });\r
9524 \r
9525                         t.contentAreaContainer = o.iframeContainer;\r
9526                         DOM.get(o.editorContainer).style.display = t.orgDisplay;\r
9527                         DOM.get(t.id).style.display = 'none';\r
9528 \r
9529                         if (!isIE || !tinymce.relaxedDomain)\r
9530                                 t.setupIframe();\r
9531 \r
9532                         e = n = o = null; // Cleanup\r
9533                 },\r
9534 \r
9535                 setupIframe : function() {\r
9536                         var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;\r
9537 \r
9538                         // Setup iframe body\r
9539                         if (!isIE || !tinymce.relaxedDomain) {\r
9540                                 d.open();\r
9541                                 d.write(t.iframeHTML);\r
9542                                 d.close();\r
9543                         }\r
9544 \r
9545                         // Design mode needs to be added here Ctrl+A will fail otherwise\r
9546                         if (!isIE) {\r
9547                                 try {\r
9548                                         if (!s.readonly)\r
9549                                                 d.designMode = 'On';\r
9550                                 } catch (ex) {\r
9551                                         // Will fail on Gecko if the editor is placed in an hidden container element\r
9552                                         // The design mode will be set ones the editor is focused\r
9553                                 }\r
9554                         }\r
9555 \r
9556                         // IE needs to use contentEditable or it will display non secure items for HTTPS\r
9557                         if (isIE) {\r
9558                                 // It will not steal focus if we hide it while setting contentEditable\r
9559                                 b = t.getBody();\r
9560                                 DOM.hide(b);\r
9561 \r
9562                                 if (!s.readonly)\r
9563                                         b.contentEditable = true;\r
9564 \r
9565                                 DOM.show(b);\r
9566                         }\r
9567 \r
9568                         t.dom = new tinymce.dom.DOMUtils(t.getDoc(), {\r
9569                                 keep_values : true,\r
9570                                 url_converter : t.convertURL,\r
9571                                 url_converter_scope : t,\r
9572                                 hex_colors : s.force_hex_style_colors,\r
9573                                 class_filter : s.class_filter,\r
9574                                 update_styles : 1,\r
9575                                 fix_ie_paragraphs : 1,\r
9576                                 valid_styles : s.valid_styles\r
9577                         });\r
9578 \r
9579                         t.schema = new tinymce.dom.Schema();\r
9580 \r
9581                         t.serializer = new tinymce.dom.Serializer(extend(s, {\r
9582                                 valid_elements : s.verify_html === false ? '*[*]' : s.valid_elements,\r
9583                                 dom : t.dom,\r
9584                                 schema : t.schema\r
9585                         }));\r
9586 \r
9587                         t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);\r
9588 \r
9589                         t.formatter = new tinymce.Formatter(this);\r
9590 \r
9591                         // Register default formats\r
9592                         t.formatter.register({\r
9593                                 alignleft : [\r
9594                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}},\r
9595                                         {selector : 'img,table', styles : {'float' : 'left'}}\r
9596                                 ],\r
9597 \r
9598                                 aligncenter : [\r
9599                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}},\r
9600                                         {selector : 'img', styles : {display : 'block', marginLeft : 'auto', marginRight : 'auto'}},\r
9601                                         {selector : 'table', styles : {marginLeft : 'auto', marginRight : 'auto'}}\r
9602                                 ],\r
9603 \r
9604                                 alignright : [\r
9605                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}},\r
9606                                         {selector : 'img,table', styles : {'float' : 'right'}}\r
9607                                 ],\r
9608 \r
9609                                 alignfull : [\r
9610                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'justify'}}\r
9611                                 ],\r
9612 \r
9613                                 bold : [\r
9614                                         {inline : 'strong'},\r
9615                                         {inline : 'span', styles : {fontWeight : 'bold'}},\r
9616                                         {inline : 'b'}\r
9617                                 ],\r
9618 \r
9619                                 italic : [\r
9620                                         {inline : 'em'},\r
9621                                         {inline : 'span', styles : {fontStyle : 'italic'}},\r
9622                                         {inline : 'i'}\r
9623                                 ],\r
9624 \r
9625                                 underline : [\r
9626                                         {inline : 'span', styles : {textDecoration : 'underline'}, exact : true},\r
9627                                         {inline : 'u'}\r
9628                                 ],\r
9629 \r
9630                                 strikethrough : [\r
9631                                         {inline : 'span', styles : {textDecoration : 'line-through'}, exact : true},\r
9632                                         {inline : 'u'}\r
9633                                 ],\r
9634 \r
9635                                 forecolor : {inline : 'span', styles : {color : '%value'}},\r
9636                                 hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}},\r
9637                                 fontname : {inline : 'span', styles : {fontFamily : '%value'}},\r
9638                                 fontsize : {inline : 'span', styles : {fontSize : '%value'}},\r
9639                                 fontsize_class : {inline : 'span', attributes : {'class' : '%value'}},\r
9640                                 blockquote : {block : 'blockquote', wrapper : 1, remove : 'all'},\r
9641 \r
9642                                 removeformat : [\r
9643                                         {selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},\r
9644                                         {selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},\r
9645                                         {selector : '*', attributes : ['style', 'class'], split : false, expand : false, deep : true}\r
9646                                 ]\r
9647                         });\r
9648 \r
9649                         // Register default block formats\r
9650                         each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) {\r
9651                                 t.formatter.register(name, {block : name, remove : 'all'});\r
9652                         });\r
9653 \r
9654                         // Register user defined formats\r
9655                         t.formatter.register(t.settings.formats);\r
9656 \r
9657                         t.undoManager = new tinymce.UndoManager(t);\r
9658 \r
9659                         // Pass through\r
9660                         t.undoManager.onAdd.add(function(um, l) {\r
9661                                 if (!l.initial)\r
9662                                         return t.onChange.dispatch(t, l, um);\r
9663                         });\r
9664 \r
9665                         t.undoManager.onUndo.add(function(um, l) {\r
9666                                 return t.onUndo.dispatch(t, l, um);\r
9667                         });\r
9668 \r
9669                         t.undoManager.onRedo.add(function(um, l) {\r
9670                                 return t.onRedo.dispatch(t, l, um);\r
9671                         });\r
9672 \r
9673                         t.forceBlocks = new tinymce.ForceBlocks(t, {\r
9674                                 forced_root_block : s.forced_root_block\r
9675                         });\r
9676 \r
9677                         t.editorCommands = new tinymce.EditorCommands(t);\r
9678 \r
9679                         // Pass through\r
9680                         t.serializer.onPreProcess.add(function(se, o) {\r
9681                                 return t.onPreProcess.dispatch(t, o, se);\r
9682                         });\r
9683 \r
9684                         t.serializer.onPostProcess.add(function(se, o) {\r
9685                                 return t.onPostProcess.dispatch(t, o, se);\r
9686                         });\r
9687 \r
9688                         t.onPreInit.dispatch(t);\r
9689 \r
9690                         if (!s.gecko_spellcheck)\r
9691                                 t.getBody().spellcheck = 0;\r
9692 \r
9693                         if (!s.readonly)\r
9694                                 t._addEvents();\r
9695 \r
9696                         t.controlManager.onPostRender.dispatch(t, t.controlManager);\r
9697                         t.onPostRender.dispatch(t);\r
9698 \r
9699                         if (s.directionality)\r
9700                                 t.getBody().dir = s.directionality;\r
9701 \r
9702                         if (s.nowrap)\r
9703                                 t.getBody().style.whiteSpace = "nowrap";\r
9704 \r
9705                         if (s.custom_elements) {\r
9706                                 function handleCustom(ed, o) {\r
9707                                         each(explode(s.custom_elements), function(v) {\r
9708                                                 var n;\r
9709 \r
9710                                                 if (v.indexOf('~') === 0) {\r
9711                                                         v = v.substring(1);\r
9712                                                         n = 'span';\r
9713                                                 } else\r
9714                                                         n = 'div';\r
9715 \r
9716                                                 o.content = o.content.replace(new RegExp('<(' + v + ')([^>]*)>', 'g'), '<' + n + ' _mce_name="$1"$2>');\r
9717                                                 o.content = o.content.replace(new RegExp('</(' + v + ')>', 'g'), '</' + n + '>');\r
9718                                         });\r
9719                                 };\r
9720 \r
9721                                 t.onBeforeSetContent.add(handleCustom);\r
9722                                 t.onPostProcess.add(function(ed, o) {\r
9723                                         if (o.set)\r
9724                                                 handleCustom(ed, o);\r
9725                                 });\r
9726                         }\r
9727 \r
9728                         if (s.handle_node_change_callback) {\r
9729                                 t.onNodeChange.add(function(ed, cm, n) {\r
9730                                         t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());\r
9731                                 });\r
9732                         }\r
9733 \r
9734                         if (s.save_callback) {\r
9735                                 t.onSaveContent.add(function(ed, o) {\r
9736                                         var h = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
9737 \r
9738                                         if (h)\r
9739                                                 o.content = h;\r
9740                                 });\r
9741                         }\r
9742 \r
9743                         if (s.onchange_callback) {\r
9744                                 t.onChange.add(function(ed, l) {\r
9745                                         t.execCallback('onchange_callback', t, l);\r
9746                                 });\r
9747                         }\r
9748 \r
9749                         if (s.convert_newlines_to_brs) {\r
9750                                 t.onBeforeSetContent.add(function(ed, o) {\r
9751                                         if (o.initial)\r
9752                                                 o.content = o.content.replace(/\r?\n/g, '<br />');\r
9753                                 });\r
9754                         }\r
9755 \r
9756                         if (s.fix_nesting && isIE) {\r
9757                                 t.onBeforeSetContent.add(function(ed, o) {\r
9758                                         o.content = t._fixNesting(o.content);\r
9759                                 });\r
9760                         }\r
9761 \r
9762                         if (s.preformatted) {\r
9763                                 t.onPostProcess.add(function(ed, o) {\r
9764                                         o.content = o.content.replace(/^\s*<pre.*?>/, '');\r
9765                                         o.content = o.content.replace(/<\/pre>\s*$/, '');\r
9766 \r
9767                                         if (o.set)\r
9768                                                 o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';\r
9769                                 });\r
9770                         }\r
9771 \r
9772                         if (s.verify_css_classes) {\r
9773                                 t.serializer.attribValueFilter = function(n, v) {\r
9774                                         var s, cl;\r
9775 \r
9776                                         if (n == 'class') {\r
9777                                                 // Build regexp for classes\r
9778                                                 if (!t.classesRE) {\r
9779                                                         cl = t.dom.getClasses();\r
9780 \r
9781                                                         if (cl.length > 0) {\r
9782                                                                 s = '';\r
9783 \r
9784                                                                 each (cl, function(o) {\r
9785                                                                         s += (s ? '|' : '') + o['class'];\r
9786                                                                 });\r
9787 \r
9788                                                                 t.classesRE = new RegExp('(' + s + ')', 'gi');\r
9789                                                         }\r
9790                                                 }\r
9791 \r
9792                                                 return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';\r
9793                                         }\r
9794 \r
9795                                         return v;\r
9796                                 };\r
9797                         }\r
9798 \r
9799                         if (s.cleanup_callback) {\r
9800                                 t.onBeforeSetContent.add(function(ed, o) {\r
9801                                         o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
9802                                 });\r
9803 \r
9804                                 t.onPreProcess.add(function(ed, o) {\r
9805                                         if (o.set)\r
9806                                                 t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);\r
9807 \r
9808                                         if (o.get)\r
9809                                                 t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);\r
9810                                 });\r
9811 \r
9812                                 t.onPostProcess.add(function(ed, o) {\r
9813                                         if (o.set)\r
9814                                                 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);\r
9815 \r
9816                                         if (o.get)                                              \r
9817                                                 o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);\r
9818                                 });\r
9819                         }\r
9820 \r
9821                         if (s.save_callback) {\r
9822                                 t.onGetContent.add(function(ed, o) {\r
9823                                         if (o.save)\r
9824                                                 o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());\r
9825                                 });\r
9826                         }\r
9827 \r
9828                         if (s.handle_event_callback) {\r
9829                                 t.onEvent.add(function(ed, e, o) {\r
9830                                         if (t.execCallback('handle_event_callback', e, ed, o) === false)\r
9831                                                 Event.cancel(e);\r
9832                                 });\r
9833                         }\r
9834 \r
9835                         // Add visual aids when new contents is added\r
9836                         t.onSetContent.add(function() {\r
9837                                 t.addVisual(t.getBody());\r
9838                         });\r
9839 \r
9840                         // Remove empty contents\r
9841                         if (s.padd_empty_editor) {\r
9842                                 t.onPostProcess.add(function(ed, o) {\r
9843                                         o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');\r
9844                                 });\r
9845                         }\r
9846 \r
9847                         if (isGecko) {\r
9848                                 // Fix gecko link bug, when a link is placed at the end of block elements there is\r
9849                                 // no way to move the caret behind the link. This fix adds a bogus br element after the link\r
9850                                 function fixLinks(ed, o) {\r
9851                                         each(ed.dom.select('a'), function(n) {\r
9852                                                 var pn = n.parentNode;\r
9853 \r
9854                                                 if (ed.dom.isBlock(pn) && pn.lastChild === n)\r
9855                                                         ed.dom.add(pn, 'br', {'_mce_bogus' : 1});\r
9856                                         });\r
9857                                 };\r
9858 \r
9859                                 t.onExecCommand.add(function(ed, cmd) {\r
9860                                         if (cmd === 'CreateLink')\r
9861                                                 fixLinks(ed);\r
9862                                 });\r
9863 \r
9864                                 t.onSetContent.add(t.selection.onSetContent.add(fixLinks));\r
9865 \r
9866                                 if (!s.readonly) {\r
9867                                         try {\r
9868                                                 // Design mode must be set here once again to fix a bug where\r
9869                                                 // Ctrl+A/Delete/Backspace didn't work if the editor was added using mceAddControl then removed then added again\r
9870                                                 d.designMode = 'Off';\r
9871                                                 d.designMode = 'On';\r
9872                                         } catch (ex) {\r
9873                                                 // Will fail on Gecko if the editor is placed in an hidden container element\r
9874                                                 // The design mode will be set ones the editor is focused\r
9875                                         }\r
9876                                 }\r
9877                         }\r
9878 \r
9879                         // A small timeout was needed since firefox will remove. Bug: #1838304\r
9880                         setTimeout(function () {\r
9881                                 if (t.removed)\r
9882                                         return;\r
9883 \r
9884                                 t.load({initial : true, format : (s.cleanup_on_startup ? 'html' : 'raw')});\r
9885                                 t.startContent = t.getContent({format : 'raw'});\r
9886                                 t.initialized = true;\r
9887 \r
9888                                 t.onInit.dispatch(t);\r
9889                                 t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());\r
9890                                 t.execCallback('init_instance_callback', t);\r
9891                                 t.focus(true);\r
9892                                 t.nodeChanged({initial : 1});\r
9893 \r
9894                                 // Load specified content CSS last\r
9895                                 if (s.content_css) {\r
9896                                         tinymce.each(explode(s.content_css), function(u) {\r
9897                                                 t.dom.loadCSS(t.documentBaseURI.toAbsolute(u));\r
9898                                         });\r
9899                                 }\r
9900 \r
9901                                 // Handle auto focus\r
9902                                 if (s.auto_focus) {\r
9903                                         setTimeout(function () {\r
9904                                                 var ed = tinymce.get(s.auto_focus);\r
9905 \r
9906                                                 ed.selection.select(ed.getBody(), 1);\r
9907                                                 ed.selection.collapse(1);\r
9908                                                 ed.getWin().focus();\r
9909                                         }, 100);\r
9910                                 }\r
9911                         }, 1);\r
9912         \r
9913                         e = null;\r
9914                 },\r
9915 \r
9916 \r
9917                 focus : function(sf) {\r
9918                         var oed, t = this, ce = t.settings.content_editable, ieRng, controlElm, doc = t.getDoc();\r
9919 \r
9920                         if (!sf) {\r
9921                                 // Get selected control element\r
9922                                 ieRng = t.selection.getRng();\r
9923                                 if (ieRng.item) {\r
9924                                         controlElm = ieRng.item(0);\r
9925                                 }\r
9926 \r
9927                                 // Is not content editable\r
9928                                 if (!ce)\r
9929                                         t.getWin().focus();\r
9930 \r
9931                                 // Restore selected control element\r
9932                                 // This is needed when for example an image is selected within a\r
9933                                 // layer a call to focus will then remove the control selection\r
9934                                 if (controlElm && controlElm.ownerDocument == doc) {\r
9935                                         ieRng = doc.body.createControlRange();\r
9936                                         ieRng.addElement(controlElm);\r
9937                                         ieRng.select();\r
9938                                 }\r
9939 \r
9940                         }\r
9941 \r
9942                         if (tinymce.activeEditor != t) {\r
9943                                 if ((oed = tinymce.activeEditor) != null)\r
9944                                         oed.onDeactivate.dispatch(oed, t);\r
9945 \r
9946                                 t.onActivate.dispatch(t, oed);\r
9947                         }\r
9948 \r
9949                         tinymce._setActive(t);\r
9950                 },\r
9951 \r
9952                 execCallback : function(n) {\r
9953                         var t = this, f = t.settings[n], s;\r
9954 \r
9955                         if (!f)\r
9956                                 return;\r
9957 \r
9958                         // Look through lookup\r
9959                         if (t.callbackLookup && (s = t.callbackLookup[n])) {\r
9960                                 f = s.func;\r
9961                                 s = s.scope;\r
9962                         }\r
9963 \r
9964                         if (is(f, 'string')) {\r
9965                                 s = f.replace(/\.\w+$/, '');\r
9966                                 s = s ? tinymce.resolve(s) : 0;\r
9967                                 f = tinymce.resolve(f);\r
9968                                 t.callbackLookup = t.callbackLookup || {};\r
9969                                 t.callbackLookup[n] = {func : f, scope : s};\r
9970                         }\r
9971 \r
9972                         return f.apply(s || t, Array.prototype.slice.call(arguments, 1));\r
9973                 },\r
9974 \r
9975                 translate : function(s) {\r
9976                         var c = this.settings.language || 'en', i18n = tinymce.i18n;\r
9977 \r
9978                         if (!s)\r
9979                                 return '';\r
9980 \r
9981                         return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {\r
9982                                 return i18n[c + '.' + b] || '{#' + b + '}';\r
9983                         });\r
9984                 },\r
9985 \r
9986                 getLang : function(n, dv) {\r
9987                         return tinymce.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}');\r
9988                 },\r
9989 \r
9990                 getParam : function(n, dv, ty) {\r
9991                         var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o;\r
9992 \r
9993                         if (ty === 'hash') {\r
9994                                 o = {};\r
9995 \r
9996                                 if (is(v, 'string')) {\r
9997                                         each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) {\r
9998                                                 v = v.split('=');\r
9999 \r
10000                                                 if (v.length > 1)\r
10001                                                         o[tr(v[0])] = tr(v[1]);\r
10002                                                 else\r
10003                                                         o[tr(v[0])] = tr(v);\r
10004                                         });\r
10005                                 } else\r
10006                                         o = v;\r
10007 \r
10008                                 return o;\r
10009                         }\r
10010 \r
10011                         return v;\r
10012                 },\r
10013 \r
10014                 nodeChanged : function(o) {\r
10015                         var t = this, s = t.selection, n = (isIE ? s.getNode() : s.getStart()) || t.getBody();\r
10016 \r
10017                         // Fix for bug #1896577 it seems that this can not be fired while the editor is loading\r
10018                         if (t.initialized) {\r
10019                                 o = o || {};\r
10020                                 n = isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n; // Fix for IE initial state\r
10021 \r
10022                                 // Get parents and add them to object\r
10023                                 o.parents = [];\r
10024                                 t.dom.getParent(n, function(node) {\r
10025                                         if (node.nodeName == 'BODY')\r
10026                                                 return true;\r
10027 \r
10028                                         o.parents.push(node);\r
10029                                 });\r
10030 \r
10031                                 t.onNodeChange.dispatch(\r
10032                                         t,\r
10033                                         o ? o.controlManager || t.controlManager : t.controlManager,\r
10034                                         n,\r
10035                                         s.isCollapsed(),\r
10036                                         o\r
10037                                 );\r
10038                         }\r
10039                 },\r
10040 \r
10041                 addButton : function(n, s) {\r
10042                         var t = this;\r
10043 \r
10044                         t.buttons = t.buttons || {};\r
10045                         t.buttons[n] = s;\r
10046                 },\r
10047 \r
10048                 addCommand : function(n, f, s) {\r
10049                         this.execCommands[n] = {func : f, scope : s || this};\r
10050                 },\r
10051 \r
10052                 addQueryStateHandler : function(n, f, s) {\r
10053                         this.queryStateCommands[n] = {func : f, scope : s || this};\r
10054                 },\r
10055 \r
10056                 addQueryValueHandler : function(n, f, s) {\r
10057                         this.queryValueCommands[n] = {func : f, scope : s || this};\r
10058                 },\r
10059 \r
10060                 addShortcut : function(pa, desc, cmd_func, sc) {\r
10061                         var t = this, c;\r
10062 \r
10063                         if (!t.settings.custom_shortcuts)\r
10064                                 return false;\r
10065 \r
10066                         t.shortcuts = t.shortcuts || {};\r
10067 \r
10068                         if (is(cmd_func, 'string')) {\r
10069                                 c = cmd_func;\r
10070 \r
10071                                 cmd_func = function() {\r
10072                                         t.execCommand(c, false, null);\r
10073                                 };\r
10074                         }\r
10075 \r
10076                         if (is(cmd_func, 'object')) {\r
10077                                 c = cmd_func;\r
10078 \r
10079                                 cmd_func = function() {\r
10080                                         t.execCommand(c[0], c[1], c[2]);\r
10081                                 };\r
10082                         }\r
10083 \r
10084                         each(explode(pa), function(pa) {\r
10085                                 var o = {\r
10086                                         func : cmd_func,\r
10087                                         scope : sc || this,\r
10088                                         desc : desc,\r
10089                                         alt : false,\r
10090                                         ctrl : false,\r
10091                                         shift : false\r
10092                                 };\r
10093 \r
10094                                 each(explode(pa, '+'), function(v) {\r
10095                                         switch (v) {\r
10096                                                 case 'alt':\r
10097                                                 case 'ctrl':\r
10098                                                 case 'shift':\r
10099                                                         o[v] = true;\r
10100                                                         break;\r
10101 \r
10102                                                 default:\r
10103                                                         o.charCode = v.charCodeAt(0);\r
10104                                                         o.keyCode = v.toUpperCase().charCodeAt(0);\r
10105                                         }\r
10106                                 });\r
10107 \r
10108                                 t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o;\r
10109                         });\r
10110 \r
10111                         return true;\r
10112                 },\r
10113 \r
10114                 execCommand : function(cmd, ui, val, a) {\r
10115                         var t = this, s = 0, o, st;\r
10116 \r
10117                         if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))\r
10118                                 t.focus();\r
10119 \r
10120                         o = {};\r
10121                         t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);\r
10122                         if (o.terminate)\r
10123                                 return false;\r
10124 \r
10125                         // Command callback\r
10126                         if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) {\r
10127                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10128                                 return true;\r
10129                         }\r
10130 \r
10131                         // Registred commands\r
10132                         if (o = t.execCommands[cmd]) {\r
10133                                 st = o.func.call(o.scope, ui, val);\r
10134 \r
10135                                 // Fall through on true\r
10136                                 if (st !== true) {\r
10137                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10138                                         return st;\r
10139                                 }\r
10140                         }\r
10141 \r
10142                         // Plugin commands\r
10143                         each(t.plugins, function(p) {\r
10144                                 if (p.execCommand && p.execCommand(cmd, ui, val)) {\r
10145                                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10146                                         s = 1;\r
10147                                         return false;\r
10148                                 }\r
10149                         });\r
10150 \r
10151                         if (s)\r
10152                                 return true;\r
10153 \r
10154                         // Theme commands\r
10155                         if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) {\r
10156                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10157                                 return true;\r
10158                         }\r
10159 \r
10160                         // Execute global commands\r
10161                         if (tinymce.GlobalCommands.execCommand(t, cmd, ui, val)) {\r
10162                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10163                                 return true;\r
10164                         }\r
10165 \r
10166                         // Editor commands\r
10167                         if (t.editorCommands.execCommand(cmd, ui, val)) {\r
10168                                 t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10169                                 return true;\r
10170                         }\r
10171 \r
10172                         // Browser commands\r
10173                         t.getDoc().execCommand(cmd, ui, val);\r
10174                         t.onExecCommand.dispatch(t, cmd, ui, val, a);\r
10175                 },\r
10176 \r
10177                 queryCommandState : function(cmd) {\r
10178                         var t = this, o, s;\r
10179 \r
10180                         // Is hidden then return undefined\r
10181                         if (t._isHidden())\r
10182                                 return;\r
10183 \r
10184                         // Registred commands\r
10185                         if (o = t.queryStateCommands[cmd]) {\r
10186                                 s = o.func.call(o.scope);\r
10187 \r
10188                                 // Fall though on true\r
10189                                 if (s !== true)\r
10190                                         return s;\r
10191                         }\r
10192 \r
10193                         // Registred commands\r
10194                         o = t.editorCommands.queryCommandState(cmd);\r
10195                         if (o !== -1)\r
10196                                 return o;\r
10197 \r
10198                         // Browser commands\r
10199                         try {\r
10200                                 return this.getDoc().queryCommandState(cmd);\r
10201                         } catch (ex) {\r
10202                                 // Fails sometimes see bug: 1896577\r
10203                         }\r
10204                 },\r
10205 \r
10206                 queryCommandValue : function(c) {\r
10207                         var t = this, o, s;\r
10208 \r
10209                         // Is hidden then return undefined\r
10210                         if (t._isHidden())\r
10211                                 return;\r
10212 \r
10213                         // Registred commands\r
10214                         if (o = t.queryValueCommands[c]) {\r
10215                                 s = o.func.call(o.scope);\r
10216 \r
10217                                 // Fall though on true\r
10218                                 if (s !== true)\r
10219                                         return s;\r
10220                         }\r
10221 \r
10222                         // Registred commands\r
10223                         o = t.editorCommands.queryCommandValue(c);\r
10224                         if (is(o))\r
10225                                 return o;\r
10226 \r
10227                         // Browser commands\r
10228                         try {\r
10229                                 return this.getDoc().queryCommandValue(c);\r
10230                         } catch (ex) {\r
10231                                 // Fails sometimes see bug: 1896577\r
10232                         }\r
10233                 },\r
10234 \r
10235                 show : function() {\r
10236                         var t = this;\r
10237 \r
10238                         DOM.show(t.getContainer());\r
10239                         DOM.hide(t.id);\r
10240                         t.load();\r
10241                 },\r
10242 \r
10243                 hide : function() {\r
10244                         var t = this, d = t.getDoc();\r
10245 \r
10246                         // Fixed bug where IE has a blinking cursor left from the editor\r
10247                         if (isIE && d)\r
10248                                 d.execCommand('SelectAll');\r
10249 \r
10250                         // We must save before we hide so Safari doesn't crash\r
10251                         t.save();\r
10252                         DOM.hide(t.getContainer());\r
10253                         DOM.setStyle(t.id, 'display', t.orgDisplay);\r
10254                 },\r
10255 \r
10256                 isHidden : function() {\r
10257                         return !DOM.isHidden(this.id);\r
10258                 },\r
10259 \r
10260                 setProgressState : function(b, ti, o) {\r
10261                         this.onSetProgressState.dispatch(this, b, ti, o);\r
10262 \r
10263                         return b;\r
10264                 },\r
10265 \r
10266                 load : function(o) {\r
10267                         var t = this, e = t.getElement(), h;\r
10268 \r
10269                         if (e) {\r
10270                                 o = o || {};\r
10271                                 o.load = true;\r
10272 \r
10273                                 // Double encode existing entities in the value\r
10274                                 h = t.setContent(is(e.value) ? e.value : e.innerHTML, o);\r
10275                                 o.element = e;\r
10276 \r
10277                                 if (!o.no_events)\r
10278                                         t.onLoadContent.dispatch(t, o);\r
10279 \r
10280                                 o.element = e = null;\r
10281 \r
10282                                 return h;\r
10283                         }\r
10284                 },\r
10285 \r
10286                 save : function(o) {\r
10287                         var t = this, e = t.getElement(), h, f;\r
10288 \r
10289                         if (!e || !t.initialized)\r
10290                                 return;\r
10291 \r
10292                         o = o || {};\r
10293                         o.save = true;\r
10294 \r
10295                         // Add undo level will trigger onchange event\r
10296                         if (!o.no_events) {\r
10297                                 t.undoManager.typing = 0;\r
10298                                 t.undoManager.add();\r
10299                         }\r
10300 \r
10301                         o.element = e;\r
10302                         h = o.content = t.getContent(o);\r
10303 \r
10304                         if (!o.no_events)\r
10305                                 t.onSaveContent.dispatch(t, o);\r
10306 \r
10307                         h = o.content;\r
10308 \r
10309                         if (!/TEXTAREA|INPUT/i.test(e.nodeName)) {\r
10310                                 e.innerHTML = h;\r
10311 \r
10312                                 // Update hidden form element\r
10313                                 if (f = DOM.getParent(t.id, 'form')) {\r
10314                                         each(f.elements, function(e) {\r
10315                                                 if (e.name == t.id) {\r
10316                                                         e.value = h;\r
10317                                                         return false;\r
10318                                                 }\r
10319                                         });\r
10320                                 }\r
10321                         } else\r
10322                                 e.value = h;\r
10323 \r
10324                         o.element = e = null;\r
10325 \r
10326                         return h;\r
10327                 },\r
10328 \r
10329                 setContent : function(h, o) {\r
10330                         var t = this;\r
10331 \r
10332                         o = o || {};\r
10333                         o.format = o.format || 'html';\r
10334                         o.set = true;\r
10335                         o.content = h;\r
10336 \r
10337                         if (!o.no_events)\r
10338                                 t.onBeforeSetContent.dispatch(t, o);\r
10339 \r
10340                         // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content\r
10341                         // It will also be impossible to place the caret in the editor unless there is a BR element present\r
10342                         if (!tinymce.isIE && (h.length === 0 || /^\s+$/.test(h))) {\r
10343                                 o.content = t.dom.setHTML(t.getBody(), '<br _mce_bogus="1" />');\r
10344                                 o.format = 'raw';\r
10345                         }\r
10346 \r
10347                         o.content = t.dom.setHTML(t.getBody(), tinymce.trim(o.content));\r
10348 \r
10349                         if (o.format != 'raw' && t.settings.cleanup) {\r
10350                                 o.getInner = true;\r
10351                                 o.content = t.dom.setHTML(t.getBody(), t.serializer.serialize(t.getBody(), o));\r
10352                         }\r
10353 \r
10354                         if (!o.no_events)\r
10355                                 t.onSetContent.dispatch(t, o);\r
10356 \r
10357                         return o.content;\r
10358                 },\r
10359 \r
10360                 getContent : function(o) {\r
10361                         var t = this, h;\r
10362 \r
10363                         o = o || {};\r
10364                         o.format = o.format || 'html';\r
10365                         o.get = true;\r
10366 \r
10367                         if (!o.no_events)\r
10368                                 t.onBeforeGetContent.dispatch(t, o);\r
10369 \r
10370                         if (o.format != 'raw' && t.settings.cleanup) {\r
10371                                 o.getInner = true;\r
10372                                 h = t.serializer.serialize(t.getBody(), o);\r
10373                         } else\r
10374                                 h = t.getBody().innerHTML;\r
10375 \r
10376                         h = h.replace(/^\s*|\s*$/g, '');\r
10377                         o.content = h;\r
10378 \r
10379                         if (!o.no_events)\r
10380                                 t.onGetContent.dispatch(t, o);\r
10381 \r
10382                         return o.content;\r
10383                 },\r
10384 \r
10385                 isDirty : function() {\r
10386                         var t = this;\r
10387 \r
10388                         return tinymce.trim(t.startContent) != tinymce.trim(t.getContent({format : 'raw', no_events : 1})) && !t.isNotDirty;\r
10389                 },\r
10390 \r
10391                 getContainer : function() {\r
10392                         var t = this;\r
10393 \r
10394                         if (!t.container)\r
10395                                 t.container = DOM.get(t.editorContainer || t.id + '_parent');\r
10396 \r
10397                         return t.container;\r
10398                 },\r
10399 \r
10400                 getContentAreaContainer : function() {\r
10401                         return this.contentAreaContainer;\r
10402                 },\r
10403 \r
10404                 getElement : function() {\r
10405                         return DOM.get(this.settings.content_element || this.id);\r
10406                 },\r
10407 \r
10408                 getWin : function() {\r
10409                         var t = this, e;\r
10410 \r
10411                         if (!t.contentWindow) {\r
10412                                 e = DOM.get(t.id + "_ifr");\r
10413 \r
10414                                 if (e)\r
10415                                         t.contentWindow = e.contentWindow;\r
10416                         }\r
10417 \r
10418                         return t.contentWindow;\r
10419                 },\r
10420 \r
10421                 getDoc : function() {\r
10422                         var t = this, w;\r
10423 \r
10424                         if (!t.contentDocument) {\r
10425                                 w = t.getWin();\r
10426 \r
10427                                 if (w)\r
10428                                         t.contentDocument = w.document;\r
10429                         }\r
10430 \r
10431                         return t.contentDocument;\r
10432                 },\r
10433 \r
10434                 getBody : function() {\r
10435                         return this.bodyElement || this.getDoc().body;\r
10436                 },\r
10437 \r
10438                 convertURL : function(u, n, e) {\r
10439                         var t = this, s = t.settings;\r
10440 \r
10441                         // Use callback instead\r
10442                         if (s.urlconverter_callback)\r
10443                                 return t.execCallback('urlconverter_callback', u, e, true, n);\r
10444 \r
10445                         // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs\r
10446                         if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)\r
10447                                 return u;\r
10448 \r
10449                         // Convert to relative\r
10450                         if (s.relative_urls)\r
10451                                 return t.documentBaseURI.toRelative(u);\r
10452 \r
10453                         // Convert to absolute\r
10454                         u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);\r
10455 \r
10456                         return u;\r
10457                 },\r
10458 \r
10459                 addVisual : function(e) {\r
10460                         var t = this, s = t.settings;\r
10461 \r
10462                         e = e || t.getBody();\r
10463 \r
10464                         if (!is(t.hasVisual))\r
10465                                 t.hasVisual = s.visual;\r
10466 \r
10467                         each(t.dom.select('table,a', e), function(e) {\r
10468                                 var v;\r
10469 \r
10470                                 switch (e.nodeName) {\r
10471                                         case 'TABLE':\r
10472                                                 v = t.dom.getAttrib(e, 'border');\r
10473 \r
10474                                                 if (!v || v == '0') {\r
10475                                                         if (t.hasVisual)\r
10476                                                                 t.dom.addClass(e, s.visual_table_class);\r
10477                                                         else\r
10478                                                                 t.dom.removeClass(e, s.visual_table_class);\r
10479                                                 }\r
10480 \r
10481                                                 return;\r
10482 \r
10483                                         case 'A':\r
10484                                                 v = t.dom.getAttrib(e, 'name');\r
10485 \r
10486                                                 if (v) {\r
10487                                                         if (t.hasVisual)\r
10488                                                                 t.dom.addClass(e, 'mceItemAnchor');\r
10489                                                         else\r
10490                                                                 t.dom.removeClass(e, 'mceItemAnchor');\r
10491                                                 }\r
10492 \r
10493                                                 return;\r
10494                                 }\r
10495                         });\r
10496 \r
10497                         t.onVisualAid.dispatch(t, e, t.hasVisual);\r
10498                 },\r
10499 \r
10500                 remove : function() {\r
10501                         var t = this, e = t.getContainer();\r
10502 \r
10503                         t.removed = 1; // Cancels post remove event execution\r
10504                         t.hide();\r
10505 \r
10506                         t.execCallback('remove_instance_callback', t);\r
10507                         t.onRemove.dispatch(t);\r
10508 \r
10509                         // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command\r
10510                         t.onExecCommand.listeners = [];\r
10511 \r
10512                         tinymce.remove(t);\r
10513                         DOM.remove(e);\r
10514                 },\r
10515 \r
10516                 destroy : function(s) {\r
10517                         var t = this;\r
10518 \r
10519                         // One time is enough\r
10520                         if (t.destroyed)\r
10521                                 return;\r
10522 \r
10523                         if (!s) {\r
10524                                 tinymce.removeUnload(t.destroy);\r
10525                                 tinyMCE.onBeforeUnload.remove(t._beforeUnload);\r
10526 \r
10527                                 // Manual destroy\r
10528                                 if (t.theme && t.theme.destroy)\r
10529                                         t.theme.destroy();\r
10530 \r
10531                                 // Destroy controls, selection and dom\r
10532                                 t.controlManager.destroy();\r
10533                                 t.selection.destroy();\r
10534                                 t.dom.destroy();\r
10535 \r
10536                                 // Remove all events\r
10537 \r
10538                                 // Don't clear the window or document if content editable\r
10539                                 // is enabled since other instances might still be present\r
10540                                 if (!t.settings.content_editable) {\r
10541                                         Event.clear(t.getWin());\r
10542                                         Event.clear(t.getDoc());\r
10543                                 }\r
10544 \r
10545                                 Event.clear(t.getBody());\r
10546                                 Event.clear(t.formElement);\r
10547                         }\r
10548 \r
10549                         if (t.formElement) {\r
10550                                 t.formElement.submit = t.formElement._mceOldSubmit;\r
10551                                 t.formElement._mceOldSubmit = null;\r
10552                         }\r
10553 \r
10554                         t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null;\r
10555 \r
10556                         if (t.selection)\r
10557                                 t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null;\r
10558 \r
10559                         t.destroyed = 1;\r
10560                 },\r
10561 \r
10562                 // Internal functions\r
10563 \r
10564                 _addEvents : function() {\r
10565                         // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset\r
10566                         var t = this, i, s = t.settings, lo = {\r
10567                                 mouseup : 'onMouseUp',\r
10568                                 mousedown : 'onMouseDown',\r
10569                                 click : 'onClick',\r
10570                                 keyup : 'onKeyUp',\r
10571                                 keydown : 'onKeyDown',\r
10572                                 keypress : 'onKeyPress',\r
10573                                 submit : 'onSubmit',\r
10574                                 reset : 'onReset',\r
10575                                 contextmenu : 'onContextMenu',\r
10576                                 dblclick : 'onDblClick',\r
10577                                 paste : 'onPaste' // Doesn't work in all browsers yet\r
10578                         };\r
10579 \r
10580                         function eventHandler(e, o) {\r
10581                                 var ty = e.type;\r
10582 \r
10583                                 // Don't fire events when it's removed\r
10584                                 if (t.removed)\r
10585                                         return;\r
10586 \r
10587                                 // Generic event handler\r
10588                                 if (t.onEvent.dispatch(t, e, o) !== false) {\r
10589                                         // Specific event handler\r
10590                                         t[lo[e.fakeType || e.type]].dispatch(t, e, o);\r
10591                                 }\r
10592                         };\r
10593 \r
10594                         // Add DOM events\r
10595                         each(lo, function(v, k) {\r
10596                                 switch (k) {\r
10597                                         case 'contextmenu':\r
10598                                                 if (tinymce.isOpera) {\r
10599                                                         // Fake contextmenu on Opera\r
10600                                                         t.dom.bind(t.getBody(), 'mousedown', function(e) {\r
10601                                                                 if (e.ctrlKey) {\r
10602                                                                         e.fakeType = 'contextmenu';\r
10603                                                                         eventHandler(e);\r
10604                                                                 }\r
10605                                                         });\r
10606                                                 } else\r
10607                                                         t.dom.bind(t.getBody(), k, eventHandler);\r
10608                                                 break;\r
10609 \r
10610                                         case 'paste':\r
10611                                                 t.dom.bind(t.getBody(), k, function(e) {\r
10612                                                         eventHandler(e);\r
10613                                                 });\r
10614                                                 break;\r
10615 \r
10616                                         case 'submit':\r
10617                                         case 'reset':\r
10618                                                 t.dom.bind(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);\r
10619                                                 break;\r
10620 \r
10621                                         default:\r
10622                                                 t.dom.bind(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler);\r
10623                                 }\r
10624                         });\r
10625 \r
10626                         t.dom.bind(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) {\r
10627                                 t.focus(true);\r
10628                         });\r
10629 \r
10630 \r
10631                         // Fixes bug where a specified document_base_uri could result in broken images\r
10632                         // This will also fix drag drop of images in Gecko\r
10633                         if (tinymce.isGecko) {\r
10634                                 // Convert all images to absolute URLs\r
10635 /*                              t.onSetContent.add(function(ed, o) {\r
10636                                         each(ed.dom.select('img'), function(e) {\r
10637                                                 var v;\r
10638 \r
10639                                                 if (v = e.getAttribute('_mce_src'))\r
10640                                                         e.src = t.documentBaseURI.toAbsolute(v);\r
10641                                         })\r
10642                                 });*/\r
10643 \r
10644                                 t.dom.bind(t.getDoc(), 'DOMNodeInserted', function(e) {\r
10645                                         var v;\r
10646 \r
10647                                         e = e.target;\r
10648 \r
10649                                         if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('_mce_src')))\r
10650                                                 e.src = t.documentBaseURI.toAbsolute(v);\r
10651                                 });\r
10652                         }\r
10653 \r
10654                         // Set various midas options in Gecko\r
10655                         if (isGecko) {\r
10656                                 function setOpts() {\r
10657                                         var t = this, d = t.getDoc(), s = t.settings;\r
10658 \r
10659                                         if (isGecko && !s.readonly) {\r
10660                                                 if (t._isHidden()) {\r
10661                                                         try {\r
10662                                                                 if (!s.content_editable)\r
10663                                                                         d.designMode = 'On';\r
10664                                                         } catch (ex) {\r
10665                                                                 // Fails if it's hidden\r
10666                                                         }\r
10667                                                 }\r
10668 \r
10669                                                 try {\r
10670                                                         // Try new Gecko method\r
10671                                                         d.execCommand("styleWithCSS", 0, false);\r
10672                                                 } catch (ex) {\r
10673                                                         // Use old method\r
10674                                                         if (!t._isHidden())\r
10675                                                                 try {d.execCommand("useCSS", 0, true);} catch (ex) {}\r
10676                                                 }\r
10677 \r
10678                                                 if (!s.table_inline_editing)\r
10679                                                         try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {}\r
10680 \r
10681                                                 if (!s.object_resizing)\r
10682                                                         try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {}\r
10683                                         }\r
10684                                 };\r
10685 \r
10686                                 t.onBeforeExecCommand.add(setOpts);\r
10687                                 t.onMouseDown.add(setOpts);\r
10688                         }\r
10689 \r
10690                         // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250\r
10691                         // WebKit can't even do simple things like selecting an image\r
10692                         // This also fixes so it's possible to select mceItemAnchors\r
10693                         if (tinymce.isWebKit) {\r
10694                                 t.onClick.add(function(ed, e) {\r
10695                                         e = e.target;\r
10696 \r
10697                                         // Needs tobe the setBaseAndExtend or it will fail to select floated images\r
10698                                         if (e.nodeName == 'IMG' || (e.nodeName == 'A' && t.dom.hasClass(e, 'mceItemAnchor')))\r
10699                                                 t.selection.getSel().setBaseAndExtent(e, 0, e, 1);\r
10700                                 });\r
10701                         }\r
10702 \r
10703                         // Add node change handlers\r
10704                         t.onMouseUp.add(t.nodeChanged);\r
10705                         //t.onClick.add(t.nodeChanged);\r
10706                         t.onKeyUp.add(function(ed, e) {\r
10707                                 var c = e.keyCode;\r
10708 \r
10709                                 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
10710                                         t.nodeChanged();\r
10711                         });\r
10712 \r
10713                         // Add reset handler\r
10714                         t.onReset.add(function() {\r
10715                                 t.setContent(t.startContent, {format : 'raw'});\r
10716                         });\r
10717 \r
10718                         // Add shortcuts\r
10719                         if (s.custom_shortcuts) {\r
10720                                 if (s.custom_undo_redo_keyboard_shortcuts) {\r
10721                                         t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo');\r
10722                                         t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo');\r
10723                                 }\r
10724 \r
10725                                 // Add default shortcuts for gecko\r
10726                                 t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');\r
10727                                 t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');\r
10728                                 t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');\r
10729 \r
10730                                 // BlockFormat shortcuts keys\r
10731                                 for (i=1; i<=6; i++)\r
10732                                         t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);\r
10733 \r
10734                                 t.addShortcut('ctrl+7', '', ['FormatBlock', false, '<p>']);\r
10735                                 t.addShortcut('ctrl+8', '', ['FormatBlock', false, '<div>']);\r
10736                                 t.addShortcut('ctrl+9', '', ['FormatBlock', false, '<address>']);\r
10737 \r
10738                                 function find(e) {\r
10739                                         var v = null;\r
10740 \r
10741                                         if (!e.altKey && !e.ctrlKey && !e.metaKey)\r
10742                                                 return v;\r
10743 \r
10744                                         each(t.shortcuts, function(o) {\r
10745                                                 if (tinymce.isMac && o.ctrl != e.metaKey)\r
10746                                                         return;\r
10747                                                 else if (!tinymce.isMac && o.ctrl != e.ctrlKey)\r
10748                                                         return;\r
10749 \r
10750                                                 if (o.alt != e.altKey)\r
10751                                                         return;\r
10752 \r
10753                                                 if (o.shift != e.shiftKey)\r
10754                                                         return;\r
10755 \r
10756                                                 if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {\r
10757                                                         v = o;\r
10758                                                         return false;\r
10759                                                 }\r
10760                                         });\r
10761 \r
10762                                         return v;\r
10763                                 };\r
10764 \r
10765                                 t.onKeyUp.add(function(ed, e) {\r
10766                                         var o = find(e);\r
10767 \r
10768                                         if (o)\r
10769                                                 return Event.cancel(e);\r
10770                                 });\r
10771 \r
10772                                 t.onKeyPress.add(function(ed, e) {\r
10773                                         var o = find(e);\r
10774 \r
10775                                         if (o)\r
10776                                                 return Event.cancel(e);\r
10777                                 });\r
10778 \r
10779                                 t.onKeyDown.add(function(ed, e) {\r
10780                                         var o = find(e);\r
10781 \r
10782                                         if (o) {\r
10783                                                 o.func.call(o.scope);\r
10784                                                 return Event.cancel(e);\r
10785                                         }\r
10786                                 });\r
10787                         }\r
10788 \r
10789                         if (tinymce.isIE) {\r
10790                                 // Fix so resize will only update the width and height attributes not the styles of an image\r
10791                                 // It will also block mceItemNoResize items\r
10792                                 t.dom.bind(t.getDoc(), 'controlselect', function(e) {\r
10793                                         var re = t.resizeInfo, cb;\r
10794 \r
10795                                         e = e.target;\r
10796 \r
10797                                         // Don't do this action for non image elements\r
10798                                         if (e.nodeName !== 'IMG')\r
10799                                                 return;\r
10800 \r
10801                                         if (re)\r
10802                                                 t.dom.unbind(re.node, re.ev, re.cb);\r
10803 \r
10804                                         if (!t.dom.hasClass(e, 'mceItemNoResize')) {\r
10805                                                 ev = 'resizeend';\r
10806                                                 cb = t.dom.bind(e, ev, function(e) {\r
10807                                                         var v;\r
10808 \r
10809                                                         e = e.target;\r
10810 \r
10811                                                         if (v = t.dom.getStyle(e, 'width')) {\r
10812                                                                 t.dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, ''));\r
10813                                                                 t.dom.setStyle(e, 'width', '');\r
10814                                                         }\r
10815 \r
10816                                                         if (v = t.dom.getStyle(e, 'height')) {\r
10817                                                                 t.dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, ''));\r
10818                                                                 t.dom.setStyle(e, 'height', '');\r
10819                                                         }\r
10820                                                 });\r
10821                                         } else {\r
10822                                                 ev = 'resizestart';\r
10823                                                 cb = t.dom.bind(e, 'resizestart', Event.cancel, Event);\r
10824                                         }\r
10825 \r
10826                                         re = t.resizeInfo = {\r
10827                                                 node : e,\r
10828                                                 ev : ev,\r
10829                                                 cb : cb\r
10830                                         };\r
10831                                 });\r
10832 \r
10833                                 t.onKeyDown.add(function(ed, e) {\r
10834                                         switch (e.keyCode) {\r
10835                                                 case 8:\r
10836                                                         // Fix IE control + backspace browser bug\r
10837                                                         if (t.selection.getRng().item) {\r
10838                                                                 ed.dom.remove(t.selection.getRng().item(0));\r
10839                                                                 return Event.cancel(e);\r
10840                                                         }\r
10841                                         }\r
10842                                 });\r
10843 \r
10844                                 /*if (t.dom.boxModel) {\r
10845                                         t.getBody().style.height = '100%';\r
10846 \r
10847                                         Event.add(t.getWin(), 'resize', function(e) {\r
10848                                                 var docElm = t.getDoc().documentElement;\r
10849 \r
10850                                                 docElm.style.height = (docElm.offsetHeight - 10) + 'px';\r
10851                                         });\r
10852                                 }*/\r
10853                         }\r
10854 \r
10855                         if (tinymce.isOpera) {\r
10856                                 t.onClick.add(function(ed, e) {\r
10857                                         Event.prevent(e);\r
10858                                 });\r
10859                         }\r
10860 \r
10861                         // Add custom undo/redo handlers\r
10862                         if (s.custom_undo_redo) {\r
10863                                 function addUndo() {\r
10864                                         t.undoManager.typing = 0;\r
10865                                         t.undoManager.add();\r
10866                                 };\r
10867 \r
10868                                 t.dom.bind(t.getDoc(), 'focusout', function(e) {\r
10869                                         if (!t.removed && t.undoManager.typing)\r
10870                                                 addUndo();\r
10871                                 });\r
10872 \r
10873                                 t.onKeyUp.add(function(ed, e) {\r
10874                                         if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45 || e.ctrlKey)\r
10875                                                 addUndo();\r
10876                                 });\r
10877 \r
10878                                 t.onKeyDown.add(function(ed, e) {\r
10879                                         var rng, parent, bookmark;\r
10880 \r
10881                                         // IE has a really odd bug where the DOM might include an node that doesn't have\r
10882                                         // a proper structure. If you try to access nodeValue it would throw an illegal value exception.\r
10883                                         // This seems to only happen when you delete contents and it seems to be avoidable if you refresh the element\r
10884                                         // after you delete contents from it. See: #3008923\r
10885                                         if (isIE && e.keyCode == 46) {\r
10886                                                 rng = t.selection.getRng();\r
10887 \r
10888                                                 if (rng.parentElement) {\r
10889                                                         parent = rng.parentElement();\r
10890 \r
10891                                                         // Select next word when ctrl key is used in combo with delete\r
10892                                                         if (e.ctrlKey) {\r
10893                                                                 rng.moveEnd('word', 1);\r
10894                                                                 rng.select();\r
10895                                                         }\r
10896 \r
10897                                                         // Delete contents\r
10898                                                         t.selection.getSel().clear();\r
10899 \r
10900                                                         // Check if we are within the same parent\r
10901                                                         if (rng.parentElement() == parent) {\r
10902                                                                 bookmark = t.selection.getBookmark();\r
10903 \r
10904                                                                 try {\r
10905                                                                         // Update the HTML and hopefully it will remove the artifacts\r
10906                                                                         parent.innerHTML = parent.innerHTML;\r
10907                                                                 } catch (ex) {\r
10908                                                                         // And since it's IE it can sometimes produce an unknown runtime error\r
10909                                                                 }\r
10910 \r
10911                                                                 // Restore the caret position\r
10912                                                                 t.selection.moveToBookmark(bookmark);\r
10913                                                         }\r
10914 \r
10915                                                         // Block the default delete behavior since it might be broken\r
10916                                                         e.preventDefault();\r
10917                                                         return;\r
10918                                                 }\r
10919                                         }\r
10920 \r
10921                                         // Is caracter positon keys\r
10922                                         if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) {\r
10923                                                 if (t.undoManager.typing)\r
10924                                                         addUndo();\r
10925 \r
10926                                                 return;\r
10927                                         }\r
10928 \r
10929                                         if (!t.undoManager.typing) {\r
10930                                                 t.undoManager.add();\r
10931                                                 t.undoManager.typing = 1;\r
10932                                         }\r
10933                                 });\r
10934 \r
10935                                 t.onMouseDown.add(function() {\r
10936                                         if (t.undoManager.typing)\r
10937                                                 addUndo();\r
10938                                 });\r
10939                         }\r
10940                 },\r
10941 \r
10942                 _isHidden : function() {\r
10943                         var s;\r
10944 \r
10945                         if (!isGecko)\r
10946                                 return 0;\r
10947 \r
10948                         // Weird, wheres that cursor selection?\r
10949                         s = this.selection.getSel();\r
10950                         return (!s || !s.rangeCount || s.rangeCount == 0);\r
10951                 },\r
10952 \r
10953                 // Fix for bug #1867292\r
10954                 _fixNesting : function(s) {\r
10955                         var d = [], i;\r
10956 \r
10957                         s = s.replace(/<(\/)?([^\s>]+)[^>]*?>/g, function(a, b, c) {\r
10958                                 var e;\r
10959 \r
10960                                 // Handle end element\r
10961                                 if (b === '/') {\r
10962                                         if (!d.length)\r
10963                                                 return '';\r
10964 \r
10965                                         if (c !== d[d.length - 1].tag) {\r
10966                                                 for (i=d.length - 1; i>=0; i--) {\r
10967                                                         if (d[i].tag === c) {\r
10968                                                                 d[i].close = 1;\r
10969                                                                 break;\r
10970                                                         }\r
10971                                                 }\r
10972 \r
10973                                                 return '';\r
10974                                         } else {\r
10975                                                 d.pop();\r
10976 \r
10977                                                 if (d.length && d[d.length - 1].close) {\r
10978                                                         a = a + '</' + d[d.length - 1].tag + '>';\r
10979                                                         d.pop();\r
10980                                                 }\r
10981                                         }\r
10982                                 } else {\r
10983                                         // Ignore these\r
10984                                         if (/^(br|hr|input|meta|img|link|param)$/i.test(c))\r
10985                                                 return a;\r
10986 \r
10987                                         // Ignore closed ones\r
10988                                         if (/\/>$/.test(a))\r
10989                                                 return a;\r
10990 \r
10991                                         d.push({tag : c}); // Push start element\r
10992                                 }\r
10993 \r
10994                                 return a;\r
10995                         });\r
10996 \r
10997                         // End all open tags\r
10998                         for (i=d.length - 1; i>=0; i--)\r
10999                                 s += '</' + d[i].tag + '>';\r
11000 \r
11001                         return s;\r
11002                 }\r
11003         });\r
11004 })(tinymce);\r
11005 \r
11006 (function(tinymce) {\r
11007         // Added for compression purposes\r
11008         var each = tinymce.each, undefined, TRUE = true, FALSE = false;\r
11009 \r
11010         tinymce.EditorCommands = function(editor) {\r
11011                 var dom = editor.dom,\r
11012                         selection = editor.selection,\r
11013                         commands = {state: {}, exec : {}, value : {}},\r
11014                         settings = editor.settings,\r
11015                         bookmark;\r
11016 \r
11017                 function execCommand(command, ui, value) {\r
11018                         var func;\r
11019 \r
11020                         command = command.toLowerCase();\r
11021                         if (func = commands.exec[command]) {\r
11022                                 func(command, ui, value);\r
11023                                 return TRUE;\r
11024                         }\r
11025 \r
11026                         return FALSE;\r
11027                 };\r
11028 \r
11029                 function queryCommandState(command) {\r
11030                         var func;\r
11031 \r
11032                         command = command.toLowerCase();\r
11033                         if (func = commands.state[command])\r
11034                                 return func(command);\r
11035 \r
11036                         return -1;\r
11037                 };\r
11038 \r
11039                 function queryCommandValue(command) {\r
11040                         var func;\r
11041 \r
11042                         command = command.toLowerCase();\r
11043                         if (func = commands.value[command])\r
11044                                 return func(command);\r
11045 \r
11046                         return FALSE;\r
11047                 };\r
11048 \r
11049                 function addCommands(command_list, type) {\r
11050                         type = type || 'exec';\r
11051 \r
11052                         each(command_list, function(callback, command) {\r
11053                                 each(command.toLowerCase().split(','), function(command) {\r
11054                                         commands[type][command] = callback;\r
11055                                 });\r
11056                         });\r
11057                 };\r
11058 \r
11059                 // Expose public methods\r
11060                 tinymce.extend(this, {\r
11061                         execCommand : execCommand,\r
11062                         queryCommandState : queryCommandState,\r
11063                         queryCommandValue : queryCommandValue,\r
11064                         addCommands : addCommands\r
11065                 });\r
11066 \r
11067                 // Private methods\r
11068 \r
11069                 function execNativeCommand(command, ui, value) {\r
11070                         if (ui === undefined)\r
11071                                 ui = FALSE;\r
11072 \r
11073                         if (value === undefined)\r
11074                                 value = null;\r
11075 \r
11076                         return editor.getDoc().execCommand(command, ui, value);\r
11077                 };\r
11078 \r
11079                 function isFormatMatch(name) {\r
11080                         return editor.formatter.match(name);\r
11081                 };\r
11082 \r
11083                 function toggleFormat(name, value) {\r
11084                         editor.formatter.toggle(name, value ? {value : value} : undefined);\r
11085                 };\r
11086 \r
11087                 function storeSelection(type) {\r
11088                         bookmark = selection.getBookmark(type);\r
11089                 };\r
11090 \r
11091                 function restoreSelection() {\r
11092                         selection.moveToBookmark(bookmark);\r
11093                 };\r
11094 \r
11095                 // Add execCommand overrides\r
11096                 addCommands({\r
11097                         // Ignore these, added for compatibility\r
11098                         'mceResetDesignMode,mceBeginUndoLevel' : function() {},\r
11099 \r
11100                         // Add undo manager logic\r
11101                         'mceEndUndoLevel,mceAddUndoLevel' : function() {\r
11102                                 editor.undoManager.add();\r
11103                         },\r
11104 \r
11105                         'Cut,Copy,Paste' : function(command) {\r
11106                                 var doc = editor.getDoc(), failed;\r
11107 \r
11108                                 // Try executing the native command\r
11109                                 try {\r
11110                                         execNativeCommand(command);\r
11111                                 } catch (ex) {\r
11112                                         // Command failed\r
11113                                         failed = TRUE;\r
11114                                 }\r
11115 \r
11116                                 // Present alert message about clipboard access not being available\r
11117                                 if (failed || !doc.queryCommandSupported(command)) {\r
11118                                         if (tinymce.isGecko) {\r
11119                                                 editor.windowManager.confirm(editor.getLang('clipboard_msg'), function(state) {\r
11120                                                         if (state)\r
11121                                                                 open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', '_blank');\r
11122                                                 });\r
11123                                         } else\r
11124                                                 editor.windowManager.alert(editor.getLang('clipboard_no_support'));\r
11125                                 }\r
11126                         },\r
11127 \r
11128                         // Override unlink command\r
11129                         unlink : function(command) {\r
11130                                 if (selection.isCollapsed())\r
11131                                         selection.select(selection.getNode());\r
11132 \r
11133                                 execNativeCommand(command);\r
11134                                 selection.collapse(FALSE);\r
11135                         },\r
11136 \r
11137                         // Override justify commands to use the text formatter engine\r
11138                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
11139                                 var align = command.substring(7);\r
11140 \r
11141                                 // Remove all other alignments first\r
11142                                 each('left,center,right,full'.split(','), function(name) {\r
11143                                         if (align != name)\r
11144                                                 editor.formatter.remove('align' + name);\r
11145                                 });\r
11146 \r
11147                                 toggleFormat('align' + align);\r
11148                         },\r
11149 \r
11150                         // Override list commands to fix WebKit bug\r
11151                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
11152                                 var listElm, listParent;\r
11153 \r
11154                                 execNativeCommand(command);\r
11155 \r
11156                                 // WebKit produces lists within block elements so we need to split them\r
11157                                 // we will replace the native list creation logic to custom logic later on\r
11158                                 // TODO: Remove this when the list creation logic is removed\r
11159                                 listElm = dom.getParent(selection.getNode(), 'ol,ul');\r
11160                                 if (listElm) {\r
11161                                         listParent = listElm.parentNode;\r
11162 \r
11163                                         // If list is within a text block then split that block\r
11164                                         if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {\r
11165                                                 storeSelection();\r
11166                                                 dom.split(listParent, listElm);\r
11167                                                 restoreSelection();\r
11168                                         }\r
11169                                 }\r
11170                         },\r
11171 \r
11172                         // Override commands to use the text formatter engine\r
11173                         'Bold,Italic,Underline,Strikethrough' : function(command) {\r
11174                                 toggleFormat(command);\r
11175                         },\r
11176 \r
11177                         // Override commands to use the text formatter engine\r
11178                         'ForeColor,HiliteColor,FontName' : function(command, ui, value) {\r
11179                                 toggleFormat(command, value);\r
11180                         },\r
11181 \r
11182                         FontSize : function(command, ui, value) {\r
11183                                 var fontClasses, fontSizes;\r
11184 \r
11185                                 // Convert font size 1-7 to styles\r
11186                                 if (value >= 1 && value <= 7) {\r
11187                                         fontSizes = tinymce.explode(settings.font_size_style_values);\r
11188                                         fontClasses = tinymce.explode(settings.font_size_classes);\r
11189 \r
11190                                         if (fontClasses)\r
11191                                                 value = fontClasses[value - 1] || value;\r
11192                                         else\r
11193                                                 value = fontSizes[value - 1] || value;\r
11194                                 }\r
11195 \r
11196                                 toggleFormat(command, value);\r
11197                         },\r
11198 \r
11199                         RemoveFormat : function(command) {\r
11200                                 editor.formatter.remove(command);\r
11201                         },\r
11202 \r
11203                         mceBlockQuote : function(command) {\r
11204                                 toggleFormat('blockquote');\r
11205                         },\r
11206 \r
11207                         FormatBlock : function(command, ui, value) {\r
11208                                 return toggleFormat(value || 'p');\r
11209                         },\r
11210 \r
11211                         mceCleanup : function() {\r
11212                                 var bookmark = selection.getBookmark();\r
11213 \r
11214                                 editor.setContent(editor.getContent({cleanup : TRUE}), {cleanup : TRUE});\r
11215 \r
11216                                 selection.moveToBookmark(bookmark);\r
11217                         },\r
11218 \r
11219                         mceRemoveNode : function(command, ui, value) {\r
11220                                 var node = value || selection.getNode();\r
11221 \r
11222                                 // Make sure that the body node isn't removed\r
11223                                 if (node != editor.getBody()) {\r
11224                                         storeSelection();\r
11225                                         editor.dom.remove(node, TRUE);\r
11226                                         restoreSelection();\r
11227                                 }\r
11228                         },\r
11229 \r
11230                         mceSelectNodeDepth : function(command, ui, value) {\r
11231                                 var counter = 0;\r
11232 \r
11233                                 dom.getParent(selection.getNode(), function(node) {\r
11234                                         if (node.nodeType == 1 && counter++ == value) {\r
11235                                                 selection.select(node);\r
11236                                                 return FALSE;\r
11237                                         }\r
11238                                 }, editor.getBody());\r
11239                         },\r
11240 \r
11241                         mceSelectNode : function(command, ui, value) {\r
11242                                 selection.select(value);\r
11243                         },\r
11244 \r
11245                         mceInsertContent : function(command, ui, value) {\r
11246                                 selection.setContent(value);\r
11247                         },\r
11248 \r
11249                         mceInsertRawHTML : function(command, ui, value) {\r
11250                                 selection.setContent('tiny_mce_marker');\r
11251                                 editor.setContent(editor.getContent().replace(/tiny_mce_marker/g, value));\r
11252                         },\r
11253 \r
11254                         mceSetContent : function(command, ui, value) {\r
11255                                 editor.setContent(value);\r
11256                         },\r
11257 \r
11258                         'Indent,Outdent' : function(command) {\r
11259                                 var intentValue, indentUnit, value;\r
11260 \r
11261                                 // Setup indent level\r
11262                                 intentValue = settings.indentation;\r
11263                                 indentUnit = /[a-z%]+$/i.exec(intentValue);\r
11264                                 intentValue = parseInt(intentValue);\r
11265 \r
11266                                 if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) {\r
11267                                         each(selection.getSelectedBlocks(), function(element) {\r
11268                                                 if (command == 'outdent') {\r
11269                                                         value = Math.max(0, parseInt(element.style.paddingLeft || 0) - intentValue);\r
11270                                                         dom.setStyle(element, 'paddingLeft', value ? value + indentUnit : '');\r
11271                                                 } else\r
11272                                                         dom.setStyle(element, 'paddingLeft', (parseInt(element.style.paddingLeft || 0) + intentValue) + indentUnit);\r
11273                                         });\r
11274                                 } else\r
11275                                         execNativeCommand(command);\r
11276                         },\r
11277 \r
11278                         mceRepaint : function() {\r
11279                                 var bookmark;\r
11280 \r
11281                                 if (tinymce.isGecko) {\r
11282                                         try {\r
11283                                                 storeSelection(TRUE);\r
11284 \r
11285                                                 if (selection.getSel())\r
11286                                                         selection.getSel().selectAllChildren(editor.getBody());\r
11287 \r
11288                                                 selection.collapse(TRUE);\r
11289                                                 restoreSelection();\r
11290                                         } catch (ex) {\r
11291                                                 // Ignore\r
11292                                         }\r
11293                                 }\r
11294                         },\r
11295 \r
11296                         mceToggleFormat : function(command, ui, value) {\r
11297                                 editor.formatter.toggle(value);\r
11298                         },\r
11299 \r
11300                         InsertHorizontalRule : function() {\r
11301                                 selection.setContent('<hr />');\r
11302                         },\r
11303 \r
11304                         mceToggleVisualAid : function() {\r
11305                                 editor.hasVisual = !editor.hasVisual;\r
11306                                 editor.addVisual();\r
11307                         },\r
11308 \r
11309                         mceReplaceContent : function(command, ui, value) {\r
11310                                 selection.setContent(value.replace(/\{\$selection\}/g, selection.getContent({format : 'text'})));\r
11311                         },\r
11312 \r
11313                         mceInsertLink : function(command, ui, value) {\r
11314                                 var link = dom.getParent(selection.getNode(), 'a');\r
11315 \r
11316                                 if (tinymce.is(value, 'string'))\r
11317                                         value = {href : value};\r
11318 \r
11319                                 if (!link) {\r
11320                                         execNativeCommand('CreateLink', FALSE, 'javascript:mctmp(0);');\r
11321                                         each(dom.select('a[href=javascript:mctmp(0);]'), function(link) {\r
11322                                                 dom.setAttribs(link, value);\r
11323                                         });\r
11324                                 } else {\r
11325                                         if (value.href)\r
11326                                                 dom.setAttribs(link, value);\r
11327                                         else\r
11328                                                 editor.dom.remove(link, TRUE);\r
11329                                 }\r
11330                         },\r
11331                         \r
11332                         selectAll : function() {\r
11333                                 var root = dom.getRoot(), rng = dom.createRng();\r
11334 \r
11335                                 rng.setStart(root, 0);\r
11336                                 rng.setEnd(root, root.childNodes.length);\r
11337 \r
11338                                 editor.selection.setRng(rng);\r
11339                         }\r
11340                 });\r
11341 \r
11342                 // Add queryCommandState overrides\r
11343                 addCommands({\r
11344                         // Override justify commands\r
11345                         'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
11346                                 return isFormatMatch('align' + command.substring(7));\r
11347                         },\r
11348 \r
11349                         'Bold,Italic,Underline,Strikethrough' : function(command) {\r
11350                                 return isFormatMatch(command);\r
11351                         },\r
11352 \r
11353                         mceBlockQuote : function() {\r
11354                                 return isFormatMatch('blockquote');\r
11355                         },\r
11356 \r
11357                         Outdent : function() {\r
11358                                 var node;\r
11359 \r
11360                                 if (settings.inline_styles) {\r
11361                                         if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
11362                                                 return TRUE;\r
11363 \r
11364                                         if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0)\r
11365                                                 return TRUE;\r
11366                                 }\r
11367 \r
11368                                 return queryCommandState('InsertUnorderedList') || queryCommandState('InsertOrderedList') || (!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE'));\r
11369                         },\r
11370 \r
11371                         'InsertUnorderedList,InsertOrderedList' : function(command) {\r
11372                                 return dom.getParent(selection.getNode(), command == 'insertunorderedlist' ? 'UL' : 'OL');\r
11373                         }\r
11374                 }, 'state');\r
11375 \r
11376                 // Add queryCommandValue overrides\r
11377                 addCommands({\r
11378                         'FontSize,FontName' : function(command) {\r
11379                                 var value = 0, parent;\r
11380 \r
11381                                 if (parent = dom.getParent(selection.getNode(), 'span')) {\r
11382                                         if (command == 'fontsize')\r
11383                                                 value = parent.style.fontSize;\r
11384                                         else\r
11385                                                 value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();\r
11386                                 }\r
11387 \r
11388                                 return value;\r
11389                         }\r
11390                 }, 'value');\r
11391 \r
11392                 // Add undo manager logic\r
11393                 if (settings.custom_undo_redo) {\r
11394                         addCommands({\r
11395                                 Undo : function() {\r
11396                                         editor.undoManager.undo();\r
11397                                 },\r
11398 \r
11399                                 Redo : function() {\r
11400                                         editor.undoManager.redo();\r
11401                                 }\r
11402                         });\r
11403                 }\r
11404         };\r
11405 })(tinymce);\r
11406 (function(tinymce) {\r
11407         var Dispatcher = tinymce.util.Dispatcher;\r
11408 \r
11409         tinymce.UndoManager = function(editor) {\r
11410                 var self, index = 0, data = [];\r
11411 \r
11412                 function getContent() {\r
11413                         return tinymce.trim(editor.getContent({format : 'raw', no_events : 1}));\r
11414                 };\r
11415 \r
11416                 return self = {\r
11417                         typing : 0,\r
11418 \r
11419                         onAdd : new Dispatcher(self),\r
11420                         onUndo : new Dispatcher(self),\r
11421                         onRedo : new Dispatcher(self),\r
11422 \r
11423                         add : function(level) {\r
11424                                 var i, settings = editor.settings, lastLevel;\r
11425 \r
11426                                 level = level || {};\r
11427                                 level.content = getContent();\r
11428 \r
11429                                 // Add undo level if needed\r
11430                                 lastLevel = data[index];\r
11431                                 if (lastLevel && lastLevel.content == level.content) {\r
11432                                         if (index > 0 || data.length == 1)\r
11433                                                 return null;\r
11434                                 }\r
11435 \r
11436                                 // Time to compress\r
11437                                 if (settings.custom_undo_redo_levels) {\r
11438                                         if (data.length > settings.custom_undo_redo_levels) {\r
11439                                                 for (i = 0; i < data.length - 1; i++)\r
11440                                                         data[i] = data[i + 1];\r
11441 \r
11442                                                 data.length--;\r
11443                                                 index = data.length;\r
11444                                         }\r
11445                                 }\r
11446 \r
11447                                 // Get a non intrusive normalized bookmark\r
11448                                 level.bookmark = editor.selection.getBookmark(2, true);\r
11449 \r
11450                                 // Crop array if needed\r
11451                                 if (index < data.length - 1) {\r
11452                                         // Treat first level as initial\r
11453                                         if (index == 0)\r
11454                                                 data = [];\r
11455                                         else\r
11456                                                 data.length = index + 1;\r
11457                                 }\r
11458 \r
11459                                 data.push(level);\r
11460                                 index = data.length - 1;\r
11461 \r
11462                                 self.onAdd.dispatch(self, level);\r
11463                                 editor.isNotDirty = 0;\r
11464 \r
11465                                 return level;\r
11466                         },\r
11467 \r
11468                         undo : function() {\r
11469                                 var level, i;\r
11470 \r
11471                                 if (self.typing) {\r
11472                                         self.add();\r
11473                                         self.typing = 0;\r
11474                                 }\r
11475 \r
11476                                 if (index > 0) {\r
11477                                         level = data[--index];\r
11478 \r
11479                                         editor.setContent(level.content, {format : 'raw'});\r
11480                                         editor.selection.moveToBookmark(level.bookmark);\r
11481 \r
11482                                         self.onUndo.dispatch(self, level);\r
11483                                 }\r
11484 \r
11485                                 return level;\r
11486                         },\r
11487 \r
11488                         redo : function() {\r
11489                                 var level;\r
11490 \r
11491                                 if (index < data.length - 1) {\r
11492                                         level = data[++index];\r
11493 \r
11494                                         editor.setContent(level.content, {format : 'raw'});\r
11495                                         editor.selection.moveToBookmark(level.bookmark);\r
11496 \r
11497                                         self.onRedo.dispatch(self, level);\r
11498                                 }\r
11499 \r
11500                                 return level;\r
11501                         },\r
11502 \r
11503                         clear : function() {\r
11504                                 data = [];\r
11505                                 index = self.typing = 0;\r
11506                         },\r
11507 \r
11508                         hasUndo : function() {\r
11509                                 return index > 0 || self.typing;\r
11510                         },\r
11511 \r
11512                         hasRedo : function() {\r
11513                                 return index < data.length - 1;\r
11514                         }\r
11515                 };\r
11516         };\r
11517 })(tinymce);\r
11518 \r
11519 (function(tinymce) {\r
11520         // Shorten names\r
11521         var Event = tinymce.dom.Event,\r
11522                 isIE = tinymce.isIE,\r
11523                 isGecko = tinymce.isGecko,\r
11524                 isOpera = tinymce.isOpera,\r
11525                 each = tinymce.each,\r
11526                 extend = tinymce.extend,\r
11527                 TRUE = true,\r
11528                 FALSE = false;\r
11529 \r
11530         function cloneFormats(node) {\r
11531                 var clone, temp, inner;\r
11532 \r
11533                 do {\r
11534                         if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) {\r
11535                                 if (clone) {\r
11536                                         temp = node.cloneNode(false);\r
11537                                         temp.appendChild(clone);\r
11538                                         clone = temp;\r
11539                                 } else {\r
11540                                         clone = inner = node.cloneNode(false);\r
11541                                 }\r
11542 \r
11543                                 clone.removeAttribute('id');\r
11544                         }\r
11545                 } while (node = node.parentNode);\r
11546 \r
11547                 if (clone)\r
11548                         return {wrapper : clone, inner : inner};\r
11549         };\r
11550 \r
11551         // Checks if the selection/caret is at the end of the specified block element\r
11552         function isAtEnd(rng, par) {\r
11553                 var rng2 = par.ownerDocument.createRange();\r
11554 \r
11555                 rng2.setStart(rng.endContainer, rng.endOffset);\r
11556                 rng2.setEndAfter(par);\r
11557 \r
11558                 // 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
11559                 return rng2.cloneContents().textContent.length == 0;\r
11560         };\r
11561 \r
11562         function isEmpty(n) {\r
11563                 n = n.innerHTML;\r
11564 \r
11565                 n = n.replace(/<(img|hr|table|input|select|textarea)[ \>]/gi, '-'); // Keep these convert them to - chars\r
11566                 n = n.replace(/<[^>]+>/g, ''); // Remove all tags\r
11567 \r
11568                 return n.replace(/[ \u00a0\t\r\n]+/g, '') == '';\r
11569         };\r
11570 \r
11571         function splitList(selection, dom, li) {\r
11572                 var listBlock, block;\r
11573 \r
11574                 if (isEmpty(li)) {\r
11575                         listBlock = dom.getParent(li, 'ul,ol');\r
11576 \r
11577                         if (!dom.getParent(listBlock.parentNode, 'ul,ol')) {\r
11578                                 dom.split(listBlock, li);\r
11579                                 block = dom.create('p', 0, '<br _mce_bogus="1" />');\r
11580                                 dom.replace(block, li);\r
11581                                 selection.select(block, 1);\r
11582                         }\r
11583 \r
11584                         return FALSE;\r
11585                 }\r
11586 \r
11587                 return TRUE;\r
11588         };\r
11589 \r
11590         tinymce.create('tinymce.ForceBlocks', {\r
11591                 ForceBlocks : function(ed) {\r
11592                         var t = this, s = ed.settings, elm;\r
11593 \r
11594                         t.editor = ed;\r
11595                         t.dom = ed.dom;\r
11596                         elm = (s.forced_root_block || 'p').toLowerCase();\r
11597                         s.element = elm.toUpperCase();\r
11598 \r
11599                         ed.onPreInit.add(t.setup, t);\r
11600 \r
11601                         t.reOpera = new RegExp('(\\u00a0|&#160;|&nbsp;)<\/' + elm + '>', 'gi');\r
11602                         t.rePadd = new RegExp('<p( )([^>]+)><\\\/p>|<p( )([^>]+)\\\/>|<p( )([^>]+)>\\s+<\\\/p>|<p><\\\/p>|<p\\\/>|<p>\\s+<\\\/p>'.replace(/p/g, elm), 'gi');\r
11603                         t.reNbsp2BR1 = new RegExp('<p( )([^>]+)>[\\s\\u00a0]+<\\\/p>|<p>[\\s\\u00a0]+<\\\/p>'.replace(/p/g, elm), 'gi');\r
11604                         t.reNbsp2BR2 = new RegExp('<%p()([^>]+)>(&nbsp;|&#160;)<\\\/%p>|<%p>(&nbsp;|&#160;)<\\\/%p>'.replace(/%p/g, elm), 'gi');\r
11605                         t.reBR2Nbsp = new RegExp('<p( )([^>]+)>\\s*<br \\\/>\\s*<\\\/p>|<p>\\s*<br \\\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi');\r
11606 \r
11607                         function padd(ed, o) {\r
11608                                 if (isOpera)\r
11609                                         o.content = o.content.replace(t.reOpera, '</' + elm + '>');\r
11610 \r
11611                                 o.content = o.content.replace(t.rePadd, '<' + elm + '$1$2$3$4$5$6>\u00a0</' + elm + '>');\r
11612 \r
11613                                 if (!isIE && !isOpera && o.set) {\r
11614                                         // Use &nbsp; instead of BR in padded paragraphs\r
11615                                         o.content = o.content.replace(t.reNbsp2BR1, '<' + elm + '$1$2><br /></' + elm + '>');\r
11616                                         o.content = o.content.replace(t.reNbsp2BR2, '<' + elm + '$1$2><br /></' + elm + '>');\r
11617                                 } else\r
11618                                         o.content = o.content.replace(t.reBR2Nbsp, '<' + elm + '$1$2>\u00a0</' + elm + '>');\r
11619                         };\r
11620 \r
11621                         ed.onBeforeSetContent.add(padd);\r
11622                         ed.onPostProcess.add(padd);\r
11623 \r
11624                         if (s.forced_root_block) {\r
11625                                 ed.onInit.add(t.forceRoots, t);\r
11626                                 ed.onSetContent.add(t.forceRoots, t);\r
11627                                 ed.onBeforeGetContent.add(t.forceRoots, t);\r
11628                         }\r
11629                 },\r
11630 \r
11631                 setup : function() {\r
11632                         var t = this, ed = t.editor, s = ed.settings, dom = ed.dom, selection = ed.selection;\r
11633 \r
11634                         // Force root blocks when typing and when getting output\r
11635                         if (s.forced_root_block) {\r
11636                                 ed.onBeforeExecCommand.add(t.forceRoots, t);\r
11637                                 ed.onKeyUp.add(t.forceRoots, t);\r
11638                                 ed.onPreProcess.add(t.forceRoots, t);\r
11639                         }\r
11640 \r
11641                         if (s.force_br_newlines) {\r
11642                                 // Force IE to produce BRs on enter\r
11643                                 if (isIE) {\r
11644                                         ed.onKeyPress.add(function(ed, e) {\r
11645                                                 var n;\r
11646 \r
11647                                                 if (e.keyCode == 13 && selection.getNode().nodeName != 'LI') {\r
11648                                                         selection.setContent('<br id="__" /> ', {format : 'raw'});\r
11649                                                         n = dom.get('__');\r
11650                                                         n.removeAttribute('id');\r
11651                                                         selection.select(n);\r
11652                                                         selection.collapse();\r
11653                                                         return Event.cancel(e);\r
11654                                                 }\r
11655                                         });\r
11656                                 }\r
11657                         }\r
11658 \r
11659                         if (s.force_p_newlines) {\r
11660                                 if (!isIE) {\r
11661                                         ed.onKeyPress.add(function(ed, e) {\r
11662                                                 if (e.keyCode == 13 && !e.shiftKey && !t.insertPara(e))\r
11663                                                         Event.cancel(e);\r
11664                                         });\r
11665                                 } else {\r
11666                                         // Ungly hack to for IE to preserve the formatting when you press\r
11667                                         // enter at the end of a block element with formatted contents\r
11668                                         // This logic overrides the browsers default logic with\r
11669                                         // custom logic that enables us to control the output\r
11670                                         tinymce.addUnload(function() {\r
11671                                                 t._previousFormats = 0; // Fix IE leak\r
11672                                         });\r
11673 \r
11674                                         ed.onKeyPress.add(function(ed, e) {\r
11675                                                 t._previousFormats = 0;\r
11676 \r
11677                                                 // Clone the current formats, this will later be applied to the new block contents\r
11678                                                 if (e.keyCode == 13 && !e.shiftKey && ed.selection.isCollapsed() && s.keep_styles)\r
11679                                                         t._previousFormats = cloneFormats(ed.selection.getStart());\r
11680                                         });\r
11681 \r
11682                                         ed.onKeyUp.add(function(ed, e) {\r
11683                                                 // Let IE break the element and the wrap the new caret location in the previous formats\r
11684                                                 if (e.keyCode == 13 && !e.shiftKey) {\r
11685                                                         var parent = ed.selection.getStart(), fmt = t._previousFormats;\r
11686 \r
11687                                                         // Parent is an empty block\r
11688                                                         if (!parent.hasChildNodes()) {\r
11689                                                                 parent = dom.getParent(parent, dom.isBlock);\r
11690 \r
11691                                                                 if (parent) {\r
11692                                                                         parent.innerHTML = '';\r
11693         \r
11694                                                                         if (t._previousFormats) {\r
11695                                                                                 parent.appendChild(fmt.wrapper);\r
11696                                                                                 fmt.inner.innerHTML = '\uFEFF';\r
11697                                                                         } else\r
11698                                                                                 parent.innerHTML = '\uFEFF';\r
11699 \r
11700                                                                         selection.select(parent, 1);\r
11701                                                                         ed.getDoc().execCommand('Delete', false, null);\r
11702                                                                 }\r
11703                                                         }\r
11704                                                 }\r
11705                                         });\r
11706                                 }\r
11707 \r
11708                                 if (isGecko) {\r
11709                                         ed.onKeyDown.add(function(ed, e) {\r
11710                                                 if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey)\r
11711                                                         t.backspaceDelete(e, e.keyCode == 8);\r
11712                                         });\r
11713                                 }\r
11714                         }\r
11715 \r
11716                         // Workaround for missing shift+enter support, http://bugs.webkit.org/show_bug.cgi?id=16973\r
11717                         if (tinymce.isWebKit) {\r
11718                                 function insertBr(ed) {\r
11719                                         var rng = selection.getRng(), br, div = dom.create('div', null, ' '), divYPos, vpHeight = dom.getViewPort(ed.getWin()).h;\r
11720 \r
11721                                         // Insert BR element\r
11722                                         rng.insertNode(br = dom.create('br'));\r
11723 \r
11724                                         // Place caret after BR\r
11725                                         rng.setStartAfter(br);\r
11726                                         rng.setEndAfter(br);\r
11727                                         selection.setRng(rng);\r
11728 \r
11729                                         // Could not place caret after BR then insert an nbsp entity and move the caret\r
11730                                         if (selection.getSel().focusNode == br.previousSibling) {\r
11731                                                 selection.select(dom.insertAfter(dom.doc.createTextNode('\u00a0'), br));\r
11732                                                 selection.collapse(TRUE);\r
11733                                         }\r
11734 \r
11735                                         // Create a temporary DIV after the BR and get the position as it\r
11736                                         // seems like getPos() returns 0 for text nodes and BR elements.\r
11737                                         dom.insertAfter(div, br);\r
11738                                         divYPos = dom.getPos(div).y;\r
11739                                         dom.remove(div);\r
11740 \r
11741                                         // Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117\r
11742                                         if (divYPos > vpHeight) // It is not necessary to scroll if the DIV is inside the view port.\r
11743                                                 ed.getWin().scrollTo(0, divYPos);\r
11744                                 };\r
11745 \r
11746                                 ed.onKeyPress.add(function(ed, e) {\r
11747                                         if (e.keyCode == 13 && (e.shiftKey || (s.force_br_newlines && !dom.getParent(selection.getNode(), 'h1,h2,h3,h4,h5,h6,ol,ul')))) {\r
11748                                                 insertBr(ed);\r
11749                                                 Event.cancel(e);\r
11750                                         }\r
11751                                 });\r
11752                         }\r
11753 \r
11754                         // Padd empty inline elements within block elements\r
11755                         // For example: <p><strong><em></em></strong></p> becomes <p><strong><em>&nbsp;</em></strong></p>\r
11756                         ed.onPreProcess.add(function(ed, o) {\r
11757                                 each(dom.select('p,h1,h2,h3,h4,h5,h6,div', o.node), function(p) {\r
11758                                         if (isEmpty(p)) {\r
11759                                                 each(dom.select('span,em,strong,b,i', o.node), function(n) {\r
11760                                                         if (!n.hasChildNodes()) {\r
11761                                                                 n.appendChild(ed.getDoc().createTextNode('\u00a0'));\r
11762                                                                 return FALSE; // Break the loop one padding is enough\r
11763                                                         }\r
11764                                                 });\r
11765                                         }\r
11766                                 });\r
11767                         });\r
11768 \r
11769                         // IE specific fixes\r
11770                         if (isIE) {\r
11771                                 // Replaces IE:s auto generated paragraphs with the specified element name\r
11772                                 if (s.element != 'P') {\r
11773                                         ed.onKeyPress.add(function(ed, e) {\r
11774                                                 t.lastElm = selection.getNode().nodeName;\r
11775                                         });\r
11776 \r
11777                                         ed.onKeyUp.add(function(ed, e) {\r
11778                                                 var bl, n = selection.getNode(), b = ed.getBody();\r
11779 \r
11780                                                 if (b.childNodes.length === 1 && n.nodeName == 'P') {\r
11781                                                         n = dom.rename(n, s.element);\r
11782                                                         selection.select(n);\r
11783                                                         selection.collapse();\r
11784                                                         ed.nodeChanged();\r
11785                                                 } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') {\r
11786                                                         bl = dom.getParent(n, 'p');\r
11787 \r
11788                                                         if (bl) {\r
11789                                                                 dom.rename(bl, s.element);\r
11790                                                                 ed.nodeChanged();\r
11791                                                         }\r
11792                                                 }\r
11793                                         });\r
11794                                 }\r
11795                         }\r
11796                 },\r
11797 \r
11798                 find : function(n, t, s) {\r
11799                         var ed = this.editor, w = ed.getDoc().createTreeWalker(n, 4, null, FALSE), c = -1;\r
11800 \r
11801                         while (n = w.nextNode()) {\r
11802                                 c++;\r
11803 \r
11804                                 // Index by node\r
11805                                 if (t == 0 && n == s)\r
11806                                         return c;\r
11807 \r
11808                                 // Node by index\r
11809                                 if (t == 1 && c == s)\r
11810                                         return n;\r
11811                         }\r
11812 \r
11813                         return -1;\r
11814                 },\r
11815 \r
11816                 forceRoots : function(ed, e) {\r
11817                         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
11818                         var nx, bl, bp, sp, le, nl = b.childNodes, i, n, eid;\r
11819 \r
11820                         // Fix for bug #1863847\r
11821                         //if (e && e.keyCode == 13)\r
11822                         //      return TRUE;\r
11823 \r
11824                         // Wrap non blocks into blocks\r
11825                         for (i = nl.length - 1; i >= 0; i--) {\r
11826                                 nx = nl[i];\r
11827 \r
11828                                 // Ignore internal elements\r
11829                                 if (nx.nodeType === 1 && nx.getAttribute('_mce_type')) {\r
11830                                         bl = null;\r
11831                                         continue;\r
11832                                 }\r
11833 \r
11834                                 // Is text or non block element\r
11835                                 if (nx.nodeType === 3 || (!t.dom.isBlock(nx) && nx.nodeType !== 8 && !/^(script|mce:script|style|mce:style)$/i.test(nx.nodeName))) {\r
11836                                         if (!bl) {\r
11837                                                 // Create new block but ignore whitespace\r
11838                                                 if (nx.nodeType != 3 || /[^\s]/g.test(nx.nodeValue)) {\r
11839                                                         // Store selection\r
11840                                                         if (si == -2 && r) {\r
11841                                                                 if (!isIE) {\r
11842                                                                         // If selection is element then mark it\r
11843                                                                         if (r.startContainer.nodeType == 1 && (n = r.startContainer.childNodes[r.startOffset]) && n.nodeType == 1) {\r
11844                                                                                 // Save the id of the selected element\r
11845                                                                                 eid = n.getAttribute("id");\r
11846                                                                                 n.setAttribute("id", "__mce");\r
11847                                                                         } else {\r
11848                                                                                 // If element is inside body, might not be the case in contentEdiable mode\r
11849                                                                                 if (ed.dom.getParent(r.startContainer, function(e) {return e === b;})) {\r
11850                                                                                         so = r.startOffset;\r
11851                                                                                         eo = r.endOffset;\r
11852                                                                                         si = t.find(b, 0, r.startContainer);\r
11853                                                                                         ei = t.find(b, 0, r.endContainer);\r
11854                                                                                 }\r
11855                                                                         }\r
11856                                                                 } else {\r
11857                                                                         // Force control range into text range\r
11858                                                                         if (r.item) {\r
11859                                                                                 tr = d.body.createTextRange();\r
11860                                                                                 tr.moveToElementText(r.item(0));\r
11861                                                                                 r = tr;\r
11862                                                                         }\r
11863 \r
11864                                                                         tr = d.body.createTextRange();\r
11865                                                                         tr.moveToElementText(b);\r
11866                                                                         tr.collapse(1);\r
11867                                                                         bp = tr.move('character', c) * -1;\r
11868 \r
11869                                                                         tr = r.duplicate();\r
11870                                                                         tr.collapse(1);\r
11871                                                                         sp = tr.move('character', c) * -1;\r
11872 \r
11873                                                                         tr = r.duplicate();\r
11874                                                                         tr.collapse(0);\r
11875                                                                         le = (tr.move('character', c) * -1) - sp;\r
11876 \r
11877                                                                         si = sp - bp;\r
11878                                                                         ei = le;\r
11879                                                                 }\r
11880                                                         }\r
11881 \r
11882                                                         // Uses replaceChild instead of cloneNode since it removes selected attribute from option elements on IE\r
11883                                                         // See: http://support.microsoft.com/kb/829907\r
11884                                                         bl = ed.dom.create(ed.settings.forced_root_block);\r
11885                                                         nx.parentNode.replaceChild(bl, nx);\r
11886                                                         bl.appendChild(nx);\r
11887                                                 }\r
11888                                         } else {\r
11889                                                 if (bl.hasChildNodes())\r
11890                                                         bl.insertBefore(nx, bl.firstChild);\r
11891                                                 else\r
11892                                                         bl.appendChild(nx);\r
11893                                         }\r
11894                                 } else\r
11895                                         bl = null; // Time to create new block\r
11896                         }\r
11897 \r
11898                         // Restore selection\r
11899                         if (si != -2) {\r
11900                                 if (!isIE) {\r
11901                                         bl = b.getElementsByTagName(ed.settings.element)[0];\r
11902                                         r = d.createRange();\r
11903 \r
11904                                         // Select last location or generated block\r
11905                                         if (si != -1)\r
11906                                                 r.setStart(t.find(b, 1, si), so);\r
11907                                         else\r
11908                                                 r.setStart(bl, 0);\r
11909 \r
11910                                         // Select last location or generated block\r
11911                                         if (ei != -1)\r
11912                                                 r.setEnd(t.find(b, 1, ei), eo);\r
11913                                         else\r
11914                                                 r.setEnd(bl, 0);\r
11915 \r
11916                                         if (s) {\r
11917                                                 s.removeAllRanges();\r
11918                                                 s.addRange(r);\r
11919                                         }\r
11920                                 } else {\r
11921                                         try {\r
11922                                                 r = s.createRange();\r
11923                                                 r.moveToElementText(b);\r
11924                                                 r.collapse(1);\r
11925                                                 r.moveStart('character', si);\r
11926                                                 r.moveEnd('character', ei);\r
11927                                                 r.select();\r
11928                                         } catch (ex) {\r
11929                                                 // Ignore\r
11930                                         }\r
11931                                 }\r
11932                         } else if (!isIE && (n = ed.dom.get('__mce'))) {\r
11933                                 // Restore the id of the selected element\r
11934                                 if (eid)\r
11935                                         n.setAttribute('id', eid);\r
11936                                 else\r
11937                                         n.removeAttribute('id');\r
11938 \r
11939                                 // Move caret before selected element\r
11940                                 r = d.createRange();\r
11941                                 r.setStartBefore(n);\r
11942                                 r.setEndBefore(n);\r
11943                                 se.setRng(r);\r
11944                         }\r
11945                 },\r
11946 \r
11947                 getParentBlock : function(n) {\r
11948                         var d = this.dom;\r
11949 \r
11950                         return d.getParent(n, d.isBlock);\r
11951                 },\r
11952 \r
11953                 insertPara : function(e) {\r
11954                         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
11955                         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
11956 \r
11957                         // If root blocks are forced then use Operas default behavior since it's really good\r
11958 // Removed due to bug: #1853816\r
11959 //                      if (se.forced_root_block && isOpera)\r
11960 //                              return TRUE;\r
11961 \r
11962                         // Setup before range\r
11963                         rb = d.createRange();\r
11964 \r
11965                         // If is before the first block element and in body, then move it into first block element\r
11966                         rb.setStart(s.anchorNode, s.anchorOffset);\r
11967                         rb.collapse(TRUE);\r
11968 \r
11969                         // Setup after range\r
11970                         ra = d.createRange();\r
11971 \r
11972                         // If is before the first block element and in body, then move it into first block element\r
11973                         ra.setStart(s.focusNode, s.focusOffset);\r
11974                         ra.collapse(TRUE);\r
11975 \r
11976                         // Setup start/end points\r
11977                         dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0;\r
11978                         sn = dir ? s.anchorNode : s.focusNode;\r
11979                         so = dir ? s.anchorOffset : s.focusOffset;\r
11980                         en = dir ? s.focusNode : s.anchorNode;\r
11981                         eo = dir ? s.focusOffset : s.anchorOffset;\r
11982 \r
11983                         // If selection is in empty table cell\r
11984                         if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) {\r
11985                                 if (sn.firstChild.nodeName == 'BR')\r
11986                                         dom.remove(sn.firstChild); // Remove BR\r
11987 \r
11988                                 // Create two new block elements\r
11989                                 if (sn.childNodes.length == 0) {\r
11990                                         ed.dom.add(sn, se.element, null, '<br />');\r
11991                                         aft = ed.dom.add(sn, se.element, null, '<br />');\r
11992                                 } else {\r
11993                                         n = sn.innerHTML;\r
11994                                         sn.innerHTML = '';\r
11995                                         ed.dom.add(sn, se.element, null, n);\r
11996                                         aft = ed.dom.add(sn, se.element, null, '<br />');\r
11997                                 }\r
11998 \r
11999                                 // Move caret into the last one\r
12000                                 r = d.createRange();\r
12001                                 r.selectNodeContents(aft);\r
12002                                 r.collapse(1);\r
12003                                 ed.selection.setRng(r);\r
12004 \r
12005                                 return FALSE;\r
12006                         }\r
12007 \r
12008                         // If the caret is in an invalid location in FF we need to move it into the first block\r
12009                         if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) {\r
12010                                 sn = en = sn.firstChild;\r
12011                                 so = eo = 0;\r
12012                                 rb = d.createRange();\r
12013                                 rb.setStart(sn, 0);\r
12014                                 ra = d.createRange();\r
12015                                 ra.setStart(en, 0);\r
12016                         }\r
12017 \r
12018                         // Never use body as start or end node\r
12019                         sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes\r
12020                         sn = sn.nodeName == "BODY" ? sn.firstChild : sn;\r
12021                         en = en.nodeName == "HTML" ? d.body : en; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes\r
12022                         en = en.nodeName == "BODY" ? en.firstChild : en;\r
12023 \r
12024                         // Get start and end blocks\r
12025                         sb = t.getParentBlock(sn);\r
12026                         eb = t.getParentBlock(en);\r
12027                         bn = sb ? sb.nodeName : se.element; // Get block name to create\r
12028 \r
12029                         // Return inside list use default browser behavior\r
12030                         if (n = t.dom.getParent(sb, 'li,pre')) {\r
12031                                 if (n.nodeName == 'LI')\r
12032                                         return splitList(ed.selection, t.dom, n);\r
12033 \r
12034                                 return TRUE;\r
12035                         }\r
12036 \r
12037                         // If caption or absolute layers then always generate new blocks within\r
12038                         if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {\r
12039                                 bn = se.element;\r
12040                                 sb = null;\r
12041                         }\r
12042 \r
12043                         // If caption or absolute layers then always generate new blocks within\r
12044                         if (eb && (eb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {\r
12045                                 bn = se.element;\r
12046                                 eb = null;\r
12047                         }\r
12048 \r
12049                         // Use P instead\r
12050                         if (/(TD|TABLE|TH|CAPTION)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(dom.getStyle(sb, 'float', 1)))) {\r
12051                                 bn = se.element;\r
12052                                 sb = eb = null;\r
12053                         }\r
12054 \r
12055                         // Setup new before and after blocks\r
12056                         bef = (sb && sb.nodeName == bn) ? sb.cloneNode(0) : ed.dom.create(bn);\r
12057                         aft = (eb && eb.nodeName == bn) ? eb.cloneNode(0) : ed.dom.create(bn);\r
12058 \r
12059                         // Remove id from after clone\r
12060                         aft.removeAttribute('id');\r
12061 \r
12062                         // Is header and cursor is at the end, then force paragraph under\r
12063                         if (/^(H[1-6])$/.test(bn) && isAtEnd(r, sb))\r
12064                                 aft = ed.dom.create(se.element);\r
12065 \r
12066                         // Find start chop node\r
12067                         n = sc = sn;\r
12068                         do {\r
12069                                 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))\r
12070                                         break;\r
12071 \r
12072                                 sc = n;\r
12073                         } while ((n = n.previousSibling ? n.previousSibling : n.parentNode));\r
12074 \r
12075                         // Find end chop node\r
12076                         n = ec = en;\r
12077                         do {\r
12078                                 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))\r
12079                                         break;\r
12080 \r
12081                                 ec = n;\r
12082                         } while ((n = n.nextSibling ? n.nextSibling : n.parentNode));\r
12083 \r
12084                         // Place first chop part into before block element\r
12085                         if (sc.nodeName == bn)\r
12086                                 rb.setStart(sc, 0);\r
12087                         else\r
12088                                 rb.setStartBefore(sc);\r
12089 \r
12090                         rb.setEnd(sn, so);\r
12091                         bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari\r
12092 \r
12093                         // Place secnd chop part within new block element\r
12094                         try {\r
12095                                 ra.setEndAfter(ec);\r
12096                         } catch(ex) {\r
12097                                 //console.debug(s.focusNode, s.focusOffset);\r
12098                         }\r
12099 \r
12100                         ra.setStart(en, eo);\r
12101                         aft.appendChild(ra.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari\r
12102 \r
12103                         // Create range around everything\r
12104                         r = d.createRange();\r
12105                         if (!sc.previousSibling && sc.parentNode.nodeName == bn) {\r
12106                                 r.setStartBefore(sc.parentNode);\r
12107                         } else {\r
12108                                 if (rb.startContainer.nodeName == bn && rb.startOffset == 0)\r
12109                                         r.setStartBefore(rb.startContainer);\r
12110                                 else\r
12111                                         r.setStart(rb.startContainer, rb.startOffset);\r
12112                         }\r
12113 \r
12114                         if (!ec.nextSibling && ec.parentNode.nodeName == bn)\r
12115                                 r.setEndAfter(ec.parentNode);\r
12116                         else\r
12117                                 r.setEnd(ra.endContainer, ra.endOffset);\r
12118 \r
12119                         // Delete and replace it with new block elements\r
12120                         r.deleteContents();\r
12121 \r
12122                         if (isOpera)\r
12123                                 ed.getWin().scrollTo(0, vp.y);\r
12124 \r
12125                         // Never wrap blocks in blocks\r
12126                         if (bef.firstChild && bef.firstChild.nodeName == bn)\r
12127                                 bef.innerHTML = bef.firstChild.innerHTML;\r
12128 \r
12129                         if (aft.firstChild && aft.firstChild.nodeName == bn)\r
12130                                 aft.innerHTML = aft.firstChild.innerHTML;\r
12131 \r
12132                         // Padd empty blocks\r
12133                         if (isEmpty(bef))\r
12134                                 bef.innerHTML = '<br />';\r
12135 \r
12136                         function appendStyles(e, en) {\r
12137                                 var nl = [], nn, n, i;\r
12138 \r
12139                                 e.innerHTML = '';\r
12140 \r
12141                                 // Make clones of style elements\r
12142                                 if (se.keep_styles) {\r
12143                                         n = en;\r
12144                                         do {\r
12145                                                 // We only want style specific elements\r
12146                                                 if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) {\r
12147                                                         nn = n.cloneNode(FALSE);\r
12148                                                         dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique\r
12149                                                         nl.push(nn);\r
12150                                                 }\r
12151                                         } while (n = n.parentNode);\r
12152                                 }\r
12153 \r
12154                                 // Append style elements to aft\r
12155                                 if (nl.length > 0) {\r
12156                                         for (i = nl.length - 1, nn = e; i >= 0; i--)\r
12157                                                 nn = nn.appendChild(nl[i]);\r
12158 \r
12159                                         // Padd most inner style element\r
12160                                         nl[0].innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there\r
12161                                         return nl[0]; // Move caret to most inner element\r
12162                                 } else\r
12163                                         e.innerHTML = isOpera ? '&nbsp;' : '<br />'; // Extra space for Opera so that the caret can move there\r
12164                         };\r
12165 \r
12166                         // Fill empty afterblook with current style\r
12167                         if (isEmpty(aft))\r
12168                                 car = appendStyles(aft, en);\r
12169 \r
12170                         // Opera needs this one backwards for older versions\r
12171                         if (isOpera && parseFloat(opera.version()) < 9.5) {\r
12172                                 r.insertNode(bef);\r
12173                                 r.insertNode(aft);\r
12174                         } else {\r
12175                                 r.insertNode(aft);\r
12176                                 r.insertNode(bef);\r
12177                         }\r
12178 \r
12179                         // Normalize\r
12180                         aft.normalize();\r
12181                         bef.normalize();\r
12182 \r
12183                         function first(n) {\r
12184                                 return d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE).nextNode() || n;\r
12185                         };\r
12186 \r
12187                         // Move cursor and scroll into view\r
12188                         r = d.createRange();\r
12189                         r.selectNodeContents(isGecko ? first(car || aft) : car || aft);\r
12190                         r.collapse(1);\r
12191                         s.removeAllRanges();\r
12192                         s.addRange(r);\r
12193 \r
12194                         // 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
12195                         y = ed.dom.getPos(aft).y;\r
12196                         ch = aft.clientHeight;\r
12197 \r
12198                         // Is element within viewport\r
12199                         if (y < vp.y || y + ch > vp.y + vp.h) {\r
12200                                 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
12201                                 //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
12202                         }\r
12203 \r
12204                         return FALSE;\r
12205                 },\r
12206 \r
12207                 backspaceDelete : function(e, bs) {\r
12208                         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
12209 \r
12210                         // Delete when caret is behind a element doesn't work correctly on Gecko see #3011651\r
12211                         if (!bs && r.collapsed && sc.nodeType == 1 && r.startOffset == sc.childNodes.length) {\r
12212                                 walker = new tinymce.dom.TreeWalker(sc.lastChild, sc);\r
12213 \r
12214                                 // Walk the dom backwards until we find a text node\r
12215                                 for (n = sc.lastChild; n; n = walker.prev()) {\r
12216                                         if (n.nodeType == 3) {\r
12217                                                 r.setStart(n, n.nodeValue.length);\r
12218                                                 r.collapse(true);\r
12219                                                 se.setRng(r);\r
12220                                                 return;\r
12221                                         }\r
12222                                 }\r
12223                         }\r
12224 \r
12225                         // The caret sometimes gets stuck in Gecko if you delete empty paragraphs\r
12226                         // This workaround removes the element by hand and moves the caret to the previous element\r
12227                         if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) {\r
12228                                 if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) {\r
12229                                         // Find previous block element\r
12230                                         n = sc;\r
12231                                         while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ;\r
12232 \r
12233                                         if (n) {\r
12234                                                 if (sc != b.firstChild) {\r
12235                                                         // Find last text node\r
12236                                                         w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE);\r
12237                                                         while (tn = w.nextNode())\r
12238                                                                 n = tn;\r
12239 \r
12240                                                         // Place caret at the end of last text node\r
12241                                                         r = ed.getDoc().createRange();\r
12242                                                         r.setStart(n, n.nodeValue ? n.nodeValue.length : 0);\r
12243                                                         r.setEnd(n, n.nodeValue ? n.nodeValue.length : 0);\r
12244                                                         se.setRng(r);\r
12245 \r
12246                                                         // Remove the target container\r
12247                                                         ed.dom.remove(sc);\r
12248                                                 }\r
12249 \r
12250                                                 return Event.cancel(e);\r
12251                                         }\r
12252                                 }\r
12253                         }\r
12254                 }\r
12255         });\r
12256 })(tinymce);\r
12257 \r
12258 (function(tinymce) {\r
12259         // Shorten names\r
12260         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;\r
12261 \r
12262         tinymce.create('tinymce.ControlManager', {\r
12263                 ControlManager : function(ed, s) {\r
12264                         var t = this, i;\r
12265 \r
12266                         s = s || {};\r
12267                         t.editor = ed;\r
12268                         t.controls = {};\r
12269                         t.onAdd = new tinymce.util.Dispatcher(t);\r
12270                         t.onPostRender = new tinymce.util.Dispatcher(t);\r
12271                         t.prefix = s.prefix || ed.id + '_';\r
12272                         t._cls = {};\r
12273 \r
12274                         t.onPostRender.add(function() {\r
12275                                 each(t.controls, function(c) {\r
12276                                         c.postRender();\r
12277                                 });\r
12278                         });\r
12279                 },\r
12280 \r
12281                 get : function(id) {\r
12282                         return this.controls[this.prefix + id] || this.controls[id];\r
12283                 },\r
12284 \r
12285                 setActive : function(id, s) {\r
12286                         var c = null;\r
12287 \r
12288                         if (c = this.get(id))\r
12289                                 c.setActive(s);\r
12290 \r
12291                         return c;\r
12292                 },\r
12293 \r
12294                 setDisabled : function(id, s) {\r
12295                         var c = null;\r
12296 \r
12297                         if (c = this.get(id))\r
12298                                 c.setDisabled(s);\r
12299 \r
12300                         return c;\r
12301                 },\r
12302 \r
12303                 add : function(c) {\r
12304                         var t = this;\r
12305 \r
12306                         if (c) {\r
12307                                 t.controls[c.id] = c;\r
12308                                 t.onAdd.dispatch(c, t);\r
12309                         }\r
12310 \r
12311                         return c;\r
12312                 },\r
12313 \r
12314                 createControl : function(n) {\r
12315                         var c, t = this, ed = t.editor;\r
12316 \r
12317                         each(ed.plugins, function(p) {\r
12318                                 if (p.createControl) {\r
12319                                         c = p.createControl(n, t);\r
12320 \r
12321                                         if (c)\r
12322                                                 return false;\r
12323                                 }\r
12324                         });\r
12325 \r
12326                         switch (n) {\r
12327                                 case "|":\r
12328                                 case "separator":\r
12329                                         return t.createSeparator();\r
12330                         }\r
12331 \r
12332                         if (!c && ed.buttons && (c = ed.buttons[n]))\r
12333                                 return t.createButton(n, c);\r
12334 \r
12335                         return t.add(c);\r
12336                 },\r
12337 \r
12338                 createDropMenu : function(id, s, cc) {\r
12339                         var t = this, ed = t.editor, c, bm, v, cls;\r
12340 \r
12341                         s = extend({\r
12342                                 'class' : 'mceDropDown',\r
12343                                 constrain : ed.settings.constrain_menus\r
12344                         }, s);\r
12345 \r
12346                         s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin';\r
12347                         if (v = ed.getParam('skin_variant'))\r
12348                                 s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);\r
12349 \r
12350                         id = t.prefix + id;\r
12351                         cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;\r
12352                         c = t.controls[id] = new cls(id, s);\r
12353                         c.onAddItem.add(function(c, o) {\r
12354                                 var s = o.settings;\r
12355 \r
12356                                 s.title = ed.getLang(s.title, s.title);\r
12357 \r
12358                                 if (!s.onclick) {\r
12359                                         s.onclick = function(v) {\r
12360                                                 if (s.cmd)\r
12361                                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
12362                                         };\r
12363                                 }\r
12364                         });\r
12365 \r
12366                         ed.onRemove.add(function() {\r
12367                                 c.destroy();\r
12368                         });\r
12369 \r
12370                         // Fix for bug #1897785, #1898007\r
12371                         if (tinymce.isIE) {\r
12372                                 c.onShowMenu.add(function() {\r
12373                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
12374                                         ed.focus();\r
12375 \r
12376                                         bm = ed.selection.getBookmark(1);\r
12377                                 });\r
12378 \r
12379                                 c.onHideMenu.add(function() {\r
12380                                         if (bm) {\r
12381                                                 ed.selection.moveToBookmark(bm);\r
12382                                                 bm = 0;\r
12383                                         }\r
12384                                 });\r
12385                         }\r
12386 \r
12387                         return t.add(c);\r
12388                 },\r
12389 \r
12390                 createListBox : function(id, s, cc) {\r
12391                         var t = this, ed = t.editor, cmd, c, cls;\r
12392 \r
12393                         if (t.get(id))\r
12394                                 return null;\r
12395 \r
12396                         s.title = ed.translate(s.title);\r
12397                         s.scope = s.scope || ed;\r
12398 \r
12399                         if (!s.onselect) {\r
12400                                 s.onselect = function(v) {\r
12401                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12402                                 };\r
12403                         }\r
12404 \r
12405                         s = extend({\r
12406                                 title : s.title,\r
12407                                 'class' : 'mce_' + id,\r
12408                                 scope : s.scope,\r
12409                                 control_manager : t\r
12410                         }, s);\r
12411 \r
12412                         id = t.prefix + id;\r
12413 \r
12414                         if (ed.settings.use_native_selects)\r
12415                                 c = new tinymce.ui.NativeListBox(id, s);\r
12416                         else {\r
12417                                 cls = cc || t._cls.listbox || tinymce.ui.ListBox;\r
12418                                 c = new cls(id, s);\r
12419                         }\r
12420 \r
12421                         t.controls[id] = c;\r
12422 \r
12423                         // Fix focus problem in Safari\r
12424                         if (tinymce.isWebKit) {\r
12425                                 c.onPostRender.add(function(c, n) {\r
12426                                         // Store bookmark on mousedown\r
12427                                         Event.add(n, 'mousedown', function() {\r
12428                                                 ed.bookmark = ed.selection.getBookmark(1);\r
12429                                         });\r
12430 \r
12431                                         // Restore on focus, since it might be lost\r
12432                                         Event.add(n, 'focus', function() {\r
12433                                                 ed.selection.moveToBookmark(ed.bookmark);\r
12434                                                 ed.bookmark = null;\r
12435                                         });\r
12436                                 });\r
12437                         }\r
12438 \r
12439                         if (c.hideMenu)\r
12440                                 ed.onMouseDown.add(c.hideMenu, c);\r
12441 \r
12442                         return t.add(c);\r
12443                 },\r
12444 \r
12445                 createButton : function(id, s, cc) {\r
12446                         var t = this, ed = t.editor, o, c, cls;\r
12447 \r
12448                         if (t.get(id))\r
12449                                 return null;\r
12450 \r
12451                         s.title = ed.translate(s.title);\r
12452                         s.label = ed.translate(s.label);\r
12453                         s.scope = s.scope || ed;\r
12454 \r
12455                         if (!s.onclick && !s.menu_button) {\r
12456                                 s.onclick = function() {\r
12457                                         ed.execCommand(s.cmd, s.ui || false, s.value);\r
12458                                 };\r
12459                         }\r
12460 \r
12461                         s = extend({\r
12462                                 title : s.title,\r
12463                                 'class' : 'mce_' + id,\r
12464                                 unavailable_prefix : ed.getLang('unavailable', ''),\r
12465                                 scope : s.scope,\r
12466                                 control_manager : t\r
12467                         }, s);\r
12468 \r
12469                         id = t.prefix + id;\r
12470 \r
12471                         if (s.menu_button) {\r
12472                                 cls = cc || t._cls.menubutton || tinymce.ui.MenuButton;\r
12473                                 c = new cls(id, s);\r
12474                                 ed.onMouseDown.add(c.hideMenu, c);\r
12475                         } else {\r
12476                                 cls = t._cls.button || tinymce.ui.Button;\r
12477                                 c = new cls(id, s);\r
12478                         }\r
12479 \r
12480                         return t.add(c);\r
12481                 },\r
12482 \r
12483                 createMenuButton : function(id, s, cc) {\r
12484                         s = s || {};\r
12485                         s.menu_button = 1;\r
12486 \r
12487                         return this.createButton(id, s, cc);\r
12488                 },\r
12489 \r
12490                 createSplitButton : function(id, s, cc) {\r
12491                         var t = this, ed = t.editor, cmd, c, cls;\r
12492 \r
12493                         if (t.get(id))\r
12494                                 return null;\r
12495 \r
12496                         s.title = ed.translate(s.title);\r
12497                         s.scope = s.scope || ed;\r
12498 \r
12499                         if (!s.onclick) {\r
12500                                 s.onclick = function(v) {\r
12501                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12502                                 };\r
12503                         }\r
12504 \r
12505                         if (!s.onselect) {\r
12506                                 s.onselect = function(v) {\r
12507                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12508                                 };\r
12509                         }\r
12510 \r
12511                         s = extend({\r
12512                                 title : s.title,\r
12513                                 'class' : 'mce_' + id,\r
12514                                 scope : s.scope,\r
12515                                 control_manager : t\r
12516                         }, s);\r
12517 \r
12518                         id = t.prefix + id;\r
12519                         cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton;\r
12520                         c = t.add(new cls(id, s));\r
12521                         ed.onMouseDown.add(c.hideMenu, c);\r
12522 \r
12523                         return c;\r
12524                 },\r
12525 \r
12526                 createColorSplitButton : function(id, s, cc) {\r
12527                         var t = this, ed = t.editor, cmd, c, cls, bm;\r
12528 \r
12529                         if (t.get(id))\r
12530                                 return null;\r
12531 \r
12532                         s.title = ed.translate(s.title);\r
12533                         s.scope = s.scope || ed;\r
12534 \r
12535                         if (!s.onclick) {\r
12536                                 s.onclick = function(v) {\r
12537                                         if (tinymce.isIE)\r
12538                                                 bm = ed.selection.getBookmark(1);\r
12539 \r
12540                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12541                                 };\r
12542                         }\r
12543 \r
12544                         if (!s.onselect) {\r
12545                                 s.onselect = function(v) {\r
12546                                         ed.execCommand(s.cmd, s.ui || false, v || s.value);\r
12547                                 };\r
12548                         }\r
12549 \r
12550                         s = extend({\r
12551                                 title : s.title,\r
12552                                 'class' : 'mce_' + id,\r
12553                                 'menu_class' : ed.getParam('skin') + 'Skin',\r
12554                                 scope : s.scope,\r
12555                                 more_colors_title : ed.getLang('more_colors')\r
12556                         }, s);\r
12557 \r
12558                         id = t.prefix + id;\r
12559                         cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton;\r
12560                         c = new cls(id, s);\r
12561                         ed.onMouseDown.add(c.hideMenu, c);\r
12562 \r
12563                         // Remove the menu element when the editor is removed\r
12564                         ed.onRemove.add(function() {\r
12565                                 c.destroy();\r
12566                         });\r
12567 \r
12568                         // Fix for bug #1897785, #1898007\r
12569                         if (tinymce.isIE) {\r
12570                                 c.onShowMenu.add(function() {\r
12571                                         // IE 8 needs focus in order to store away a range with the current collapsed caret location\r
12572                                         ed.focus();\r
12573                                         bm = ed.selection.getBookmark(1);\r
12574                                 });\r
12575 \r
12576                                 c.onHideMenu.add(function() {\r
12577                                         if (bm) {\r
12578                                                 ed.selection.moveToBookmark(bm);\r
12579                                                 bm = 0;\r
12580                                         }\r
12581                                 });\r
12582                         }\r
12583 \r
12584                         return t.add(c);\r
12585                 },\r
12586 \r
12587                 createToolbar : function(id, s, cc) {\r
12588                         var c, t = this, cls;\r
12589 \r
12590                         id = t.prefix + id;\r
12591                         cls = cc || t._cls.toolbar || tinymce.ui.Toolbar;\r
12592                         c = new cls(id, s);\r
12593 \r
12594                         if (t.get(id))\r
12595                                 return null;\r
12596 \r
12597                         return t.add(c);\r
12598                 },\r
12599 \r
12600                 createSeparator : function(cc) {\r
12601                         var cls = cc || this._cls.separator || tinymce.ui.Separator;\r
12602 \r
12603                         return new cls();\r
12604                 },\r
12605 \r
12606                 setControlType : function(n, c) {\r
12607                         return this._cls[n.toLowerCase()] = c;\r
12608                 },\r
12609         \r
12610                 destroy : function() {\r
12611                         each(this.controls, function(c) {\r
12612                                 c.destroy();\r
12613                         });\r
12614 \r
12615                         this.controls = null;\r
12616                 }\r
12617         });\r
12618 })(tinymce);\r
12619 \r
12620 (function(tinymce) {\r
12621         var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;\r
12622 \r
12623         tinymce.create('tinymce.WindowManager', {\r
12624                 WindowManager : function(ed) {\r
12625                         var t = this;\r
12626 \r
12627                         t.editor = ed;\r
12628                         t.onOpen = new Dispatcher(t);\r
12629                         t.onClose = new Dispatcher(t);\r
12630                         t.params = {};\r
12631                         t.features = {};\r
12632                 },\r
12633 \r
12634                 open : function(s, p) {\r
12635                         var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u;\r
12636 \r
12637                         // Default some options\r
12638                         s = s || {};\r
12639                         p = p || {};\r
12640                         sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window\r
12641                         sh = isOpera ? vp.h : screen.height;\r
12642                         s.name = s.name || 'mc_' + new Date().getTime();\r
12643                         s.width = parseInt(s.width || 320);\r
12644                         s.height = parseInt(s.height || 240);\r
12645                         s.resizable = true;\r
12646                         s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0);\r
12647                         s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0);\r
12648                         p.inline = false;\r
12649                         p.mce_width = s.width;\r
12650                         p.mce_height = s.height;\r
12651                         p.mce_auto_focus = s.auto_focus;\r
12652 \r
12653                         if (mo) {\r
12654                                 if (isIE) {\r
12655                                         s.center = true;\r
12656                                         s.help = false;\r
12657                                         s.dialogWidth = s.width + 'px';\r
12658                                         s.dialogHeight = s.height + 'px';\r
12659                                         s.scroll = s.scrollbars || false;\r
12660                                 }\r
12661                         }\r
12662 \r
12663                         // Build features string\r
12664                         each(s, function(v, k) {\r
12665                                 if (tinymce.is(v, 'boolean'))\r
12666                                         v = v ? 'yes' : 'no';\r
12667 \r
12668                                 if (!/^(name|url)$/.test(k)) {\r
12669                                         if (isIE && mo)\r
12670                                                 f += (f ? ';' : '') + k + ':' + v;\r
12671                                         else\r
12672                                                 f += (f ? ',' : '') + k + '=' + v;\r
12673                                 }\r
12674                         });\r
12675 \r
12676                         t.features = s;\r
12677                         t.params = p;\r
12678                         t.onOpen.dispatch(t, s, p);\r
12679 \r
12680                         u = s.url || s.file;\r
12681                         u = tinymce._addVer(u);\r
12682 \r
12683                         try {\r
12684                                 if (isIE && mo) {\r
12685                                         w = 1;\r
12686                                         window.showModalDialog(u, window, f);\r
12687                                 } else\r
12688                                         w = window.open(u, s.name, f);\r
12689                         } catch (ex) {\r
12690                                 // Ignore\r
12691                         }\r
12692 \r
12693                         if (!w)\r
12694                                 alert(t.editor.getLang('popup_blocked'));\r
12695                 },\r
12696 \r
12697                 close : function(w) {\r
12698                         w.close();\r
12699                         this.onClose.dispatch(this);\r
12700                 },\r
12701 \r
12702                 createInstance : function(cl, a, b, c, d, e) {\r
12703                         var f = tinymce.resolve(cl);\r
12704 \r
12705                         return new f(a, b, c, d, e);\r
12706                 },\r
12707 \r
12708                 confirm : function(t, cb, s, w) {\r
12709                         w = w || window;\r
12710 \r
12711                         cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t))));\r
12712                 },\r
12713 \r
12714                 alert : function(tx, cb, s, w) {\r
12715                         var t = this;\r
12716 \r
12717                         w = w || window;\r
12718                         w.alert(t._decode(t.editor.getLang(tx, tx)));\r
12719 \r
12720                         if (cb)\r
12721                                 cb.call(s || t);\r
12722                 },\r
12723 \r
12724                 resizeBy : function(dw, dh, win) {\r
12725                         win.resizeBy(dw, dh);\r
12726                 },\r
12727 \r
12728                 // Internal functions\r
12729 \r
12730                 _decode : function(s) {\r
12731                         return tinymce.DOM.decode(s).replace(/\\n/g, '\n');\r
12732                 }\r
12733         });\r
12734 }(tinymce));\r
12735 (function(tinymce) {\r
12736         function CommandManager() {\r
12737                 var execCommands = {}, queryStateCommands = {}, queryValueCommands = {};\r
12738 \r
12739                 function add(collection, cmd, func, scope) {\r
12740                         if (typeof(cmd) == 'string')\r
12741                                 cmd = [cmd];\r
12742 \r
12743                         tinymce.each(cmd, function(cmd) {\r
12744                                 collection[cmd.toLowerCase()] = {func : func, scope : scope};\r
12745                         });\r
12746                 };\r
12747 \r
12748                 tinymce.extend(this, {\r
12749                         add : function(cmd, func, scope) {\r
12750                                 add(execCommands, cmd, func, scope);\r
12751                         },\r
12752 \r
12753                         addQueryStateHandler : function(cmd, func, scope) {\r
12754                                 add(queryStateCommands, cmd, func, scope);\r
12755                         },\r
12756 \r
12757                         addQueryValueHandler : function(cmd, func, scope) {\r
12758                                 add(queryValueCommands, cmd, func, scope);\r
12759                         },\r
12760 \r
12761                         execCommand : function(scope, cmd, ui, value, args) {\r
12762                                 if (cmd = execCommands[cmd.toLowerCase()]) {\r
12763                                         if (cmd.func.call(scope || cmd.scope, ui, value, args) !== false)\r
12764                                                 return true;\r
12765                                 }\r
12766                         },\r
12767 \r
12768                         queryCommandValue : function() {\r
12769                                 if (cmd = queryValueCommands[cmd.toLowerCase()])\r
12770                                         return cmd.func.call(scope || cmd.scope, ui, value, args);\r
12771                         },\r
12772 \r
12773                         queryCommandState : function() {\r
12774                                 if (cmd = queryStateCommands[cmd.toLowerCase()])\r
12775                                         return cmd.func.call(scope || cmd.scope, ui, value, args);\r
12776                         }\r
12777                 });\r
12778         };\r
12779 \r
12780         tinymce.GlobalCommands = new CommandManager();\r
12781 })(tinymce);\r
12782 (function(tinymce) {\r
12783         tinymce.Formatter = function(ed) {\r
12784                 var formats = {},\r
12785                         each = tinymce.each,\r
12786                         dom = ed.dom,\r
12787                         selection = ed.selection,\r
12788                         TreeWalker = tinymce.dom.TreeWalker,\r
12789                         rangeUtils = new tinymce.dom.RangeUtils(dom),\r
12790                         isValid = ed.schema.isValid,\r
12791                         isBlock = dom.isBlock,\r
12792                         forcedRootBlock = ed.settings.forced_root_block,\r
12793                         nodeIndex = dom.nodeIndex,\r
12794                         INVISIBLE_CHAR = '\uFEFF',\r
12795                         MCE_ATTR_RE = /^(src|href|style)$/,\r
12796                         FALSE = false,\r
12797                         TRUE = true,\r
12798                         undefined,\r
12799                         pendingFormats = {apply : [], remove : []};\r
12800 \r
12801                 function isArray(obj) {\r
12802                         return obj instanceof Array;\r
12803                 };\r
12804 \r
12805                 function getParents(node, selector) {\r
12806                         return dom.getParents(node, selector, dom.getRoot());\r
12807                 };\r
12808 \r
12809                 function isCaretNode(node) {\r
12810                         return node.nodeType === 1 && (node.face === 'mceinline' || node.style.fontFamily === 'mceinline');\r
12811                 };\r
12812 \r
12813                 // Public functions\r
12814 \r
12815                 function get(name) {\r
12816                         return name ? formats[name] : formats;\r
12817                 };\r
12818 \r
12819                 function register(name, format) {\r
12820                         if (name) {\r
12821                                 if (typeof(name) !== 'string') {\r
12822                                         each(name, function(format, name) {\r
12823                                                 register(name, format);\r
12824                                         });\r
12825                                 } else {\r
12826                                         // Force format into array and add it to internal collection\r
12827                                         format = format.length ? format : [format];\r
12828 \r
12829                                         each(format, function(format) {\r
12830                                                 // Set deep to false by default on selector formats this to avoid removing\r
12831                                                 // alignment on images inside paragraphs when alignment is changed on paragraphs\r
12832                                                 if (format.deep === undefined)\r
12833                                                         format.deep = !format.selector;\r
12834 \r
12835                                                 // Default to true\r
12836                                                 if (format.split === undefined)\r
12837                                                         format.split = !format.selector || format.inline;\r
12838 \r
12839                                                 // Default to true\r
12840                                                 if (format.remove === undefined && format.selector && !format.inline)\r
12841                                                         format.remove = 'none';\r
12842 \r
12843                                                 // Mark format as a mixed format inline + block level\r
12844                                                 if (format.selector && format.inline) {\r
12845                                                         format.mixed = true;\r
12846                                                         format.block_expand = true;\r
12847                                                 }\r
12848 \r
12849                                                 // Split classes if needed\r
12850                                                 if (typeof(format.classes) === 'string')\r
12851                                                         format.classes = format.classes.split(/\s+/);\r
12852                                         });\r
12853 \r
12854                                         formats[name] = format;\r
12855                                 }\r
12856                         }\r
12857                 };\r
12858 \r
12859                 function apply(name, vars, node) {\r
12860                         var formatList = get(name), format = formatList[0], bookmark, rng, i;\r
12861 \r
12862                         function moveStart(rng) {\r
12863                                 var container = rng.startContainer,\r
12864                                         offset = rng.startOffset,\r
12865                                         walker, node;\r
12866 \r
12867                                 // Move startContainer/startOffset in to a suitable node\r
12868                                 if (container.nodeType == 1 || container.nodeValue === "") {\r
12869                                         container = container.nodeType == 1 ? container.childNodes[offset] : container;\r
12870 \r
12871                                         // Might fail if the offset is behind the last element in it's container\r
12872                                         if (container) {\r
12873                                                 walker = new TreeWalker(container, container.parentNode);\r
12874                                                 for (node = walker.current(); node; node = walker.next()) {\r
12875                                                         if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {\r
12876                                                                 rng.setStart(node, 0);\r
12877                                                                 break;\r
12878                                                         }\r
12879                                                 }\r
12880                                         }\r
12881                                 }\r
12882 \r
12883                                 return rng;\r
12884                         };\r
12885 \r
12886                         function setElementFormat(elm, fmt) {\r
12887                                 fmt = fmt || format;\r
12888 \r
12889                                 if (elm) {\r
12890                                         each(fmt.styles, function(value, name) {\r
12891                                                 dom.setStyle(elm, name, replaceVars(value, vars));\r
12892                                         });\r
12893 \r
12894                                         each(fmt.attributes, function(value, name) {\r
12895                                                 dom.setAttrib(elm, name, replaceVars(value, vars));\r
12896                                         });\r
12897 \r
12898                                         each(fmt.classes, function(value) {\r
12899                                                 value = replaceVars(value, vars);\r
12900 \r
12901                                                 if (!dom.hasClass(elm, value))\r
12902                                                         dom.addClass(elm, value);\r
12903                                         });\r
12904                                 }\r
12905                         };\r
12906 \r
12907                         function applyRngStyle(rng) {\r
12908                                 var newWrappers = [], wrapName, wrapElm;\r
12909 \r
12910                                 // Setup wrapper element\r
12911                                 wrapName = format.inline || format.block;\r
12912                                 wrapElm = dom.create(wrapName);\r
12913                                 setElementFormat(wrapElm);\r
12914 \r
12915                                 rangeUtils.walk(rng, function(nodes) {\r
12916                                         var currentWrapElm;\r
12917 \r
12918                                         function process(node) {\r
12919                                                 var nodeName = node.nodeName.toLowerCase(), parentName = node.parentNode.nodeName.toLowerCase(), found;\r
12920 \r
12921                                                 // Stop wrapping on br elements\r
12922                                                 if (isEq(nodeName, 'br')) {\r
12923                                                         currentWrapElm = 0;\r
12924 \r
12925                                                         // Remove any br elements when we wrap things\r
12926                                                         if (format.block)\r
12927                                                                 dom.remove(node);\r
12928 \r
12929                                                         return;\r
12930                                                 }\r
12931 \r
12932                                                 // If node is wrapper type\r
12933                                                 if (format.wrapper && matchNode(node, name, vars)) {\r
12934                                                         currentWrapElm = 0;\r
12935                                                         return;\r
12936                                                 }\r
12937 \r
12938                                                 // Can we rename the block\r
12939                                                 if (format.block && !format.wrapper && isTextBlock(nodeName)) {\r
12940                                                         node = dom.rename(node, wrapName);\r
12941                                                         setElementFormat(node);\r
12942                                                         newWrappers.push(node);\r
12943                                                         currentWrapElm = 0;\r
12944                                                         return;\r
12945                                                 }\r
12946 \r
12947                                                 // Handle selector patterns\r
12948                                                 if (format.selector) {\r
12949                                                         // Look for matching formats\r
12950                                                         each(formatList, function(format) {\r
12951                                                                 if (dom.is(node, format.selector) && !isCaretNode(node)) {\r
12952                                                                         setElementFormat(node, format);\r
12953                                                                         found = true;\r
12954                                                                 }\r
12955                                                         });\r
12956 \r
12957                                                         // Continue processing if a selector match wasn't found and a inline element is defined\r
12958                                                         if (!format.inline || found) {\r
12959                                                                 currentWrapElm = 0;\r
12960                                                                 return;\r
12961                                                         }\r
12962                                                 }\r
12963 \r
12964                                                 // Is it valid to wrap this item\r
12965                                                 if (isValid(wrapName, nodeName) && isValid(parentName, wrapName)) {\r
12966                                                         // Start wrapping\r
12967                                                         if (!currentWrapElm) {\r
12968                                                                 // Wrap the node\r
12969                                                                 currentWrapElm = wrapElm.cloneNode(FALSE);\r
12970                                                                 node.parentNode.insertBefore(currentWrapElm, node);\r
12971                                                                 newWrappers.push(currentWrapElm);\r
12972                                                         }\r
12973 \r
12974                                                         currentWrapElm.appendChild(node);\r
12975                                                 } else {\r
12976                                                         // Start a new wrapper for possible children\r
12977                                                         currentWrapElm = 0;\r
12978 \r
12979                                                         each(tinymce.grep(node.childNodes), process);\r
12980 \r
12981                                                         // End the last wrapper\r
12982                                                         currentWrapElm = 0;\r
12983                                                 }\r
12984                                         };\r
12985 \r
12986                                         // Process siblings from range\r
12987                                         each(nodes, process);\r
12988                                 });\r
12989 \r
12990                                 // Cleanup\r
12991                                 each(newWrappers, function(node) {\r
12992                                         var childCount;\r
12993 \r
12994                                         function getChildCount(node) {\r
12995                                                 var count = 0;\r
12996 \r
12997                                                 each(node.childNodes, function(node) {\r
12998                                                         if (!isWhiteSpaceNode(node) && !isBookmarkNode(node))\r
12999                                                                 count++;\r
13000                                                 });\r
13001 \r
13002                                                 return count;\r
13003                                         };\r
13004 \r
13005                                         function mergeStyles(node) {\r
13006                                                 var child, clone;\r
13007 \r
13008                                                 each(node.childNodes, function(node) {\r
13009                                                         if (node.nodeType == 1 && !isBookmarkNode(node) && !isCaretNode(node)) {\r
13010                                                                 child = node;\r
13011                                                                 return FALSE; // break loop\r
13012                                                         }\r
13013                                                 });\r
13014 \r
13015                                                 // If child was found and of the same type as the current node\r
13016                                                 if (child && matchName(child, format)) {\r
13017                                                         clone = child.cloneNode(FALSE);\r
13018                                                         setElementFormat(clone);\r
13019 \r
13020                                                         dom.replace(clone, node, TRUE);\r
13021                                                         dom.remove(child, 1);\r
13022                                                 }\r
13023 \r
13024                                                 return clone || node;\r
13025                                         };\r
13026 \r
13027                                         childCount = getChildCount(node);\r
13028 \r
13029                                         // Remove empty nodes\r
13030                                         if (childCount === 0) {\r
13031                                                 dom.remove(node, 1);\r
13032                                                 return;\r
13033                                         }\r
13034 \r
13035                                         if (format.inline || format.wrapper) {\r
13036                                                 // Merges the current node with it's children of similar type to reduce the number of elements\r
13037                                                 if (!format.exact && childCount === 1)\r
13038                                                         node = mergeStyles(node);\r
13039 \r
13040                                                 // Remove/merge children\r
13041                                                 each(formatList, function(format) {\r
13042                                                         // Merge all children of similar type will move styles from child to parent\r
13043                                                         // this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span>\r
13044                                                         // will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span>\r
13045                                                         each(dom.select(format.inline, node), function(child) {\r
13046                                                                 removeFormat(format, vars, child, format.exact ? child : null);\r
13047                                                         });\r
13048                                                 });\r
13049 \r
13050                                                 // Remove child if direct parent is of same type\r
13051                                                 if (matchNode(node.parentNode, name, vars)) {\r
13052                                                         dom.remove(node, 1);\r
13053                                                         node = 0;\r
13054                                                         return TRUE;\r
13055                                                 }\r
13056 \r
13057                                                 // Look for parent with similar style format\r
13058                                                 if (format.merge_with_parents) {\r
13059                                                         dom.getParent(node.parentNode, function(parent) {\r
13060                                                                 if (matchNode(parent, name, vars)) {\r
13061                                                                         dom.remove(node, 1);\r
13062                                                                         node = 0;\r
13063                                                                         return TRUE;\r
13064                                                                 }\r
13065                                                         });\r
13066                                                 }\r
13067 \r
13068                                                 // Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>\r
13069                                                 if (node) {\r
13070                                                         node = mergeSiblings(getNonWhiteSpaceSibling(node), node);\r
13071                                                         node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE));\r
13072                                                 }\r
13073                                         }\r
13074                                 });\r
13075                         };\r
13076 \r
13077                         if (format) {\r
13078                                 if (node) {\r
13079                                         rng = dom.createRng();\r
13080 \r
13081                                         rng.setStartBefore(node);\r
13082                                         rng.setEndAfter(node);\r
13083 \r
13084                                         applyRngStyle(expandRng(rng, formatList));\r
13085                                 } else {\r
13086                                         if (!selection.isCollapsed() || !format.inline) {\r
13087                                                 // Apply formatting to selection\r
13088                                                 bookmark = selection.getBookmark();\r
13089                                                 applyRngStyle(expandRng(selection.getRng(TRUE), formatList));\r
13090 \r
13091                                                 selection.moveToBookmark(bookmark);\r
13092                                                 selection.setRng(moveStart(selection.getRng(TRUE)));\r
13093                                                 ed.nodeChanged();\r
13094                                         } else\r
13095                                                 performCaretAction('apply', name, vars);\r
13096                                 }\r
13097                         }\r
13098                 };\r
13099 \r
13100                 function remove(name, vars, node) {\r
13101                         var formatList = get(name), format = formatList[0], bookmark, i, rng;\r
13102 \r
13103                         function moveStart(rng) {\r
13104                                 var container = rng.startContainer,\r
13105                                         offset = rng.startOffset,\r
13106                                         walker, node, nodes, tmpNode;\r
13107 \r
13108                                 // Convert text node into index if possible\r
13109                                 if (container.nodeType == 3 && offset >= container.nodeValue.length - 1) {\r
13110                                         container = container.parentNode;\r
13111                                         offset = nodeIndex(container) + 1;\r
13112                                 }\r
13113 \r
13114                                 // Move startContainer/startOffset in to a suitable node\r
13115                                 if (container.nodeType == 1) {\r
13116                                         nodes = container.childNodes;\r
13117                                         container = nodes[Math.min(offset, nodes.length - 1)];\r
13118                                         walker = new TreeWalker(container);\r
13119 \r
13120                                         // If offset is at end of the parent node walk to the next one\r
13121                                         if (offset > nodes.length - 1)\r
13122                                                 walker.next();\r
13123 \r
13124                                         for (node = walker.current(); node; node = walker.next()) {\r
13125                                                 if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {\r
13126                                                         // IE has a "neat" feature where it moves the start node into the closest element\r
13127                                                         // we can avoid this by inserting an element before it and then remove it after we set the selection\r
13128                                                         tmpNode = dom.create('a', null, INVISIBLE_CHAR);\r
13129                                                         node.parentNode.insertBefore(tmpNode, node);\r
13130 \r
13131                                                         // Set selection and remove tmpNode\r
13132                                                         rng.setStart(node, 0);\r
13133                                                         selection.setRng(rng);\r
13134                                                         dom.remove(tmpNode);\r
13135 \r
13136                                                         return;\r
13137                                                 }\r
13138                                         }\r
13139                                 }\r
13140                         };\r
13141 \r
13142                         // Merges the styles for each node\r
13143                         function process(node) {\r
13144                                 var children, i, l;\r
13145 \r
13146                                 // Grab the children first since the nodelist might be changed\r
13147                                 children = tinymce.grep(node.childNodes);\r
13148 \r
13149                                 // Process current node\r
13150                                 for (i = 0, l = formatList.length; i < l; i++) {\r
13151                                         if (removeFormat(formatList[i], vars, node, node))\r
13152                                                 break;\r
13153                                 }\r
13154 \r
13155                                 // Process the children\r
13156                                 if (format.deep) {\r
13157                                         for (i = 0, l = children.length; i < l; i++)\r
13158                                                 process(children[i]);\r
13159                                 }\r
13160                         };\r
13161 \r
13162                         function findFormatRoot(container) {\r
13163                                 var formatRoot;\r
13164 \r
13165                                 // Find format root\r
13166                                 each(getParents(container.parentNode).reverse(), function(parent) {\r
13167                                         var format;\r
13168 \r
13169                                         // Find format root element\r
13170                                         if (!formatRoot && parent.id != '_start' && parent.id != '_end') {\r
13171                                                 // Is the node matching the format we are looking for\r
13172                                                 format = matchNode(parent, name, vars);\r
13173                                                 if (format && format.split !== false)\r
13174                                                         formatRoot = parent;\r
13175                                         }\r
13176                                 });\r
13177 \r
13178                                 return formatRoot;\r
13179                         };\r
13180 \r
13181                         function wrapAndSplit(format_root, container, target, split) {\r
13182                                 var parent, clone, lastClone, firstClone, i, formatRootParent;\r
13183 \r
13184                                 // Format root found then clone formats and split it\r
13185                                 if (format_root) {\r
13186                                         formatRootParent = format_root.parentNode;\r
13187 \r
13188                                         for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) {\r
13189                                                 clone = parent.cloneNode(FALSE);\r
13190 \r
13191                                                 for (i = 0; i < formatList.length; i++) {\r
13192                                                         if (removeFormat(formatList[i], vars, clone, clone)) {\r
13193                                                                 clone = 0;\r
13194                                                                 break;\r
13195                                                         }\r
13196                                                 }\r
13197 \r
13198                                                 // Build wrapper node\r
13199                                                 if (clone) {\r
13200                                                         if (lastClone)\r
13201                                                                 clone.appendChild(lastClone);\r
13202 \r
13203                                                         if (!firstClone)\r
13204                                                                 firstClone = clone;\r
13205 \r
13206                                                         lastClone = clone;\r
13207                                                 }\r
13208                                         }\r
13209 \r
13210                                         // Never split block elements if the format is mixed\r
13211                                         if (split && (!format.mixed || !isBlock(format_root)))\r
13212                                                 container = dom.split(format_root, container);\r
13213 \r
13214                                         // Wrap container in cloned formats\r
13215                                         if (lastClone) {\r
13216                                                 target.parentNode.insertBefore(lastClone, target);\r
13217                                                 firstClone.appendChild(target);\r
13218                                         }\r
13219                                 }\r
13220 \r
13221                                 return container;\r
13222                         };\r
13223 \r
13224                         function splitToFormatRoot(container) {\r
13225                                 return wrapAndSplit(findFormatRoot(container), container, container, true);\r
13226                         };\r
13227 \r
13228                         function unwrap(start) {\r
13229                                 var node = dom.get(start ? '_start' : '_end'),\r
13230                                         out = node[start ? 'firstChild' : 'lastChild'];\r
13231 \r
13232                                 // If the end is placed within the start the result will be removed\r
13233                                 // So this checks if the out node is a bookmark node if it is it\r
13234                                 // checks for another more suitable node\r
13235                                 if (isBookmarkNode(out))\r
13236                                         out = out[start ? 'firstChild' : 'lastChild'];\r
13237 \r
13238                                 dom.remove(node, true);\r
13239 \r
13240                                 return out;\r
13241                         };\r
13242 \r
13243                         function removeRngStyle(rng) {\r
13244                                 var startContainer, endContainer;\r
13245 \r
13246                                 rng = expandRng(rng, formatList, TRUE);\r
13247 \r
13248                                 if (format.split) {\r
13249                                         startContainer = getContainer(rng, TRUE);\r
13250                                         endContainer = getContainer(rng);\r
13251 \r
13252                                         if (startContainer != endContainer) {\r
13253                                                 // Wrap start/end nodes in span element since these might be cloned/moved\r
13254                                                 startContainer = wrap(startContainer, 'span', {id : '_start', _mce_type : 'bookmark'});\r
13255                                                 endContainer = wrap(endContainer, 'span', {id : '_end', _mce_type : 'bookmark'});\r
13256 \r
13257                                                 // Split start/end\r
13258                                                 splitToFormatRoot(startContainer);\r
13259                                                 splitToFormatRoot(endContainer);\r
13260 \r
13261                                                 // Unwrap start/end to get real elements again\r
13262                                                 startContainer = unwrap(TRUE);\r
13263                                                 endContainer = unwrap();\r
13264                                         } else\r
13265                                                 startContainer = endContainer = splitToFormatRoot(startContainer);\r
13266 \r
13267                                         // Update range positions since they might have changed after the split operations\r
13268                                         rng.startContainer = startContainer.parentNode;\r
13269                                         rng.startOffset = nodeIndex(startContainer);\r
13270                                         rng.endContainer = endContainer.parentNode;\r
13271                                         rng.endOffset = nodeIndex(endContainer) + 1;\r
13272                                 }\r
13273 \r
13274                                 // Remove items between start/end\r
13275                                 rangeUtils.walk(rng, function(nodes) {\r
13276                                         each(nodes, function(node) {\r
13277                                                 process(node);\r
13278                                         });\r
13279                                 });\r
13280                         };\r
13281 \r
13282                         // Handle node\r
13283                         if (node) {\r
13284                                 rng = dom.createRng();\r
13285                                 rng.setStartBefore(node);\r
13286                                 rng.setEndAfter(node);\r
13287                                 removeRngStyle(rng);\r
13288                                 return;\r
13289                         }\r
13290 \r
13291                         if (!selection.isCollapsed() || !format.inline) {\r
13292                                 bookmark = selection.getBookmark();\r
13293                                 removeRngStyle(selection.getRng(TRUE));\r
13294                                 selection.moveToBookmark(bookmark);\r
13295 \r
13296                                 // 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
13297                                 if (match(name, vars, selection.getStart())) {\r
13298                                         moveStart(selection.getRng(true));\r
13299                                 }\r
13300 \r
13301                                 ed.nodeChanged();\r
13302                         } else\r
13303                                 performCaretAction('remove', name, vars);\r
13304                 };\r
13305 \r
13306                 function toggle(name, vars, node) {\r
13307                         if (match(name, vars, node))\r
13308                                 remove(name, vars, node);\r
13309                         else\r
13310                                 apply(name, vars, node);\r
13311                 };\r
13312 \r
13313                 function matchNode(node, name, vars, similar) {\r
13314                         var formatList = get(name), format, i, classes;\r
13315 \r
13316                         function matchItems(node, format, item_name) {\r
13317                                 var key, value, items = format[item_name], i;\r
13318 \r
13319                                 // Check all items\r
13320                                 if (items) {\r
13321                                         // Non indexed object\r
13322                                         if (items.length === undefined) {\r
13323                                                 for (key in items) {\r
13324                                                         if (items.hasOwnProperty(key)) {\r
13325                                                                 if (item_name === 'attributes')\r
13326                                                                         value = dom.getAttrib(node, key);\r
13327                                                                 else\r
13328                                                                         value = getStyle(node, key);\r
13329 \r
13330                                                                 if (similar && !value && !format.exact)\r
13331                                                                         return;\r
13332 \r
13333                                                                 if ((!similar || format.exact) && !isEq(value, replaceVars(items[key], vars)))\r
13334                                                                         return;\r
13335                                                         }\r
13336                                                 }\r
13337                                         } else {\r
13338                                                 // Only one match needed for indexed arrays\r
13339                                                 for (i = 0; i < items.length; i++) {\r
13340                                                         if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i]))\r
13341                                                                 return format;\r
13342                                                 }\r
13343                                         }\r
13344                                 }\r
13345 \r
13346                                 return format;\r
13347                         };\r
13348 \r
13349                         if (formatList && node) {\r
13350                                 // Check each format in list\r
13351                                 for (i = 0; i < formatList.length; i++) {\r
13352                                         format = formatList[i];\r
13353 \r
13354                                         // Name name, attributes, styles and classes\r
13355                                         if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) {\r
13356                                                 // Match classes\r
13357                                                 if (classes = format.classes) {\r
13358                                                         for (i = 0; i < classes.length; i++) {\r
13359                                                                 if (!dom.hasClass(node, classes[i]))\r
13360                                                                         return;\r
13361                                                         }\r
13362                                                 }\r
13363 \r
13364                                                 return format;\r
13365                                         }\r
13366                                 }\r
13367                         }\r
13368                 };\r
13369 \r
13370                 function match(name, vars, node) {\r
13371                         var startNode, i;\r
13372 \r
13373                         function matchParents(node) {\r
13374                                 // Find first node with similar format settings\r
13375                                 node = dom.getParent(node, function(node) {\r
13376                                         return !!matchNode(node, name, vars, true);\r
13377                                 });\r
13378 \r
13379                                 // Do an exact check on the similar format element\r
13380                                 return matchNode(node, name, vars);\r
13381                         };\r
13382 \r
13383                         // Check specified node\r
13384                         if (node)\r
13385                                 return matchParents(node);\r
13386 \r
13387                         // Check pending formats\r
13388                         if (selection.isCollapsed()) {\r
13389                                 for (i = pendingFormats.apply.length - 1; i >= 0; i--) {\r
13390                                         if (pendingFormats.apply[i].name == name)\r
13391                                                 return true;\r
13392                                 }\r
13393 \r
13394                                 for (i = pendingFormats.remove.length - 1; i >= 0; i--) {\r
13395                                         if (pendingFormats.remove[i].name == name)\r
13396                                                 return false;\r
13397                                 }\r
13398 \r
13399                                 return matchParents(selection.getNode());\r
13400                         }\r
13401 \r
13402                         // Check selected node\r
13403                         node = selection.getNode();\r
13404                         if (matchParents(node))\r
13405                                 return TRUE;\r
13406 \r
13407                         // Check start node if it's different\r
13408                         startNode = selection.getStart();\r
13409                         if (startNode != node) {\r
13410                                 if (matchParents(startNode))\r
13411                                         return TRUE;\r
13412                         }\r
13413 \r
13414                         return FALSE;\r
13415                 };\r
13416 \r
13417                 function matchAll(names, vars) {\r
13418                         var startElement, matchedFormatNames = [], checkedMap = {}, i, ni, name;\r
13419 \r
13420                         // If the selection is collapsed then check pending formats\r
13421                         if (selection.isCollapsed()) {\r
13422                                 for (ni = 0; ni < names.length; ni++) {\r
13423                                         // If the name is to be removed, then stop it from being added\r
13424                                         for (i = pendingFormats.remove.length - 1; i >= 0; i--) {\r
13425                                                 name = names[ni];\r
13426 \r
13427                                                 if (pendingFormats.remove[i].name == name) {\r
13428                                                         checkedMap[name] = true;\r
13429                                                         break;\r
13430                                                 }\r
13431                                         }\r
13432                                 }\r
13433 \r
13434                                 // If the format is to be applied\r
13435                                 for (i = pendingFormats.apply.length - 1; i >= 0; i--) {\r
13436                                         for (ni = 0; ni < names.length; ni++) {\r
13437                                                 name = names[ni];\r
13438 \r
13439                                                 if (!checkedMap[name] && pendingFormats.apply[i].name == name) {\r
13440                                                         checkedMap[name] = true;\r
13441                                                         matchedFormatNames.push(name);\r
13442                                                 }\r
13443                                         }\r
13444                                 }\r
13445                         }\r
13446 \r
13447                         // Check start of selection for formats\r
13448                         startElement = selection.getStart();\r
13449                         dom.getParent(startElement, function(node) {\r
13450                                 var i, name;\r
13451 \r
13452                                 for (i = 0; i < names.length; i++) {\r
13453                                         name = names[i];\r
13454 \r
13455                                         if (!checkedMap[name] && matchNode(node, name, vars)) {\r
13456                                                 checkedMap[name] = true;\r
13457                                                 matchedFormatNames.push(name);\r
13458                                         }\r
13459                                 }\r
13460                         });\r
13461 \r
13462                         return matchedFormatNames;\r
13463                 };\r
13464 \r
13465                 function canApply(name) {\r
13466                         var formatList = get(name), startNode, parents, i, x, selector;\r
13467 \r
13468                         if (formatList) {\r
13469                                 startNode = selection.getStart();\r
13470                                 parents = getParents(startNode);\r
13471 \r
13472                                 for (x = formatList.length - 1; x >= 0; x--) {\r
13473                                         selector = formatList[x].selector;\r
13474 \r
13475                                         // Format is not selector based, then always return TRUE\r
13476                                         if (!selector)\r
13477                                                 return TRUE;\r
13478 \r
13479                                         for (i = parents.length - 1; i >= 0; i--) {\r
13480                                                 if (dom.is(parents[i], selector))\r
13481                                                         return TRUE;\r
13482                                         }\r
13483                                 }\r
13484                         }\r
13485 \r
13486                         return FALSE;\r
13487                 };\r
13488 \r
13489                 // Expose to public\r
13490                 tinymce.extend(this, {\r
13491                         get : get,\r
13492                         register : register,\r
13493                         apply : apply,\r
13494                         remove : remove,\r
13495                         toggle : toggle,\r
13496                         match : match,\r
13497                         matchAll : matchAll,\r
13498                         matchNode : matchNode,\r
13499                         canApply : canApply\r
13500                 });\r
13501 \r
13502                 // Private functions\r
13503 \r
13504                 function matchName(node, format) {\r
13505                         // Check for inline match\r
13506                         if (isEq(node, format.inline))\r
13507                                 return TRUE;\r
13508 \r
13509                         // Check for block match\r
13510                         if (isEq(node, format.block))\r
13511                                 return TRUE;\r
13512 \r
13513                         // Check for selector match\r
13514                         if (format.selector)\r
13515                                 return dom.is(node, format.selector);\r
13516                 };\r
13517 \r
13518                 function isEq(str1, str2) {\r
13519                         str1 = str1 || '';\r
13520                         str2 = str2 || '';\r
13521 \r
13522                         str1 = '' + (str1.nodeName || str1);\r
13523                         str2 = '' + (str2.nodeName || str2);\r
13524 \r
13525                         return str1.toLowerCase() == str2.toLowerCase();\r
13526                 };\r
13527 \r
13528                 function getStyle(node, name) {\r
13529                         var styleVal = dom.getStyle(node, name);\r
13530 \r
13531                         // Force the format to hex\r
13532                         if (name == 'color' || name == 'backgroundColor')\r
13533                                 styleVal = dom.toHex(styleVal);\r
13534 \r
13535                         // Opera will return bold as 700\r
13536                         if (name == 'fontWeight' && styleVal == 700)\r
13537                                 styleVal = 'bold';\r
13538 \r
13539                         return '' + styleVal;\r
13540                 };\r
13541 \r
13542                 function replaceVars(value, vars) {\r
13543                         if (typeof(value) != "string")\r
13544                                 value = value(vars);\r
13545                         else if (vars) {\r
13546                                 value = value.replace(/%(\w+)/g, function(str, name) {\r
13547                                         return vars[name] || str;\r
13548                                 });\r
13549                         }\r
13550 \r
13551                         return value;\r
13552                 };\r
13553 \r
13554                 function isWhiteSpaceNode(node) {\r
13555                         return node && node.nodeType === 3 && /^([\s\r\n]+|)$/.test(node.nodeValue);\r
13556                 };\r
13557 \r
13558                 function wrap(node, name, attrs) {\r
13559                         var wrapper = dom.create(name, attrs);\r
13560 \r
13561                         node.parentNode.insertBefore(wrapper, node);\r
13562                         wrapper.appendChild(node);\r
13563 \r
13564                         return wrapper;\r
13565                 };\r
13566 \r
13567                 function expandRng(rng, format, remove) {\r
13568                         var startContainer = rng.startContainer,\r
13569                                 startOffset = rng.startOffset,\r
13570                                 endContainer = rng.endContainer,\r
13571                                 endOffset = rng.endOffset, sibling, lastIdx;\r
13572 \r
13573                         // This function walks up the tree if there is no siblings before/after the node\r
13574                         function findParentContainer(container, child_name, sibling_name, root) {\r
13575                                 var parent, child;\r
13576 \r
13577                                 root = root || dom.getRoot();\r
13578 \r
13579                                 for (;;) {\r
13580                                         // Check if we can move up are we at root level or body level\r
13581                                         parent = container.parentNode;\r
13582 \r
13583                                         // Stop expanding on block elements or root depending on format\r
13584                                         if (parent == root || (!format[0].block_expand && isBlock(parent)))\r
13585                                                 return container;\r
13586 \r
13587                                         for (sibling = parent[child_name]; sibling && sibling != container; sibling = sibling[sibling_name]) {\r
13588                                                 if (sibling.nodeType == 1 && !isBookmarkNode(sibling))\r
13589                                                         return container;\r
13590 \r
13591                                                 if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling))\r
13592                                                         return container;\r
13593                                         }\r
13594 \r
13595                                         container = container.parentNode;\r
13596                                 }\r
13597 \r
13598                                 return container;\r
13599                         };\r
13600 \r
13601                         // If index based start position then resolve it\r
13602                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {\r
13603                                 lastIdx = startContainer.childNodes.length - 1;\r
13604                                 startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset];\r
13605 \r
13606                                 if (startContainer.nodeType == 3)\r
13607                                         startOffset = 0;\r
13608                         }\r
13609 \r
13610                         // If index based end position then resolve it\r
13611                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {\r
13612                                 lastIdx = endContainer.childNodes.length - 1;\r
13613                                 endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1];\r
13614 \r
13615                                 if (endContainer.nodeType == 3)\r
13616                                         endOffset = endContainer.nodeValue.length;\r
13617                         }\r
13618 \r
13619                         // Exclude bookmark nodes if possible\r
13620                         if (isBookmarkNode(startContainer.parentNode))\r
13621                                 startContainer = startContainer.parentNode;\r
13622 \r
13623                         if (isBookmarkNode(startContainer))\r
13624                                 startContainer = startContainer.nextSibling || startContainer;\r
13625 \r
13626                         if (isBookmarkNode(endContainer.parentNode))\r
13627                                 endContainer = endContainer.parentNode;\r
13628 \r
13629                         if (isBookmarkNode(endContainer))\r
13630                                 endContainer = endContainer.previousSibling || endContainer;\r
13631 \r
13632                         // Move start/end point up the tree if the leaves are sharp and if we are in different containers\r
13633                         // Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!\r
13634                         // This will reduce the number of wrapper elements that needs to be created\r
13635                         // Move start point up the tree\r
13636                         if (format[0].inline || format[0].block_expand) {\r
13637                                 startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');\r
13638                                 endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');\r
13639                         }\r
13640 \r
13641                         // Expand start/end container to matching selector\r
13642                         if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) {\r
13643                                 function findSelectorEndPoint(container, sibling_name) {\r
13644                                         var parents, i, y;\r
13645 \r
13646                                         if (container.nodeType == 3 && container.nodeValue.length == 0 && container[sibling_name])\r
13647                                                 container = container[sibling_name];\r
13648 \r
13649                                         parents = getParents(container);\r
13650                                         for (i = 0; i < parents.length; i++) {\r
13651                                                 for (y = 0; y < format.length; y++) {\r
13652                                                         if (dom.is(parents[i], format[y].selector))\r
13653                                                                 return parents[i];\r
13654                                                 }\r
13655                                         }\r
13656 \r
13657                                         return container;\r
13658                                 };\r
13659 \r
13660                                 // Find new startContainer/endContainer if there is better one\r
13661                                 startContainer = findSelectorEndPoint(startContainer, 'previousSibling');\r
13662                                 endContainer = findSelectorEndPoint(endContainer, 'nextSibling');\r
13663                         }\r
13664 \r
13665                         // Expand start/end container to matching block element or text node\r
13666                         if (format[0].block || format[0].selector) {\r
13667                                 function findBlockEndPoint(container, sibling_name, sibling_name2) {\r
13668                                         var node;\r
13669 \r
13670                                         // Expand to block of similar type\r
13671                                         if (!format[0].wrapper)\r
13672                                                 node = dom.getParent(container, format[0].block);\r
13673 \r
13674                                         // Expand to first wrappable block element or any block element\r
13675                                         if (!node)\r
13676                                                 node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, isBlock);\r
13677 \r
13678                                         // Exclude inner lists from wrapping\r
13679                                         if (node && format[0].wrapper)\r
13680                                                 node = getParents(node, 'ul,ol').reverse()[0] || node;\r
13681 \r
13682                                         // Didn't find a block element look for first/last wrappable element\r
13683                                         if (!node) {\r
13684                                                 node = container;\r
13685 \r
13686                                                 while (node[sibling_name] && !isBlock(node[sibling_name])) {\r
13687                                                         node = node[sibling_name];\r
13688 \r
13689                                                         // Break on BR but include it will be removed later on\r
13690                                                         // we can't remove it now since we need to check if it can be wrapped\r
13691                                                         if (isEq(node, 'br'))\r
13692                                                                 break;\r
13693                                                 }\r
13694                                         }\r
13695 \r
13696                                         return node || container;\r
13697                                 };\r
13698 \r
13699                                 // Find new startContainer/endContainer if there is better one\r
13700                                 startContainer = findBlockEndPoint(startContainer, 'previousSibling');\r
13701                                 endContainer = findBlockEndPoint(endContainer, 'nextSibling');\r
13702 \r
13703                                 // Non block element then try to expand up the leaf\r
13704                                 if (format[0].block) {\r
13705                                         if (!isBlock(startContainer))\r
13706                                                 startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');\r
13707 \r
13708                                         if (!isBlock(endContainer))\r
13709                                                 endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');\r
13710                                 }\r
13711                         }\r
13712 \r
13713                         // Setup index for startContainer\r
13714                         if (startContainer.nodeType == 1) {\r
13715                                 startOffset = nodeIndex(startContainer);\r
13716                                 startContainer = startContainer.parentNode;\r
13717                         }\r
13718 \r
13719                         // Setup index for endContainer\r
13720                         if (endContainer.nodeType == 1) {\r
13721                                 endOffset = nodeIndex(endContainer) + 1;\r
13722                                 endContainer = endContainer.parentNode;\r
13723                         }\r
13724 \r
13725                         // Return new range like object\r
13726                         return {\r
13727                                 startContainer : startContainer,\r
13728                                 startOffset : startOffset,\r
13729                                 endContainer : endContainer,\r
13730                                 endOffset : endOffset\r
13731                         };\r
13732                 }\r
13733 \r
13734                 function removeFormat(format, vars, node, compare_node) {\r
13735                         var i, attrs, stylesModified;\r
13736 \r
13737                         // Check if node matches format\r
13738                         if (!matchName(node, format))\r
13739                                 return FALSE;\r
13740 \r
13741                         // Should we compare with format attribs and styles\r
13742                         if (format.remove != 'all') {\r
13743                                 // Remove styles\r
13744                                 each(format.styles, function(value, name) {\r
13745                                         value = replaceVars(value, vars);\r
13746 \r
13747                                         // Indexed array\r
13748                                         if (typeof(name) === 'number') {\r
13749                                                 name = value;\r
13750                                                 compare_node = 0;\r
13751                                         }\r
13752 \r
13753                                         if (!compare_node || isEq(getStyle(compare_node, name), value))\r
13754                                                 dom.setStyle(node, name, '');\r
13755 \r
13756                                         stylesModified = 1;\r
13757                                 });\r
13758 \r
13759                                 // Remove style attribute if it's empty\r
13760                                 if (stylesModified && dom.getAttrib(node, 'style') == '') {\r
13761                                         node.removeAttribute('style');\r
13762                                         node.removeAttribute('_mce_style');\r
13763                                 }\r
13764 \r
13765                                 // Remove attributes\r
13766                                 each(format.attributes, function(value, name) {\r
13767                                         var valueOut;\r
13768 \r
13769                                         value = replaceVars(value, vars);\r
13770 \r
13771                                         // Indexed array\r
13772                                         if (typeof(name) === 'number') {\r
13773                                                 name = value;\r
13774                                                 compare_node = 0;\r
13775                                         }\r
13776 \r
13777                                         if (!compare_node || isEq(dom.getAttrib(compare_node, name), value)) {\r
13778                                                 // Keep internal classes\r
13779                                                 if (name == 'class') {\r
13780                                                         value = dom.getAttrib(node, name);\r
13781                                                         if (value) {\r
13782                                                                 // Build new class value where everything is removed except the internal prefixed classes\r
13783                                                                 valueOut = '';\r
13784                                                                 each(value.split(/\s+/), function(cls) {\r
13785                                                                         if (/mce\w+/.test(cls))\r
13786                                                                                 valueOut += (valueOut ? ' ' : '') + cls;\r
13787                                                                 });\r
13788 \r
13789                                                                 // We got some internal classes left\r
13790                                                                 if (valueOut) {\r
13791                                                                         dom.setAttrib(node, name, valueOut);\r
13792                                                                         return;\r
13793                                                                 }\r
13794                                                         }\r
13795                                                 }\r
13796 \r
13797                                                 // IE6 has a bug where the attribute doesn't get removed correctly\r
13798                                                 if (name == "class")\r
13799                                                         node.removeAttribute('className');\r
13800 \r
13801                                                 // Remove mce prefixed attributes\r
13802                                                 if (MCE_ATTR_RE.test(name))\r
13803                                                         node.removeAttribute('_mce_' + name);\r
13804 \r
13805                                                 node.removeAttribute(name);\r
13806                                         }\r
13807                                 });\r
13808 \r
13809                                 // Remove classes\r
13810                                 each(format.classes, function(value) {\r
13811                                         value = replaceVars(value, vars);\r
13812 \r
13813                                         if (!compare_node || dom.hasClass(compare_node, value))\r
13814                                                 dom.removeClass(node, value);\r
13815                                 });\r
13816 \r
13817                                 // Check for non internal attributes\r
13818                                 attrs = dom.getAttribs(node);\r
13819                                 for (i = 0; i < attrs.length; i++) {\r
13820                                         if (attrs[i].nodeName.indexOf('_') !== 0)\r
13821                                                 return FALSE;\r
13822                                 }\r
13823                         }\r
13824 \r
13825                         // Remove the inline child if it's empty for example <b> or <span>\r
13826                         if (format.remove != 'none') {\r
13827                                 removeNode(node, format);\r
13828                                 return TRUE;\r
13829                         }\r
13830                 };\r
13831 \r
13832                 function removeNode(node, format) {\r
13833                         var parentNode = node.parentNode, rootBlockElm;\r
13834 \r
13835                         if (format.block) {\r
13836                                 if (!forcedRootBlock) {\r
13837                                         function find(node, next, inc) {\r
13838                                                 node = getNonWhiteSpaceSibling(node, next, inc);\r
13839 \r
13840                                                 return !node || (node.nodeName == 'BR' || isBlock(node));\r
13841                                         };\r
13842 \r
13843                                         // Append BR elements if needed before we remove the block\r
13844                                         if (isBlock(node) && !isBlock(parentNode)) {\r
13845                                                 if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1))\r
13846                                                         node.insertBefore(dom.create('br'), node.firstChild);\r
13847 \r
13848                                                 if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1))\r
13849                                                         node.appendChild(dom.create('br'));\r
13850                                         }\r
13851                                 } else {\r
13852                                         // Wrap the block in a forcedRootBlock if we are at the root of document\r
13853                                         if (parentNode == dom.getRoot()) {\r
13854                                                 if (!format.list_block || !isEq(node, format.list_block)) {\r
13855                                                         each(tinymce.grep(node.childNodes), function(node) {\r
13856                                                                 if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) {\r
13857                                                                         if (!rootBlockElm)\r
13858                                                                                 rootBlockElm = wrap(node, forcedRootBlock);\r
13859                                                                         else\r
13860                                                                                 rootBlockElm.appendChild(node);\r
13861                                                                 } else\r
13862                                                                         rootBlockElm = 0;\r
13863                                                         });\r
13864                                                 }\r
13865                                         }\r
13866                                 }\r
13867                         }\r
13868 \r
13869                         // Never remove nodes that isn't the specified inline element if a selector is specified too\r
13870                         if (format.selector && format.inline && !isEq(format.inline, node))\r
13871                                 return;\r
13872 \r
13873                         dom.remove(node, 1);\r
13874                 };\r
13875 \r
13876                 function getNonWhiteSpaceSibling(node, next, inc) {\r
13877                         if (node) {\r
13878                                 next = next ? 'nextSibling' : 'previousSibling';\r
13879 \r
13880                                 for (node = inc ? node : node[next]; node; node = node[next]) {\r
13881                                         if (node.nodeType == 1 || !isWhiteSpaceNode(node))\r
13882                                                 return node;\r
13883                                 }\r
13884                         }\r
13885                 };\r
13886 \r
13887                 function isBookmarkNode(node) {\r
13888                         return node && node.nodeType == 1 && node.getAttribute('_mce_type') == 'bookmark';\r
13889                 };\r
13890 \r
13891                 function mergeSiblings(prev, next) {\r
13892                         var marker, sibling, tmpSibling;\r
13893 \r
13894                         function compareElements(node1, node2) {\r
13895                                 // Not the same name\r
13896                                 if (node1.nodeName != node2.nodeName)\r
13897                                         return FALSE;\r
13898 \r
13899                                 function getAttribs(node) {\r
13900                                         var attribs = {};\r
13901 \r
13902                                         each(dom.getAttribs(node), function(attr) {\r
13903                                                 var name = attr.nodeName.toLowerCase();\r
13904 \r
13905                                                 // Don't compare internal attributes or style\r
13906                                                 if (name.indexOf('_') !== 0 && name !== 'style')\r
13907                                                         attribs[name] = dom.getAttrib(node, name);\r
13908                                         });\r
13909 \r
13910                                         return attribs;\r
13911                                 };\r
13912 \r
13913                                 function compareObjects(obj1, obj2) {\r
13914                                         var value, name;\r
13915 \r
13916                                         for (name in obj1) {\r
13917                                                 // Obj1 has item obj2 doesn't have\r
13918                                                 if (obj1.hasOwnProperty(name)) {\r
13919                                                         value = obj2[name];\r
13920 \r
13921                                                         // Obj2 doesn't have obj1 item\r
13922                                                         if (value === undefined)\r
13923                                                                 return FALSE;\r
13924 \r
13925                                                         // Obj2 item has a different value\r
13926                                                         if (obj1[name] != value)\r
13927                                                                 return FALSE;\r
13928 \r
13929                                                         // Delete similar value\r
13930                                                         delete obj2[name];\r
13931                                                 }\r
13932                                         }\r
13933 \r
13934                                         // Check if obj 2 has something obj 1 doesn't have\r
13935                                         for (name in obj2) {\r
13936                                                 // Obj2 has item obj1 doesn't have\r
13937                                                 if (obj2.hasOwnProperty(name))\r
13938                                                         return FALSE;\r
13939                                         }\r
13940 \r
13941                                         return TRUE;\r
13942                                 };\r
13943 \r
13944                                 // Attribs are not the same\r
13945                                 if (!compareObjects(getAttribs(node1), getAttribs(node2)))\r
13946                                         return FALSE;\r
13947 \r
13948                                 // Styles are not the same\r
13949                                 if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style'))))\r
13950                                         return FALSE;\r
13951 \r
13952                                 return TRUE;\r
13953                         };\r
13954 \r
13955                         // Check if next/prev exists and that they are elements\r
13956                         if (prev && next) {\r
13957                                 function findElementSibling(node, sibling_name) {\r
13958                                         for (sibling = node; sibling; sibling = sibling[sibling_name]) {\r
13959                                                 if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling))\r
13960                                                         return node;\r
13961 \r
13962                                                 if (sibling.nodeType == 1 && !isBookmarkNode(sibling))\r
13963                                                         return sibling;\r
13964                                         }\r
13965 \r
13966                                         return node;\r
13967                                 };\r
13968 \r
13969                                 // If previous sibling is empty then jump over it\r
13970                                 prev = findElementSibling(prev, 'previousSibling');\r
13971                                 next = findElementSibling(next, 'nextSibling');\r
13972 \r
13973                                 // Compare next and previous nodes\r
13974                                 if (compareElements(prev, next)) {\r
13975                                         // Append nodes between\r
13976                                         for (sibling = prev.nextSibling; sibling && sibling != next;) {\r
13977                                                 tmpSibling = sibling;\r
13978                                                 sibling = sibling.nextSibling;\r
13979                                                 prev.appendChild(tmpSibling);\r
13980                                         }\r
13981 \r
13982                                         // Remove next node\r
13983                                         dom.remove(next);\r
13984 \r
13985                                         // Move children into prev node\r
13986                                         each(tinymce.grep(next.childNodes), function(node) {\r
13987                                                 prev.appendChild(node);\r
13988                                         });\r
13989 \r
13990                                         return prev;\r
13991                                 }\r
13992                         }\r
13993 \r
13994                         return next;\r
13995                 };\r
13996 \r
13997                 function isTextBlock(name) {\r
13998                         return /^(h[1-6]|p|div|pre|address|dl|dt|dd)$/.test(name);\r
13999                 };\r
14000 \r
14001                 function getContainer(rng, start) {\r
14002                         var container, offset, lastIdx;\r
14003 \r
14004                         container = rng[start ? 'startContainer' : 'endContainer'];\r
14005                         offset = rng[start ? 'startOffset' : 'endOffset'];\r
14006 \r
14007                         if (container.nodeType == 1) {\r
14008                                 lastIdx = container.childNodes.length - 1;\r
14009 \r
14010                                 if (!start && offset)\r
14011                                         offset--;\r
14012 \r
14013                                 container = container.childNodes[offset > lastIdx ? lastIdx : offset];\r
14014                         }\r
14015 \r
14016                         return container;\r
14017                 };\r
14018 \r
14019                 function performCaretAction(type, name, vars) {\r
14020                         var i, currentPendingFormats = pendingFormats[type],\r
14021                                 otherPendingFormats = pendingFormats[type == 'apply' ? 'remove' : 'apply'];\r
14022 \r
14023                         function hasPending() {\r
14024                                 return pendingFormats.apply.length || pendingFormats.remove.length;\r
14025                         };\r
14026 \r
14027                         function resetPending() {\r
14028                                 pendingFormats.apply = [];\r
14029                                 pendingFormats.remove = [];\r
14030                         };\r
14031 \r
14032                         function perform(caret_node) {\r
14033                                 // Apply pending formats\r
14034                                 each(pendingFormats.apply.reverse(), function(item) {\r
14035                                         apply(item.name, item.vars, caret_node);\r
14036                                 });\r
14037 \r
14038                                 // Remove pending formats\r
14039                                 each(pendingFormats.remove.reverse(), function(item) {\r
14040                                         remove(item.name, item.vars, caret_node);\r
14041                                 });\r
14042 \r
14043                                 dom.remove(caret_node, 1);\r
14044                                 resetPending();\r
14045                         };\r
14046 \r
14047                         // Check if it already exists then ignore it\r
14048                         for (i = currentPendingFormats.length - 1; i >= 0; i--) {\r
14049                                 if (currentPendingFormats[i].name == name)\r
14050                                         return;\r
14051                         }\r
14052 \r
14053                         currentPendingFormats.push({name : name, vars : vars});\r
14054 \r
14055                         // Check if it's in the other type, then remove it\r
14056                         for (i = otherPendingFormats.length - 1; i >= 0; i--) {\r
14057                                 if (otherPendingFormats[i].name == name)\r
14058                                         otherPendingFormats.splice(i, 1);\r
14059                         }\r
14060 \r
14061                         // Pending apply or remove formats\r
14062                         if (hasPending()) {\r
14063                                 ed.getDoc().execCommand('FontName', false, 'mceinline');\r
14064                                 pendingFormats.lastRng = selection.getRng();\r
14065 \r
14066                                 // IE will convert the current word\r
14067                                 each(dom.select('font,span'), function(node) {\r
14068                                         var bookmark;\r
14069 \r
14070                                         if (isCaretNode(node)) {\r
14071                                                 bookmark = selection.getBookmark();\r
14072                                                 perform(node);\r
14073                                                 selection.moveToBookmark(bookmark);\r
14074                                                 ed.nodeChanged();\r
14075                                         }\r
14076                                 });\r
14077 \r
14078                                 // Only register listeners once if we need to\r
14079                                 if (!pendingFormats.isListening && hasPending()) {\r
14080                                         pendingFormats.isListening = true;\r
14081 \r
14082                                         each('onKeyDown,onKeyUp,onKeyPress,onMouseUp'.split(','), function(event) {\r
14083                                                 ed[event].addToTop(function(ed, e) {\r
14084                                                         // Do we have pending formats and is the selection moved has moved\r
14085                                                         if (hasPending() && !tinymce.dom.RangeUtils.compareRanges(pendingFormats.lastRng, selection.getRng())) {\r
14086                                                                 each(dom.select('font,span'), function(node) {\r
14087                                                                         var textNode, rng;\r
14088 \r
14089                                                                         // Look for marker\r
14090                                                                         if (isCaretNode(node)) {\r
14091                                                                                 textNode = node.firstChild;\r
14092 \r
14093                                                                                 if (textNode) {\r
14094                                                                                         perform(node);\r
14095 \r
14096                                                                                         rng = dom.createRng();\r
14097                                                                                         rng.setStart(textNode, textNode.nodeValue.length);\r
14098                                                                                         rng.setEnd(textNode, textNode.nodeValue.length);\r
14099                                                                                         selection.setRng(rng);\r
14100                                                                                         ed.nodeChanged();\r
14101                                                                                 } else\r
14102                                                                                         dom.remove(node);\r
14103                                                                         }\r
14104                                                                 });\r
14105 \r
14106                                                                 // Always unbind and clear pending styles on keyup\r
14107                                                                 if (e.type == 'keyup' || e.type == 'mouseup')\r
14108                                                                         resetPending();\r
14109                                                         }\r
14110                                                 });\r
14111                                         });\r
14112                                 }\r
14113                         }\r
14114                 };\r
14115         };\r
14116 })(tinymce);\r
14117 \r
14118 tinymce.onAddEditor.add(function(tinymce, ed) {\r
14119         var filters, fontSizes, dom, settings = ed.settings;\r
14120 \r
14121         if (settings.inline_styles) {\r
14122                 fontSizes = tinymce.explode(settings.font_size_style_values);\r
14123 \r
14124                 function replaceWithSpan(node, styles) {\r
14125                         dom.replace(dom.create('span', {\r
14126                                 style : styles\r
14127                         }), node, 1);\r
14128                 };\r
14129 \r
14130                 filters = {\r
14131                         font : function(dom, node) {\r
14132                                 replaceWithSpan(node, {\r
14133                                         backgroundColor : node.style.backgroundColor,\r
14134                                         color : node.color,\r
14135                                         fontFamily : node.face,\r
14136                                         fontSize : fontSizes[parseInt(node.size) - 1]\r
14137                                 });\r
14138                         },\r
14139 \r
14140                         u : function(dom, node) {\r
14141                                 replaceWithSpan(node, {\r
14142                                         textDecoration : 'underline'\r
14143                                 });\r
14144                         },\r
14145 \r
14146                         strike : function(dom, node) {\r
14147                                 replaceWithSpan(node, {\r
14148                                         textDecoration : 'line-through'\r
14149                                 });\r
14150                         }\r
14151                 };\r
14152 \r
14153                 function convert(editor, params) {\r
14154                         dom = editor.dom;\r
14155 \r
14156                         if (settings.convert_fonts_to_spans) {\r
14157                                 tinymce.each(dom.select('font,u,strike', params.node), function(node) {\r
14158                                         filters[node.nodeName.toLowerCase()](ed.dom, node);\r
14159                                 });\r
14160                         }\r
14161                 };\r
14162 \r
14163                 ed.onPreProcess.add(convert);\r
14164 \r
14165                 ed.onInit.add(function() {\r
14166                         ed.selection.onSetContent.add(convert);\r
14167                 });\r
14168         }\r
14169 });\r
14170 \r